命名空间在保存克隆/编辑的docx时消失,导致Word中出现“不可读内容”消息

Namespace disappearing on save of cloned/edited docx, leading to "Unreadable content" message in Word

提问人:BenHT 提问时间:7/5/2023 最后编辑:BenHT 更新时间:7/6/2023 访问量:37

问:

我们使用 docx4j (docx4j-JAXB-MOXy v11.4.9) 将图像添加到 docx,然后将其传递到另一个系统(only-office)进行编辑。 如果我加载 docx,克隆它,清除正文,将一些原始内容复制回去然后保存它,就会删除其中一个命名空间(现在图像需要)。因此,当您在 Word 中打开时,您当然会收到“不可读内容”消息。

保存的文档缺少document.xml顶部的原始文档中的此命名空间:

xmlns:wpg="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup"

文档.xml 中的 Excerts,显示如何使用 wpg 命名空间添加回退图像:

<w:r>
  <w:rPr>
    <w:color w:val="000000"/>
  </w:rPr>
  <w:t xml:space="preserve">Image :</w:t>
  <mc:AlternateContent>
    <mc:Choice Requires="wpg">
      <w:drawing>
...
      </w:drawing>
    </mc:Choice>
    <mc:Fallback>
      <w:pict>
...
      </w:pict>
    </mc:Fallback>
  </mc:AlternateContent>
</w:r>

从包含该命名空间的原始代码重现的示例代码:

InputStream mergedDocStream = new BufferedInputStream(new FileInputStream("c:\\tmp\\original.docx"), 8 * 1024 * 1024);
WordprocessingMLPackage mergedDoc = WordprocessingMLPackage.load(mergedDocStream);
mergedDocStream.close();
List<Object> mergedDocContentList = mergedDoc.getMainDocumentPart().getContent();
    
// get the body section (contains styles, orientation, headers/footers) which needs adding to each doc
SectPr finalSectionProperties = mergedDoc.getMainDocumentPart().getJaxbElement().getBody().getSectPr();
    
// Create a blank target using the merged file
ObjectFactory objectFactory = new ObjectFactory();
mergedDoc.getMainDocumentPart().getJaxbElement().setBody(objectFactory.createBody());
WordprocessingMLPackage outputDoc = (WordprocessingMLPackage) mergedDoc.clone();
    
// loop through the original doc's contents and add
for (Object content : mergedDocContentList) {
  outputDoc.getMainDocumentPart().addObject(content);
}
    
// add body section for original styles etc
outputDoc.getMainDocumentPart().getJaxbElement().getBody().setSectPr(finalSectionProperties);
    
// save last doc    
File outputFile = new File("c:\\tmp\\output.docx");
outputDoc.save(outputFile);

有没有办法说服它保留该命名空间?

docx4j

评论


答:

1赞 JasonPlutext 7/6/2023 #1

作为后台,JAXB 会自动声明必需的名称空间。

@Requires中指定的是不同的,因为尽管 Word 需要它们,但在 XML 规范意义上不需要它们。

因此,docx4j 会在解组过程中遇到这些情况时对其进行跟踪(请参阅 Docx4jUnmarshallerListener)。但是,如果当时 docx 中不存在 @Requires 属性,则无法执行此操作。

JaxbXmlPart 包含:

/**
 * Specify a namespace prefix (used in mc:Choice/@Requires) which
 * docx4j should declare on the top-level element of the part
 * (otherwise Microsoft Office won't be able to open the file).  
 * 
 * Specify a prefix (eg 'wpg') as opposed to the namespace itself.
 * 
 * This is often done automatically (see further McIgnorableNamespaceDeclarator),
 * but where it isn't, you should invoke this method directly
 * from your code.
 * 
 * @param mcChoiceNamespace
 */
public void addMcChoiceNamespace(String mcChoiceNamespace) {
    this.mcChoiceNamespaces.add(mcChoiceNamespace);
}

就您而言,

outputDoc.getMainDocumentPart().addMcChoiceNamespace("wpg");

应该可以解决问题。