From 73b2874824d1314b5ff0a5a6c36493ba23620c70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jacobo=20Aragunde=20P=C3=A9rez?= Date: Tue, 15 Jul 2025 11:05:17 +0200 Subject: [PATCH] Add getters and setters for XWPFTable indentation. (#843) * Add getters and setters for XWPFTable indentation. The element tblPr->tblInd represents "table indent from leading margin". It specifies the indentation which shall be added before the leading edge of the current table in the document (the left edge in a left-to-right table, and the right edge in a right-to-left table). This indentation should shift the table into the text margin by the specified amount. This value is specified in the units applied via its type attribute. Any width value of type pct or auto for this element shall be ignored. If this element is omitted, then the table shall inherit the table indentation from the associated table style. If table indentation is never specified in the style hierarchy, no indentation shall be added to the parent table. If the resulting justification on any table row is not left, then this property shall be ignored. To implement this property, we included a getter and a setter for the property value, and another couple of getter/setter for the existence (and validity) or absence of the property itself, for clients to know they must fall back to the table style. * Address reviewer comments. --- .../apache/poi/xwpf/usermodel/XWPFTable.java | 76 +++++++++++++++++- .../poi/xwpf/usermodel/TestXWPFTable.java | 68 +++++++++++++++- test-data/document/table-indent.docx | Bin 0 -> 7639 bytes 3 files changed, 139 insertions(+), 5 deletions(-) create mode 100644 test-data/document/table-indent.docx diff --git a/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFTable.java b/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFTable.java index bbd43447d3..dc4d0e4116 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFTable.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFTable.java @@ -295,7 +295,7 @@ public class XWPFTable implements IBodyElement, ISDTContents { public void setWidth(int width) { CTTblPr tblPr = getTblPr(); CTTblWidth tblWidth = tblPr.isSetTblW() ? tblPr.getTblW() : tblPr.addNewTblW(); - tblWidth.setW(new BigInteger(Integer.toString(width))); + tblWidth.setW(BigInteger.valueOf(width)); tblWidth.setType(STTblWidth.DXA); } @@ -306,6 +306,80 @@ public class XWPFTable implements IBodyElement, ISDTContents { return ctTbl.sizeOfTrArray(); } + /** + * Get the indentation value in 20ths of a point (twips). + * + *

This element specifies the indentation which shall be added before the leading edge of + * the current table in the document (the left edge in a left-to-right table, and the right + * edge in a right-to-left table).

+ *

If the table alignment is not left/start, this property shall be ignored.

+ * + * @see boolean hasIndent() + * @return indentation value as an integer (20ths of a point) + */ + public int getIndent() { + CTTblPr tblPr = getTblPr(false); + if (tblPr.isSetTblInd()) { + STTblWidth.Enum typeValue = tblPr.getTblInd().getType(); + if (typeValue == null) { + // "§17.4.87: If [type] is omitted, then its value shall be assumed to be dxa" + typeValue = STTblWidth.DXA; + } + switch (typeValue.intValue()) { + case STTblWidth.INT_DXA: + return (int) Units.toDXA(POIXMLUnits.parseLength(tblPr.getTblInd().xgetW())); + case STTblWidth.INT_NIL: + // "§17.18.90: [nil] Specifies that the current width is zero, regardless of + // any width value specified on the parent element" + return 0; + case STTblWidth.INT_PCT: + case STTblWidth.INT_AUTO: + // "§17.4.50: Any width value of type pct or auto for this element shall be ignored" + return 0; + } + } + return 0; + } + + /** + * Set the indentation in 20ths of a point (twips). + * @see int getIndent() + * @param indent Indentation value (20ths of a point) + */ + public void setIndent(int indent) { + CTTblPr tblPr = getTblPr(); + CTTblWidth tblInd = tblPr.isSetTblInd() ? tblPr.getTblInd() : tblPr.addNewTblInd(); + tblInd.setW(BigInteger.valueOf(indent)); + tblInd.setType(STTblWidth.DXA); + } + + /** + * Check if some indentation value is set for the table. + * + *

If this attribute is omitted, then the table shall inherit the table indentation from + * the associated table style. If table indentation is never specified in the style hierarchy, + * no indentation shall be added to the parent table.

+ * + * @return true if the indent value is set and is valid, false if it is not set or shall be + * ignored (e.g. due to invalid type). + */ + public boolean hasIndent() { + CTTblPr tblPr = getTblPr(false); + // According to §17.4.50, values with type pct or auto shall be ignored. + return tblPr.isSetTblInd() && tblPr.getTblInd().getType() != STTblWidth.PCT + && tblPr.getTblInd().getType() != STTblWidth.AUTO; + } + + /** + * Removes the table indentation attribute from a table + */ + public void removeIndent() { + CTTblPr tPr = getTblPr(false); + if (tPr != null && tPr.isSetTblInd()) { + tPr.unsetTblInd(); + } + } + /** * Returns CTTblPr object for table. Creates it if it does not exist. */ diff --git a/poi-ooxml/src/test/java/org/apache/poi/xwpf/usermodel/TestXWPFTable.java b/poi-ooxml/src/test/java/org/apache/poi/xwpf/usermodel/TestXWPFTable.java index 67e911c15a..3539dac261 100644 --- a/poi-ooxml/src/test/java/org/apache/poi/xwpf/usermodel/TestXWPFTable.java +++ b/poi-ooxml/src/test/java/org/apache/poi/xwpf/usermodel/TestXWPFTable.java @@ -16,9 +16,7 @@ ==================================================================== */ package org.apache.poi.xwpf.usermodel; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.*; import java.io.IOException; import java.math.BigInteger; @@ -586,4 +584,66 @@ class TestXWPFTable { assertNull(tbl.getTableAlignment()); } } -} \ No newline at end of file + + @Test + public void testGetTableIndent() throws Exception { + // open an empty document + try (XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("table-indent.docx")) { + + XWPFTable table1 = doc.getTableArray(0); + // Indent not present in the document + assertFalse(table1.hasIndent()); + assertEquals(0, table1.getIndent()); + + XWPFTable table2 = doc.getTableArray(1); + // Valid indent value with type dxa + assertTrue(table2.hasIndent()); + assertEquals(732, table2.getIndent()); + + XWPFTable table3 = doc.getTableArray(2); + // Indent is of type "nil" + assertTrue(table3.hasIndent()); + assertEquals(0, table3.getIndent()); + + XWPFTable table4 = doc.getTableArray(3); + // Indent is of type "pct" which should be ignored + assertFalse(table4.hasIndent()); + assertEquals(0, table4.getIndent()); + + XWPFTable table5 = doc.getTableArray(4); + // Indent is of type "auto" which should be ignored + assertFalse(table5.hasIndent()); + assertEquals(0, table5.getIndent()); + + XWPFTable table6 = doc.getTableArray(5); + // Valid indent value with empty type (defaults to dxa) + assertTrue(table6.hasIndent()); + assertEquals(732, table6.getIndent()); + + XWPFTable table7 = doc.getTableArray(6); + // Valid indent value, negative values are allowed + assertTrue(table7.hasIndent()); + assertEquals(-500, table7.getIndent()); + } + } + + @Test + void testSetGetTableIndent() throws IOException { + try (XWPFDocument doc = new XWPFDocument()) { + XWPFTable tbl = doc.createTable(1, 1); + assertFalse(tbl.hasIndent()); + tbl.setIndent(100); + assertTrue(tbl.hasIndent()); + assertEquals(100, tbl.getIndent()); + tbl.setIndent(0); + assertTrue(tbl.hasIndent()); + assertEquals(0, tbl.getIndent()); + tbl.setIndent(-100); + assertTrue(tbl.hasIndent()); + assertEquals(-100, tbl.getIndent()); + tbl.removeIndent(); + assertFalse(tbl.hasIndent()); + assertEquals(0, tbl.getIndent()); + } + } +} diff --git a/test-data/document/table-indent.docx b/test-data/document/table-indent.docx new file mode 100644 index 0000000000000000000000000000000000000000..c4306aa9370f1d998ebb7da90816f1db2a31320d GIT binary patch literal 7639 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 literal 0 HcmV?d00001