Some time ago, I received a request for a web application to automatically generate Word. Now I have compiled some key steps to share.
Idea: (Note: This is only for the WORD2003 version, other versions are similar.)
Because the internal data and format of WORD files are stored in the form of XML files, WORD files can easily convert from DOC to XML format, and it is much more convenient to operate XML files, thus realizing the integration with Various platform-independent operations generate Word files through node query, replacement, deletion, addition, etc. Therefore, the essence of generating a WORD file based on a template is the process of replacing the special tags in the XML file with user data and then saving it as a DOC file.
Here are some of the key steps involved (taking a letter of introduction as an example)
Step one: Make a WORD template according to your needs
Create a new WORD file in DOC format, fill in the template content as needed, and set the format of the template, including fonts, styles, blank lines, etc. Use special tags (such as: [※Unit Name※]) to pre-occupy the data that needs to be filled. bit, and then save the newly created WORD file as an XML format file. In this way, the WORD template is completed, the code is as follows:
Add a new configuration file named template-rule.xml, each template node corresponds to a template type. There is a taglist node in each template. All child nodes contained in this node contain information about all nodes that will be replaced or deleted in the template. The node information includes: node value, node attribute English name, Chinese description, field type, whether it can be deleted, etc. When setting this configuration file, you need to note that the value of the desc attribute must be consistent with the placeholder in the template XML. For example: the year input item [※Year※] set in the template XML needs to correspond to the desc="Year" name in template-rule.xml. The code is as follows:
Copy the code code as follows:
<!--?xml version="1.0" encoding="GB2312"?-->
<!-- Template definition-->
<templates>
<!-- Description: S-string; D-date; E-amount; M-uppercase amount; ifEmptyDelete: T-value is empty to delete the parent node, the default is F -->
<template name="RECOMMEND-LETTER" desc="Letter of Introduction" templatefile="template4.xml">
<taglist remark="Single value tag list">
<tag id="1" name="ToPartment" desc="Receiving Department" type="S" ifemptydelete="T">#ToPartment</tag><!--Receiving Department-->
<tag id="2" name="OwnerName" desc="Name" type="S">#OwnerName</tag><!--Name-->
<tag id="3" name="CountNum" desc="Number of people" type="S">#CountNum</tag><!--Number of people-->
<tag id="4" name="Business" desc="Content" type="S">#Business</tag><!--Content-->
<tag id="5" name="UsefulDays" desc="Validity period" type="S">#UsefulDays</tag><!--Validity period-->
<tag id="6" name="Year" desc="year" type="S">#Year</tag><!--year-->
<tag id="7" name="Month" desc="month" type="S">#Month</tag><!--month-->
<tag id="8" name="Day" desc="日" type="S">#Day</tag><!--Day-->
</taglist>
</template>
</templates>
Step 3: Write java code
Copy the code code as follows:
/**
* Parameters and rules
*/
public class RuleDTO {
/**
* tag name
*/
private String parmName;
/**
* tag description
*/
private String parmDesc;
/**
* tag serial number
*/
private String parmSeq;
/**
* tag value type
*/
private String parmType;
/**
* tag parameter name
*/
private String parmRegular;
/**
* tag value
*/
private String parmValue;
/**
* If the tag value is empty, delete this attribute
*/
private String ifEmptyDelete;
}
Copy the code code as follows:
/**
* Description: Word template information
*/
public class Template {
private String name;//template name
private String desc;//template description
private String templateFile;//template file
private Vector<ruledto> rules;//template rules
}</ruledto>
Copy the code code as follows:
public class WordBuilder {
/**
* Read replacement rules based on template
* @param templateName template ID
*/
@SuppressWarnings("unchecked")
public Template loadRules(Map<string, string=""> ruleValue) {
InputStream in = null;
Template template = new Template();
// Rule configuration file path
String ruleFile = "template-rule.xml";
// Template rule name
String templateRuleName = "";
try {
templateRuleName = ruleValue.get("ruleName");
//Read template rule file
in = this.getClass().getClassLoader().getResourceAsStream(ruleFile);
// Parse template rules
SAXBuilder sb = new SAXBuilder();
Document doc = sb.build(in);
Element root = doc.getRootElement(); // Get the root element
List<element> templateList = root.getChildren();//All template configurations
Element element = null;
Vector<ruledto> rules = null;
for (int i = 0; i < templateList.size(); i++) {// Traverse all templates
element = (Element) templateList.get(i);
String templateName = element.getAttributeValue("name");
if (templateRuleName.equalsIgnoreCase(templateName)) {//Find the given template configuration
template.setName(templateName);
template.setDesc(element.getAttributeValue("desc"));
template.setTemplateFile(element
.getAttributeValue("templateFile"));
List<element> tagList = ((Element) element.getChildren()
.get(0)).getChildren(); // tag list
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"))) {// Whether the mark can be deleted
ruleDTO.setIfEmptyDelete("T");
} else {
ruleDTO.setIfEmptyDelete("F");
}
ruleDTO.setParmRegular(tag.getText());
// value
// Determine parameter type
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) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
in.close();
} catch (Exception e) {
e.printStackTrace();
}
}
return template;
}
/**
* Find the parent node
*/
public Element findElement(Element currNode, String parentNodeId) {
//The node is marked as empty
if (currNode == null || parentNodeId == null) {
return null;
}
Element pNode = null;
do {
pNode = currNode.getParent();
currNode = pNode;
} while (parentNodeId.equalsIgnoreCase(pNode.getName()));
return pNode;
}
/**
* Generate Word file
*/
@SuppressWarnings("unchecked")
public String build(Template template) {
InputStream in = null;
OutputStream fo = null;
//The path to the generated file
String file = "d://test//" + template.getDesc() + ".doc";
try {
//Read template file
in = this.getClass().getClassLoader()
.getResourceAsStream(template.getTemplateFile());
SAXBuilder sb = new SAXBuilder();
Document doc = sb.build(in);
Element root = doc.getRootElement(); // Get the root element
Namespace ns = root.getNamespace();// NameSpace
// The <wx:sect> element exists in the word 03 template
List<element> sectList = root.getChild("body", ns).getChildren();
Element sectElement = (Element) sectList.get(0);
// Collection of tags under <w:p>
List<element> pTagList = sectElement.getChildren("p", ns);
// Collection of tags under <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);
}
// write file
XMLOutputter outp = new XMLOutputter(" ", true, "UTF-8");
fo = new FileOutputStream(file);
outp.output(doc, fo);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (JDOMException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
in.close();
fo.close();
} catch (Exception e) {
e.printStackTrace();
}
}
return file;
}
/**
* For WORD templates of the level <w:body><wx:sect><w:p>, find and replace tags under <w:p>.
* @param pTagList:<w:p>collection
* @param rulesValue: RuleDTO collection
* @param ns: NameSpace object
* @param trChildren: the collection of child nodes <w:tr> of <w:tbl>
*/
@SuppressWarnings("unchecked")
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;//Delete the current node
boolean delCurrNode4TabWR = false;//Delete a single row node in the 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) {
// Determine whether the attribute value is nullable for deletion
if ("T".equals(dto.getIfEmptyDelete())
&& StringUtils.isBlank(dto
.getParmValue())) {
//Delete the top node of this node
text = "";
if (trChildren != null) {//Delete this row for <w:tbl>
Element element = ((Element) p
.getParent()).getParent();
trChildren.remove(element);
delCurrNode4TabWR = true;
} else {//Delete segment for <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>The row node under TABLE has been deleted
delFlag = true;
break;
} else if (delCurrNode) {// The node under <w:p> has been deleted
i--;
delFlag = true;
break;
}
}
}
}
return delFlag;
}
/**
* For WORD templates containing tables, find and replace tags under <w:tbl>.
* @param tblTagList:<w:tbl> collection
* @param rulesValue: RuleDTO collection
* @param ns: NameSpace object
*/
@SuppressWarnings("unchecked")
private void changeValue4TblTag(List<element> tblTagList,
Vector<ruledto> rulesValue, Namespace ns) {
Element p = null;
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++) {// Loop<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++) {// Loop <w:tc> to get the <w:p> collection
Element tcChildren = (Element) tcTagList.get(c);
List<element> pTagList = tcChildren.getChildren("p", ns);
boolean delFlag = changeValue4PTag(pTagList, rulesValue,
ns, trChildren);
if (delFlag) {// After deleting the row, you need to change the pointer position of trChildren
j--;
}
}
}
}
}
public static void main(String[] args) throws Exception {
WordBuilder word = new WordBuilder();
Map<string, string=""> map = new HashMap<string, string="">();
//fill parameters
map.put("ToPartment", "XXX Company");
map.put("OwnerName", "Zhang San");
map.put("CountNum", "5");
map.put("Business", "Routine Check");
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);
//Open the file directly
Runtime.getRuntime().exec("explorer " + word.build(template));
}
}</string,></string,></element></w:p></w:tc></element></w:tr></element></ruledto></element>< /w:tbl></w:tbl></w:p></w:tbl></w:r></w:tbl></element></element></ruledto></ele ment></w:tr></w:tbl></w:p></w:p></w:p></wx:sect></w:body></element></ w:tbl></element></w:p></element></wx:sect></string,></ruledto></element></ruledto></element></string,>
Step 4: Done
Some summary points and notes:
1. The defined element name must be consistent with the value corresponding to the same name in template_rule.xml, otherwise a conversion rule needs to be set.
2. The text in the placeholder [※※] defined in the template xml must be the same as the corresponding desc in template_rule.xml, otherwise a conversion rule needs to be set.
3. After configuring the template XML, you need to check whether the child node under the label is a label (related to the WORD version). If not, the label must be added.
4. If you want to dynamically delete a label node, the content of this node needs to be in the same line in the template. If not, you can manually adjust the template XML.
5. If you need to implement the automatic line wrapping function of WORD (there is no better solution for line wrapping in templates yet), you need to first calculate the number of words in the corresponding line of the template, and then use space filling to achieve it.