Search code examples
c#openxml

OpenXML SDK (Word) - Edit font size of paragraph following last table in document


Word has an annoying feature where a Paragraph is placed after every Table containing formatting information for that Table.

If your Table falls at the end of a page, this Paragraph can end up on the next page, creating a blank/empty page.

I know there is a workaround for this by setting the font for that Paragraph to be something very small (such as 1 point), however I can only get that working when editing the document via Word after the document is created. Trying edit the font-size of that paragraph programmatically using OpenXML does not give me the results I expect.

The manual workaround is not acceptable for my client, is there any way to remedy this?

Here are some things I have tried:

1. Deleting the last Paragraph:

  • Expectation: Blank Page would be removed
  • Reality: Corrupted the file
  • This makes sense, as that Paragraph is needed for formatting information for the table.

2. Grabbing all RunProperties of the last paragraph, removing all instances of FontSize, and adding new instances of FontSize.

OpenXML.Table lastTable = oNewReportDoc.MainDocumentPart.Document.Body
    .Descendants<OpenXML.Table>()
    .Last();

OpenXML.Paragraph paraToEdit = lastTable.NextSibling<OpenXML.Paragraph>();

List<RunProperties> runProps = paraToEdit.Descendants<RunProperties>().ToList();

foreach (RunProperties rp in runProps)
{
    rp.RemoveAllChildren<FontSize>();
    rp.AddChild(new FontSize() { Val = "1" });
}

When tracing through the debugger, this is what paraToEdit's InnerXML looks like before the loop

paraToEdit.InnerXML:

<w:pPr xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
    <w:jc w:val="center" />
    <w:rPr>
        <w:rFonts w:ascii="Monospac821 BT" w:hAnsi="Monospac821 BT" />
        <w:sz w:val="15" /> // Font Size
    </w:rPr>
</w:pPr>
<w:r xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
    <w:rPr>
        <w:rFonts w:ascii="Monospac821 BT" w:hAnsi="Monospac821 BT" />
        <w:sz w:val="15" /> // Font Size
    </w:rPr>
    <w:t />
</w:r>

and now after the loop

paraToEdit.InnerXML:

<w:pPr xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
    <w:jc w:val="center" />
    <w:rPr>
        <w:rFonts w:ascii="Monospac821 BT" w:hAnsi="Monospac821 BT" />
        <w:sz w:val="15" /> // Unchanged font size
    </w:rPr>
</w:pPr>
<w:r xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
    <w:rPr>
        <w:rFonts w:ascii="Monospac821 BT" w:hAnsi="Monospac821 BT" />
        <w:sz w:val="1" /> // Changed font size
    </w:rPr>
    <w:t />
</w:r>

As you can see, there are 2 instances of RunProperties here (w:rPr), so my next approach tries to grab the first RunProperties located within the ParagraphProperties (w:pPr)

3. (Attempting to) Grab the RunProperties from the ParagraphProperties in addition

OpenXML.Table lastTable = oNewReportDoc.MainDocumentPart.Document.Body
    .Descendants<OpenXML.Table>()
    .Last();

OpenXML.Paragraph paraToEdit = lastTable.NextSibling<OpenXML.Paragraph>();


List<RunProperties> runProps = paraToEdit.Descendants<RunProperties>().ToList();

List<ParagraphProperties> paraProps = paraToEdit.Descendants<ParagraphProperties>().ToList();

foreach(ParagraphProperties pp in paraProps)
{
    // I also tried using Descendents<RunProperties> here, but both return null
    RunProperties addRunProps = pp.GetFirstChild<RunProperties>();

    runProps.Add(addRunProps); // Nothing gets added
}

foreach (RunProperties rp in runProps)
{
    rp.RemoveAllChildren<FontSize>();
    rp.AddChild(new FontSize() { Val = "1" });
}

Would anyone be able to provide me guidance on where to go from here?


Solution

  • I have found a solution to my problem.

    Thanks to @itallmakescents, I was able to use the OpenXML Productivity Tool to compare the XML output of what my program generated vs. my expectations.

    It turns out the first <w:rPr> tag is not a RunProperties tag, but a ParagraphMarkRunProperties tag instead. I needed to remove the FontSize instance belonging to the RunProperties tag, and change the FontSize of the ParagraphMarkRunProperties to 1pt.

    My final solution can be seen here:

    OpenXML.Table lastTable = oNewReportDoc.MainDocumentPart.Document.Body
        .Descendants<OpenXML.Table>()
        .Last();
    
    OpenXML.Paragraph paraToEdit = lastTable.NextSibling<OpenXML.Paragraph>();
    
    
    List<RunProperties> runProps = paraToEdit.Descendants<RunProperties>().ToList();
    
    List<ParagraphMarkRunProperties> pMarkProps = paraToEdit.Descendants<ParagraphMarkRunProperties>().ToList();
    
    foreach (RunProperties rp in runProps)
    {
        rp.RemoveAllChildren<FontSize>();
        rp.Remove();
    }
    
    foreach (ParagraphMarkRunProperties prp in pMarkProps)
    {
        prp.RemoveAllChildren<FontSize>();
        prp.AddChild(new FontSize() { Val = "2" });
    }