更改现有.docx文件的纯文本内容控件的默认文本

时间:2018-02-22 13:44:27

标签: java ms-office docx4j

我获得了一个.docx模板,我需要在我的java应用程序中填充它。最初,我打算使用Apache POI,因为在此之前,我的任务是填写一个.xlsx模板,它运行良好。但是,根据我的研究,doc4j更适合我的情况。

我的情况是这个.docx模板使用纯文本内容控件,如下所示:

enter image description here

现在,在检查其XML结构后,我直接在<w:sdt>标记下的<w:p>下看到<w:body>

<w:body>
    ...
    <w:p w:rsidR="00ED05E8" w:rsidRPr="00DA4BE7" w:rsidRDefault="00AC5B37" w:rsidP="00BA6F7F">
        ...
        <w:sdt>
            <w:sdtPr>
                <w:rPr>
                    <w:rFonts w:ascii="Arial" w:hAnsi="Arial" w:cs="Arial"/>
                    <w:i/>
                    <w:sz w:val="24"/>
                    <w:szCs w:val="24"/>
                    <w:u w:val="single"/>
                </w:rPr>
                <w:alias w:val="Name of Office/Agency Name"/>
                <w:tag w:val="Name of Office/Agency Name"/>
                <w:id w:val="-781645881"/>
                <w:placeholder>
                    <w:docPart w:val="DefaultPlaceholder_-1854013440"/>
                </w:placeholder>
                <w:text/>
            </w:sdtPr>
            <w:sdtEndPr/>
            <w:sdtContent>
                <w:r w:rsidR="00340180" w:rsidRPr="00616BA5">
                    <w:rPr>
                        <w:rFonts w:ascii="Arial" w:hAnsi="Arial" w:cs="Arial"/>
                        <w:i/>
                        <w:sz w:val="24"/>
                        <w:szCs w:val="24"/>
                        <w:u w:val="single"/>
                    </w:rPr>
                    <w:t>(Name of Office/Agency Name)</w:t>
                </w:r>
            </w:sdtContent>
        </w:sdt>
        ...
</w:body>

我想将<w:t>的{​​{1}}上的文字从“(代理机构名称)”更改为另一个字符串。问题是,我不知道如何以及在这些方面之后如何坚持:

<w:sdt>

我有WordprocessingMLPackage document = WordprocessingMLPackage.load(new java.io.File(...)); MainDocumentPart mainDocument = document.getMainDocumentPart(); w:id,但我不知道如何处理这些信息。这甚至是ContentControlsXmlEdit sample class from the docx4j site上引用的-781645881吗?

即使使用以下代码,我也无法获取itemId节点:

<w:sdt>

如何更改纯文本内容控件的值?

1 个答案:

答案 0 :(得分:0)

这个答案是我出于绝望而设计的,原因如下:

  1. 我还没有掌握使用以编程方式访问Word .xml中的内容。
  2. 我提取了我正在处理的.xml文件的确切.docx文件。
  3. storeItemid文件的.xml找不到.docx
  4. 这是我用.groovy编写的实用工具类:

    import javax.xml.bind.JAXBElement
    import org.apache.poi.openxml4j.exceptions.InvalidFormatException
    import org.docx4j.openpackaging.packages.WordprocessingMLPackage
    import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart
    import org.docx4j.wml.CTBookmark
    import org.docx4j.wml.P
    import org.docx4j.wml.R
    import org.docx4j.wml.SdtBlock
    import org.docx4j.wml.SdtContent
    import org.docx4j.wml.SdtRun
    import org.docx4j.wml.Text
    
    class WordReport {
        private WordprocessingMLPackage document
        private Map<String, String> contentControlMapping
        private Map<String, Object> reportArgs
    
        public WordReport(Map<String, Object> reportArgs) {
            document = WordprocessingMLPackage.createPackage()
            this.reportArgs = reportArgs
        }
    
        public WordprocessingMLPackage exportReport() {
            return document
        }
    
        private String getNewMapping(String contentControlText)  {
            return contentControlMapping.get(contentControlText)
        }
    
        private boolean isMapped(String contentControlText) {
            return contentControlMapping.containsKey(contentControlText)
        }
    
        protected void mapNewMapping() {
            MainDocumentPart mainDocument = document.getMainDocumentPart()
            List<Object> nodes = mainDocument.getJAXBNodesViaXPath("//w:sdt", false)
    
            String key
            SdtContent content
            nodes.each { n ->
                if(n instanceof SdtBlock) {
                    content = n.getSdtContent()
                }
                else if(n instanceof JAXBElement) {
                    if(n.getValue() instanceof SdtRun) {
                        content = n.getValue().getSdtContent()
                    }
                }
    
                content.getContent().each { sdtcc ->
                    if(sdtcc instanceof P) {
                        sdtcc.getContent().each { pc ->
                            pc.getContent().each { rc ->
                                println "rc.getValue().getClass(): " + rc.getValue().getClass()
                                if(rc.getValue() instanceof Text) {
                                    key = rc.getValue().getValue()
                                    isMapped(key) ? rc.getValue().setValue(getNewMapping(key)) : null
                                }
                                else if(rc.getValue() instanceof R) {
                                    rc.getValue().getContent().each { rrc ->
                                        if(rrc instanceof JAXBElement) {
                                            key = rrc.getValue().getValue()
                                            isMapped(key) ? rrc.getValue().setValue(getNewMapping(key)) : null
                                        }
                                    }
                                }
                            }
                        }
                    }
                    else if(sdtcc instanceof R) {
                        sdtcc.getContent().each { rc ->
                            if(rc instanceof JAXBElement) {
                                key = rc.getValue().getValue()
                                isMapped(key) ? rc.getValue().setValue(getNewMapping(key)) : null
                            }
                        }
                    }
                    else if(sdtcc instanceof JAXBElement) {
                        if(sdtcc.getValue() instanceof CTBookmark) {
    
                        }
                        else if(sdtcc.getValue() instanceof JAXBElement) {
                            key = sdtcc.getValue().getValue()
                            isMapped(key) ? sdtcc.getValue().setValue(getNewMapping(key)) : null
                        }
                    }
                }
            }
        }
    
        public void setMapping(Map contentControlMapping) {
            this.contentControlMapping = contentControlMapping
        }
    }
    

    此类的核心部分是mapNewMapping()方法。基本上它的作用是将contentControlMapping变量上的映射映射到<w:t>内的任何<w:sdt>,无论它是直接在<w:sdt>下还是在<w:rPr>下。在<w:sdt>等内部。我使用getJAXBNodesViaXPath()方法检索所有P的列表。

    此限制是,这只能支持RCTBookmarkSdtBlockSdtContentSdtRun,{{1}的有限组合}}。如果在我未预料到的复杂或深嵌套<w:t>内找到.xml,则不会映射它。这就是为什么我提到我已经先阅读了.xml文件的.docx