打开动态创建的 Word 文档中缺少 XML 个部分
Open XML parts are missing in dynamically created Word document
我正在使用 Open XML SDK 在 C# 中创建 WordprocessingDocument
s,然后将它们转换为 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.xml
或 styles.xml
)
您需要区分:
- Open XML 标准及其对
WordprocessingDocument
和 的最低要求
- 由 Microsoft Word 或其他应用程序创建的 "minimum" 文档。
按照标准,最小WordprocessingDocument
只需要一个主文件部分(MainDocumentPart
,document.xml
),内容如下:
<w:document xmlns:w="...">
<w:body>
<w:p />
</w:body>
</w:document>
其他部分,例如 StyleDefinitionsPart
(styles.xml
) 或 NumberingDefintionsPart
(numbering.xml
) 仅在您有样式或编号时才需要,在这种情况下,您必须在您的代码中明确创建它们。
接下来,查看您的示例代码,您似乎正在创建:
- 引用样式的段落(参见
CreateStyledParagraph(docBody)
),必须在 StyleDefinitionsPart
(styles.xml
) 中定义;和
- 编号列表(例如,
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();
}
我正在使用 Open XML SDK 在 C# 中创建 WordprocessingDocument
s,然后将它们转换为 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.xml
或 styles.xml
)
您需要区分:
- Open XML 标准及其对
WordprocessingDocument
和 的最低要求
- 由 Microsoft Word 或其他应用程序创建的 "minimum" 文档。
按照标准,最小WordprocessingDocument
只需要一个主文件部分(MainDocumentPart
,document.xml
),内容如下:
<w:document xmlns:w="...">
<w:body>
<w:p />
</w:body>
</w:document>
其他部分,例如 StyleDefinitionsPart
(styles.xml
) 或 NumberingDefintionsPart
(numbering.xml
) 仅在您有样式或编号时才需要,在这种情况下,您必须在您的代码中明确创建它们。
接下来,查看您的示例代码,您似乎正在创建:
- 引用样式的段落(参见
CreateStyledParagraph(docBody)
),必须在StyleDefinitionsPart
(styles.xml
) 中定义;和 - 编号列表(例如,
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();
}