12

I am using Word and OpenXml to provide mail merge functionality in a C# ASP.NET web application:

1) A document is uploaded with a number of pre-defined strings for substitution.

2) Using the OpenXML SDK 2.0 I open the Word document, get the mainDocumentPart as a string and perform the substitution using Regex.

3) I then create a new document using OpenXML, add a new mainDocumentPart and insert the string resulting from the substitution into this mainDocumentPart.

However, all formatting/styles etc. are lost in the new document.

I'm guessing I can copy and add the Style, Definitions, Comment parts etc.. individually to mimic the orginal document.

However is there a method using Open XML to duplicate a document allowing me to perform the substitutions on the new copy?

Thanks.

4

6 回答 6

16

这段代码应该将现有文档中的所有部分复制到新文档中。

using (var mainDoc = WordprocessingDocument.Open(@"c:\sourcedoc.docx", false))
using (var resultDoc = WordprocessingDocument.Create(@"c:\newdoc.docx",
  WordprocessingDocumentType.Document))
{
  // copy parts from source document to new document
  foreach (var part in mainDoc.Parts)
    resultDoc.AddPart(part.OpenXmlPart, part.RelationshipId);
  // perform replacements in resultDoc.MainDocumentPart
  // ...
}
于 2010-03-31T16:53:49.987 回答
7

我第二次使用 Content Controls 推荐。使用它们来标记要执行替换的文档区域是迄今为止最简单的方法。

至于复制文档(并保留整个文档的内容、样式和所有内容),它相对容易:

string documentURL = "full URL to your document";
byte[] docAsArray = File.ReadAllBytes(documentURL);

using (MemoryStream stream = new MemoryStream)
{
    stream.Write(docAsArray, 0, docAsArray.Length);    // THIS performs doc copy
    using (WordprocessingDocument doc = WordprocessingDocument.Open(stream, true))
    {
        // perform content control substitution here, making sure to call .Save()
        // on any documents Part's changed.
    }
    File.WriteAllBytes("full URL of your new doc to save, including .docx", stream.ToArray());
}

实际上使用 LINQ 找到内容控件是小菜一碟。以下示例查找所有简单文本内容控件(键入为 SdtRun):

using (WordprocessingDocument doc = WordprocessingDocument.Open(stream, true))
{                    
    var mainDocument = doc.MainDocumentPart.Document;
    var contentControls = from sdt in mainDocument.Descendants<SdtRun>() select sdt;

    foreach (var cc in contentControls)
    {
        // drill down through the containment hierarchy to get to 
        // the contained <Text> object
        cc.SdtContentRun.GetFirstChild<Run>().GetFirstChild<Text>().Text = "my replacement string";
    }
}

<Run>and<Text>元素可能不存在,但创建它们很简单:

cc.SdtContentRun.Append(new Run(new Text("my replacement string")));

希望对某人有所帮助。:D

于 2010-02-08T20:50:52.110 回答
4

最初的问题是在向 Open XML SDK 添加许多有用的功能之前提出的。如今,如果您已经打开了WordprocessingDocument,您只需克隆原始文档并对该克隆执行任何转换。

// Say you have done this somewhere before you want to duplicate your document.
using WordprocessingDocument originalDoc = WordprocessingDocument.Open("original.docx", false);

// Then this is how you can clone the opened WordprocessingDocument.
using var newDoc = (WordprocessingDocument) originalDoc.Clone("copy.docx", true);

// Perform whatever transformation you want to do.
PerformTransformation(newDoc);

您也可以在Stream或上克隆Package。总体而言,您有以下选择:

OpenXmlPackage Clone()

OpenXmlPackage Clone(Stream stream)
OpenXmlPackage Clone(Stream stream, bool isEditable)
OpenXmlPackage Clone(Stream stream, bool isEditable, OpenSettings openSettings)

OpenXmlPackage Clone(string path)
OpenXmlPackage Clone(string path, bool isEditable)
OpenXmlPackage Clone(string path, bool isEditable, OpenSettings openSettings)

OpenXmlPackage Clone(Package package)
OpenXmlPackage Clone(Package package, OpenSettings openSettings)

有关这些方法的详细信息,请查看 Open XML SDK 文档。

话虽如此,如果您还没有打开WordprocessingDocument,至少有更快的方法来复制或克隆文档。我在关于克隆 Office Open XML 文档的最有效方法的回答中已经证明了这一点。

于 2019-11-16T15:57:52.773 回答
2

我做了一些非常相似的事情,但我没有使用文本替换字符串,而是使用 Word 内容控件。我在以下博客文章SharePoint 和 Open Xml中记录了一些详细信息。该技术并非特定于 SharePoint。您可以在纯 ASP.NET 或其他应用程序中重用该模式。

此外,我强烈建议您查看Eric White 的博客,了解有关 Open Xml 的提示、技巧和技术。具体来说,查看 Open Xml post 的内存操作,以及Word 内容控制帖子。我认为从长远来看,您会发现这些更有帮助。

希望这可以帮助。

于 2009-07-23T20:01:37.950 回答
2

作为上述内容的补充;可能更有用的是查找已标记的内容控件(使用 GUI 一词)。我最近编写了一些填充文档模板的软件,这些模板包含带有附加标签的内容控件。查找它们只是上述 LINQ 查询的扩展:

var mainDocument = doc.MainDocumentPart.Document;
var taggedContentControls = from sdt in mainDocument.Descendants<SdtElement>()
                            let sdtPr = sdt.GetFirstChild<SdtProperties>()
                            let tag = (sdtPr == null ? null : sdtPr.GetFirstChild<Tag>())
                            where (tag != null)
                            select new
                            {
                                SdtElem = sdt,
                                TagName = tag.GetAttribute("val", W).Value
                            };   

我从其他地方得到了这个代码,但现在不记得在哪里;完全归功于他们。

该查询只创建一个匿名类型的 IEnumerable,其中包含内容控件及其关联标记作为属性。便利!

于 2010-02-08T20:59:51.017 回答
0

当您通过将扩展名更改为 zip 并打开它来查看 openxml 文档时,您会看到该单词子文件夹包含一个 _rels 文件夹,其中列出了所有关系。这些关系指向您提到的部分(样式...)。实际上,您需要这些部分,因为它们包含格式的定义。因此,不复制它们将导致新文档使用 normal.dot 文件中定义的格式,而不是原始文档中定义的格式。所以我认为你必须复制它们。

于 2009-07-17T12:31:12.190 回答