Skip to content

Commit 4e5dd91

Browse files
committed
feat(table): add _Row.grid_cols_before
1 parent 6c34f12 commit 4e5dd91

File tree

7 files changed

+75
-0
lines changed

7 files changed

+75
-0
lines changed

features/steps/table.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,13 @@ def given_a_table_row_having_height_rule_state(context: Context, state: str):
170170
context.row = table.rows[0]
171171

172172

173+
@given("a table row starting with {count} empty grid columns")
174+
def given_a_table_row_starting_with_count_empty_grid_columns(context: Context, count: str):
175+
document = Document(test_docx("tbl-props"))
176+
table = document.tables[7]
177+
context.row = table.rows[int(count)]
178+
179+
173180
# when =====================================================
174181

175182

@@ -347,6 +354,13 @@ def then_can_iterate_over_row_collection(context: Context):
347354
assert actual_count == 2
348355

349356

357+
@then("row.grid_cols_before is {value}")
358+
def then_row_grid_cols_before_is_value(context: Context, value: str):
359+
expected = int(value)
360+
actual = context.row.grid_cols_before
361+
assert actual == expected, "expected %s, got %s" % (expected, actual)
362+
363+
350364
@then("row.height is {value}")
351365
def then_row_height_is_value(context: Context, value: str):
352366
expected_height = None if value == "None" else int(value)
219 Bytes
Binary file not shown.

features/tbl-row-props.feature

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,17 @@ Feature: Get and set table row properties
44
I need a way to get and set the properties of a table row
55

66

7+
Scenario Outline: Get Row.grid_cols_before
8+
Given a table row starting with <count> empty grid columns
9+
Then row.grid_cols_before is <count>
10+
11+
Examples: Row.grid_cols_before value cases
12+
| count |
13+
| 0 |
14+
| 1 |
15+
| 3 |
16+
17+
718
Scenario Outline: Get Row.height_rule
819
Given a table row having height rule <state>
920
Then row.height_rule is <value>

src/docx/oxml/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@
159159
)
160160

161161
register_element_cls("w:bidiVisual", CT_OnOff)
162+
register_element_cls("w:gridBefore", CT_DecimalNumber)
162163
register_element_cls("w:gridCol", CT_TblGridCol)
163164
register_element_cls("w:gridSpan", CT_DecimalNumber)
164165
register_element_cls("w:tbl", CT_Tbl)

src/docx/oxml/table.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,14 @@ class CT_Row(BaseOxmlElement):
6060
trPr: CT_TrPr | None = ZeroOrOne("w:trPr") # pyright: ignore[reportAssignmentType]
6161
tc = ZeroOrMore("w:tc")
6262

63+
@property
64+
def grid_before(self) -> int:
65+
"""The number of unpopulated layout-grid cells at the start of this row."""
66+
trPr = self.trPr
67+
if trPr is None:
68+
return 0
69+
return trPr.grid_before
70+
6371
def tc_at_grid_col(self, idx: int) -> CT_Tc:
6472
"""`<w:tc>` element appearing at grid column `idx`.
6573
@@ -885,11 +893,20 @@ class CT_TrPr(BaseOxmlElement):
885893
"w:del",
886894
"w:trPrChange",
887895
)
896+
gridBefore: CT_DecimalNumber | None = ZeroOrOne( # pyright: ignore[reportAssignmentType]
897+
"w:gridBefore", successors=_tag_seq[3:]
898+
)
888899
trHeight: CT_Height | None = ZeroOrOne( # pyright: ignore[reportAssignmentType]
889900
"w:trHeight", successors=_tag_seq[8:]
890901
)
891902
del _tag_seq
892903

904+
@property
905+
def grid_before(self) -> int:
906+
"""The number of unpopulated layout-grid cells at the start of this row."""
907+
gridBefore = self.gridBefore
908+
return 0 if gridBefore is None else gridBefore.val
909+
893910
@property
894911
def trHeight_hRule(self) -> WD_ROW_HEIGHT_RULE | None:
895912
"""Return the value of `w:trHeight@w:hRule`, or |None| if not present."""

src/docx/table.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,23 @@ def cells(self) -> tuple[_Cell, ...]:
385385
"""Sequence of |_Cell| instances corresponding to cells in this row."""
386386
return tuple(self.table.row_cells(self._index))
387387

388+
@property
389+
def grid_cols_before(self) -> int:
390+
"""Count of unpopulated grid-columns before the first cell in this row.
391+
392+
Word allows a row to "start late", meaning that one or more cells are not present at the
393+
beginning of that row.
394+
395+
Note these are not simply "empty" cells. The renderer reads this value and skips forward to
396+
the table layout-grid position of the first cell in this row; the renderer "skips" this many
397+
columns before drawing the first cell.
398+
399+
Note this also implies that not all rows are guaranteed to have the same number of cells,
400+
e.g. `_Row.cells` could have length `n` for one row and `n - m` for the next row in the same
401+
table.
402+
"""
403+
return self._tr.grid_before
404+
388405
@property
389406
def height(self) -> Length | None:
390407
"""Return a |Length| object representing the height of this cell, or |None| if

tests/test_table.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -645,6 +645,21 @@ def table_(self, request: FixtureRequest):
645645
class Describe_Row:
646646
"""Unit-test suite for `docx.table._Row` objects."""
647647

648+
@pytest.mark.parametrize(
649+
("tr_cxml", "expected_value"),
650+
[
651+
("w:tr", 0),
652+
("w:tr/w:trPr", 0),
653+
("w:tr/w:trPr/w:gridBefore{w:val=0}", 0),
654+
("w:tr/w:trPr/w:gridBefore{w:val=3}", 3),
655+
],
656+
)
657+
def it_knows_its_grid_cols_before(
658+
self, tr_cxml: str, expected_value: int | None, parent_: Mock
659+
):
660+
row = _Row(cast(CT_Row, element(tr_cxml)), parent_)
661+
assert row.grid_cols_before == expected_value
662+
648663
@pytest.mark.parametrize(
649664
("tr_cxml", "expected_value"),
650665
[

0 commit comments

Comments
 (0)