Table row.
| 385 | |
| 386 | |
| 387 | class _Row(Parented): |
| 388 | """Table row.""" |
| 389 | |
| 390 | def __init__(self, tr: CT_Row, parent: TableParent): |
| 391 | super(_Row, self).__init__(parent) |
| 392 | self._parent = parent |
| 393 | self._tr = self._element = tr |
| 394 | |
| 395 | @property |
| 396 | def cells(self) -> tuple[_Cell, ...]: |
| 397 | """Sequence of |_Cell| instances corresponding to cells in this row. |
| 398 | |
| 399 | Note that Word allows table rows to start later than the first column and end before the |
| 400 | last column. |
| 401 | |
| 402 | - Only cells actually present are included in the return value. |
| 403 | - This implies the length of this cell sequence may differ between rows of the same table. |
| 404 | - If you are reading the cells from each row to form a rectangular "matrix" data structure |
| 405 | of the table cell values, you will need to account for empty leading and/or trailing |
| 406 | layout-grid positions using `.grid_cols_before` and `.grid_cols_after`. |
| 407 | |
| 408 | """ |
| 409 | |
| 410 | def iter_tc_cells(tc: CT_Tc) -> Iterator[_Cell]: |
| 411 | """Generate a cell object for each layout-grid cell in `tc`. |
| 412 | |
| 413 | In particular, a `<w:tc>` element with a horizontal "span" with generate the same cell |
| 414 | multiple times, one for each grid-cell being spanned. This approximates a row in a |
| 415 | "uniform" table, where each row has a cell for each column in the table. |
| 416 | """ |
| 417 | # -- a cell comprising the second or later row of a vertical span is indicated by |
| 418 | # -- tc.vMerge="continue" (the default value of the `w:vMerge` attribute, when it is |
| 419 | # -- present in the XML). The `w:tc` element at the same grid-offset in the prior row |
| 420 | # -- is guaranteed to be the same width (gridSpan). So we can delegate content |
| 421 | # -- discovery to that prior-row `w:tc` element (recursively) until we arrive at the |
| 422 | # -- "root" cell -- for the vertical span. |
| 423 | if tc.vMerge == "continue": |
| 424 | yield from iter_tc_cells(tc._tc_above) # pyright: ignore[reportPrivateUsage] |
| 425 | return |
| 426 | |
| 427 | # -- Otherwise, vMerge is either "restart" or None, meaning this `tc` holds the actual |
| 428 | # -- content of the cell (whether it is vertically merged or not). |
| 429 | cell = _Cell(tc, self.table) |
| 430 | for _ in range(tc.grid_span): |
| 431 | yield cell |
| 432 | |
| 433 | def _iter_row_cells() -> Iterator[_Cell]: |
| 434 | """Generate `_Cell` instance for each populated layout-grid cell in this row.""" |
| 435 | for tc in self._tr.tc_lst: |
| 436 | yield from iter_tc_cells(tc) |
| 437 | |
| 438 | return tuple(_iter_row_cells()) |
| 439 | |
| 440 | @property |
| 441 | def grid_cols_after(self) -> int: |
| 442 | """Count of unpopulated grid-columns after the last cell in this row. |
| 443 | |
| 444 | Word allows a row to "end early", meaning that one or more cells are not present at the |
no outgoing calls
searching dependent graphs…