【问题标题】:Superscript an existing text in Word file using Open XML C#使用 Open XML C# 在 Word 文件中为现有文本添加上标
【发布时间】:2019-11-25 07:55:58
【问题描述】:

如何通过 OpenXML C# SDK 为文档中已存在的文本添加上标(见下图)?我找到了一篇文章,但它只是添加了新的上标(不存在文本) This link

【问题讨论】:

  • 了解如何使用 OpenXML SDK 的最佳方法是从 Microsoft 站点下载 OpenXML Productivity Tool。创建具有所需功能的文档(使用 Word),保存,然后使用该工具将其破解。您还可以创建文档的两个版本,一个带有您感兴趣的功能,一个没有。然后使用该工具对两个文档进行diff。您的功能将很容易看到

标签: c# ms-word openxml openxml-sdk


【解决方案1】:

以下 Open XML 标记表示您的“出生日期:2019 年 10 月 15 日”行,其中“th”格式为上标:

    <w:p>
        <w:r>
            <w:t>Date of birth: October 15</w:t>
        </w:r>
        <w:r>
            <w:rPr>
                <w:vertAlign w:val="superscript"/>
            </w:rPr>
            <w:t>th</w:t>
        </w:r>
        <w:r>
            <w:t>, 2019</w:t>
        </w:r>
    </w:p>

注意第二个w:r 元素(Run 类)及其w:rPr 子元素(RunProperties 类)和w:vertAlign 孙子元素(VerticalTextAlignment 类),它将“th”格式化为上标。

使用 Open XML SDK,您将创建上述段落,如下所示(注意缩进匹配,因此您可以看到对应关系):

var paragraph =
    new Paragraph(
        new Run(
            new Text("Date of birth: October 15")),
        new Run(
            new RunProperties(
                new VerticalTextAlignment { Val = VerticalPositionValues.Superscript }),
            new Text("th")),
        new Run(
            new Text(", 2019")));

2019 年 11 月 25 日更新:

如果您想格式化现有文本,如上所示,您必须识别和格式化序数足以“st”、“nd”、“rd”和“th”。 Paragraph FormatSuperscript(string innerText) 方法可以做到这一点。它使用Text CreateText(string text) 方法为您要呈现的任何文本(即没有前导或尾随空格)生成具有正确xml:space 属性的w:text 元素。

// Matches ordinal number suffixes "st", "nd", "rd", and "th".
private static readonly Regex OrdinalNumberSuffixRegex =
    new Regex("(?<=[0-9]+)(st|nd|rd|th)");

/// <summary>
/// Creates a new <see cref="Paragraph" /> with ordinal number suffixes
/// (i.e., "st", "nd", "rd", and "4th") formatted as a superscript.
/// </summary>
/// <param name="innerText">The paragraph's inner text.</param>
/// <returns>A new, formatted <see cref="Paragraph" />.</returns>
public static Paragraph FormatSuperscript(string innerText)
{
    var destParagraph = new Paragraph();
    var startIndex = 0;

    foreach (Match match in OrdinalNumberSuffixRegex.Matches(innerText))
    {
        if (match.Index > startIndex)
        {
            string text = innerText[startIndex..match.Index];
            destParagraph.AppendChild(new Run(CreateText(text)));
        }

        destParagraph.AppendChild(
            new Run(
                new RunProperties(
                    new VerticalTextAlignment
                    {
                        Val = VerticalPositionValues.Superscript
                    }),
                CreateText(match.Value)));

        startIndex = match.Index + match.Length;
    }

    if (startIndex < innerText.Length)
    {
        string text = innerText.Substring(startIndex);
        destParagraph.AppendChild(new Run(CreateText(text)));
    }

    return destParagraph;
}

/// <summary>
/// Creates a new <see cref="Text" /> instance with the correct xml:space
/// attribute value.
/// </summary>
/// <param name="text">The text.</param>
/// <returns>A new <see cref="Text" /> instance.</returns>
public static Text CreateText(string text)
{
    if (string.IsNullOrEmpty(text))
    {
        return new Text();
    }

    if (char.IsWhiteSpace(text[0]) || char.IsWhiteSpace(text[^1]))
    {
        return new Text(text) { Space = SpaceProcessingModeValues.Preserve };
    }

    return new Text(text);
}

以下单元测试展示了如何使用上面的FormatSuperscript() 方法:

[Fact]
public void FormatSuperscript_DateOfBirth_CorrectlyFormatted()
{
    // Say we have a body or other container with a number of paragraphs, one
    // of which is the paragraph that we want to format. In our case, we want
    // the paragraph the inner text of which starts with "Date of birth:"
    var body =
        new Body(
            new Paragraph(new Run(new Text("Full name: Phung Anh Tu"))),
            new Paragraph(new Run(new Text("Date of birth: October 15th, 2019"))),
            new Paragraph(new Run(new Text("Gender: male"))));

    Paragraph sourceParagraph = body
        .Descendants<Paragraph>()
        .First(p => p.InnerText.StartsWith("Date of birth:"));

    // In a first step, we'll create a new, formatted paragraph.
    Paragraph destParagraph = FormatSuperscript(sourceParagraph.InnerText);

    // Next, we format the existing paragraph by replacing it with the new,
    // formatted one.
    body.ReplaceChild(destParagraph, sourceParagraph);

    // Finally, let's verify that we have a single "th" run that is:
    // - preceded by one run with inner text "Date of birth: October 15",
    // - followed by one run with inner text ", 2019", and
    // - formatted as a superscript.
    Assert.Single(body
        .Descendants<Run>()
        .Where(r => r.InnerText == "th" &&
                    r.PreviousSibling().InnerText == "Date of birth: October 15" &&
                    r.NextSibling().InnerText == ", 2019" &&
                    r.RunProperties.VerticalTextAlignment.Val == VerticalPositionValues.Superscript));
}

您可以在我的CodeSnippets GitHub 存储库中找到完整的源代码。查找OrdinalNumberFormattingTests 类。

【讨论】:

  • 感谢您的帮助,但我想编辑现有段落(现有文档)中的文本(不添加新段落),有什么方法可以处理选择吗?还是别的什么?
  • 如果您需要任何更具体的帮助,您需要非常准确地了解它是如何嵌入到您的代码中的。如果您的文本行包含在 w:p 中,则用新的 w:p 替换现有的 w:p 绝对是一种解决方案。您至少必须重新创建和替换 w:r 元素以根据需要格式化文本。例如,如果您的文本包含在单个运行中,则必须创建三个运行才能达到所需的效果,正如我的回答中所示。
  • @AnhTú,您在格式化/创建这些运行方面需要更多帮助吗?
  • @AnhTú,我添加了另一个代码示例,展示了如何格式化现有段落。这会产生完全相同的结果。
猜你喜欢
  • 2013-08-21
  • 1970-01-01
  • 1970-01-01
  • 2015-04-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多