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

时间:2019-12-02 17:51:39

标签: c# openxml libreoffice

我正在使用Open XML SDK在C#中创建WordprocessingDocument,然后将它们转换为pdf。最初,我是使用Interop将文档保存为PDF格式的,但是现在这不是一个选择。我发现LibreOffice可以转换从cmd调用soffice.exe的文档,使用普通文档我得到了很好的结果。但是,当我用动态文档测试LibreOffice转换器时,该转换器崩溃了。

我复制了其中一个文档,并使用LibreOffice Writer打开了该文档,其结构错误,然后我使用Microsoft Word打开了相同的文档,并且其结构还不错。最后,我用Microsoft Word将其保存,并按如下所示打开两个文档作为ZIP文件:

这是好东西:

Good document structure

这是坏事:

Bad document structure

我注意到,当我在Microsoft Word中保存文档时,出现了这些Open XML部分(在此问题的早期版本中称为“文件”)。当我在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文档,其结构如下:

Result.docx structure

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

1 个答案:

答案 0 :(得分:1)

您需要在以下方面有所作为:

  • Open XML标准及其在WordprocessingDocument
  • 上的最低要求
  • 由Microsoft Word或其他应用程序创建的“最小”文档。

根据标准,最低WordprocessingDocument仅需要一个主要文档部分(MainDocumentPartdocument.xml),其内容如下:

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

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

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

  1. 引用样式的段落(请参见CreateStyledParagraph(docBody)),必须在StyleDefinitionsPartstyles.xml)中进行定义;和
  2. 编号列表(例如CreateList(docBody)),必须在NumberingDefinitionsPartnumbering.xml)中定义。

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

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

相反,根据LibreOffice的容错程度,无效的Open XML标记可能会导致崩溃。例如,如果LibreOffice在您的StyleDefinitionsPart中找到类似<w:pStyle w:val="MyStyleName" />的元素时只是假设w:document存在,然后在询问时不检查是否获得了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();
}