docx/nested-table

A 2x1 outer table whose first cell contains another 2x2 inner table with the literal cells Inner A1 / Inner A2 / Inner B1 / Inner B2.

Library verdicts: python-docx: — docxjs: —

Metadata

Feature iddocx/nested-table
Formatdocx
Categorytables
Spececma-376-5-part-1 § 17.4.24 w:tbl

XPath assertions

ID / partPredicateXPathpython-docxdocxjs
outer-tbl-present
word/document.xml
exist
The outer <w:tbl> must be a direct child of <w:body>.
//w:body/w:tbl
nested-tbl-present
word/document.xml
exist
A <w:tbl> must appear as a child of a <w:tc> inside another <w:tbl> — the canonical nested-table shape.
//w:tbl/w:tr/w:tc/w:tbl
inner-cell-a1-text
word/document.xml
exist
The inner table must contain a cell with the literal 'Inner A1'.
//w:tbl//w:tbl//w:t[normalize-space()='Inner A1']
inner-cell-a2-text
word/document.xml
exist
The inner table must contain a cell with the literal 'Inner A2'.
//w:tbl//w:tbl//w:t[normalize-space()='Inner A2']
inner-cell-b1-text
word/document.xml
exist
The inner table must contain a cell with the literal 'Inner B1'.
//w:tbl//w:tbl//w:t[normalize-space()='Inner B1']
inner-cell-b2-text
word/document.xml
exist
The inner table must contain a cell with the literal 'Inner B2'.
//w:tbl//w:tbl//w:t[normalize-space()='Inner B2']
outer-bottom-cell-text
word/document.xml
exist
The outer table's second row must contain a cell with the literal 'Outer bottom'. Anchors the outer-table shape independently of the nested table.
//w:tbl/w:tr[2]/w:tc//w:t[normalize-space()='Outer bottom']

Render assertions

IDPredicateSelectorpython-docxdocxjs
nested-table-inner-cell-a1css_selector/ match-text = Inner A1
A rendered <td>/<th> inside a <table> that is itself inside another <table> must contain 'Inner A1'. Catches renderers that flatten nested tables.
.docx-wrapper table table td, .docx-wrapper table table th
nested-table-inner-cell-countcss_selector/ equal-count
The inner (nested) table must contribute exactly four rendered cells.
.docx-wrapper table table td, .docx-wrapper table table th

Generator source

scripts/gen_nested_table.py

#!/usr/bin/env python3
"""Generate ``fixtures/docx/nested-table.docx``.

A minimal document exercising table-inside-cell nesting (ECMA-376
17.4.24). The outer table is 2x1:

- cell(0, 0) contains an inner 2x2 table with cells ``Inner A1``,
  ``Inner A2``, ``Inner B1``, ``Inner B2``.
- cell(1, 0) contains a plain-text paragraph ``Outer bottom`` so the
  outer-table shape is obvious even when the nested one is ignored.

python-docx's ``_Cell.add_table()`` does the nesting natively; no raw
XML authoring is needed.
"""

from __future__ import annotations

from pathlib import Path

from docx import Document

_REPO_ROOT = Path(__file__).resolve().parent.parent
_OUT = _REPO_ROOT / "fixtures" / "docx" / "nested-table.docx"


def main() -> int:
    doc = Document()

    outer = doc.add_table(rows=2, cols=1)

    # -- nested 2x2 table inside the outer top-left cell --
    inner = outer.cell(0, 0).add_table(rows=2, cols=2)
    inner.cell(0, 0).text = "Inner A1"
    inner.cell(0, 1).text = "Inner A2"
    inner.cell(1, 0).text = "Inner B1"
    inner.cell(1, 1).text = "Inner B2"

    outer.cell(1, 0).text = "Outer bottom"

    _OUT.parent.mkdir(parents=True, exist_ok=True)
    doc.save(_OUT, reproducible=True)
    print(_OUT)
    return 0


if __name__ == "__main__":
    raise SystemExit(main())

Manifest (expanded)

{
  "$schema": "../manifest.schema.json",
  "id": "docx/nested-table",
  "title": "Nested table (table inside a cell)",
  "format": "docx",
  "category": "tables",
  "summary": "A 2x1 outer table whose first cell contains another 2x2 inner table with the literal cells Inner A1 / Inner A2 / Inner B1 / Inner B2.",
  "spec": {
    "source": "ecma-376-5-part-1",
    "clause": "17.4.24",
    "element": "w:tbl",
    "rnc_reference": "spec/ecma-376-5/part-1/rnc/WordprocessingML.rnc",
    "xsd_reference": "spec/ecma-376-5/part-1/xsd/wml.xsd",
    "notes": "OOXML allows a <w:tc> (table cell) to contain another <w:tbl> as a child, producing a nested table. This fixture has a 2x1 outer table; cell(0,0) holds a 2x2 inner table whose four cells contain 'Inner A1', 'Inner A2', 'Inner B1', 'Inner B2'. cell(1,0) of the outer table carries a plain text paragraph 'Outer bottom' to make the outer-table shape obvious. python-docx exposes _Cell.add_table() for nested-table construction, so no raw XML authoring is needed."
  },
  "fixtures": {
    "machine": "docx/nested-table",
    "office": "docx/nested-table"
  },
  "generator": {
    "python": "scripts/gen_nested_table.py"
  },
  "assertions": [
    {
      "id": "outer-tbl-present",
      "part": "word/document.xml",
      "namespaces": {
        "w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
      },
      "xpath": "//w:body/w:tbl",
      "must": "exist",
      "description": "The outer <w:tbl> must be a direct child of <w:body>."
    },
    {
      "id": "nested-tbl-present",
      "part": "word/document.xml",
      "namespaces": {
        "w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
      },
      "xpath": "//w:tbl/w:tr/w:tc/w:tbl",
      "must": "exist",
      "description": "A <w:tbl> must appear as a child of a <w:tc> inside another <w:tbl> — the canonical nested-table shape."
    },
    {
      "id": "inner-cell-a1-text",
      "part": "word/document.xml",
      "namespaces": {
        "w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
      },
      "xpath": "//w:tbl//w:tbl//w:t[normalize-space()='Inner A1']",
      "must": "exist",
      "description": "The inner table must contain a cell with the literal 'Inner A1'."
    },
    {
      "id": "inner-cell-a2-text",
      "part": "word/document.xml",
      "namespaces": {
        "w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
      },
      "xpath": "//w:tbl//w:tbl//w:t[normalize-space()='Inner A2']",
      "must": "exist",
      "description": "The inner table must contain a cell with the literal 'Inner A2'."
    },
    {
      "id": "inner-cell-b1-text",
      "part": "word/document.xml",
      "namespaces": {
        "w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
      },
      "xpath": "//w:tbl//w:tbl//w:t[normalize-space()='Inner B1']",
      "must": "exist",
      "description": "The inner table must contain a cell with the literal 'Inner B1'."
    },
    {
      "id": "inner-cell-b2-text",
      "part": "word/document.xml",
      "namespaces": {
        "w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
      },
      "xpath": "//w:tbl//w:tbl//w:t[normalize-space()='Inner B2']",
      "must": "exist",
      "description": "The inner table must contain a cell with the literal 'Inner B2'."
    },
    {
      "id": "outer-bottom-cell-text",
      "part": "word/document.xml",
      "namespaces": {
        "w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
      },
      "xpath": "//w:tbl/w:tr[2]/w:tc//w:t[normalize-space()='Outer bottom']",
      "must": "exist",
      "description": "The outer table's second row must contain a cell with the literal 'Outer bottom'. Anchors the outer-table shape independently of the nested table."
    }
  ],
  "render_assertions": [
    {
      "id": "nested-table-inner-cell-a1",
      "kind": "css_selector",
      "selector": ".docx-wrapper table table td, .docx-wrapper table table th",
      "must": "match-text",
      "value": "Inner A1",
      "description": "A rendered <td>/<th> inside a <table> that is itself inside another <table> must contain 'Inner A1'. Catches renderers that flatten nested tables."
    },
    {
      "id": "nested-table-inner-cell-count",
      "kind": "css_selector",
      "selector": ".docx-wrapper table table td, .docx-wrapper table table th",
      "must": "equal-count",
      "count": 4,
      "description": "The inner (nested) table must contribute exactly four rendered cells."
    }
  ]
}

Fixture

Download nested-table.docx (18.8 KB)

Reference preview

No rendered reference is available for this case.

Spec notes

OOXML allows a <w:tc> (table cell) to contain another <w:tbl> as a child, producing a nested table. This fixture has a 2x1 outer table; cell(0,0) holds a 2x2 inner table whose four cells contain 'Inner A1', 'Inner A2', 'Inner B1', 'Inner B2'. cell(1,0) of the outer table carries a plain text paragraph 'Outer bottom' to make the outer-table shape obvious. python-docx exposes _Cell.add_table() for nested-table construction, so no raw XML authoring is needed.