Skip to content

table

table

Custom element classes for tables.

CT_Border

Bases: BaseOxmlElement

A single border edge, e.g. w:tblBorders/w:top.

The same complex type serves every edge of w:tblBorders, w:tcBorders and w:pBdr.

_CT_BordersBase

Bases: BaseOxmlElement

Common behavior of the w:tblBorders and w:tcBorders elements.

Each holds an optional CT_Border child per edge and they differ only in which edges they admit; edges names those, in schema order.

get_border

get_border(edge: str) -> CT_Border | None

The w:{edge} child element, or None when this edge has no border.

Source code in src/docx/oxml/table.py
def get_border(self, edge: str) -> CT_Border | None:
    """The `w:{edge}` child element, or |None| when this edge has no border."""
    return cast("CT_Border | None", getattr(self, edge))

get_or_add_border

get_or_add_border(edge: str) -> CT_Border

The w:{edge} child element, newly added in schema order if not present.

A newly added border is given w:val="single", since w:val is required and a border element without a line style is not valid.

Source code in src/docx/oxml/table.py
def get_or_add_border(self, edge: str) -> CT_Border:
    """The `w:{edge}` child element, newly added in schema order if not present.

    A newly added border is given `w:val="single"`, since `w:val` is required and a
    border element without a line style is not valid.
    """
    border = self.get_border(edge)
    if border is not None:
        return border
    border = cast("CT_Border", getattr(self, "get_or_add_%s" % edge)())
    border.val = WD_LINE_STYLE.SINGLE
    return border

remove_border

remove_border(edge: str) -> None

Remove the w:{edge} child element; does nothing when it is not present.

Source code in src/docx/oxml/table.py
def remove_border(self, edge: str) -> None:
    """Remove the `w:{edge}` child element; does nothing when it is not present."""
    cast("Callable[[], None]", getattr(self, "_remove_%s" % edge))()

CT_TblBorders

Bases: _CT_BordersBase

w:tblBorders element, the set of border edges of a table.

CT_TcBorders

Bases: _CT_BordersBase

w:tcBorders element, the set of border edges of a table cell.

Adds the two diagonals to the edges a table admits.

CT_Height

Bases: BaseOxmlElement

Used for w:trHeight to specify a row height and row height rule.

CT_Row

Bases: BaseOxmlElement

<w:tr> element.

grid_after property

grid_after: int

The number of unpopulated layout-grid cells at the end of this row.

grid_before property

grid_before: int

The number of unpopulated layout-grid cells at the start of this row.

tr_idx property

tr_idx: int

Index of this w:tr element within its parent w:tbl element.

trHeight_hRule property writable

trHeight_hRule: WD_ROW_HEIGHT_RULE | None

The value of ./w:trPr/w:trHeight/@w:hRule, or None if not present.

trHeight_val property writable

trHeight_val

Return the value of w:trPr/w:trHeight@w:val, or None if not present.

cantSplit_val property writable

cantSplit_val: bool | None

Value of w:trPr/w:cantSplit@w:val, or None if not present.

tblHeader_val property writable

tblHeader_val: bool | None

Value of w:trPr/w:tblHeader/@w:val, or None if not present.

hidden_val property writable

hidden_val: bool | None

Value of w:trPr/w:hidden/@w:val, or None if not present.

alignment property writable

alignment: WD_TABLE_ALIGNMENT | None

Value of w:trPr/w:jc/@w:val, or None if not present.

cell_spacing property writable

cell_spacing: Length | None

Value of w:trPr/w:tblCellSpacing, or None if not present.

width_after property writable

width_after: Length | None

Value of w:trPr/w:wAfter, or None if not present.

width_before property writable

width_before: Length | None

Value of w:trPr/w:wBefore, or None if not present.

grid_width

grid_width() -> int

The count of layout-grid columns this row occupies.

Includes the grid positions this row leaves unpopulated at either end.

Source code in src/docx/oxml/table.py
def grid_width(self) -> int:
    """The count of layout-grid columns this row occupies.

    Includes the grid positions this row leaves unpopulated at either end.
    """
    return self.grid_before + sum(tc.grid_span for tc in self.tc_lst) + self.grid_after

delete_grid_column

delete_grid_column(grid_offset: int, part=None) -> None

Remove this row's occupancy of layout-grid column grid_offset.

A cell that starts at grid_offset and spans no further is removed; one that spans this column and others is narrowed by one, so the rest of its span survives. A row that does not populate the column is left alone, and its w:gridBefore or w:gridAfter adjusted when the removed column falls inside the unpopulated run.

Source code in src/docx/oxml/table.py
def delete_grid_column(self, grid_offset: int, part=None) -> None:
    """Remove this row's occupancy of layout-grid column `grid_offset`.

    A cell that starts at `grid_offset` and spans no further is removed; one that
    spans this column and others is narrowed by one, so the rest of its span
    survives. A row that does not populate the column is left alone, and its
    `w:gridBefore` or `w:gridAfter` adjusted when the removed column falls inside
    the unpopulated run.
    """
    from docx.oxml.deletion import delete_element

    grid_before = self.grid_before
    if grid_offset < grid_before:
        self.trPr.grid_before = grid_before - 1  # pyright: ignore[reportOptionalMemberAccess]
        return

    try:
        tc = self.tc_covering_grid_offset(grid_offset)
    except ValueError:
        # -- the column falls after this row's last cell, in its `w:gridAfter` run --
        trPr = self.trPr
        if trPr is not None and trPr.grid_after > 0:
            trPr.grid_after = trPr.grid_after - 1
        return

    if tc.grid_span > 1:
        tc.grid_span = tc.grid_span - 1
        return
    delete_element(tc, part)

transfer_vertical_spans_to_row_below

transfer_vertical_spans_to_row_below() -> None

Make the row below own any vertical span that starts in this row.

Called before deleting this row: a continuation cell whose origin disappears would otherwise be left referring to nothing. The cell below becomes the origin, keeping the content and the remainder of the span, which is what Word does.

Source code in src/docx/oxml/table.py
def transfer_vertical_spans_to_row_below(self) -> None:
    """Make the row below own any vertical span that starts in this row.

    Called before deleting this row: a continuation cell whose origin disappears
    would otherwise be left referring to nothing. The cell below becomes the origin,
    keeping the content and the remainder of the span, which is what Word does.
    """
    tr_below = self._tr_below
    if tr_below is None:
        return
    for tc in self.tc_lst:
        if tc.vMerge != ST_Merge.RESTART:
            continue
        try:
            tc_below = tr_below.tc_covering_grid_offset(tc.grid_offset)
        except ValueError:
            continue
        if tc_below.vMerge != ST_Merge.CONTINUE:
            continue
        tc._move_content_to(tc_below)
        # -- the cell below is the origin now; it keeps "restart" only if the span
        # -- continues past it --
        tc_below.vMerge = ST_Merge.RESTART if tc_below.bottom > tc_below._tr_idx + 1 else None

tc_covering_grid_offset

tc_covering_grid_offset(grid_offset: int) -> CT_Tc

The w:tc element in this tr occupying layout-grid column grid_offset.

Unlike .tc_at_grid_offset(), a horizontally merged cell is returned for every grid column it spans, not only for the one it starts at.

Raises ValueError when this row does not populate grid_offset, which happens when the row starts late or ends early.

Source code in src/docx/oxml/table.py
def tc_covering_grid_offset(self, grid_offset: int) -> CT_Tc:
    """The `w:tc` element in this tr occupying layout-grid column `grid_offset`.

    Unlike `.tc_at_grid_offset()`, a horizontally merged cell is returned for every
    grid column it spans, not only for the one it starts at.

    Raises |ValueError| when this row does not populate `grid_offset`, which happens
    when the row starts late or ends early.
    """
    remaining_offset = grid_offset - self.grid_before

    if remaining_offset >= 0:
        for tc in self.tc_lst:
            grid_span = tc.grid_span
            if remaining_offset < grid_span:
                return tc
            remaining_offset -= grid_span

    raise ValueError(f"row does not populate grid_offset={grid_offset}")

tc_at_grid_offset

tc_at_grid_offset(grid_offset: int) -> CT_Tc

The tc element in this tr at exact grid offset.

Raises ValueError when this w:tr contains no w:tc with exact starting grid_offset.

Source code in src/docx/oxml/table.py
def tc_at_grid_offset(self, grid_offset: int) -> CT_Tc:
    """The `tc` element in this tr at exact `grid offset`.

    Raises ValueError when this `w:tr` contains no `w:tc` with exact starting `grid_offset`.
    """
    # -- account for omitted cells at the start of the row --
    remaining_offset = grid_offset - self.grid_before

    for tc in self.tc_lst:
        # -- We've gone past grid_offset without finding a tc, no sense searching further. --
        if remaining_offset < 0:
            break
        # -- We've arrived at grid_offset, this is the `w:tc` we're looking for. --
        if remaining_offset == 0:
            return tc
        # -- We're not there yet, skip forward the number of layout-grid cells this cell
        # -- occupies.
        remaining_offset -= tc.grid_span

    raise ValueError(f"no `tc` element at grid_offset={grid_offset}")

CT_Tbl

Bases: BaseOxmlElement

<w:tbl> element.

tblGrid property

tblGrid: CT_TblGrid

The w:tblGrid child of this table, synthesized when absent.

w:tblGrid is required by the schema, but Word opens a table without one by reconstructing the grid from the row contents, and enough generators emit such a table that refusing to read one is harsher than the situation warrants.

Note the synthesized element is inserted into the tree, so saving a document read this way repairs the table. This is deliberate; the alternative is an add_column() that silently does nothing and a save that writes the invalid table straight back out.

A w:tblGrid that is present but has fewer w:gridCol children than the widest row is left alone. Nothing in this library depends on the grid to locate a cell, so the short grid affects only len(table.columns), which reports what the document actually says.

bidiVisual_val property writable

bidiVisual_val: bool | None

Value of ./w:tblPr/w:bidiVisual/@w:val or None if not present.

Controls whether table cells are displayed right-to-left or left-to-right.

col_count property

col_count

The number of grid columns in this table.

tblStyle_val property writable

tblStyle_val: str | None

w:tblPr/w:tblStyle/@w:val (a table style id) or None if not present.

tr_at_idx

tr_at_idx(idx: int) -> CT_Row

The w:tr child of this table at idx, counting from zero.

Raises IndexError when idx is out of range. Locating the row this way avoids materializing the full row list, which is what makes reading a table row by row cost time proportional to its size rather than to its square.

Source code in src/docx/oxml/table.py
def tr_at_idx(self, idx: int) -> CT_Row:
    """The `w:tr` child of this table at `idx`, counting from zero.

    Raises |IndexError| when `idx` is out of range. Locating the row this way avoids
    materializing the full row list, which is what makes reading a table row by row
    cost time proportional to its size rather than to its square.
    """
    if idx < 0:
        return self.tr_lst[idx]
    tr = next(islice(self.iterchildren(qn("w:tr")), idx, idx + 1), None)
    if tr is None:
        raise IndexError("table row index [%d] is out of range" % idx)
    return cast(CT_Row, tr)

iter_tcs

iter_tcs()

Generate each of the w:tc elements in this table, left to right and top to bottom.

Each cell in the first row is generated, followed by each cell in the second row, etc.

Source code in src/docx/oxml/table.py
def iter_tcs(self):
    """Generate each of the `w:tc` elements in this table, left to right and top to
    bottom.

    Each cell in the first row is generated, followed by each cell in the second
    row, etc.
    """
    for tr in self.tr_lst:
        for tc in tr.tc_lst:
            yield tc

new_tbl classmethod

new_tbl(rows: int, cols: int, width: Length) -> CT_Tbl

Return a new w:tbl element having rows rows and cols columns.

width is distributed evenly between the columns.

Source code in src/docx/oxml/table.py
@classmethod
def new_tbl(cls, rows: int, cols: int, width: Length) -> CT_Tbl:
    """Return a new `w:tbl` element having `rows` rows and `cols` columns.

    `width` is distributed evenly between the columns.
    """
    return cast(CT_Tbl, parse_xml(cls._tbl_xml(rows, cols, width)))

CT_TblGrid

Bases: BaseOxmlElement

w:tblGrid element.

Child of w:tbl, holds `w:gridCol> elements that define column count, width, etc.

CT_TblGridCol

Bases: BaseOxmlElement

w:gridCol element, child of w:tblGrid, defines a table column.

gridCol_idx property

gridCol_idx: int

Index of this w:gridCol element within its parent w:tblGrid element.

CT_TblLayoutType

Bases: BaseOxmlElement

w:tblLayout element.

Specifies whether column widths are fixed or can be automatically adjusted based on content.

CT_TblPr

Bases: BaseOxmlElement

<w:tblPr> element, child of <w:tbl>, holds child elements that define table properties such as style and borders.

alignment property writable

alignment: WD_TABLE_ALIGNMENT | None

Horizontal alignment of table, None if ./w:jc is not present.

autofit property writable

autofit: bool

False when there is a w:tblLayout child with @w:type="fixed".

Otherwise True.

style property writable

style

Return the value of the val attribute of the <w:tblStyle> child or None if not present.

CT_TblPrEx

Bases: BaseOxmlElement

w:tblPrEx element, exceptions to table-properties.

Applied at a lower level, like a w:tr to modify the appearance. Possibly used when two tables are merged. For more see: http://officeopenxml.com/WPtablePropertyExceptions.php

CT_TblWidth

Bases: BaseOxmlElement

Used for w:tblW and w:tcW and others, specifies a table-related width.

width property writable

width: Length | None

EMU length indicated by the combined w:w and w:type attrs.

None for any w:type other than dxa, which includes the percentage widths a Length cannot represent. Use .value for the reading that covers those.

value property writable

value: Length | Pct | None

The width this element expresses, whatever unit it is written in.

A Length for w:type="dxa", a Pct for "pct", and None for "auto" and "nil" — neither of those carries a width of its own, the first meaning "size to the content" and the second "no width".

CT_TblCellMar

Bases: BaseOxmlElement

w:tblCellMar element, the default cell margins for a whole table.

An absent edge means the value is inherited from the table style.

Each edge is a CT_TblWidth in the schema, but the edge tag names are shared with w:tblBorders and lxml resolves an element class by tag name alone, so these children arrive typed as CT_Border — see the note above the registrations in oxml/__init__.py. The w:w and w:type attributes are therefore read and written directly here rather than through element-class attributes.

get_margin

get_margin(edge: str) -> Length | None

The width of the w:{edge} child, or None when that edge is absent.

Also None when the edge is present but expressed in a unit other than dxa, which is the only one Word writes here.

Source code in src/docx/oxml/table.py
def get_margin(self, edge: str) -> Length | None:
    """The width of the `w:{edge}` child, or |None| when that edge is absent.

    Also |None| when the edge is present but expressed in a unit other than `dxa`,
    which is the only one Word writes here.
    """
    child = self.find(qn("w:%s" % edge))
    if child is None or child.get(qn("w:type")) != "dxa":
        return None
    w = child.get(qn("w:w"))
    return None if w is None else Twips(int(w))

set_margin

set_margin(edge: str, value: Length | None) -> None

Set the w:{edge} child to value, removing it when value is None.

Source code in src/docx/oxml/table.py
def set_margin(self, edge: str, value: Length | None) -> None:
    """Set the `w:{edge}` child to `value`, removing it when `value` is |None|."""
    tag = "w:%s" % edge
    child = self.find(qn(tag))
    if value is None:
        if child is not None:
            self.remove(child)
        return
    if child is None:
        child = OxmlElement(tag)
        self._insert_edge(tag, child)
    child.set(qn("w:type"), "dxa")
    child.set(qn("w:w"), str(Emu(value).twips))

CT_TblLook

Bases: BaseOxmlElement

w:tblLook element, selecting which parts of the table style apply.

All attributes, no children. w:val is the legacy bitmask carrying the same six flags as the named attributes; Word writes both and keeps them in step, so docx.table._TableLook rewrites it whenever a flag changes.

update_val

update_val() -> None

Rewrite @w:val from the six named attributes.

Word reads the named attributes, but some older consumers read only the bitmask, so the two are kept in step rather than letting @w:val go stale.

Source code in src/docx/oxml/table.py
def update_val(self) -> None:
    """Rewrite `@w:val` from the six named attributes.

    Word reads the named attributes, but some older consumers read only the bitmask,
    so the two are kept in step rather than letting `@w:val` go stale.
    """
    bits = 0
    for name, bit in self._BITS.items():
        if getattr(self, name):
            bits |= bit
    self.val = bits

CT_Tc

Bases: BaseOxmlElement

w:tc table cell element.

bottom property

bottom: int

The row index that marks the bottom extent of the vertical span of this cell.

This is one greater than the index of the bottom-most row of the span, similar to how a slice of the cell's rows would be specified.

The span is measured by following continuation cells downward, without requiring this cell to carry w:vMerge of "restart". That is what the schema calls for, but a merge whose origin cell simply omits w:vMerge is common from other generators and renders as a merge in Word.

grid_offset property

grid_offset: int

Starting offset of tc in the layout-grid columns of its table.

A cell in the leftmost grid-column has offset 0.

grid_span property writable

grid_span: int

The integer number of columns this cell spans.

Determined by ./w:tcPr/w:gridSpan/@val, it defaults to 1.

inner_content_elements property

inner_content_elements: list[CT_P | CT_Tbl]

Generate all w:p and w:tbl elements in this table cell.

Elements appear in document order. Content inside a w:sdt (content control) wrapper is included; content shaded by nesting in a w:ins or other wrapper is not.

left property

left: int

The grid column index at which this <w:tc> element appears.

right property

right: int

The grid column index that marks the right-side extent of the horizontal span of this cell.

This is one greater than the index of the right-most column of the span, similar to how a slice of the cell's columns would be specified.

top property

top: int

The top-most row index in the vertical span of this cell.

top_tc property

top_tc: CT_Tc

The w:tc element holding the content of this cell's vertical span.

This is this element itself unless it is a continuation cell (w:vMerge of "continue"), in which case it is the cell the span starts at.

vMerge property writable

vMerge: str | None

Value of ./w:tcPr/w:vMerge/@val, None if w:vMerge is not present.

width property writable

width: Length | None

EMU length represented in ./w:tcPr/w:tcW or None if not present.

clear_content

clear_content()

Remove all content elements, preserving w:tcPr element if present.

Note that this leaves the w:tc element in an invalid state because it doesn't contain at least one block-level element. It's up to the caller to add a w:pchild element as the last content element.

Source code in src/docx/oxml/table.py
def clear_content(self):
    """Remove all content elements, preserving `w:tcPr` element if present.

    Note that this leaves the `w:tc` element in an invalid state because it doesn't
    contain at least one block-level element. It's up to the caller to add a
    `w:p`child element as the last content element.
    """
    # -- remove all cell inner-content except a `w:tcPr` when present. --
    for e in self.xpath("./*[not(self::w:tcPr)]"):
        self.remove(e)

iter_block_items

iter_block_items()

Generate a reference to each of the block-level content elements in this cell, in the order they appear.

Source code in src/docx/oxml/table.py
def iter_block_items(self):
    """Generate a reference to each of the block-level content elements in this
    cell, in the order they appear."""
    block_item_tags = (qn("w:p"), qn("w:tbl"), qn("w:sdt"))
    for child in self:
        if child.tag in block_item_tags:
            yield child

merge

merge(other_tc: CT_Tc) -> CT_Tc

Return top-left w:tc element of a new span.

Span is formed by merging the rectangular region defined by using this tc element and other_tc as diagonal corners.

Source code in src/docx/oxml/table.py
def merge(self, other_tc: CT_Tc) -> CT_Tc:
    """Return top-left `w:tc` element of a new span.

    Span is formed by merging the rectangular region defined by using this tc
    element and `other_tc` as diagonal corners.
    """
    top, left, height, width = self._span_dimensions(other_tc)
    top_tc = self._tbl.tr_lst[top].tc_at_grid_offset(left)
    top_tc._grow_to(width, height)
    return top_tc

new classmethod

new() -> CT_Tc

A new w:tc element, containing an empty paragraph as the required EG_BlockLevelElt.

Source code in src/docx/oxml/table.py
@classmethod
def new(cls) -> CT_Tc:
    """A new `w:tc` element, containing an empty paragraph as the required EG_BlockLevelElt."""
    return cast(CT_Tc, parse_xml("<w:tc %s><w:p/></w:tc>" % nsdecls("w")))

CT_TcPr

Bases: BaseOxmlElement

<w:tcPr> element, defining table cell properties.

textDirection_val property writable

textDirection_val: WD_TEXT_DIRECTION | None

Value of ./w:textDirection/@w:val, or None if the element is absent.

grid_span property writable

grid_span: int

The integer number of columns this cell spans.

Determined by ./w:gridSpan/@val, it defaults to 1.

vAlign_val property writable

vAlign_val

Value of w:val attribute on w:vAlign child.

Value is None if w:vAlign child is not present. The w:val attribute on w:vAlign is required.

vMerge_val property writable

vMerge_val

The value of the ./w:vMerge/@val attribute, or None if the w:vMerge element is not present.

width property writable

width: Length | None

EMU length in ./w:tcW or None if not present or its type is not 'dxa'.

CT_TrPr

Bases: BaseOxmlElement

<w:trPr> element, defining table row properties.

alignment property writable

alignment: WD_TABLE_ALIGNMENT | None

Value of ./w:jc/@w:val, or None if the element is absent.

cantSplit_val property writable

cantSplit_val: bool | None

Value of ./w:cantSplit/@w:val, or None if the element is absent.

hidden_val property writable

hidden_val: bool | None

Value of ./w:hidden/@w:val, or None if the element is absent.

tblHeader_val property writable

tblHeader_val: bool | None

Value of ./w:tblHeader/@w:val, or None if the element is absent.

cell_spacing property writable

cell_spacing: Length | None

Value of ./w:tblCellSpacing, or None if the element is absent.

width_after property writable

width_after: Length | None

Value of ./w:wAfter, or None if the element is absent.

width_before property writable

width_before: Length | None

Value of ./w:wBefore, or None if the element is absent.

grid_after property

grid_after: int

The number of unpopulated layout-grid cells at the end of this row.

grid_before property

grid_before: int

The number of unpopulated layout-grid cells at the start of this row.

trHeight_hRule property writable

trHeight_hRule: WD_ROW_HEIGHT_RULE | None

Return the value of w:trHeight@w:hRule, or None if not present.

trHeight_val property writable

trHeight_val

Return the value of w:trHeight@w:val, or None if not present.

CT_VerticalJc

Bases: BaseOxmlElement

w:vAlign element, specifying vertical alignment of cell.

CT_VMerge

Bases: BaseOxmlElement

<w:vMerge> element, specifying vertical merging behavior of a cell.