替换字处理文档中的样式部件
本主题演示如何使用 Open XML SDK for Office 中的类以编程方式将字处理文档中的样式替换为另一个字处理文档中的样式。 本文包含一个演示此任务的示例 ReplaceStyles 方法,以及 ReplaceStylesPart 和 ExtractStylesPart 支持方法。
关于样式存储
字处理文档包(如扩展名为 .docx 的文件)实际上是一个包含多个部分的 .zip 文件。 可将每个部分视为与外部文件类似。 每个部分具有一个特定的内容类型,并且可能包含与外部 XML 文件、二进制文件、图像文件等文件的内容相同的内容,具体取决于类型。 定义 Open XML 文档在 .zip 文件中的存储方式的标准称为"开放打包约定"。 有关开放打包约定的详细信息,请参阅 ISO/IEC 29500-2(该链接可能指向英文页面)。
样式存储在字处理文档包内的专用部件中。 Microsoft Word 2010 文档包含一个样式部件。 Microsoft Word 2013 添加了另一个 stylesWithEffects 部件。 Microsoft Office Open XML SDK Productivity Tool 中文档资源管理器中的下图显示了包含样式的 2013 Word文档示例中的文档部件。
图 1. 字处理文档中的样式部分
为了提供从 Word 2013 年到 2010 Word 2010 年及之后的文档的“往返”,Word 2013 将保留原始样式部分和新样式部分。 (Office Open XML 文件格式规范要求 Microsoft Word忽略它无法识别的任何部分:Word 2010 不会注意到 2013 Word添加到 document 的 stylesWithEffects 部分。)
本主题中提供的示例代码可用于替换这些样式部分。
ReplaceStyles 方法
可以使用 ReplaceStyles 示例方法将一个字处理文档中的样式替换为另一字处理文档中的样式。 ReplaceStyles 方法接受两个参数:第一个参数包含的字符串指示包含要提取的样式的文件的路径。 第二个参数包含的字符串指示要将样式复制到的文件的路径,以便有效地完全替换样式。
public static void ReplaceStyles(string fromDoc, string toDoc)
ReplaceStyles 方法及其支持方法的完整代码列表位于示例代码部分。
调用示例方法
为调用示例方法,可传递一个字符串用作第一个参数,指示包含要提取的样式的文件的路径,再传递一个字符串用作第二个参数,表示要替换其中的样式的文件的路径。 以下示例代码演示了一个示例。 当代码执行完毕时,将替换目标文档中的样式,因此,文档中文本的外观将反映新样式。
const string fromDoc = @"C:\Users\Public\Documents\StylesFrom.docx";
const string toDoc = @"C:\Users\Public\Documents\StylesTo.docx";
ReplaceStyles(fromDoc, toDoc);
代码的工作方式
该代码先提取和替换样式部分,然后提取和替换 stylesWithEffects 部分,再依赖两个支持方法执行大多数工作。 ExtractStylesPart 方法的工作是提取样式或 stylesWithEffects 部件的内容,并将其置于 XDocument 对象中。 ReplaceStylesPart 方法提取 ExtractStylesPart 创建的对象,并使用其内容来替换目标文档中的 styles 或 stylesWithEffects 部分。
// Extract and replace the styles part.
var node = ExtractStylesPart(fromDoc, false);
if (node != null)
ReplaceStylesPart(toDoc, node, false);
ExtractStylesPart 或 ReplaceStylesPart 方法的签名中的最后一个参数用于确定是使用 styles 部分还是 stylesWithEffects 部分。 值为 false 指示要提取并替换 styles 部分。 缺少值(该参数为可选)或值为 true(默认)意味着要提取并替换 stylesWithEffects 部分。
// Extract and replace the stylesWithEffects part. To fully support
// round-tripping from Word 2013 to Word 2010, you should
// replace this part, as well.
node = ExtractStylesPart(fromDoc);
if (node != null)
ReplaceStylesPart(toDoc, node);
return;
有关 ExtractStylesPart 方法的详细信息,请参阅 关联的示例。 下面的部分介绍 ReplaceStylesPart 方法。
ReplaceStylesPart 方法
ReplaceStylesPart 方法可用于替换文档中的 styles 或 styleWithEffects 部件,如果 XDocument 实例包含Word 2010 或 Word 2013 文档 (的相同部件,如本主题前面的示例代码所示,ExtractStylesPart 方法可用于获取该实例) 。 ReplaceStylesPart 方法接受三个参数:第一个参数包含一个字符串,该字符串指示要修改的文件的路径。 第二个参数包含一个 XDocument 对象,该对象包含另一个字处理文档中的 styles 或 stylesWithEffect 部分,第三个参数指示是要替换 styles 部分,还是替换 stylesWithEffects 部分 (,如本主题前面的示例代码所示,对于Word 2013 文档,需要调用此过程两次, 将每个部件替换为源文档) 中的相应部件。
public static void ReplaceStylesPart(string fileName, XDocument newStyles,
bool setStylesWithEffectsPart = true)
ReplaceStylesPart 代码的工作方式
ReplaceStylesPart 方法用于检查您指定的文档,以便查找 styles 或 stylesWithEffects 部分。 如果请求的部分存在,该方法会将提供的 XDocument 保存到选定部分。
代码首先使用 Open 方法打开文档,并指示应打开文档以 (最终 true 参数) 进行读/写访问。 给定打开的文档,代码使用 MainDocumentPart 属性导航到main文档部件,然后准备一个名为 stylesPart 的变量来保存对样式部件的引用。
// Open the document for write access and get a reference.
using (var document =
WordprocessingDocument.Open(fileName, true))
{
// Get a reference to the main document part.
var docPart = document.MainDocumentPart;
// Assign a reference to the appropriate part to the
// stylesPart variable.
StylesPart stylesPart = null;
查找正确的 Styles 部分
接下来,代码使用 setStylesWithEffectsPart 布尔参数检索对请求的样式部分的引用。 然后,该代码将基于此值检索对请求的 styles 部分的引用,并将其存储在 stylesPart 变量中。
if (setStylesWithEffectsPart)
stylesPart = docPart.StylesWithEffectsPart;
else
stylesPart = docPart.StyleDefinitionsPart;
保存部分内容
假定请求的部分存在,该代码必须将传递给该方法的 XDocument 的整个内容保存到该部分。 每个部分都提供 一个 GetStream 方法,该方法返回 Stream。 代码将 Stream 实例传递给 StreamWriter 类的构造函数,从而围绕部件的流创建流编写器。 最后,代码调用 XDocument 的 Save 方法,将其内容保存到样式部分。
// If the part exists, populate it with the new styles.
if (stylesPart != null)
{
newStyles.Save(new StreamWriter(stylesPart.GetStream(
FileMode.Create, FileAccess.Write)));
}
示例代码
下面是 C# 和 Visual Basic 中的完整 ReplaceStyles、 ReplaceStylesPart 和 ExtractStylesPart 方法。
using DocumentFormat.OpenXml.Packaging;
using System;
using System.IO;
using System.Xml;
using System.Xml.Linq;
ReplaceStyles(args[0], args[1]);
// Replace the styles in the "to" document with the styles in
// the "from" document.
static void ReplaceStyles(string fromDoc, string toDoc)
{
// Extract and replace the styles part.
var node = ExtractStylesPart(fromDoc, false);
if (node is not null)
ReplaceStylesPart(toDoc, node, false);
// Extract and replace the stylesWithEffects part. To fully support
// round-tripping from Word 2010 to Word 2007, you should
// replace this part, as well.
node = ExtractStylesPart(fromDoc);
if (node is not null)
ReplaceStylesPart(toDoc, node);
return;
}
// Given a file and an XDocument instance that contains the content of
// a styles or stylesWithEffects part, replace the styles in the file
// with the styles in the XDocument.
static void ReplaceStylesPart(string fileName, XDocument newStyles, bool setStylesWithEffectsPart = true)
{
// Open the document for write access and get a reference.
using (var document = WordprocessingDocument.Open(fileName, true))
{
if (document.MainDocumentPart is null || document.MainDocumentPart.StyleDefinitionsPart is null || document.MainDocumentPart.StylesWithEffectsPart is null)
{
throw new ArgumentNullException("MainDocumentPart and/or one or both of the Styles parts is null.");
}
// Get a reference to the main document part.
var docPart = document.MainDocumentPart;
// Assign a reference to the appropriate part to the
// stylesPart variable.
StylesPart? stylesPart = null;
if (setStylesWithEffectsPart)
stylesPart = docPart.StylesWithEffectsPart;
else
stylesPart = docPart.StyleDefinitionsPart;
// If the part exists, populate it with the new styles.
if (stylesPart is not null)
{
newStyles.Save(new StreamWriter(stylesPart.GetStream(
FileMode.Create, FileAccess.Write)));
}
}
}
// Extract the styles or stylesWithEffects part from a
// word processing document as an XDocument instance.
static XDocument ExtractStylesPart(string fileName, bool getStylesWithEffectsPart = true)
{
// Declare a variable to hold the XDocument.
XDocument? styles = null;
// Open the document for read access and get a reference.
using (var document = WordprocessingDocument.Open(fileName, false))
{
// Get a reference to the main document part.
var docPart = document.MainDocumentPart;
if (docPart is null || docPart.StyleDefinitionsPart is null || docPart.StylesWithEffectsPart is null)
{
throw new ArgumentNullException("MainDocumentPart and/or one or both of the Styles parts is null.");
}
// Assign a reference to the appropriate part to the
// stylesPart variable.
StylesPart? stylesPart = null;
if (getStylesWithEffectsPart)
stylesPart = docPart.StylesWithEffectsPart;
else
stylesPart = docPart.StyleDefinitionsPart;
using (var reader = XmlNodeReader.Create(
stylesPart.GetStream(FileMode.Open, FileAccess.Read)))
{
// Create the XDocument.
styles = XDocument.Load(reader);
}
}
// Return the XDocument instance.
return styles;
}