關於Docx套版列印功能程式
11 |
前幾天又有網友問起【雛型】Docx套版列印功能試作的程式範例。
當時文章發佈後,網友ABC, alan也問過何時釋出的問題。當時的考量是,我完成的只是PoC(Proof of Concept),尚非經實務驗證可行的解決方案,況且當下專案正式上線在即(文章中寧可用較簡陋的1.0正式版,也不要用2.0 CTP可見一斑),很快就可以檢驗這個做法的彈性及可靠度,等通過考驗再公佈也不遲。無奈,世事多變化,研究出套版方法沒多久,User調整了作業流程規劃,系統不再需要自行套版產生docx... orz 而後來,在其他專案也沒能再遇到實際上場的機會。就這樣,多年下來,這套做法一直維持在PoC階段,只能終年長坐冷板凳,抑鬱不得志,每日藉酒澆愁...
適逢網友再問起,就決定把當時的PoC程式分享出來,供需要參考的朋友自行取用。但必須聲明,這個版本只是PoC,沒實際上過戰場,也跟懶人包不同,在程式考量周延性或完整度上可能仍有欠缺,大家在應用時可想像成是小麥種子,而不是烤好的麵包。也很歡迎有將它實際應用在專案上的朋友能回饋分享自己裁種、收割、烘焙的心得~
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DocumentFormat.OpenXml.Packaging;
using System.IO;
namespace Darkthread.OpenXml
{
/// <summary>
/// Ver 1.0 By Jeffrey Lee, 2009-07-29
/// </summary>
public class DocxHelper
{
/// <summary>
/// Replace the parser tags in docx document
/// </summary>
/// <param name="oxp">OpenXmlPart object</param>
/// <param name="dct">Dictionary contains parser tags to replace</param>
private static void parse(OpenXmlPart oxp, Dictionary<string, string> dct)
{
string xmlString = null;
using (StreamReader sr = new StreamReader(oxp.GetStream()))
{ xmlString = sr.ReadToEnd(); }
foreach (string key in dct.Keys)
xmlString = xmlString.Replace("[$" + key + "$]", dct[key]);
using (StreamWriter sw = new StreamWriter(oxp.GetStream(FileMode.Create)))
{ sw.Write(xmlString); }
}
/// <summary>
/// Parse template file and replace all parser tags, return the binary content of
/// new docx file.
/// </summary>
/// <param name="templateFile">template file path</param>
/// <param name="dct">a Dictionary containing parser tags and values</param>
/// <returns></returns>
public static byte[] MakeDocx(string templateFile, Dictionary<string, string> dct)
{
string tempFile = Path.GetTempPath() + ".docx";
File.Copy(templateFile, tempFile);
using (WordprocessingDocument wd = WordprocessingDocument.Open(
tempFile, true))
{
//Replace document body
parse(wd.MainDocumentPart, dct);
foreach (HeaderPart hp in wd.MainDocumentPart.HeaderParts)
parse(hp, dct);
foreach (FooterPart fp in wd.MainDocumentPart.FooterParts)
parse(fp, dct);
}
byte[] buff = File.ReadAllBytes(tempFile);
File.Delete(tempFile);
return buff;
}
}
}
PS: 雖然當時程式是配合SDK 1.0寫的,經實測這段程式可配合Open XML Format SDK 2.0運作無誤,專案要參照DocumentFormat.OpenXml以及WindowsBase,即可順利編譯、執行。
Comments
# by A*lan~
黑大響應Open Source的精神,相信一定有神人幫套版程式發揚光大。
# by Geng
感謝黑大的資訊提供,使用在Word套版上很便利,深深感謝你的分享。
# by jason
請問一下 如果有圖片,要怎麼堪入 word
# by Tom
剛玩了一下 Open XML Format SDK 2.5 可運作 不過文字取代那邊[]被其他代碼取代 只用$$是可行的 給黑大按個讚
# by Rola
To Tom: 我是取用黑大部分的程式碼(主要是[$...$]這用法很方便又不會衝突) 今天測試2.5版的依舊正常 你看一下原檔的xml是否有[$...$]不連續?
# by ken
請問要怎麼把圖片放復word檔
# by Jeffrey
to ken, 可透過OpenXML SDK達成,參考:https://msdn.microsoft.com/en-us/library/ee342530(v=office.12).aspx
# by Zach Lai
Hi 黑大, 依照這一篇實作後, 遇到了問題想跟您分享一下, 希望後面的人看到也可以避開這個問題 如果今天是很單純的套表, 不需要自己增加段落之類的, 利用replace的確是可以達到置換的功能, 但如果有自己增加段落, 此時一開始的XML跟增加段落後的XML貌似是兩個獨立的各體, 透過監看可以看到replace是有成功的, 但最後出來只有增加段落後的結果, 自訂的關鍵字不會被替換 以上提供給黑大
# by Jeffrey
to Zach Lai, 實務上我有遇過Tag看似一體在XML中卻是由多個元素組成的狀況,此時就會置換失敗。我用的技巧是將Tag加上顯目提示色彩(確保Tag在同一個XML元素內),換置時再將顯示提示樣式移除,提供你參考。
# by CHONGMAN
師兄你好呀, 我發現你個程式有個BUG, 就是內容是"<" 這個符號, 不能匯出WORD, 可否幫忙解決一下。謝謝!
# by Jeffrey
to CHONGMAN,更換 XML 是我很早期的做法,問題不少,建議改用新做法 https://blog.darkthread.net/blog/word-template-rendering-with-openxml-sdk/