Open XML SDK: Character formatting in table cells with / without content
Once again, a question on Stack Overflow offered an intriguing problem that kept my brain busy for quite a while. When generating a table in a Word document cells containing no content would not retain the custom font size being assigned to each cell.
When the user selects a table cell and applies character formatting (such as font size), or when such formatting is applied using the COM or JS object model, the formatting “sticks”. It doesn’t matter whether the cell contains any content. So I was just as astonished as the person who asked the question that this is not the case when generating a table using the Open XML SDK.
Only after closely inspecting and comparing the underlying Word Open XML did I finally track down what the Word UI does automatically.
- If a cell has no content, it also has neither run (
<w:r>
) nor text (<w:t>
) elements in the underlying Word Open XML. So assigning run properties (<w:rPr>
) to a cell run has no effect. Instead, it’s necessary to assign paragraph run properties (<w:pPr><w:rPr>
) to the paragraph:
<w:tc>
<w:tcPr>
<w:tcW w:w="2406" w:type="dxa"/>
</w:tcPr>
<w:p>
<w:pPr>
<w:rPr>
<w:rStyle w:val="Table9Point"/>
</w:rPr>
</w:pPr>
</w:p>
</w:tc>
<w:rPr>
) of the run (<w:r>
) in addition: <w:tc>
<w:tcPr>
<w:tcW w:w="2405" w:type="dxa"/>
</w:tcPr>
<w:p>
<w:pPr>
<w:rPr>
<w:rStyle w:val="Table9Point"/>
</w:rPr>
</w:pPr>
<w:r>
<w:rPr>
<w:rStyle w:val="Table9Point"/>
</w:rPr>
<w:t>A</w:t>
</w:r>
</w:p>
</w:tc>
A code sample follows. It first creates a character style definition. Note that character-level formatting can also be applied without using a style (code for that is included, but commented out). Better practice, however, is to always use styles to apply formatting, especially repeated formatting.
The code then creates a one-row, four-column table in a loop. Paragraph run properties are generated for all cells. The third cell has no content, so nothing additional is done. For the other cells text, runs and run properties are appended.
After the table is assembled it’s inserted at the end of the document.
private void btnCreateTable_Click(object sender, EventArgs e)
{
string filePath = @"C:\X\TestCreateTAble.docx";
using (WordprocessingDocument
pkg = WordprocessingDocument.Open(filePath, true))
{
MainDocumentPart partDoc = pkg.MainDocumentPart;
Document doc = partDoc.Document;
StyleDefinitionsPart stylDefPart = partDoc.StyleDefinitionsPart;
Styles styls = stylDefPart.Styles;
Style styl = CreateTableCharacterStyle();
stylDefPart.Styles.AppendChild(styl);
Table t = new Table();
TableRow tr = new TableRow();
for (int i = 1; i <= 4; i++)
{
TableCell tc =
new TableCell(new TableCellProperties(new TableCellWidth()
{ Type = TableWidthUnitValues.Dxa, Width = "500" }));
Paragraph para = new Paragraph();
ParagraphProperties paraProps = new ParagraphProperties();
ParagraphMarkRunProperties paraRunProps =
new ParagraphMarkRunProperties();
RunStyle runStyl = new RunStyle() { Val = "Table9Point" };
paraRunProps.Append(runStyl);
// FontSize runFont = new FontSize() {Val = "18" };
// paraRunProps.Append(runFont);
paraProps.Append(paraRunProps);
para.Append(paraProps);
Run run = new Run();
Text txt = null;
if (i == 3)
{
}
else
{
txt = new Text("txt");
txt.Space = SpaceProcessingModeValues.Preserve;
RunProperties runProps = new RunProperties();
RunStyle inRunStyl = (RunStyle) runStyl.Clone();
runProps.Append(inRunStyl);
// FontSize inRunFont = (FontSize) runFont.Clone();
// runProps.Append(inRunFont);
run.Append(runProps);
run.Append(txt);
para.Append(run);
}
tc.Append(para);
tr.Append(tc);
}
t.Append(tr);
//Insert at end of document
SectionProperties sectProps = doc.Body.Elements().LastOrDefault();
doc.Body.InsertBefore(t, sectProps);
}
}
private Style CreateTableCharacterStyle()
{
Style styl = new Style()
{
CustomStyle = true,
StyleId = "Table9Point",
Type = StyleValues.Character,
};
StyleName stylName = new StyleName() { Val = "Table9Point" };
styl.AppendChild(stylName);
StyleRunProperties stylRunProps = new StyleRunProperties();
stylRunProps.FontSize = new FontSize() { Val = "18" };
styl.AppendChild(stylRunProps);
BasedOn basedOn1 = new BasedOn() { Val = "DefaultParagraphFont" };
styl.AppendChild(basedOn1);
return styl;
}