使用docx4j从.dotx模板生成.docx文档(在XPage应用程序中)

时间:2014-07-23 09:46:35

标签: java ms-word xpages docx4j

我在XPage应用程序中使用docx4j来创建包含XPage内容的Word文档。 Word文档(.docx格式)是基于模板(也在docx.format中)创建的。一切正常。但是,当我将模板从.docx更改为.dotx格式时,无法打开生成的Word文档(.docx)。在尝试打开文档时,我收到一条错误消息,说明内容会导致问题。

有谁能告诉我如何使用docx4j将.dotx文件转换为.docx文件?

我目前使用的代码是:

import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;

import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;

import org.docx4j.XmlUtils;
import org.docx4j.openpackaging.exceptions.Docx4JException;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.wml.ContentAccessor;

import org.slf4j.impl.*;

import java.io.FileInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;

import org.docx4j.wml.*;

import org.apache.commons.lang3.StringUtils;

import java.util.Enumeration;
import java.util.Map;
import java.util.Iterator;
import java.util.Vector;

import lotus.domino.Document;
import lotus.domino.*;

import org.docx4j.openpackaging.parts.WordprocessingML.DocumentSettingsPart;
import org.docx4j.jaxb.Context;
import org.docx4j.openpackaging.parts.relationships.RelationshipsPart;
import org.docx4j.openpackaging.parts.relationships.Namespaces;

public class JavaTemplateDocument {


    public void mainCode(Session session, Document currDoc, String empLang, String templateType, String sArt) throws Exception {

        Database dbCurr = session.getCurrentDatabase(); 
        String viewName = "vieTemplateLookup"; 
        View tview = dbCurr.getView(viewName);
        Vector viewKey = new Vector();
        viewKey.addElement(empLang);
        viewKey.addElement(templateType);
        Document templateDoc = tview.getDocumentByKey(viewKey);
        if (tview.getDocumentByKey(viewKey) == null ) System.out.println("templateDoc is NULL");

        Item itmNotesFields = templateDoc.getFirstItem("NotesFieldList");
        Item itmWordFields = templateDoc.getFirstItem("WordFieldList");

        Vector<String[]> notesFields = itmNotesFields.getValues();
        Vector<String[]> wordFields = itmWordFields.getValues();

        int z = notesFields.size();
        int x = wordFields.size();

        Enumeration e1 = notesFields.elements();

        Enumeration e2 = wordFields.elements();

        WordprocessingMLPackage template = getTemplate("C:\\Temp\\AZG Sample Template.dotx","C:\\Temp\\AZG Sample Template.docx");

        for (int y = 0; y < x; y++) { 
        if (currDoc.hasItem(String.valueOf(notesFields.elementAt(y)))) {
        Item itmNotesName = currDoc.getFirstItem(String.valueOf(notesFields.elementAt(y))); 
        replacePlaceholder(template, itmNotesName.getText(), String.valueOf(wordFields.elementAt(y))); } 
        else {
            replacePlaceholder(template, "", String.valueOf(wordFields.elementAt(y)));
        }
        }

        writeDocxToStream(template, "C:\\Temp\\AZG Sample Document.docx");
        createResponseDocument(dbCurr, currDoc, templateDoc, sArt); 
    }

    private void createResponseDocument(Database dbCurr, Document currDoc, Document templateDoc, String sArt) throws NotesException{

        Document respDoc = dbCurr.createDocument(); // create the response document
        String refVal = currDoc.getUniversalID();

        respDoc.appendItemValue("IsDocTemplate", "1"); 
        if (currDoc.hasItem("Name")) {
            respDoc.appendItemValue("Name", currDoc.getItemValue("Name"));}
        else {System.out.println("Name is not available"); }
        if (currDoc.hasItem("Firstname")) {
            respDoc.appendItemValue("Firstname", currDoc.getItemValue("Firstname"));}
        else {System.out.println("Firstname is not available"); }
        if (currDoc.hasItem("ReferenceTypeTexts")) {
            respDoc.appendItemValue("ReferenceTypeTexts", currDoc.getItemValue("ReferenceTypeTexts"));}
        else {System.out.println("ReferenceTypeTexts is not available"); }
        if (currDoc.hasItem("ReferenceType")) {
            respDoc.appendItemValue("ReferenceType", currDoc.getItemValue("ReferenceType"));}
        else {System.out.println("ReferenceType is not available"); }   
        System.out.println("Append Form value");
        respDoc.appendItemValue("Form", "frmRespTempl"); 


        respDoc.makeResponse(currDoc);
        RichTextItem body = respDoc.createRichTextItem("Body");
        body.embedObject(1454, "", "C:\\Temp\\AZG Sample Document.docx", null);
        respDoc.save();
    }


    /*
     * Create a simple word document that we can use as a template. 
     * For this just open Word, create a new document and save it as template.docx. 
     * This is the word template we'll use to add content to. 
     * The first thing we need to do is load this document with docx4j. 
     */

    private WordprocessingMLPackage getTemplate(String source, String target) throws Docx4JException, FileNotFoundException, IOException { 

    String WORDPROCESSINGML_DOCUMENT = "application/vnd.openxmlformats-   officedocument.wordprocessingml.document.main+xml";
    final ContentType contentType = new ContentType(WORDPROCESSINGML_DOCUMENT);

    String templatePath = source;

    File sourceFile = new File(source);
    File targetFile = new File(target);
    copyFileUsingFileChannels(sourceFile, targetFile);

    WordprocessingMLPackage template = WordprocessingMLPackage.load(new FileInputStream(targetFile));

    ContentTypeManager ctm = wordMLPackage.getContentTypeManager();
        ctm.addOverrideContentType(new URI("/word/document.xml"),WORDPROCESSINGML_DOCUMENT);

    DocumentSettingsPart dsp = new DocumentSettingsPart();
        CTSettings settings = Context.getWmlObjectFactory().createCTSettings();
        dsp.setJaxbElement(settings);
        wordMLPackage.getMainDocumentPart().addTargetPart(dsp); 

        // Create external rel
        RelationshipsPart rp = RelationshipsPart.createRelationshipsPartForPart(dsp);
        org.docx4j.relationships.Relationship rel = new org.docx4j.relationships.ObjectFactory().createRelationship();
        rel.setType( Namespaces.ATTACHED_TEMPLATE );
        rel.setTarget(templatePath);
        rel.setTargetMode("External");
        rp.addRelationship(rel); // addRelationship sets the rel's @Id 

        settings.setAttachedTemplate(
        (CTRel)XmlUtils.unmarshalString("<w:attachedTemplate xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\" r:id=\"" + rel.getId() + "\"/>", Context.jc, CTRel.class)
        );

    return template;
}

    private static List<Object> getAllElementFromObject(Object obj, Class<?> toSearch) {
        List<Object> result = new ArrayList<Object>();
        if (obj instanceof JAXBElement) obj = ((JAXBElement<?>) obj).getValue();

        if (obj.getClass().equals(toSearch))
            result.add(obj);
        else if (obj instanceof ContentAccessor) {
            List<?> children = ((ContentAccessor) obj).getContent();
            for (Object child : children) {
                result.addAll(getAllElementFromObject(child, toSearch));
            }

        }
        return result;
    }

    /*
     * This will look for all the Text elements in the document, and those that match are replaced with the value we specify.
     */

    private void replacePlaceholder(WordprocessingMLPackage template, String name, String placeholder ) {
        List<Object> texts = getAllElementFromObject(template.getMainDocumentPart(), Text.class);

        for (Object text : texts) {
            Text textElement = (Text) text;
            if (textElement.getValue().equals(placeholder)) {
                textElement.setValue(name);
            }
        }
    }

    /*
     * write the document back to a file
     */

    private void writeDocxToStream(WordprocessingMLPackage template, String target) throws IOException, Docx4JException {
        File f = new File(target);
        template.save(f);
    }

    /*
     * Example code for replaceParagraph
     * 
        String placeholder = "SJ_EX1";
        String toAdd = "jos\ndirksen";

        replaceParagraph(placeholder, toAdd, template, template.getMainDocumentPart());
     */

    private void replaceParagraph(String placeholder, String textToAdd, WordprocessingMLPackage template, ContentAccessor addTo) {
        // 1. get the paragraph
        List<Object> paragraphs = getAllElementFromObject(template.getMainDocumentPart(), P.class);

        P toReplace = null;
        for (Object p : paragraphs) {
            List<Object> texts = getAllElementFromObject(p, Text.class);
            for (Object t : texts) {
                Text content = (Text) t;
                if (content.getValue().equals(placeholder)) {
                    toReplace = (P) p;
                    break;
                }
            }
        }

        // we now have the paragraph that contains our placeholder: toReplace
        // 2. split into seperate lines
        String as[] = StringUtils.splitPreserveAllTokens(textToAdd, '\n');

        for (int i = 0; i < as.length; i++) {
            String ptext = as[i];

            // 3. copy the found paragraph to keep styling correct
            P copy = (P) XmlUtils.deepCopy(toReplace);

            // replace the text elements from the copy
            List<?> texts = getAllElementFromObject(copy, Text.class);
            if (texts.size() > 0) {
                Text textToReplace = (Text) texts.get(0);
                textToReplace.setValue(ptext);
            }

            // add the paragraph to the document
            addTo.getContent().add(copy);
        }

        // 4. remove the original one
        ((ContentAccessor)toReplace.getParent()).getContent().remove(toReplace);

    }

    /*
     * A set of hashmaps that contain the name of the placeholder to replace and the value to replace it with. 
     * 
     *  Map<String,String> repl1 = new HashMap<String, String>();
        repl1.put("SJ_FUNCTION", "function1");
        repl1.put("SJ_DESC", "desc1");
        repl1.put("SJ_PERIOD", "period1");

        Map<String,String> repl2 = new HashMap<String, String>();
        repl2.put("SJ_FUNCTION", "function2");
        repl2.put("SJ_DESC", "desc2");
        repl2.put("SJ_PERIOD", "period2");

        Map<String,String> repl3 = new HashMap<String, String>();
        repl3.put("SJ_FUNCTION", "function3");
        repl3.put("SJ_DESC", "desc3");
        repl3.put("SJ_PERIOD", "period3");

        replaceTable(new String[]{"SJ_FUNCTION","SJ_DESC","SJ_PERIOD"}, Arrays.asList(repl1,repl2,repl3), template);
     */

    private void replaceTable(String[] placeholders, List<Map<String, String>> textToAdd,
            WordprocessingMLPackage template) throws Docx4JException, JAXBException {
        List<Object> tables = getAllElementFromObject(template.getMainDocumentPart(), Tbl.class);

        // 1. find the table
        Tbl tempTable = getTemplateTable(tables, placeholders[0]);
        List<Object> rows = getAllElementFromObject(tempTable, Tr.class);

        // first row is header, second row is content
        if (rows.size() == 2) {
            // this is our template row
            Tr templateRow = (Tr) rows.get(1);

            for (Map<String, String> replacements : textToAdd) {
                // 2 and 3 are done in this method
                addRowToTable(tempTable, templateRow, replacements);
            }

            // 4. remove the template row
            tempTable.getContent().remove(templateRow);
        }
    }

    private Tbl getTemplateTable(List<Object> tables, String templateKey) throws Docx4JException, JAXBException {
        for (Iterator<Object> iterator = tables.iterator(); iterator.hasNext();) {
            Object tbl = iterator.next();
            List<?> textElements = getAllElementFromObject(tbl, Text.class);
            for (Object text : textElements) {
                Text textElement = (Text) text;
                if (textElement.getValue() != null && textElement.getValue().equals(templateKey))
                    return (Tbl) tbl;
            }
        }
        return null;
    }

    private static void addRowToTable(Tbl reviewtable, Tr templateRow, Map<String, String> replacements) {
        Tr workingRow = (Tr) XmlUtils.deepCopy(templateRow);
        List<?> textElements = getAllElementFromObject(workingRow, Text.class);
        for (Object object : textElements) {
            Text text = (Text) object;
            String replacementValue = (String) replacements.get(text.getValue());
            if (replacementValue != null)
                text.setValue(replacementValue);
        }

        reviewtable.getContent().add(workingRow);
    }

private static void copyFileUsingFileChannels(File source, File dest)
                throws IOException {
            FileChannel inputChannel = null;
            FileChannel outputChannel = null;
            try {
                inputChannel = new FileInputStream(source).getChannel();
                outputChannel = new FileOutputStream(dest).getChannel();
                outputChannel.transferFrom(inputChannel, 0, inputChannel.size());
            } finally {
                inputChannel.close();
                outputChannel.close();
            }
        }

}

3 个答案:

答案 0 :(得分:2)

从广义上讲,有一些东西构成了模板(.dotx)和文档(.docx)之间的区别。这意味着您需要做一些事情 - 它不像更改文件扩展名那么简单,无论是将文档保存为模板,还是尝试创建文档< strong>来自模板。

希望这个大纲有助于:

  1. 首先执行您已经完成的操作:您的新文档应该是模板的文件副本
  2. 根据需要更改新的WordprocessingMLPackage文档类型(请参阅WORDPROCESSINGML_TEMPLATE类中的ContentTypes
  3. 创建附加模板并将其附加到您的文档:有关详细信息,请参阅sample code on GithubTemplateAttach.java示例)。
  4. 祝你好运!

答案 1 :(得分:1)

我想说你需要将返回的文件名从docx更改为dotx 从docx到dotx进行文件复制并更改此行

 body.embedObject(1454, "", "C:\\Temp\\AZG Sample Document.dotx", null);

答案 2 :(得分:1)

让我们破解它。 新的办公室格式只是具有许多XML配置和数据的ZIP。尝试在MS Word中将相同的文档另存为模板和文档。恕我直言,问题的核心是(打包)文件[Content_Types].xml

他们的财产有所不同:

的ContentType = “应用/ vnd.openxmlformats-officedocument.wordprocessingml。的模板。主要+ xml” 的 的ContentType = “应用/ vnd.openxmlformats-officedocument.wordprocessingml。的文件。主要+ xml” 的

我希望@ benpoole的建议应该有效(它应该改变所述文件的内容)。如果不是这样,只需在文件中破解它的内容(它只是普通的ZIP存档,请记住)。

免责声明:在更多文件中存在差异,可能需要进行调整以使其正常工作。