使用Asponse.Words處理Word模板
一.客戶需求
近期接到一個項目,在與客戶初步溝通後,客戶描述的需求聽起來也非常簡單,就是目前客戶需要在Excel錄入數據,然後把這些數據分別複製到多個Word的多個地方,除了單個值之外,還需要複製表格數據以及圖片數據,客戶覺得這麼操作一是容易出錯,二是重複操作的工作量太大。在初步了解用戶的需求之後,得知客戶需求的核心功能就是在Word中填充數據源,需要填充數據的數據源包括文字,表格以及圖片等,本文以此為出發點,重點介紹如何在Word中使用「域」來填充外部數據。
二.插件選型
目前處理Word等Office文檔類型的控制項,微軟自帶的Com組件問題實在太多,一個比較麻煩的問題就是部分Com組件需要註冊,並且如果客戶端與開發電腦使用的Com組件版本不一致,還會出現各種問題,因此選擇了Asponse.words插件,該軟體為收費插件,具體的收費標準可以在官網進行查看。
三.模板製作
利用Asponse進行數據填充,其實程式碼不難,核心的工作就是需要在Word模板中設置標記,也就是需要讓程式知道在什麼填充數據,目前做標記的方式有兩種,一種是使用書籤,一種是使用域。目前網上大多的案例都是使用書籤,但是在我實際開發的過程中,建議大家使用「域」,而不是使用書籤,原因有以下幾點:
1.書籤不能重複添加,比如我在第一個空白出需要插入數據源「姓名」,在最後簽名處還要插入「姓名」,這個時候使用「域」的話可以在兩處都插入「姓名」域,但是如果使用書籤,需要設置不同的書籤名稱,生成數據源的時候會增加工作量
2.書籤不能在文檔中直觀的展示出來,如果插入書籤比較多,很容易造成混亂
此外,需要特別說明的是,製作書籤或者「域"的時候務必務必使用Word而不是WPS製作,因為目前發現的一個問題就是,使用WPS製作的書籤或者是」域「,在填充數據之後會自動增加一行空行,這個可能是WPS與Word的內部機制不同;還有一個技巧,在文檔中使用Alt+F9,可以查看到模板的各種域程式碼,如下圖所示:

四.插入書籤/域
1.插入書籤

2.插入域(插入->文檔部件->域,選擇"MergeField")


五.實現程式碼
1.獲取文檔
Aspose.Words.Document doc = new Aspose.Words.Document(filePath);
Aspose.Words.DocumentBuilder builder = new Aspose.Words.DocumentBuilder(doc);
2.利用word模板填充數據
List<string> filedsValueList = new List<string>();
string[] filedsName =doc.MailMerge.GetFieldNames();//獲取文檔中所有的域
for (int i = 0; i < filedsName.Length; i++)
{
if (dataTable.Columns.Contains(filedsName[i]))
{
filedsValueList.Add(dataTable.Rows[0][filedsName[i]].ToString());
}
else
{
filedsValueList.Add("");
}
}
//核心程式碼其實就是這一句,上面的都是組織數據源
//參數是兩個數組,第一個數組是文檔中所有」域「的名稱,第二個是所對應域的值
doc.MailMerge.Execute(filedsName, filedsValueList.ToArray());
3.插入圖片
插入圖片並不需要特殊的程式碼,而是需要對Word模板中的域做特殊設置,設置的方式為:

在彈出來的對話框中,把」域「名稱的前面加上"Image:",如下圖所示:

設置好之後,只需要將該域的值設置為圖片的路徑,即可在文檔中展示圖片,如果需要在程式碼中控制圖片的大小,需要給文檔綁定圖片處理的回調事件,程式碼如下:
doc.MailMerge.FieldMergingCallback = new HandleMerFieldInsert();
4.設置圖片尺寸
在Asponse直接提供的方法中,沒有修改圖片尺寸的方法,但是可以重寫Asponse的IFieldMergingCallback類,用於設置圖片大小
class HandleMerFieldInsert : IFieldMergingCallback
{
//文本處理
void IFieldMergingCallback.FieldMerging(FieldMergingArgs e)
{
}
//圖片處理
void IFieldMergingCallback.ImageFieldMerging(ImageFieldMergingArgs args)
{
Document doc = args.Document;
DocumentBuilder builder = new DocumentBuilder(doc);
builder.MoveToMergeField(args.FieldName);
Shape shapeImage = builder.InsertImage(args.FieldValue.ToString());
//設置x,y坐標和高寬.
shapeImage .Left = 0;
shapeImage .Top = 0;
shapeImage .Width = 50;
shapeImage .Height = 25;
}
}
5.填充表格
填充表格數據,需要在模板中先設置表格綁定數據的起始位置,然後在每一列中設置數據源中的列名稱,具體步驟如下圖:

表格數據源的起始標記為」TableStart「,結束標記為"TableEnd",設置好表格數據源的起始與結束標記之後,再在每一列中單獨設置每一列需要綁定的欄位名稱即可,設置好的數據源如下圖所示:

通過Alt+F9可以看到設置好的各種域的程式碼,如下圖所示:

設置好表格的各個域之後,通過如下程式碼完成對數據源的填充:
dt.TableName = "UserInfo";//這裡的表格名稱必須和模板中表格的起始與結束源名稱相同
doc.MailMerge.ExecuteWithRegions(dt);
6.刪除表格及對應的佔位符
使用Asponse進行刪除的操作相對比較複雜,現有的業務是需要刪除模板中的某一個表格,但有個問題就是,在刪除了該表格之後,該表格所在頁面的佔位符還在,會導致生成的文檔有一個空白頁,因此在刪除類似表格,圖片這種元素時,還需要刪除對應的段落佔位符
NodeCollection nodeCollection = doc.GetChildNodes(NodeType.Table, true);//獲取所有表格
foreach (Table table in nodeCollection)
{
if (table.GetText().Contains("AAAAAA") && table.GetText().Contains("BBBBBB"))//To Do:這裡通過獲取表格中的內容來判定是否為刪除的表格,應該還有更好的辦法
{
Paragraph paragraph = (Paragraph)table.PreviousSibling;
paragraph.Remove();//刪除段落
table.Remove();//刪除表格
}
}
7.保存帶密碼的PDF
Aspose.Words.Saving.PdfSaveOptions saveOption = new Aspose.Words.Saving.PdfSaveOptions();
saveOption.SaveFormat = Aspose.Words.SaveFormat.Pdf;
PdfEncryptionDetails encryptionDetails = new PdfEncryptionDetails(pdfPwd, string.Empty, PdfEncryptionAlgorithm.RC4_128);
encryptionDetails.Permissions = PdfPermissions.AllowAll;
saveOption.EncryptionDetails = encryptionDetails;
doc.Save(fullfilepath + ".pdf", saveOption);
8.刪除未被賦值的「域」
doc.MailMerge.DeleteFields();
寫在最後
至此,Asponse利用模板處理數據的常規方法總結完畢,對於直接使用Asponse的方法,在Word中通過硬編碼的方式增加表格建議不到萬不得已不要使用,因為增加表格需要控制表格本身的樣式,還需要控制單元格的樣式,並且對於複雜的文檔,定位表格也會增加難度。

