用Visual C++增強(qiáng)Notes打印技巧
發(fā)表時(shí)間:2024-02-17 來(lái)源:明輝站整理相關(guān)軟件相關(guān)文章人氣:
[摘要]Lotus公司推出的Lotus Domino/Notes作為辦公自動(dòng)化系統(tǒng)的平臺(tái)近年來(lái)在國(guó)內(nèi)得到了廣泛的應(yīng)用,許多的政府主管部門、金融單位、企事業(yè)單位都使用了Notes以及在Notes上開發(fā)的各種辦公系統(tǒng),工作效率得到了極大的提高! ≡趯(shí)際的應(yīng)用中,為了存檔以及供沒(méi)安裝Notes系統(tǒng)的部門傳閱,...
Lotus公司推出的Lotus Domino/Notes作為辦公自動(dòng)化系統(tǒng)的平臺(tái)近年來(lái)在國(guó)內(nèi)得到了廣泛的應(yīng)用,許多的政府主管部門、金融單位、企事業(yè)單位都使用了Notes以及在Notes上開發(fā)的各種辦公系統(tǒng),工作效率得到了極大的提高。
在實(shí)際的應(yīng)用中,為了存檔以及供沒(méi)安裝Notes系統(tǒng)的部門傳閱,許多在Notes系統(tǒng)中流轉(zhuǎn)的電子文檔需要打印出來(lái)。不幸的是,Notes提供的打印功能很弱,一個(gè)文檔只能按照給定表單的版式進(jìn)行打印。但在實(shí)際的使用中,如政府部門,內(nèi)容相同的一個(gè)文檔,其上行公文和下行公文的版式是不一樣的,這就需要將同一文檔用多種樣式打印。最直接的想法當(dāng)然是在Designer中修改表單的版式,但由于應(yīng)用系統(tǒng)一般是隱藏設(shè)計(jì)的,表單無(wú)法修改。還有就是最終用戶的計(jì)算機(jī)水平有限,直接修改表單從技術(shù)上講也行不通。
這時(shí)一個(gè)可行的做法就是:用VC++給用戶提供一個(gè)"所見(jiàn)即所得"的編輯界面,并列出Notes文檔中各部分的內(nèi)容,讓用戶以拖放的方式將相關(guān)內(nèi)容放到適當(dāng)?shù)奈恢蒙,同時(shí)還可以加入文字、圖片等修飾內(nèi)容,然后按照最終的版式在Notes外部直接生成一個(gè)Notes表單,并用此表單進(jìn)行打印。這種方法既繞過(guò)了隱藏設(shè)計(jì)的障礙,又降低了對(duì)最終用戶的技術(shù)要求。當(dāng)然這一切都得益于Notes提供的API函數(shù)。
由于只需一個(gè)NSFItemScan函數(shù)就能收集到Notes文檔中所有的域,而又有多種靈活的方式實(shí)現(xiàn)"所見(jiàn)即所得"的排版功能,因此在提出上述的思路后,本文將主要介紹如何構(gòu)造Notes表單。
一 Notes表單結(jié)構(gòu)簡(jiǎn)介
一個(gè)表單中有三個(gè)必需的域:$TITLE、$INFO和$BODY,輔助性的還有$FIELDS域及屬性為placeholder的各域。
1.$TITLE域
$TITLE域的類型為TYPE_TEXT,其中保存表單的名稱,Notes客戶端窗口中"創(chuàng)建"菜單下列出的各表單名即為各表單note中$TITLE域的值。在Notes提供的C API頭文件"stdnames.h"中有預(yù)定義的常量ITEM_NAME_TEMPLATE_NAME代表表單note的名稱域,為保證程序的向后兼容,建議使用常量而避免直接使用$TITLE。
2.$INFO域
由于表單和文檔的創(chuàng)建有關(guān),$INFO域定義了通過(guò)此表單創(chuàng)建的文檔的一些屬性。實(shí)際上$INFO域中存儲(chǔ)的是一個(gè)名為CDDOCUMENT的結(jié)構(gòu)體,對(duì)生成文檔屬性的設(shè)定就是通過(guò)對(duì)該結(jié)構(gòu)體中各分量的不同賦值實(shí)現(xiàn)的。結(jié)構(gòu)體CDDOCUMENT 的定義及說(shuō)明見(jiàn)Lotus C API 的參考文檔。
$INFO域的類型為TYPE_COMPOSITE,對(duì)應(yīng)的預(yù)定義常量為ITEM_NAME_DOCUMENT。
3. $BODY域
$BODY域是表單note中的核心域,整個(gè)表單顯示和打印時(shí)的格式,還有通過(guò)此表單生成的文檔所包含的域及其類型,都是在本域中定義的。由于$BODY域的結(jié)構(gòu)非常復(fù)雜,本文將在第二部分專門介紹。$BODY域也是TYPE_COMPOSITE類型的,名稱預(yù)定義常量為ITEM_NAME_TEMPLATE。
4. $FIELDS域
$FIELDS域是一個(gè)TYPE_TEXT_LIST類型的域,其中包含了用此表單生成的文檔包含的所有域。但專為打印生成的表單中可以沒(méi)有此域。
5. "placeholder"域
對(duì)$BODY域中定義的將來(lái)文檔中要含有的每一個(gè)域,在表單中都對(duì)應(yīng)一個(gè)類型為TYPE_INVALID_OR_UNKNOWN而標(biāo)志為ITEM_PLACEHOLDER的域,域名和$BODY域中定義的一樣,而其值為NULL。
標(biāo)志為ITEM_PLACEHOLDER的域?qū)⒈患尤氲?quot;域名表"中,這樣當(dāng)用戶選擇了客戶端中的"設(shè)計(jì)"菜單中的"視圖"子菜單后,在彈出的對(duì)話框中選擇"添加域"時(shí),該域名才會(huì)被顯示出來(lái)。
同樣,這些域在打印的表單中不是必需的。
二 $BODY域詳解
$BODY域中可以包含各種Notes對(duì)象,如文本、域、圖像、熱點(diǎn)、鏈接等,還有一些輔助性對(duì)象,如段定義、段引用等。為方便管理,所有這些對(duì)象的定義都是通過(guò)不同的結(jié)構(gòu)體實(shí)現(xiàn)的。Notes中定義對(duì)象的結(jié)構(gòu)體都以"CD"開頭,如CDTEXT定義靜態(tài)文本、CDFIELD定義域等,其他對(duì)象的具體定義請(qǐng)查閱Lotus C API 的參考文檔。
通常,一個(gè)$BODY域的整體結(jié)構(gòu)是這樣的:
CDPABDEFINITION
CDPABDEFINITION
...
CDPARAGRAPH
CDPABREFERENCE
CDTEXT
text
...
CDPARAGRAPH
CDPABREFERENCE
CDBEGINRECORD
CDFIELD
CDBEGINRECORD
...
下面對(duì)其中的各部分分別予以說(shuō)明。
1.段落預(yù)定義部分
CDPABDEFINITION定義頁(yè)面上一個(gè)段落的屬性,在這個(gè)結(jié)構(gòu)體中我們可以定義段落的對(duì)齊方式、頁(yè)邊距、段間距、行間距等。在后面的某個(gè)具體段落中,如果定義了到此段定義的引用,則該段落就具有了此處定義的各屬性。
段落的定義可以放在$BODY域的開頭,也可以放在中間,只要保證序號(hào)PABID不重復(fù)就可以了。
2.靜態(tài)文本的定義
上述總體結(jié)構(gòu)的中間部分定義了一段文本:CDPARAGRAPH定義一段的開始,類似文本串中的一個(gè)回車換行符;CDPABREFERENCE定義一個(gè)到段定義的引用,從而本段就具有了前面定義的各種屬性;CDTEXT是文本的頭部,包含有文本的長(zhǎng)度、字體、顏色等信息;text是實(shí)際的文本。
3.域的定義
對(duì)域的定義也是以CDPARAGRAPH和CDPABREFERENCE開始,但與文本不同的是,像域、圖像等對(duì)象的定義,除了有作為頭部的結(jié)構(gòu)體外,還要有一對(duì)界定結(jié)構(gòu)體CDBEGINRECORD和CDENDRECORD放在對(duì)象定義的前后兩端。
有時(shí)在域的前面還要有一些提示性文字,如一個(gè)用于接收姓名的域name,通常在其前面要有"姓名"兩個(gè)字,以便具體操作者知道此處要輸入姓名。具體創(chuàng)建域時(shí),這部分內(nèi)容以文本形式放在CDBEGINRECORD之前,格式如上一步中所述。
在貨幣型或數(shù)值型的域中,為了對(duì)數(shù)據(jù)的格式進(jìn)行更進(jìn)一步的控制,在CDBEGINRECORD和CDFIELD中間還要插入一個(gè)CDEXT2FIELD結(jié)構(gòu),該結(jié)構(gòu)提供了附加的格式定義。
域中的其他元素,如默認(rèn)值計(jì)算公式、輸入變換公式、域名、描述字串等放在CDFIELD后面,排列順序和其長(zhǎng)度值在CDFIELD結(jié)構(gòu)體中的位置順序一致。當(dāng)然除域名外,其他元素如不是必要可以省略。
在本部分中,以文本和域?yàn)槔,介紹了$BODY域中各對(duì)象的具體定義方式,其他對(duì)象與此類似。
三 創(chuàng)建Notes表單
在了解了Notes表單結(jié)構(gòu)的基礎(chǔ)上,通過(guò)API函數(shù)建立表單就很容易了。
首先打開一個(gè)數(shù)據(jù)庫(kù),然后在其中新建一個(gè)空白note,接下來(lái)就可以向其中添加各域了。像$TITLE這樣的單一類型的域,可以直接調(diào)用NSFItemSetText函數(shù)創(chuàng)建。而像$INFO和$BODY這樣的復(fù)合類型的域,就比較麻煩一些。通常的做法是,先申請(qǐng)一塊足夠大的內(nèi)存,然后順序?qū)懭敫鞑糠謨?nèi)容,最后調(diào)用NSFItemAppend函數(shù)創(chuàng)建域。
在向復(fù)合域中寫入數(shù)據(jù)時(shí),文本、域名等一般字符串可以直接寫入,而各種結(jié)構(gòu)體需調(diào)用ODSWriteMemory函數(shù)以Domino規(guī)范的形式寫入,另外就是域定義中用到的各種公式,在寫入前要經(jīng)過(guò)NSFFormulaCompile變換。
四 例程
下面的程序段定義了一個(gè)帶有默認(rèn)值公式的名為"TextField"的域:
char TextFieldName[] = "TextField";
char TextDescription[] = "This is a Simple Text Field";
char TextDefValFormula[] = "\"Default\"";
char far *pBufferStart, far *pBuffer;
HANDLE hMem;
CDPABREFERENCE CDPabRef;
CDPARAGRAPH CDPara;
CDBEGINRECORD CDBegin;
CDENDRECORD CDEnd;
CDEXT2FIELD CDExt2Field;
CDFIELD CDField;
FONTIDFIELDS *pFontFields;
// 申請(qǐng)內(nèi)存并鎖定內(nèi)存,獲得指向該塊內(nèi)存的指針
OSMemAlloc (0, wCDBufferLength, &hMem);
pBufferStart = (char far *)OSLockObject(hMem);
memset( pBufferStart, 0, (size_t) wCDBufferLength );
pBuffer = pBufferStart;
// 填寫 PARAGRAPH 結(jié)構(gòu)
// 結(jié)構(gòu)體的長(zhǎng)度
CDPara.Header.Length = (BYTE) ODSLength(_CDPARAGRAPH);
// 結(jié)構(gòu)體的類型
CDPara.Header.Signature = (BYTE)SIG_CD_PARAGRAPH;
// 轉(zhuǎn)換為Domino規(guī)范的形式寫入申請(qǐng)的內(nèi)存
ODSWriteMemory( (void far * far *)&pBuffer, _CDPARAGRAPH, &CDPara, 1 );
// 填寫 PABREF 結(jié)構(gòu)
CDPabRef.Header.Signature = (BYTE)SIG_CD_PABREFERENCE;
CDPabRef.Header.Length = (BYTE) ODSLength(_CDPABREFERENCE);
// 要引用的段定義的序號(hào)
CDPabRef.PABID = wPabDefNumber;
ODSWriteMemory( (void far * far *)&pBuffer, _CDPABREFERENCE, &CDPabRef, 1 );
// 填寫CDBEGINRECORD 結(jié)構(gòu)
CDBegin.Header.Length = (BYTE)ODSLength(_CDBEGINRECORD);
CDBegin.Header.Signature = SIG_CD_BEGIN;
CDBegin.Version = 0;
CDBegin.Signature = SIG_CD_FIELD;
ODSWriteMemory( (void far * far *)&pBuffer, _CDBEGINRECORD,(void far *) &CDBegin, 1 );
// 填寫CDEXT2FIELD 結(jié)構(gòu)
memset(&CDExt2Field, 0, sizeof(CDEXT2FIELD));
CDExt2Field.Header.Length = (WORD)ODSLength(_CDEXT2FIELD);
CDExt2Field.Header.Signature = SIG_CD_EXT2_FIELD;
ODSWriteMemory( (void far * far *)&pBuffer, _CDEXT2FIELD, (void far *) &CDExt2Field, 1 );
// 填寫CDFIELD 結(jié)構(gòu),定義文本域
CDField.Header.Signature = SIG_CD_FIELD;
CDField.Flags = FEDITABLE;
CDField.DataType = TYPE_TEXT;
CDField.ListDelim = LDDELIM_SEMICOLON;
// 本域中不用數(shù)值格式參數(shù),全部清零
CDField.NumberFormat.Digits = 0;
CDField.NumberFormat.Format = 0;
CDField.NumberFormat.Attributes = 0;
CDField.NumberFormat.Unused = 0;
file://本域中不用時(shí)間格式參數(shù),全部清零
CDField.TimeFormat.Date = 0;
CDField.TimeFormat.Time = 0;
CDField.TimeFormat.Zone = 0;
CDField.TimeFormat.Structure = 0;
// 設(shè)定FontID
pFontFields = (FONTIDFIELDS *)&CDField.FontID;
pFontFields->Face = FONT_FACE_ROMAN;
pFontFields->Attrib = 0;
pFontFields->Color = NOTES_COLOR_BLACK;
pFontFields->PointSize = 14;
// 編譯默認(rèn)值公式
NSFFormulaCompile(NULL, 0, TextDefValFormula, (WORD) strlen(TextDefValFormula), &hTextDefValFormula, &wTextDefValFormulaLen, &wdc, &wdc, &wdc, &wdc, &wdc))
// 填寫CDFIELD 結(jié)構(gòu)的其余部分,因?yàn)镈VLength值只有公式編譯后才知道
CDField.DVLength = wTextDefValFormulaLen;
CDField.ITLength = 0;
CDField.TabOrder = 0;
CDField.IVLength = 0;
CDField.NameLength = strlen(TextFieldName);
CDField.DescLength = strlen(TextDescription);
CDField.TextValueLength = 0;
CDField.Header.Length = ODSLength(_CDFIELD) +CDField.DVLength +CDField.ITLength +CDField.IVLength +CDField.NameLength +CDField.DescLength +CDField.TextValueLength;
// 保證CDFIELD域長(zhǎng)度為偶數(shù)
if (CDField.Header.Length % 2)
CDField.Header.Length++;
ODSWriteMemory( (void far * far *)&pBuffer, _CDFIELD, (void far *)&CDField, 1 );
// 獲取指向編譯后公式的指針
pTextDefValFormula = OSLock( char, hTextDefValFormula );
// 寫入公式內(nèi)容到內(nèi)存
memcpy( pBuffer, pTextDefValFormula, wTextDefValFormulaLen );
pBuffer += CDField.DVLength;
// 解鎖并釋放公式占用的空間
OSUnlockObject(hTextDefValFormula);
OSMemFree(hTextDefValFormula);
// 域名部分,直接寫入
memcpy( pBuffer, TextFieldName, CDField.NameLength );
pBuffer += CDField.NameLength;
// 域描述部分,直接寫入
memcpy( pBuffer, TextDescription, CDField.DescLength );
pBuffer += CDField.DescLength;
// 保證整個(gè)域定義的長(zhǎng)度為偶數(shù)
if ((pBuffer-pBufferStart) %2)
pBuffer++;
// 填寫CDENDRECORD結(jié)構(gòu)
CDEnd.Header.Length = (BYTE)ODSLength(_CDENDRECORD);
CDEnd.Header.Signature = SIG_CD_END;
CDEnd.Version = 0;
CDEnd.Signature = SIG_CD_FIELD;
ODSWriteMemory( (void far * far *)&pBuffer, _CDENDRECORD, (void far *) &CDEnd, 1 );