Il y a quelque temps, j'ai reçu une demande pour une application Web permettant de générer automatiquement Word. J'ai maintenant compilé quelques étapes clés à partager.
Idée : (Remarque : ceci concerne uniquement la version WORD2003, les autres versions sont similaires.)
Étant donné que les données internes et le format des fichiers WORD sont stockés sous forme de fichiers XML, les fichiers WORD peuvent facilement être convertis du format DOC au format XML, et il est beaucoup plus pratique d'utiliser des fichiers XML, réalisant ainsi l'intégration avec diverses opérations indépendantes de la plate-forme. générer des fichiers Word via une requête de nœud, un remplacement, une suppression, un ajout, etc. Par conséquent, l'essence de la génération d'un fichier WORD basé sur un modèle réside dans le processus de remplacement des balises spéciales du fichier XML par des données utilisateur, puis de son enregistrement en tant que fichier DOC.
Voici quelques-unes des étapes clés à suivre (en prenant une lettre d’introduction comme exemple)
Première étape : créez un modèle WORD selon vos besoins
Créez un nouveau fichier WORD au format DOC, remplissez le contenu du modèle si nécessaire et définissez le format du modèle, y compris les polices, les styles, les lignes vides, etc. Utilisez des balises spéciales (telles que : [※Nom de l'unité※]) pour préoccupez les données qui doivent être remplies, puis enregistrez le fichier WORD nouvellement créé en tant que fichier au format XML. De cette façon, le modèle WORD est complété, le code est le suivant :
Ajoutez un nouveau fichier de configuration nommé template-rule.xml, chaque nœud de modèle correspond à un type de modèle. Il y a un nœud de liste de balises dans chaque modèle. Tous les nœuds enfants contenus dans ce nœud contiennent des informations sur tous les nœuds qui seront remplacés ou supprimés dans le modèle. Les informations sur le nœud comprennent : la valeur du nœud, le nom anglais de l'attribut du nœud, la description chinoise, le type de champ. s'il peut être supprimé, etc. Lors de la définition de ce fichier de configuration, vous devez noter que la valeur de l'attribut desc doit être cohérente avec l'espace réservé dans le modèle XML. Par exemple : l'élément d'entrée de l'année [※Year※] défini dans le modèle XML doit correspondre au nom desc="Year" dans template-rule.xml. Le code est le suivant :
Copiez le code comme suit :
<!--?xml version="1.0" encoding="GB2312"?-->
<!-- Définition du modèle-->
<modèles>
<!-- Description : S-string ; D-amount ; M-uppercase montant ; ifEmptyDelete : la valeur T est vide pour supprimer le nœud parent, la valeur par défaut est F -->
<template name="RECOMMEND-LETTER" desc="Lettre d'introduction" templatefile="template4.xml">
<taglist remarque="Liste de balises à valeur unique">
<tag id="1" name="ToPartment" desc="Département de réception" type="S" ifemptydelete="T">#ToPartment</tag><!--Département de réception-->
<tag id="2" name="OwnerName" desc="Name" type="S">#OwnerName</tag><!--Name-->
<tag id="3" name="CountNum" desc="Nombre de personnes" type="S">#CountNum</tag><!--Nombre de personnes-->
<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="Année" desc="année" type="S">#Année</tag><!--année-->
<tag id="7" name="Mois" desc="mois" type="S">#Mois</tag><!--mois-->
<tag id="8" name="Jour" desc="日" type="S">#Jour</tag><!--Jour-->
</liste de balises>
</modèle>
</modèles>
Étape 3 : Écrire du code Java
Copiez le code comme suit :
/**
* Paramètres et règles
*/
classe publique RuleDTO {
/**
* nom de la balise
*/
chaîne privée parmName ;
/**
* description de la balise
*/
chaîne privée parmDesc ;
/**
* numéro de série de l'étiquette
*/
chaîne privée parmSeq ;
/**
* type de valeur de balise
*/
chaîne privée parmType ;
/**
* nom du paramètre de balise
*/
chaîne privée parmRegular ;
/**
* valeur de la balise
*/
chaîne privée parmValue ;
/**
* Si la valeur du tag est vide, supprimez cet attribut
*/
chaîne privée ifEmptyDelete ;
}
Copiez le code comme suit :
/**
* Description : informations sur le modèle Word
*/
Modèle de classe publique {
nom de chaîne privée ; // nom du modèle
description de chaîne privée ; // description du modèle
fichier de modèle de chaîne privé ; // fichier modèle
règles vectorielles privées<ruledto> ;//règles de modèle
</ruledto>
Copiez le code comme suit :
classe publique WordBuilder {
/**
* Lire les règles de remplacement basées sur le modèle
* ID du modèle @param templateName
*/
@SuppressWarnings("non coché")
modèle public loadRules(Map<string, string=""> RuleValue) {
Flux d'entrée dans = null ;
Modèle de modèle = nouveau modèle ();
// Chemin du fichier de configuration des règles
String RuleFile = "template-rule.xml";
// Nom de la règle du modèle
Chaîne templateRuleName = "" ;
essayer {
templateRuleName = RuleValue.get("ruleName");
//Lire le fichier de règles du modèle
in = this.getClass().getClassLoader().getResourceAsStream(ruleFile);
// Analyser les règles du modèle
SAXBuilder sb = new SAXBuilder();
Document doc = sb.build(in);
Element root = doc.getRootElement(); // Récupère l'élément racine
List<element> templateList = root.getChildren();//Toutes les configurations de modèles
Élément élément = nul ;
Règles vectorielles<ruledto> = null ;
for (int i = 0; i < templateList.size(); i++) {// Parcourir tous les modèles
element = (Élément) templateList.get(i);
String templateName = element.getAttributeValue("name");
if (templateRuleName.equalsIgnoreCase(templateName)) {//Trouver la configuration du modèle donné
template.setName(templateName);
template.setDesc(element.getAttributeValue("desc"));
template.setTemplateFile(élément
.getAttributeValue("templateFile"));
List<element> tagList = ((Element) element.getChildren()
.get(0)).getChildren(); // liste de balises
Balise d'élément = null ;
RègleDTO règleDTO = null ;
règles = new Vector<ruledto>();
pour (int j = 0; j < tagList.size(); j++) {
tag = (Élément) tagList.get(j);
règleDTO = nouvelle règleDTO();
RuleDTO.setParmName(tag.getAttributeValue("name"));
règleDTO.setParmDesc("【※"
+ tag.getAttributeValue("desc") + "※】");
RuleDTO.setParmSeq(tag.getAttributeValue("id"));
RuleDTO.setParmType(tag.getAttributeValue("type"));
if ("T".equalsIgnoreCase(balise
.getAttributeValue("ifEmptyDelete"))) {// Si la marque peut être supprimée
règleDTO.setIfEmptyDelete("T");
} autre {
règleDTO.setIfEmptyDelete("F");
}
règleDTO.setParmRegular(tag.getText());
// valeur
// Détermine le type de paramètre
Valeur de chaîne = (String) ((Map<string, string="">) RuleValue)
.get(ruleDTO.getParmRegular().replaceAll("#",
""));
règleDTO.setParmValue(valeur);
règles.ajouter(ruleDTO);
}
template.setRules(règles);
casser;
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (JDOMException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} enfin {
essayer {
joindre();
} attraper (Exception e) {
e.printStackTrace();
}
}
modèle de retour ;
}
/**
* Trouver le nœud parent
*/
élément public findElement (élément currNode, chaîne parentNodeId) {
//Le nœud est marqué comme vide
if (currNode == null || parentNodeId == null) {
renvoie null ;
}
Élément pNode = null ;
faire {
pNode = currNode.getParent();
currNode = pNode;
} while (parentNodeId.equalsIgnoreCase(pNode.getName()));
retourner le pNode ;
}
/**
* Générer un fichier Word
*/
@SuppressWarnings("non coché")
public String build (modèle de modèle) {
Flux d'entrée dans = null ;
OutputStream fo = null ;
//Le chemin d'accès au fichier généré
Fichier de chaîne = "d://test//" + template.getDesc() + ".doc" ;
essayer {
//Lire le fichier modèle
dans = this.getClass().getClassLoader()
.getResourceAsStream(template.getTemplateFile());
SAXBuilder sb = new SAXBuilder();
Document doc = sb.build(in);
Element root = doc.getRootElement(); // Récupère l'élément racine
Espace de noms ns = root.getNamespace();//NameSpace
// L'élément <wx:sect> existe dans le modèle word 03
List<element> sectList = root.getChild("body", ns).getChildren();
Élément sectElement = (Élément) sectList.get(0);
// Collection de balises sous <w:p>
List<element> pTagList = sectElement.getChildren("p", ns);
// Collection de balises sous <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);
}
// écriture du fichier
XMLOutputter outp = new XMLOutputter(" ", true, "UTF-8");
fo = nouveau FileOutputStream(fichier);
outp.output(doc, fo);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (JDOMException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} enfin {
essayer {
joindre();
fo.close();
} attraper (Exception e) {
e.printStackTrace();
}
}
retourner le fichier ;
}
/**
* Pour les modèles WORD du niveau <w:body><wx:sect><w:p>, recherchez et remplacez les balises sous <w:p>.
* @param pTagList :<w:p>collection
* @param RulesValue : collection RuleDTO
* @param ns : objet NameSpace
* @param trChildren : la collection de nœuds enfants <w:tr> de <w:tbl>
*/
@SuppressWarnings("non coché")
changeValue4PTag(List<element> pTagList,
Vector<ruledto> RulesValue, Namespace ns, List<element> trChildren) {
Élément p = nul ;
booléen delFlag = faux ;
pour (int i = 0; i < pTagList.size(); i++) {
boolean delCurrNode = false;//Supprimer le nœud actuel
boolean delCurrNode4TabWR = false;//Supprimer un nœud à une seule ligne dans le tableau
p = (Élément) pTagList.get(i);
List<element> pChild = p.getChildren("r", ns);
pour (int j = 0; pChild != null && j < pChild.size(); j++) {
Élément pChildren = (Élément) pChild.get(j);
Élément t = pChildren.getChild("t", ns);
si (t != nul) {
Texte de chaîne = t.getTextTrim();
if (text.indexOf("【※") != -1) {
pour (int v = 0; v < règlesValue.size(); v++) {
RuleDTO dto = (RuleDTO) règlesValue.get(v);
if (text.indexOf(dto.getParmDesc().trim()) != -1) {
// Détermine si la valeur de l'attribut est nullable pour la suppression
si ("T".equals(dto.getIfEmptyDelete())
&& StringUtils.isBlank(dto
.getParmValue())) {
//Supprimer le nœud supérieur de ce nœud
texte = "" ;
if (trChildren != null) {//Supprimez cette ligne pour <w:tbl>
Élément élément = ((Élément) p
.getParent().getParent();
trChildren.remove(élément);
delCurrNode4TabWR = vrai ;
} else {//Supprimer le segment pour <w:r>
// pTagList.remove(p);
pTagList.remove(pChildren);
delCurrNode = vrai ;
}
casser;
} autre {
texte = text.replaceAll(dto.getParmDesc()
.trim(), dto.getParmValue());
}
}
}
t.setText(texte);
}
if (delCurrNode4TabWR) {// <w:tbl>Le nœud de ligne sous TABLE a été supprimé
delFlag = vrai ;
casser;
} else if (delCurrNode) {// Le nœud sous <w:p> a été supprimé
je--;
delFlag = vrai ;
casser;
}
}
}
}
retourner delFlag ;
}
/**
* Pour les modèles WORD contenant des tableaux, recherchez et remplacez les balises sous <w:tbl>.
* @param tblTagList :<w:tbl> collection
* @param RulesValue : collection RuleDTO
* @param ns : objet NameSpace
*/
@SuppressWarnings("non coché")
private void changeValue4TblTag(List<element> tblTagList,
Vecteur<ruledto> RulesValue, Namespace ns) {
Élément p = nul ;
pour (int i = 0; tblTagList != null && i < tblTagList.size(); i++) {
p = (Élément) tblTagList.get(i);
List<element> trChildren = p.getChildren("tr", ns);
for (int j = 0; trChildren != null && j < trChildren.size(); j++) {// Loop<w:tr>
Élément pChildren = (Élément) trChildren.get(j);
List<element> tcTagList = pChildren.getChildren("tc", ns);
for (int c = 0; tcTagList != null && c < tcTagList.size(); c++) {// Boucle <w:tc> pour obtenir la collection <w:p>
Élément tcChildren = (Élément) tcTagList.get(c);
List<element> pTagList = tcChildren.getChildren("p", ns);
booléen delFlag = changeValue4PTag(pTagList, RulesValue,
ns, trEnfants);
if (delFlag) {// Après avoir supprimé la ligne, vous devez changer la position du pointeur de trChildren
j--;
}
}
}
}
}
public static void main (String[] args) lève une exception {
Mot WordBuilder = new WordBuilder();
Map<string, string=""> map = new HashMap<string, string="">();
//remplir les paramètres
map.put("ToPartment", "XXX Company");
map.put("Nom du propriétaire", "Zhang San");
map.put("CountNum", "5");
map.put("Business", "Vérification de routine");
map.put("JoursUtiles", "15");
map.put("Année", "2014");
map.put("Mois", "5");
map.put("Jour", "13");
map.put("ruleName", "RECOMMEND-LETTER");
Modèle de modèle = word.loadRules(map);
//Ouvre le fichier directement
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,>
Étape 4 : Terminé
Quelques points de synthèse et notes :
1. Le nom de l'élément défini doit être cohérent avec la valeur correspondant au même nom dans template_rule.xml, sinon une règle de conversion doit être définie.
2. Le texte dans l'espace réservé [※※] défini dans le modèle XML doit être le même que la description correspondante dans template_rule.xml, sinon une règle de conversion doit être définie.
3. Après avoir configuré le modèle XML, vous devez vérifier si le nœud enfant sous l'étiquette est une étiquette (liée à la version WORD). Sinon, l'étiquette doit être ajoutée.
4. Si vous souhaitez supprimer dynamiquement un nœud d'étiquette, le contenu de ce nœud doit être sur la même ligne dans le modèle. Sinon, vous pouvez ajuster manuellement le modèle XML.
5. Si vous devez implémenter la fonction de retour à la ligne automatique de WORD (il n'y a pas encore de meilleure solution pour le retour à la ligne dans les modèles), vous devez d'abord calculer le nombre de mots dans la ligne correspondante du modèle, puis utiliser le remplissage d'espaces. pour y parvenir.