打开动态创建的 Word 文档中缺少 XML 个部分

Open XML parts are missing in dynamically created Word document

我正在使用 Open XML SDK 在 C# 中创建 WordprocessingDocuments,然后将它们转换为 pdf。最初,我使用 Interop 将文档保存为 PDF 格式,但现在不行了。我发现 LibreOffice 可以转换从 cmd 调用 soffice.exe 的文档,并且我对普通文档有很好的效果。不过,当我用我的动态文档测试 LibreOffice 转换器时,转换器崩溃了。

我复制了其中一份文档并用 LibreOffice Writer 打开它,它的结构是错误的,然后我用 Microsoft Word 打开同一个文档,它的结构没有问题。最后,我用 Microsoft Word 将其保存并以 ZIP 文件的形式打开两个文档,如下所示:

这个不错:

这是最糟糕的:

我注意到当我将文档保存在 Microsoft Word 中时,会出现这些打开的 XML 部分(我在此问题的早期版本中称为 "files" )。当我在 LibreOffice 中打开之前用 Microsoft Word 保存的文档时,文档又好了。

因此,有没有办法在不打开 Microsoft Word 的情况下生成这些 Open XML 部分(在 Word 文档中)?

我使用以下代码(检查它是否正在创建所有文件):

        using (MemoryStream mem = new MemoryStream())
        {
            // Create Document
            using (WordprocessingDocument wordDocument =
                WordprocessingDocument.Create(mem, WordprocessingDocumentType.Document, true))
            {
                // Add a main document part. 
                MainDocumentPart mainPart = wordDocument.AddMainDocumentPart();

                // Create the document structure and add some text.
                mainPart.Document = new Document();
                Body docBody = new Body();

                // Add your docx content here
                CreateParagraph(docBody);
                CreateStyledParagraph(docBody);
                CreateTable(docBody);
                CreateList(docBody);

                Paragraph pImg = new Paragraph();
                ImagePart imagePart = mainPart.AddImagePart(ImagePartType.Jpeg);
                string imgPath = "https://cdn.pixabay.com/photo/2019/11/15/05/23/dog-4627679_960_720.png";
                HttpWebRequest req = (HttpWebRequest)WebRequest.Create(imgPath);
                req.UseDefaultCredentials = true;
                req.PreAuthenticate = true;
                req.Credentials = CredentialCache.DefaultCredentials;
                HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
                imagePart.FeedData(resp.GetResponseStream());

                // 1500000 and 1092000 are img width and height
                Run rImg = new Run(DrawingManager(mainPart.GetIdOfPart(imagePart), "PictureName", 1500000, 1092000, string.Empty));
                pImg.Append(rImg);
                docBody.Append(pImg);

                Paragraph pLink = new Paragraph();
                // For the mainpart see above
                pLink.Append(HyperLinkManager("http://YourLink", "My awesome link", mainPart));
                docBody.Append(pLink);

                mainPart.Document.Append(docBody);
                mainPart.Document.Save();
                wordDocument.Close();
            }

            result = Convert.ToBase64String(mem.ToArray());
        }

上面的代码创建了一个名为 Result.docx 的 Word 文档,其结构如下:

但没有任何其他开放 XML 部分(例如 app.xmlstyles.xml

您需要区分:

  • Open XML 标准及其对 WordprocessingDocument
  • 的最低要求
  • 由 Microsoft Word 或其他应用程序创建的 "minimum" 文档。

按照标准,最小WordprocessingDocument只需要一个主文件部分(MainDocumentPartdocument.xml),内容如下:

<w:document xmlns:w="...">
  <w:body>
    <w:p />
  </w:body>
</w:document>

其他部分,例如 StyleDefinitionsPart (styles.xml) 或 NumberingDefintionsPart (numbering.xml) 仅在您有样式或编号时才需要,在这种情况下,您必须在您的代码中明确创建它们。

接下来,查看您的示例代码,您似乎正在创建:

  1. 引用样式的段落(参见 CreateStyledParagraph(docBody)),必须在 StyleDefinitionsPart (styles.xml) 中定义;和
  2. 编号列表(例如,CreateList(docBody)),必须在 NumberingDefinitionsPart (numbering.xml) 中定义。

但是,您的代码既没有创建 StyleDefinitionsPart 也没有创​​建 NumberingDefintionsPart,这意味着您的文档可能不是有效的 Open XML 文档。

现在,Word 非常宽容并默默地修复了各种问题,忽略了部分 Open XML 标记(例如,您可能已分配给段落的样式)。

相比之下,根据 LibreOffice 的容错程度,无效的 Open XML 标记可能会导致崩溃。例如,如果 LibreOffice 在您的 w:document 中找到类似 <w:pStyle w:val="MyStyleName" /> 的元素时简单地假设 StyleDefinitionsPart 存在,然后在询问时不检查它是否获得 null 引用对于 StyleDefinitionsPart,它可能会崩溃。

最后,要将部件添加到您的 Word 文档,您可以使用 Open XML SDK,如下所示:

[Fact]
public void CanAddParts()
{
    const string path = "Document.docx";
    const WordprocessingDocumentType type = WordprocessingDocumentType.Document;

    using WordprocessingDocument wordDocument = WordprocessingDocument.Create(path, type);

    // Create minimum main document part.
    MainDocumentPart mainDocumentPart = wordDocument.AddMainDocumentPart();
    mainDocumentPart.Document = new Document(new Body(new Paragraph()));

    // Create empty style definitions part.
    var styleDefinitionsPart = mainDocumentPart.AddNewPart<StyleDefinitionsPart>();
    styleDefinitionsPart.Styles = new Styles();

    // Create empty numbering definitions part.
    var numberingDefinitionsPart = mainDocumentPart.AddNewPart<NumberingDefinitionsPart>();
    numberingDefinitionsPart.Numbering = new Numbering();
}