天天看点

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自动换行功能(关于模板中换行的方案暂没有想到更好的),则需要首先计算出对应模板该行的字数,然后采用空格填充来实现。