天天看點

Web應用Word生成

    前段時間接到一個web應用自動生成word的需求,現整理了下一些關鍵步驟拿來分享一下。

思路:(注:這裡隻針對word2003版本,其它版本大同小異。)

因為word檔案内部的資料及格式等是通過xml檔案的形式存儲的,是以word檔案可以很友善的實作由doc到xml格式的互相轉換,而操作xml檔案就友善的多了,這樣就實作了與平台無關的各種操作,通過節點的查詢、替換、删除、新增等生成word檔案。是以,根據模闆生成word檔案實質就是由使用者資料替換xml檔案中特殊标簽,然後另存為一個doc檔案的過程。

下面列舉涉及到的一些關鍵步驟(以介紹信為例)

第一步:根據需求制作word模闆

建立一個doc格式的word檔案,根據需要填寫好模闆内容,設定好模闆的格式,包括字型,樣式,空行等等,需要填充的資料使用特殊标簽(如:【※機關名稱※】)預先占位,然後将建立的word檔案另存為xml格式檔案。這樣, word模闆就制作完成了,代碼如下:

Web應用Word生成

第二步:在配置檔案中配置好模闆資訊

新增名為template-rule.xml的配置檔案,每個template節點對應一個模闆類型。每個template中有一個taglist節點,該節點包含的所有子節點包含了模闆所有将要替換、删除節點資訊,節點資訊包括:節點值,節點屬性英文名稱,中文描述,字段類型,可否删除等資訊。在設定這個配置檔案時候,需要注意desc屬性的值必須與模闆xml中的占位符一緻。比如:模闆xml中設定的年份這個錄入項【※年※】需與template-rule.xml中的desc="年"名稱對應,代碼如下:

Web應用Word生成

<?xml version="1.0" encoding="gb2312"?>  

<!-- 模闆定義 -->  

<templates>  

    <!-- 說明: s-字元串; d-日期; e-金額; m-大寫金額; ifemptydelete: t-值為空删除父節點,預設為f -->  

    <template name="recommend-letter" desc="介紹信" templatefile="template4.xml">  

        <taglist remark="單值标簽清單">  

            <tag id="1" name="topartment" desc="接收部門" type="s" ifemptydelete="t">#topartment</tag><!--接收部門-->  

            <tag id="2" name="ownername" desc="姓名" type="s">#ownername</tag><!--姓名-->  

            <tag id="3" name="countnum" desc="人數" type="s">#countnum</tag><!--人數-->  

            <tag id="4" name="business" desc="内容" type="s">#business</tag><!--内容-->  

            <tag id="5" name="usefuldays" desc="有效期" type="s">#usefuldays</tag><!--有效期-->  

            <tag id="6" name="year" desc="年" type="s">#year</tag><!--年-->  

            <tag id="7" name="month" desc="月" type="s">#month</tag><!--月-->  

            <tag id="8" name="day" desc="日" type="s">#day</tag><!--日-->  

        </taglist>  

    </template>  

</templates>  

第三步:編寫java代碼

Web應用Word生成

/** 

 * 參數及規則 

 */  

public class ruledto {  

    /** 

     * tag名稱 

     */  

    private string parmname;  

     * tag描述 

    private string parmdesc;  

     * tag序号 

    private string parmseq;  

     * tag值類型 

    private string parmtype;  

     * tag參數名稱 

    private string parmregular;  

     * tag值 

    private string parmvalue;  

     * tag值為空删除該屬性 

    private string ifemptydelete;  

Web應用Word生成

 * 描述: word模闆資訊 

public class template {  

    private string name;//模闆名  

    private string desc;//模闆描述  

    private string templatefile;//模闆檔案  

    private vector<ruledto> rules;//模闆規則  

}  

Web應用Word生成

public class wordbuilder {  

     * 根據模闆讀取替換規則 

     * @param templatename  模闆id 

    @suppresswarnings("unchecked")  

    public template loadrules(map<string, string> rulevalue) {  

        inputstream in = null;  

        template template = new template();  

        // 規則配置檔案路徑  

        string rulefile = "template-rule.xml";  

        // 模闆規則名稱  

        string templaterulename = "";  

        try {  

            templaterulename = rulevalue.get("rulename");  

            // 讀取模闆規則檔案  

            in = this.getclass().getclassloader().getresourceasstream(rulefile);  

            // 解析模闆規則  

            saxbuilder sb = new saxbuilder();  

            document doc = sb.build(in);  

            element root = doc.getrootelement(); // 得到根元素  

            list<element> templatelist = root.getchildren();// 所有模闆配置  

            element element = null;  

            vector<ruledto> rules = null;  

            for (int i = 0; i < templatelist.size(); i++) {// 周遊所有模闆  

                element = (element) templatelist.get(i);  

                string templatename = element.getattributevalue("name");  

                if (templaterulename.equalsignorecase(templatename)) {// 查找給定的模闆配置  

                    template.setname(templatename);  

                    template.setdesc(element.getattributevalue("desc"));  

                    template.settemplatefile(element  

                            .getattributevalue("templatefile"));  

                    list<element> taglist = ((element) element.getchildren()  

                            .get(0)).getchildren();// tag清單  

                    element tag = null;  

                    ruledto ruledto = null;  

                    rules = new vector<ruledto>();  

                    for (int j = 0; j < taglist.size(); j++) {  

                        tag = (element) taglist.get(j);  

                        ruledto = new ruledto();  

                        ruledto.setparmname(tag.getattributevalue("name"));  

                        ruledto.setparmdesc("【※"  

                                + tag.getattributevalue("desc") + "※】");  

                        ruledto.setparmseq(tag.getattributevalue("id"));  

                        ruledto.setparmtype(tag.getattributevalue("type"));  

                        if ("t".equalsignorecase(tag  

                                .getattributevalue("ifemptydelete"))) {// 是否可删除标記  

                            ruledto.setifemptydelete("t");  

                        } else {  

                            ruledto.setifemptydelete("f");  

                        }  

                        ruledto.setparmregular(tag.gettext());  

                        // 值  

                        // 判斷參數類型  

                        string value = (string) ((map<string, string>) rulevalue)  

                                .get(ruledto.getparmregular().replaceall("#",  

                                        ""));  

                        ruledto.setparmvalue(value);  

                        rules.add(ruledto);  

                    }  

                    template.setrules(rules);  

                    break;  

                }  

            }  

        } catch (filenotfoundexception e) {  

            e.printstacktrace();  

        } catch (jdomexception e) {  

        } catch (ioexception e) {  

        } finally {  

            try {  

                in.close();  

            } catch (exception e) {  

                e.printstacktrace();  

        }  

        return template;  

    }  

     * 查找父節點 

    public element findelement(element currnode, string parentnodeid) {  

        // 節點标示為空  

        if (currnode == null || parentnodeid == null) {  

            return null;  

        element pnode = null;  

        do {  

            pnode = currnode.getparent();  

            currnode = pnode;  

        } while (parentnodeid.equalsignorecase(pnode.getname()));  

        return pnode;  

     * 生成word檔案 

    public string build(template template) {  

        outputstream fo = null;  

        // 生成檔案的路徑  

        string file = "d:\\test\\" + template.getdesc() + ".doc";  

            // 讀取模闆檔案  

            in = this.getclass().getclassloader()  

                    .getresourceasstream(template.gettemplatefile());  

            namespace ns = root.getnamespace();// namespace  

            // word 03模闆存在<wx:sect>元素  

            list<element> sectlist = root.getchild("body", ns).getchildren();  

            element sectelement = (element) sectlist.get(0);  

            // <w:p>下的标簽集合  

            list<element> ptaglist = sectelement.getchildren("p", ns);  

            // <w:tbl>下的标簽集合  

            list<element> tbltaglist = sectelement.getchildren("tbl", ns);  

            if (ptaglist != null && ptaglist.size() > 0) {  

                changevalue4ptag(ptaglist, template.getrules(), ns, null);  

            if (tbltaglist != null && tbltaglist.size() > 0) {  

                changevalue4tbltag(tbltaglist, template.getrules(), ns);  

            // 寫檔案  

            xmloutputter outp = new xmloutputter(" ", true, "utf-8");  

            fo = new fileoutputstream(file);  

            outp.output(doc, fo);  

                fo.close();  

        return file;  

     * 針對<w:body><wx:sect><w:p>這種層級的word模闆, 查找及替換<w:p>下的标簽。 

     * @param ptaglist :<w:p>集合 

     * @param rulesvalue :ruledto集合 

     * @param ns :namespace對象 

     * @param trchildren :<w:tbl>的子節點<w:tr>集合 

    private boolean changevalue4ptag(list<element> ptaglist,  

            vector<ruledto> rulesvalue, namespace ns, list<element> trchildren) {  

        element p = null;  

        boolean delflag = false;  

        for (int i = 0; i < ptaglist.size(); i++) {  

            boolean delcurrnode = false;// 删除目前節點  

            boolean delcurrnode4tabwr = false;// 删除table中單行節點  

            p = (element) ptaglist.get(i);  

            list<element> pchild = p.getchildren("r", ns);  

            for (int j = 0; pchild != null && j < pchild.size(); j++) {  

                element pchildren = (element) pchild.get(j);  

                element t = pchildren.getchild("t", ns);  

                if (t != null) {  

                    string text = t.gettexttrim();  

                    if (text.indexof("【※") != -1) {  

                        for (int v = 0; v < rulesvalue.size(); v++) {  

                            ruledto dto = (ruledto) rulesvalue.get(v);  

                            if (text.indexof(dto.getparmdesc().trim()) != -1) {  

                                // 判斷屬性值是否為可空删除  

                                if ("t".equals(dto.getifemptydelete())  

                                        && stringutils.isblank(dto  

                                                .getparmvalue())) {  

                                    // 删除該節點頂級節點  

                                    text = "";  

                                    if (trchildren != null) {// 針對<w:tbl>删除該行  

                                        element element = ((element) p  

                                                .getparent()).getparent();  

                                        trchildren.remove(element);  

                                        delcurrnode4tabwr = true;  

                                    } else {// 針對<w:r>删除段  

                                            // ptaglist.remove(p);  

                                        ptaglist.remove(pchildren);  

                                        delcurrnode = true;  

                                    }  

                                    break;  

                                } else {  

                                    text = text.replaceall(dto.getparmdesc()  

                                            .trim(), dto.getparmvalue());  

                                }  

                            }  

                        t.settext(text);  

                    if (delcurrnode4tabwr) {// <w:tbl>table下的行節點已删除  

                        delflag = true;  

                        break;  

                    } else if (delcurrnode) {// <w:p>下的節點已删除  

                        i--;  

        return delflag;  

     * 針對含有表格的word模闆, 查找及替換<w:tbl>下的标簽。 

     * @param tbltaglist :<w:tbl>集合 

    private void changevalue4tbltag(list<element> tbltaglist,  

            vector<ruledto> rulesvalue, namespace ns) {  

        for (int i = 0; tbltaglist != null && i < tbltaglist.size(); i++) {  

            p = (element) tbltaglist.get(i);  

            list<element> trchildren = p.getchildren("tr", ns);  

            for (int j = 0; trchildren != null && j < trchildren.size(); j++) {// 循環<w:tr>  

                element pchildren = (element) trchildren.get(j);  

                list<element> tctaglist = pchildren.getchildren("tc", ns);  

                for (int c = 0; tctaglist != null && c < tctaglist.size(); c++) {// 循環<w:tc>取<w:p>集合  

                    element tcchildren = (element) tctaglist.get(c);  

                    list<element> ptaglist = tcchildren.getchildren("p", ns);  

                    boolean delflag = changevalue4ptag(ptaglist, rulesvalue,  

                            ns, trchildren);  

                    if (delflag) {// 删除行後需要改變trchildren的指針位置  

                        j--;  

    public static void main(string[] args) throws exception {  

        wordbuilder word = new wordbuilder();  

        map<string, string> map = new hashmap<string, string>();  

        //填充參數  

        map.put("topartment", "xxx公司");  

        map.put("ownername", "張三");  

        map.put("countnum", "5");  

        map.put("business", "例行檢查");  

        map.put("usefuldays", "15");  

        map.put("year", "2014");  

        map.put("month", "5");  

        map.put("day", "13");  

        map.put("rulename", "recommend-letter");  

        template template = word.loadrules(map);  

        //直接打開檔案  

        runtime.getruntime().exec("explorer " + word.build(template));  

 第四步:大功告成

幾點總結及注意事項:

1.  定義的元素name必須與template_rule.xml中對應相同的name的值一緻,否則需要設定轉換規則。

2.  模闆xml中定義的占位符【※※】中的文字必須與template_rule.xml中對應的desc相同,否則需要設定轉換規則.

3.  在配置好模闆xml後,需要檢查<w:body>标簽下的子節點是否是<wx:sect>标簽(與word版本有關),如果沒有,則必須加上該标簽。

4.  如果要動态删除<w:p>标簽節點,則這個節點的内容需要在模闆中的同一行,如果不是,則可以手動調整模闆xml。

5.  如果需要實作word自動換行功能(關于模闆中換行的方案暫沒有想到更好的),則需要首先計算出對應模闆該行的字數,然後采用空格填充來實作。