我使用Word和OpenXml在C#ASP.NET Web应用程序中提供邮件合并功能:
1)上传的文档包含许多预定义的字符串以供替换。
2)使用OpenXML SDK 2.0我打开Word文档,将mainDocumentPart作为字符串并使用Regex执行替换。
3)然后我使用OpenXML创建一个新文档,添加一个新的mainDocumentPart并将替换产生的字符串插入到这个mainDocumentPart中。
但是,新文档中的所有格式/样式等都会丢失。
我猜我可以单独复制和添加样式,定义,注释部分等来模仿原始文档。
但是有没有一种方法使用Open XML复制文档,允许我在新副本上执行替换?
感谢。
答案 0 :(得分:13)
这段代码应该将现有文档中的所有部分复制到新文档中。
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
// ...
}
答案 1 :(得分:4)
我第二次使用内容控制建议。使用它们来标记文档中要执行替换的区域是迄今为止最简单的方法。
至于复制文档(并保留整个文档内容,样式和所有内容),这相对容易:
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>
和<Text>
元素可能尚不存在,但创建它们很简单:
cc.SdtContentRun.Append(new Run(new Text("my replacement string")));
希望有人帮助。 :d
答案 2 :(得分:2)
我做了一些非常类似的事情,但我使用的是Word内容控件,而不是使用文本替换字符串。我在以下博文SharePoint and Open Xml中记录了一些细节。该技术
此外,我强烈建议您查看Eric White's Blog有关Open Xml的提示,技巧和技巧。具体来说,请查看in-memory manipulation of Open Xml post和Word content controls帖子。从长远来看,我认为你会发现这些更有帮助。
希望这有帮助。
答案 3 :(得分: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,其中包含内容控件及其关联标记作为属性。方便!
答案 4 :(得分:0)
在将许多有用的功能添加到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
,则至少有更快的方法来复制或克隆文档。我已经在the most efficient way to clone Office Open XML documents的答案中证明了这一点。
答案 5 :(得分:-1)
当您通过将扩展名更改为zip并打开它来查看openxml文档时,您会看到该单词子文件夹包含_rels文件夹,其中列出了所有关系。这些关系指向你提到的部分(风格......)。实际上你需要这些部分,因为它们包含格式的定义。因此,不复制它们将导致新文档使用normal.dot文件中定义的格式,而不是原始文档中定义的格式。所以我认为你必须复制它们。