Fix rules for table style application in XSLFTables. (#969)

* Fix rules for table style application in XSLFTables.

Table styles contain rules for first and last rows/columns, even and
odd rows/columns... When one of these rules is empty, we are supposed
to fall back to the "whole table" rules. The fallback must also be
applied for the format that's not explicitly specified in the specific
rules.

when the corresponding specific rule is missing some info.

Fallback must also be applied when the corresponding specific rule is
missing some info. A couple of examples from the reproducer/test file
included:

The included reproducer/test file contained a few problems related to
table style application:
* The second style for horizontal/vertical banding (band2H, band2V) was
  never applied.
* In the table with horizontal banding enabled, the style band1H did
  not set a font color, POI returned a null font color instead of the
  color from wholeTable.
* In the table with horizontal banding enabled, the style band2H did
  not set a background color, POI returned null instead of the color
  specified in wholeTable.

This patches fixes the behaviors mentioned above, making POI behavior
match the one from MS Office and LibreOffice.

* Replace uses of java.util.list.getFirst().
This commit is contained in:
Jacobo Aragunde Pérez 2025-12-16 18:03:45 +01:00 committed by GitHub
parent f476c64179
commit 4fb34ebeae
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 102 additions and 12 deletions

View File

@ -438,7 +438,8 @@ public class XSLFTableCell extends XSLFTextShape implements TableCell<XSLFShape,
}
CTTablePartStyle tps = getTablePartStyle(null);
if (tps == null || !tps.isSetTcStyle()) {
if (tps == null || !tps.isSetTcStyle() ||
(!tps.getTcStyle().isSetFill() && !tps.getTcStyle().isSetFillRef())) {
tps = getTablePartStyle(TablePartStyle.wholeTbl);
if (tps == null || !tps.isSetTcStyle()) {
return null;
@ -504,10 +505,18 @@ public class XSLFTableCell extends XSLFTextShape implements TableCell<XSLFShape,
int br = row + (firstRow ? 1 : 0);
int bc = col + (firstCol ? 1 : 0);
if (bandRow && (br & 1) == 0) {
tps = TablePartStyle.band1H;
} else if (bandCol && (bc & 1) == 0) {
tps = TablePartStyle.band1V;
if (bandRow) {
if ((br & 1) == 0) {
tps = TablePartStyle.band1H;
} else {
tps = TablePartStyle.band2H;
}
} else if (bandCol) {
if ((bc & 1) == 0) {
tps = TablePartStyle.band1V;
} else {
tps = TablePartStyle.band2V;
}
}
}
@ -734,6 +743,9 @@ public class XSLFTableCell extends XSLFTextShape implements TableCell<XSLFShape,
@Override
public PaintStyle getFontColor() {
CTTableStyleTextStyle txStyle = getTextStyle();
if (!containsColor(txStyle)) {
txStyle = getFallbackTextStyle();
}
if (txStyle == null) {
// No table styling, so just use the text run output
return super.getFontColor();
@ -798,11 +810,27 @@ public class XSLFTableCell extends XSLFTextShape implements TableCell<XSLFShape,
}
}
private boolean containsColor(CTTableStyleTextStyle tcTxStyle) {
if (tcTxStyle == null) {
return false;
}
if (!tcTxStyle.isSetHslClr() && !tcTxStyle.isSetPrstClr() && !tcTxStyle.isSetSchemeClr()
&& !tcTxStyle.isSetScrgbClr() && !tcTxStyle.isSetSrgbClr() && !tcTxStyle.isSetSysClr()) {
return false;
}
return true;
}
private CTTableStyleTextStyle getTextStyle() {
CTTablePartStyle tps = getTablePartStyle(null);
if (tps == null || !tps.isSetTcTxStyle()) {
tps = getTablePartStyle(TablePartStyle.wholeTbl);
return getFallbackTextStyle();
}
return tps.getTcTxStyle();
}
private CTTableStyleTextStyle getFallbackTextStyle() {
CTTablePartStyle tps = getTablePartStyle(TablePartStyle.wholeTbl);
return (tps == null) ? null : tps.getTcTxStyle();
}
}

View File

@ -36,12 +36,8 @@ import java.io.IOException;
import java.util.List;
import org.apache.poi.sl.draw.DrawTableShape;
import org.apache.poi.sl.usermodel.ShapeType;
import org.apache.poi.sl.usermodel.Slide;
import org.apache.poi.sl.usermodel.StrokeStyle;
import org.apache.poi.sl.usermodel.*;
import org.apache.poi.sl.usermodel.TableCell.BorderEdge;
import org.apache.poi.sl.usermodel.TextParagraph;
import org.apache.poi.sl.usermodel.VerticalAlignment;
import org.apache.poi.util.RandomSingleton;
import org.apache.poi.util.TempFile;
import org.apache.poi.xslf.XSLFTestDataSamples;
@ -362,4 +358,70 @@ class TestXSLFTable {
}
}
}
private void verifyTableCellStyleColors(XSLFTableCell cell, String text, Color fontColor, Color fillColor) {
assertEquals(text, cell.getText());
PaintStyle colorText1 = cell.getTextParagraphs().get(0).getTextRuns().get(0).getFontColor();
assertTrue(colorText1 instanceof PaintStyle.SolidPaint);
assertEquals(fontColor, ((PaintStyle.SolidPaint)colorText1).getSolidColor().getColor());
assertEquals(fillColor, cell.getFillColor());
}
@Test
void testTableCellStyle() throws IOException {
XMLSlideShow ppt = XSLFTestDataSamples.openSampleDocument("table-with-different-font-colors.pptx");
// First slide: test row-related table styles
List<XSLFShape> shapes = ppt.getSlides().get(0).getShapes();
assertEquals(1, shapes.size());
assertTrue(shapes.get(0) instanceof XSLFTable);
XSLFTable tbl = (XSLFTable)shapes.get(0);
assertEquals(4, tbl.getNumberOfColumns());
assertEquals(4, tbl.getNumberOfRows());
assertNotNull(tbl.getCTTable());
// Yellow font color due to "first row" table style
verifyTableCellStyleColors(tbl.getRows().get(0).getCells().get(0), "Text 1",
new Color(255, 255, 0), new Color(21, 96, 130));
// Dark green font color due to direct format
verifyTableCellStyleColors(tbl.getRows().get(0).getCells().get(2), "Text 3",
new Color(0, 176, 80), new Color(21, 96, 130));
// Grey font color due to "even row" table style + fallback
verifyTableCellStyleColors(tbl.getRows().get(1).getCells().get(0), "Text 5",
new Color(119, 119, 119), new Color(204, 210, 216));
// Light blue font color due to "odd row" table style
verifyTableCellStyleColors(tbl.getRows().get(2).getCells().get(0), "Text 9",
new Color(0, 255, 255), new Color(231, 234, 237));
// Red font color due to direct format
verifyTableCellStyleColors(tbl.getRows().get(2).getCells().get(2), "Text 11",
new Color(255, 0, 0), new Color(231, 234, 237));
// Blue font color due to "last row" table style
verifyTableCellStyleColors(tbl.getRows().get(3).getCells().get(0), "Text 13",
new Color(0, 0, 255), new Color(21, 96, 130));
// Second slide: test column-related table styles
shapes = ppt.getSlides().get(1).getShapes();
assertEquals(1, shapes.size());
assertTrue(shapes.get(0) instanceof XSLFTable);
tbl = (XSLFTable)shapes.get(0);
assertEquals(4, tbl.getNumberOfColumns());
assertEquals(4, tbl.getNumberOfRows());
assertNotNull(tbl.getCTTable());
// Green font color due to "first column" table style
verifyTableCellStyleColors(tbl.getRows().get(0).getCells().get(0), "Text 1",
new Color(0, 255, 0), new Color(21, 96, 130));
// Grey font color due to "even column" table style + fallback
verifyTableCellStyleColors(tbl.getRows().get(0).getCells().get(1), "Text 2",
new Color(119, 119, 119), new Color(204, 210, 216));
// Light blue font color due to "odd column" table style
verifyTableCellStyleColors(tbl.getRows().get(0).getCells().get(2), "Text 3",
new Color(0, 255, 255), new Color(231, 234, 237));
// Red font color due to "last column" table style
verifyTableCellStyleColors(tbl.getRows().get(0).getCells().get(3), "Text 4",
new Color(255, 0, 0), new Color(21, 96, 130));
ppt.close();
}
}