本主题演示如何使用 Open XML SDK for Office 中的类以编程方式将字处理文档中的样式替换为另一个字处理文档中的样式。 它包含演示此任务的示例 ReplaceStyles
方法,以及 ReplaceStylesPart
和 ExtractStylesPart
支持方法。
关于样式存储
字处理文档包(如扩展名为 .docx 的文件)实际上是一个包含多个部分的 .zip 文件。 可将每个部分视为与外部文件类似。 每个部分具有一个特定的内容类型,并且可能包含与外部 XML 文件、二进制文件、图像文件等文件的内容相同的内容,具体取决于类型。 定义 Open XML 文档在 .zip 文件中的存储方式的标准称为"开放打包约定"。 有关开放打包约定的详细信息,请参阅 ISO/IEC 29500-2(该链接可能指向英文页面)。
样式存储在字处理文档包内的专用部件中。 Microsoft Word 2010文档包含单个样式部件。 更高版本的Microsoft Word添加第二个 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
接受两个参数:第一个参数包含一个字符串,该字符串指示包含要提取的样式的文件的路径。 第二个参数包含的字符串指示要将样式复制到的文件的路径,以便有效地完全替换样式。
static void ReplaceStyles(string fromDoc, string toDoc)
方法及其支持方法的完整代码列表 ReplaceStyles
可在 示例代码 部分找到。
调用示例方法
为调用示例方法,可传递一个字符串用作第一个参数,指示包含要提取的样式的文件的路径,再传递一个字符串用作第二个参数,表示要替换其中的样式的文件的路径。 以下示例代码演示了一个示例。 当代码执行完毕时,将替换目标文档中的样式,因此,文档中文本的外观将反映新样式。
string fromDoc = args[0];
string toDoc = args[1];
ReplaceStyles(fromDoc, toDoc);
代码的工作方式
该代码先提取和替换样式部分,然后提取和替换 stylesWithEffects 部分,再依赖两个支持方法执行大多数工作。 方法 ExtractStylesPart
的工作是提取样式或 stylesWithEffects 部件的内容,并将其置于 对象中 XDocument 。 方法 ReplaceStylesPart
采用 创建的对象 ExtractStylesPart
,并使用其内容替换目标文档中的 styles 或 stylesWithEffects 部件。
// Extract and replace the styles part.
XDocument? node = ExtractStylesPart(fromDoc, false);
if (node is not null)
{
ReplaceStylesPart(toDoc, node, false);
}
或 ReplaceStylesPart
方法的签名中的最后一个ExtractStylesPart
参数确定是使用 styles 部件还是 stylesWithEffects 部件。 值为 false 指示要提取并替换 styles 部分。 缺少值(该参数为可选)或值为 true(默认)意味着要提取并替换 stylesWithEffects 部分。
// 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;
有关 方法的详细信息 ExtractStylesPart
,请参阅 关联的示例。 以下部分介绍了 ReplaceStylesPart
方法。
ReplaceStylesPart 方法
方法ReplaceStylesPart
可用于替换文档中的样式或 styleWithEffects 部件,如果XDocument
某个实例包含Word 2010 或 Word 2013+ 文档 (的相同部件,如本主题前面的示例代码所示,ExtractStylesPart
该方法可用于获取该实例) 。 方法 ReplaceStylesPart
接受三个参数:第一个参数包含一个字符串,该字符串指示要修改的文件的路径。 第二个参数包含一个XDocument
对象,该对象包含另一个字处理文档中的 styles 或 stylesWithEffect 部分,第三个参数指示是要替换 styles 部件,还是要替换 stylesWithEffects 部件 (,如本主题前面示例代码所示,对于 Word 2013+ 文档,需要调用此过程两次, 将每个部件替换为源文档) 中的相应部件。
static void ReplaceStylesPart(string fileName, XDocument newStyles, bool setStylesWithEffectsPart = true)
ReplaceStylesPart 代码的工作方式
方法 ReplaceStylesPart
检查指定的文档,查找样式或 stylesWithEffects 部分。
如果请求的部件存在,该方法会将提供的 XDocument
保存到所选部件中。
代码首先使用 Open 方法打开文档,并指示应打开文档以 (最终 true
参数) 进行读/写访问。 给定打开的文档,代码使用 MainDocumentPart 属性导航到 main 文档部件,然后准备一个名为 的stylesPart
变量来保存对样式部件的引用。
// 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;
查找正确的 Styles 部分
接下来,代码使用布尔参数检索对请求的样式部分的 setStylesWithEffectsPart
引用。 然后,该代码将基于此值检索对请求的 styles 部分的引用,并将其存储在 stylesPart
变量中。
if (setStylesWithEffectsPart)
{
stylesPart = docPart.StylesWithEffectsPart;
}
else
{
stylesPart = docPart.StyleDefinitionsPart;
}
保存部分内容
假设请求的部件存在,则代码必须将传递给 方法的 XDocument
的全部内容保存到 部件。 每个部分都提供一个 GetStream() 方法,该方法返回 Stream。
代码将 Stream 实例传递给 类的StreamWriter(Stream)构造函数,从而围绕部件的流创建流编写器。 最后,代码调用 Save(Stream) XDocument 的 方法,将其内容保存到样式部分。
// 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)));
}
示例代码
下面是 C# 和 ExtractStylesPart
Visual Basic 中的完整 ReplaceStyles
、 ReplaceStylesPart
和 方法。
// 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.
XDocument? 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)
{
throw new ArgumentNullException("MainDocumentPart is null.");
}
// Assign a reference to the appropriate part to the
// stylesPart variable.
StylesPart stylesPart;
if (getStylesWithEffectsPart && docPart.StylesWithEffectsPart is not null)
{
stylesPart = docPart.StylesWithEffectsPart;
}
else if (docPart.StyleDefinitionsPart is not null)
{
stylesPart = docPart.StyleDefinitionsPart;
}
else
{
throw new ArgumentNullException("StyleWithEffectsPart and StyleDefinitionsPart are undefined");
}
using (var reader = XmlNodeReader.Create(stylesPart.GetStream(FileMode.Open, FileAccess.Read)))
{
// Create the XDocument.
styles = XDocument.Load(reader);
}
}
// Return the XDocument instance.
return styles;
}