将样式应用于字处理文档中的段落

本主题演示如何使用 Open XML SDK for Office 中的类以编程方式将样式应用于字处理文档中的段落。 它包含演示此任务的示例ApplyStyleToParagraph方法,以及几个补充示例方法,用于检查样式是否存在、添加新样式和添加样式部分。

ApplyStyleToParagraph 方法

示例 ApplyStyleToParagraph 方法可用于将样式应用于段落。 必须首先获取对文档的引用以及对您要应用样式的段落的引用。 方法接受四个参数,这些参数指示:要打开的字处理文档的路径、要应用的样式的 styleid、要应用的样式的名称,以及要对其应用样式的段落的引用。

static void ApplyStyleToParagraph(WordprocessingDocument doc, string styleid, string stylename, Paragraph p)

本主题的后续节将说明此方法和支持代码的实现及其调用方式。 可以在本主题的示例代码一节找到完整示例代码列表。

获取 WordprocessingDocument 对象

"示例代码"一节还显示调用示例方法所需设置的代码。 若要使用将样式应用到文档的段落中的方法,首先需要对打开文件的引用。 在 Open XML SDK 中WordprocessingDocument, 类表示Word文档包。 若要打开和使用Word文档,请从文档创建 类的WordprocessingDocument实例。 创建实例后,使用它获取对包含文档文本的 main 文档部件的访问权限。 主文档部件中的内容使用 WordprocessingML 标记在包中表示为 XML。

若要创建类实例,请调用 方法的 Open 重载之一。 下面的示例代码演示如何使用 DocumentFormat.OpenXml.Packaging.WordprocessingDocument.Open(String, Boolean) 重载。 第一个参数获取代表要打开的文档的完整路径的字符串。 第二个参数采用 或 falsetrue,并表示是否打开文件进行编辑。 在此示例中, 参数用于 true 启用对文件的读/写访问。

using (WordprocessingDocument doc = WordprocessingDocument.Open(args[0], true))

WordProcessingML 文档的结构

文档的基本文档结构WordProcessingML由 和 body 元素组成document,后跟一个或多个块级元素(例如 p表示段落)。 段落包含一个或多个 r 元素。 r代表 run,它是具有一组通用属性(如格式设置)的文本区域。 运行包含一个或多个 t 元素。 元素 t 包含文本范围。 下面的代码示例演示 WordprocessingML 包含文本“示例文本”的文档的标记。

    <w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
      <w:body>
        <w:p>
          <w:r>
            <w:t>Example text.</w:t>
          </w:r>
        </w:p>
      </w:body>
    </w:document>

使用 Open XML SDK,可以使用与元素对应的 WordprocessingML 强类型类创建文档结构和内容。 可以在 命名空间中找到 这些类。 下表列出了对应于 、、bodyrpt 元素的类的document类名。

WordprocessingML 元素 Open XML SDK 类 说明
<document/> Document 主文档部件的根元素。
<body/> Body 块级结构(如段落、表格、批注和 ISO/IEC 29500 规范中指定的其他项)的容器。
<p/> Paragraph 段落。
<r/> Run 一段连续文本。
<t/> Text 文本范围。

有关 WordprocessingML 文档的各个部分和元素的整体结构的详细信息,请参阅 WordprocessingML 文档的结构

获取要设置样式的段落

打开文件后,示例代码会检索对第一段的引用。 由于典型的字处理文档正文包含多种类型的元素,因此代码会将文档正文中的后代筛选为 类型 Paragraph的后代。 然后,使用 ElementAtOrDefault 方法检索对段落的引用。 因为从零开始对元素编写了索引,因此传递零以检索对第一段的引用,如以下代码示例所示。

// Get the first paragraph in the document.
Paragraph? paragraph = doc?.MainDocumentPart?.Document?.Body?.Descendants<Paragraph>().ElementAtOrDefault(0);

对找到的段落的引用存储在名为 paragraph 的变量中。 如果在指定的索引处找不到段落,该方法 ElementAtOrDefault 将返回 null 作为默认值。 这提供了测试 null 并引发具有相应错误消息的错误的机会。

对文档和段落的引用后,可以调用 ApplyStyleToParagraph 示例方法来完成剩余的工作。 若要调用该方法,请将对文档的引用作为第一个参数传递、要应用的样式的 styleid 作为第二个参数、样式的名称作为第三个参数、对要对其应用样式的段落的引用作为第四个参数。

添加段落属性元素

示例方法的第一步是确保段落具有段落属性元素。 段落属性元素是段落的子元素并且包括一组可让您指定段落的格式设置的属性。

ISO/IEC 29500 规范中的以下信息介绍了 pPr 用于指定段落格式的元素) (段落属性。 注意以 § 开头的节编号都来自 ISO 规范。

在段落中,段落级别的所有丰富格式都存储在 pPr 元素 (§17.3.1.25;§17.3.1.26) 中。 [注意:段落属性的一些示例包括对齐方式、边框、断字替代、缩进、行距、底纹、文本方向和寡/孤立控件。

属性中包括 pStyle 用于指定要应用于段落的样式的元素。 例如,以下示例标记显示了指定"OverdueAmount"类型的 pStyle 元素。

    <w:p  xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
      <w:pPr>
        <w:pStyle w:val="OverdueAmount" />
      </w:pPr>
      ... 
    </w:p>

在 Open XML SDK 中, pPr 元素由 ParagraphProperties 类表示。 代码确定 元素是否存在,如果不存在,则创建 类的新实例 ParagraphProperties 。 元素 pPr 是 (段落) 元素的 p 子元素;因此, PrependChild 方法用于添加 实例,如以下代码示例所示。

// If the paragraph has no ParagraphProperties object, create one.
if (p.Elements<ParagraphProperties>().Count() == 0)
{
    p.PrependChild<ParagraphProperties>(new ParagraphProperties());
}

// Get the paragraph properties element of the paragraph.
ParagraphProperties pPr = p.Elements<ParagraphProperties>().First();

添加“样式”部件

找到了段落并且存在段数属性元素,现在请确保存在应用该样式的必备项。 WordprocessingML 中的样式存储在其自己唯一的部件中。 尽管通常通过使用应用程序(如 Microsoft Word)创建文档时会自动创建部件以及一组基准样式,但是无需将文档的样式部件视为有效。 如果使用 Open XML SDK 以编程的方式创建文档,则不会自动创建样式部件;您必须明确创建样式部件。 因此以下代码验证是否存在样式部件,如果没有就创建。

// Get the Styles part for this document.
StyleDefinitionsPart? part = doc.MainDocumentPart?.StyleDefinitionsPart;

// If the Styles part does not exist, add it and then add the style.
if (part is null)
{
    part = AddStylesPartToPackage(doc);

示例 AddStylesPartToPackage 方法执行添加样式部分的工作。 它会创建类型的一部分StyleDefinitionsPart,并将其作为子部分添加到 main 文档部件。 然后,代码追加 Styles 根元素,根元素是包含所有样式的父元素。 元素 StylesStyles Open XML SDK 中的 类表示。 最后,代码保存该部件。

// Add a StylesDefinitionsPart to the document.  Returns a reference to it.
static StyleDefinitionsPart AddStylesPartToPackage(WordprocessingDocument doc)
{
    MainDocumentPart mainDocumentPart = doc.MainDocumentPart ?? doc.AddMainDocumentPart();
    StyleDefinitionsPart part = mainDocumentPart.AddNewPart<StyleDefinitionsPart>();
    Styles root = new Styles();

    return part;
}

验证样式是否存在

应用对于段落不存在的样式是没有效果的;没有异常生成,也不会发生格式设置更改。 示例代码在尝试应用样式之前验证样式。 样式存储在样式部件中,因此如果样式部件不存在,样式本身也无法存在。

如果样式部分存在,则代码通过调用 IsStyleIdInDocument 示例方法并传递文档和 styleid 来验证匹配的样式。 如果在 styleid 上找不到匹配项,则代码会尝试通过调用 GetStyleIdFromStyleName 示例方法并向其传递样式名称来查找 styleid。

如果样式不存在(因为样式部分不存在,或者样式部分存在,但样式不存在),则代码将调用 AddNewStyle 示例方法来添加样式。


// Get the Styles part for this document.
StyleDefinitionsPart? part = doc.MainDocumentPart?.StyleDefinitionsPart;

// If the Styles part does not exist, add it and then add the style.
if (part is null)
{
    part = AddStylesPartToPackage(doc);
    AddNewStyle(part, styleid, stylename);
}
else
{
    // If the style is not in the document, add it.
    if (IsStyleIdInDocument(doc, styleid) != true)
    {
        // No match on styleid, so let's try style name.
        string? styleidFromName = GetStyleIdFromStyleName(doc, stylename);

        if (styleidFromName is null)
        {
            AddNewStyle(part, styleid, stylename);
        }
        else
            styleid = styleidFromName;
    }
}

IsStyleInDocument示例方法中,工作首先通过 Styles main 文档部件的 StyleDefinitionsPart 属性检索Styles元素,然后确定是否存在任何样式作为该元素的子元素。 所有样式元素都存储为样式元素的子级。

如果存在样式,代码会在 styleid 上寻找匹配项。 styleid 是文档中的许多位置用于引用样式的样式属性,并且可被视为其主要标识符。 通常会使用 styleid 来标识代码中的样式。 如果未找到匹配项,则 FirstOrDefault 方法默认为 null,因此代码将验证 null 以查看样式是否匹配,如以下摘录所示。

// Return true if the style id is in the document, false otherwise.
static bool IsStyleIdInDocument(WordprocessingDocument doc, string styleid)
{
    // Get access to the Styles element for this document.
    Styles? s = doc.MainDocumentPart?.StyleDefinitionsPart?.Styles;

    if (s is null)
    {
        return false;
    }

    // Check that there are styles and how many.
    int n = s.Elements<Style>().Count();

    if (n == 0)
    {
        return false;
    }

    // Look for a match on styleid.
    Style? style = s.Elements<Style>()
        .Where(st => (st.StyleId is not null && st.StyleId == styleid) && (st.Type is not null && st.Type == StyleValues.Paragraph))
        .FirstOrDefault();
    if (style is null)
    {
        return false;
    }

    return true;
}

当无法基于 styleid 找到样式时,代码会尝试根据样式名称查找匹配项。 示例 GetStyleIdFromStyleName 方法执行此操作,在样式名称上查找匹配项并返回匹配元素的 styleid(如果找到),如果没有,则返回 null。

将样式添加到样式部件

示例 AddNewStyle 方法采用三个参数。 第一个参数获取对样式部件的引用。 第二个参数获取样式的 styleid,以及第三个参数获取样式名称。 代码 AddNewStyle 在指定的部件中创建命名样式定义。

为了创建样式,代码实例化 Style 类并设置某些属性,例如 Type 样式 (段落) 和 StyleId。 如上所述,文档使用 styleid 表示样式,并可以将其视为主要标识符。 通常使用 styleid 来标识代码中的样式。 样式还可以在用户界面中显示一个单独的用户友好样式名称。 因此,样式名称通常以正确大小写显示,并带有空格(例如,Heading 1),而 styleid 则更简洁(例如,heading1),且旨在供内部使用。 在以下示例代码中,styleid 和样式名称从 styleid 和 stylename 参数中获取值。

下一步是指定几种附加属性(如新样式所基于的样式)和要自动应用到下一段落的样式。 代码将它们都指定为"Normal"样式。 请注意,此处要指定的值为常规样式的 styleid。 代码会将这些属性作为样式元素的子级追加。

代码完成样式的实例化和设置基本属性之后,就着手样式格式设置。 样式格式在段落属性 () pPr 执行,并运行 (rPr) 元素的属性。 若要在段落中为连续文本设置字体和颜色特征,请使用连续文本属性。

若要创建 rPr 具有相应子元素的元素,代码会创建 类的 StyleRunProperties 实例,然后追加相应属性类的实例。 以此代码为例,样式指定 Lucida Console 字体,磅值为 12,,以粗体和斜体呈现,使用文档主题部件中的 Accent2 颜色。 磅值指定为半磅,因此值 24 相当于 12 磅

样式定义完整时,代码会将样式追加到样式部件的样式元素中,如以下代码示例所示。

// Create a new style with the specified styleid and stylename and add it to the specified
// style definitions part.
static void AddNewStyle(StyleDefinitionsPart styleDefinitionsPart, string styleid, string stylename)
{
    // Get access to the root element of the styles part.
    styleDefinitionsPart.Styles ??= new Styles();
    Styles styles = styleDefinitionsPart.Styles;

    // Create a new paragraph style and specify some of the properties.
    Style style = new Style()
    {
        Type = StyleValues.Paragraph,
        StyleId = styleid,
        CustomStyle = true
    };
    StyleName styleName1 = new StyleName() { Val = stylename };
    BasedOn basedOn1 = new BasedOn() { Val = "Normal" };
    NextParagraphStyle nextParagraphStyle1 = new NextParagraphStyle() { Val = "Normal" };
    style.Append(styleName1);
    style.Append(basedOn1);
    style.Append(nextParagraphStyle1);

    // Create the StyleRunProperties object and specify some of the run properties.
    StyleRunProperties styleRunProperties1 = new StyleRunProperties();
    Bold bold1 = new Bold();
    Color color1 = new Color() { ThemeColor = ThemeColorValues.Accent2 };
    RunFonts font1 = new RunFonts() { Ascii = "Lucida Console" };
    Italic italic1 = new Italic();
    // Specify a 12 point size.
    FontSize fontSize1 = new FontSize() { Val = "24" };
    styleRunProperties1.Append(bold1);
    styleRunProperties1.Append(color1);
    styleRunProperties1.Append(font1);
    styleRunProperties1.Append(fontSize1);
    styleRunProperties1.Append(italic1);

    // Add the run properties to the style.
    style.Append(styleRunProperties1);

    // Add the style to the styles part.
    styles.Append(style);
}

示例代码

以下是使用 C# 和 Visual Basic 编写的完整示例代码。

using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using System;
using System.Linq;

using (WordprocessingDocument doc = WordprocessingDocument.Open(args[0], true))
{
    // Get the first paragraph in the document.
    Paragraph? paragraph = doc?.MainDocumentPart?.Document?.Body?.Descendants<Paragraph>().ElementAtOrDefault(0);

    if (paragraph is not null)
    {
        ApplyStyleToParagraph(doc!, "MyStyle", "MyStyleName", paragraph);
    }
}


// Apply a style to a paragraph.
static void ApplyStyleToParagraph(WordprocessingDocument doc, string styleid, string stylename, Paragraph p)
{
    if (doc is null)
    {
        throw new ArgumentNullException(nameof(doc));
    }

    // If the paragraph has no ParagraphProperties object, create one.
    if (p.Elements<ParagraphProperties>().Count() == 0)
    {
        p.PrependChild<ParagraphProperties>(new ParagraphProperties());
    }

    // Get the paragraph properties element of the paragraph.
    ParagraphProperties pPr = p.Elements<ParagraphProperties>().First();


    // Get the Styles part for this document.
    StyleDefinitionsPart? part = doc.MainDocumentPart?.StyleDefinitionsPart;

    // If the Styles part does not exist, add it and then add the style.
    if (part is null)
    {
        part = AddStylesPartToPackage(doc);
        AddNewStyle(part, styleid, stylename);
    }
    else
    {
        // If the style is not in the document, add it.
        if (IsStyleIdInDocument(doc, styleid) != true)
        {
            // No match on styleid, so let's try style name.
            string? styleidFromName = GetStyleIdFromStyleName(doc, stylename);

            if (styleidFromName is null)
            {
                AddNewStyle(part, styleid, stylename);
            }
            else
                styleid = styleidFromName;
        }
    }

    // Set the style of the paragraph.
    pPr.ParagraphStyleId = new ParagraphStyleId() { Val = styleid };
}

// Return true if the style id is in the document, false otherwise.
static bool IsStyleIdInDocument(WordprocessingDocument doc, string styleid)
{
    // Get access to the Styles element for this document.
    Styles? s = doc.MainDocumentPart?.StyleDefinitionsPart?.Styles;

    if (s is null)
    {
        return false;
    }

    // Check that there are styles and how many.
    int n = s.Elements<Style>().Count();

    if (n == 0)
    {
        return false;
    }

    // Look for a match on styleid.
    Style? style = s.Elements<Style>()
        .Where(st => (st.StyleId is not null && st.StyleId == styleid) && (st.Type is not null && st.Type == StyleValues.Paragraph))
        .FirstOrDefault();
    if (style is null)
    {
        return false;
    }

    return true;
}

// Return styleid that matches the styleName, or null when there's no match.
static string? GetStyleIdFromStyleName(WordprocessingDocument doc, string styleName)
{
    StyleDefinitionsPart? stylePart = doc.MainDocumentPart?.StyleDefinitionsPart;
    string? styleId = stylePart?.Styles?.Descendants<StyleName>()
        .Where(s =>
        {
            OpenXmlElement? p = s.Parent;
            EnumValue<StyleValues>? styleValue = p is null ? null : ((Style)p).Type;

            return s.Val is not null && s.Val.Value is not null && s.Val.Value.Equals(styleName) &&
            (styleValue is not null && styleValue == StyleValues.Paragraph);
        })
        .Select(n =>
        {

            OpenXmlElement? p = n.Parent;
            return p is null ? null : ((Style)p).StyleId;
        }).FirstOrDefault();

    return styleId;
}

// Create a new style with the specified styleid and stylename and add it to the specified
// style definitions part.
static void AddNewStyle(StyleDefinitionsPart styleDefinitionsPart, string styleid, string stylename)
{
    // Get access to the root element of the styles part.
    styleDefinitionsPart.Styles ??= new Styles();
    Styles styles = styleDefinitionsPart.Styles;

    // Create a new paragraph style and specify some of the properties.
    Style style = new Style()
    {
        Type = StyleValues.Paragraph,
        StyleId = styleid,
        CustomStyle = true
    };
    StyleName styleName1 = new StyleName() { Val = stylename };
    BasedOn basedOn1 = new BasedOn() { Val = "Normal" };
    NextParagraphStyle nextParagraphStyle1 = new NextParagraphStyle() { Val = "Normal" };
    style.Append(styleName1);
    style.Append(basedOn1);
    style.Append(nextParagraphStyle1);

    // Create the StyleRunProperties object and specify some of the run properties.
    StyleRunProperties styleRunProperties1 = new StyleRunProperties();
    Bold bold1 = new Bold();
    Color color1 = new Color() { ThemeColor = ThemeColorValues.Accent2 };
    RunFonts font1 = new RunFonts() { Ascii = "Lucida Console" };
    Italic italic1 = new Italic();
    // Specify a 12 point size.
    FontSize fontSize1 = new FontSize() { Val = "24" };
    styleRunProperties1.Append(bold1);
    styleRunProperties1.Append(color1);
    styleRunProperties1.Append(font1);
    styleRunProperties1.Append(fontSize1);
    styleRunProperties1.Append(italic1);

    // Add the run properties to the style.
    style.Append(styleRunProperties1);

    // Add the style to the styles part.
    styles.Append(style);
}

// Add a StylesDefinitionsPart to the document.  Returns a reference to it.
static StyleDefinitionsPart AddStylesPartToPackage(WordprocessingDocument doc)
{
    MainDocumentPart mainDocumentPart = doc.MainDocumentPart ?? doc.AddMainDocumentPart();
    StyleDefinitionsPart part = mainDocumentPart.AddNewPart<StyleDefinitionsPart>();
    Styles root = new Styles();

    return part;
}