0

How to superscript an existed text in document by OpenXML C# SDK (see image below)? I found an article but it just add new superscript (not exist text) This link

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

Anh Tú
  • 636
  • 7
  • 21
  • 1
    The best way to figure out how to do something with the OpenXML SDK is to download the OpenXML Productivity Tool from the Microsoft site. Create a document (using Word) that has the feature you want, save it, and then crack it open with the tool. You can also create two versions of the document, one with the feature you are interested in, and one without. Then use the tool to *diff* the two docs. Your feature will be easy to see – Flydog57 Nov 13 '19 at 04:50

1 Answers1

0

The following Open XML markup represents your line "Date of birth: October 15th, 2019" with the "th" formatted as a superscript:

    <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>

Note the second w:r element (Run class) with its w:rPr child (RunProperties class) and w:vertAlign grandchild (VerticalTextAlignment class), which formats the "th" as superscript.

Using the Open XML SDK, you would create the above paragraph as follows (noting that the indentation matches so you see the correspondence):

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")));

Update 2019-11-25:

If you want to format existing text as shown above, you'll have to identify and format the ordinal number suffices "st", "nd", "rd", and "th". The Paragraph FormatSuperscript(string innerText) method does that. It uses the Text CreateText(string text) method to produce a w:text element with the correct xml:space attribute for whatever text (i.e., without leading or trailing whitespace) you want to render.

// 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);
}

The following unit test shows how to use the FormatSuperscript() method from above:

[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));
}

You'll find the full source code in my CodeSnippets GitHub repository. Look for the OrdinalNumberFormattingTests class.

Thomas Barnekow
  • 2,059
  • 1
  • 12
  • 21
  • Thanks for your help, but i want to edit text in an existed paragraph (existed document) (not add new paragraph), Is there any way to work with selection? or something else? – Anh Tú Nov 17 '19 at 16:10
  • If you want any more specific help, you will need to be very precise about how this is embedded in your code. Replacing an existing w:p with a new one is definitely a solution if your line of text is contained in a w:p. You will at least have to recreate and replace w:r elements to format the text as desired. For example, if your text is contained in a single run, you will have to create three runs to achieve the desired effect, exactly as shown in my answer. – Thomas Barnekow Nov 17 '19 at 19:57
  • @AnhTú, do you need any further help on formatting/creating those runs? – Thomas Barnekow Nov 19 '19 at 20:32
  • @AnhTú, I've added another code example, showing how you can format an existing paragraph. This produces the exact same result. – Thomas Barnekow Nov 24 '19 at 23:57