使用Open XML C#在Word文件中为现有文本添加上标

时间:2019-11-13 03:31:08

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

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

Superscript st, nd, rd, th... in exist document

1 个答案:

答案 0 :(得分:0)

以下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:rPrRunProperties类和孙子元素w:vertAlignVerticalTextAlignment类) ),将“ 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)方法为要渲染的任何文本(即,没有前导或尾随空格)生成具有正确w:text属性的xml:space元素。

// 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类。