docx_plus.styles.inspect¶
The cascade resolver. Walks the six OOXML formatting layers and returns
a fully-resolved ResolvedFormatting
plus optional per-field provenance.
See ARCHITECTURE.md §2 for
the algorithm walkthrough and the toggle semantics.
docx_plus.styles.inspect ¶
Cascade resolver: resolve_effective_formatting.
Walks the six layers of OOXML formatting precedence (SPEC §4) and returns a
fully-resolved :class:ResolvedFormatting describing what a paragraph, run,
or cell would render with right now. Later layers override earlier ones,
except toggle properties (bold, italic, etc.) which XOR through the chain
per ECMA-376 17.7.3.
Provenance tracking is plumbed through the same walk gated by the
include_provenance flag; with the flag off, the resolver's value output
is identical (verified by test_provenance_does_not_change_values).
ResolvedFormatting
dataclass
¶
ResolvedFormatting(
style_id: str | None = None,
style_name: str | None = None,
alignment: str | None = None,
indent_left: int | None = None,
indent_right: int | None = None,
indent_first_line: int | None = None,
spacing_before: int | None = None,
spacing_after: int | None = None,
line_spacing: float | None = None,
line_spacing_rule: str | None = None,
keep_with_next: bool | None = None,
keep_lines: bool | None = None,
page_break_before: bool | None = None,
outline_level: int | None = None,
font_name: str | None = None,
font_size: float | None = None,
bold: bool | None = None,
italic: bool | None = None,
cs_bold: bool | None = None,
cs_italic: bool | None = None,
underline: str | None = None,
strike: bool | None = None,
double_strike: bool | None = None,
color_rgb: str | None = None,
highlight: str | None = None,
caps: bool | None = None,
small_caps: bool | None = None,
vanish: bool | None = None,
emboss: bool | None = None,
imprint: bool | None = None,
outline: bool | None = None,
shadow: bool | None = None,
vert_align: str | None = None,
num_id: int | None = None,
num_level: int | None = None,
partial: bool = False,
provenance: dict[str, FormattingSource] | None = None,
)
The effective formatting for a paragraph, run, or table cell.
Every field is None until some layer of the cascade sets it. Toggle
properties carry their XOR-resolved boolean. SPEC §4 specifies the fields.
All twelve ECMA-376 17.7.3 toggle properties are surfaced: the six
base toggles (bold, italic, caps, small_caps,
strike, vanish) and the six complex-script / decorative
variants (cs_bold, cs_italic, emboss, imprint,
outline, shadow). All XOR through the cascade with the same
semantics; an explicit w:val="false" resets parity to false.
TableContext
dataclass
¶
TableContext(
is_first_row: bool = False,
is_last_row: bool = False,
is_first_col: bool = False,
is_last_col: bool = False,
is_band_row: bool = False,
is_band_col: bool = False,
is_band2_row: bool = False,
is_band2_col: bool = False,
)
A cell's position within its table — for conditional table-style formatting.
ECMA-376 17.7.6.5 lets a <w:style w:type="table"> carry
conditional formatting branches (<w:tblStylePr w:type="firstRow"/>,
"lastRow", "firstCol", "lastCol", "band1Horz",
"band1Vert", "band2Horz", "band2Vert",
"nwCell" / "neCell" / "swCell" / "seCell"). To pick
the right branches the cascade resolver needs to know where in the
table the target lives.
Construct manually for an out-of-band query, or pass a _Cell to
:func:resolve_effective_formatting to derive the context
automatically from the cell's parent row / table.
Band size: by default rows alternate band1 / band2 every row. When
the table instance's <w:tblPr> carries a <w:tblStyleRowBandSize
w:val="N"/> (resp. <w:tblStyleColBandSize>), bands span N
rows / columns each. Note that v0.2 does not yet walk the table
style chain looking for these attributes — only the table
instance's own tblPr is consulted. This is sufficient for tables
where the application or user explicitly set the band size, but
misses style-defined band sizes (deferred to v0.3+).
Scope: this context selects which <w:tblStylePr> branches apply,
but only their run / paragraph properties are resolved. Cell-,
row-, and table-level properties (<w:tcPr> / <w:trPr> /
<w:tblPr>) from a table style are not surfaced — see the
:func:resolve_effective_formatting note.
Auto-derivation limitation: when a row wraps its cells in a
<w:sdt> (a content control around table cells), the derived
column index cannot be computed and an empty (all-False)
:class:TableContext is returned. Pass an explicit context in that
case. Nested tables resolve against the inner cell's position.
Attributes:
| Name | Type | Description |
|---|---|---|
is_first_row |
bool
|
Cell is in the first |
is_last_row |
bool
|
Cell is in the last |
is_first_col |
bool
|
Cell is the first |
is_last_col |
bool
|
Cell is the last |
is_band_row |
bool
|
Cell is in a "band1" horizontal stripe (first band). |
is_band_col |
bool
|
Cell is in a "band1" vertical stripe (first band). |
is_band2_row |
bool
|
Cell is in a "band2" horizontal stripe (second band — the complement of band1 at default band-size=1). |
is_band2_col |
bool
|
Cell is in a "band2" vertical stripe. |
FormattingSource
dataclass
¶
FormattingSource(
layer: Layer,
style_id: str | None = None,
is_toggle_resolved: bool = False,
chain_depth: int | None = None,
)
Identifies the cascade layer that contributed a resolved property.
layer is the cascade layer the value came from. For style layers,
style_id names the specific style (the lowest one in the basedOn
chain that set the value); chain_depth records how many basedOn hops
away that style was from the target. is_toggle_resolved is True when
the value is the XOR result across multiple layers rather than a direct
set.
StyleCascadeError ¶
Bases: DocxPlusError
Raised when the basedOn chain cycles or exceeds Word's depth limit.
MissingPartError ¶
Bases: DocxPlusError
Raised when a referenced document part (e.g. numbering.xml) is absent.
resolve_effective_formatting ¶
resolve_effective_formatting(
target: Paragraph | Run | _Cell,
*,
include_provenance: bool = False,
table_context: TableContext | None = None,
) -> ResolvedFormatting
Resolve the effective formatting for target.
Walks the six cascade layers in precedence order, returning a fully
resolved :class:ResolvedFormatting. Toggle properties XOR through the
chain per ECMA-376 17.7.3. Theme colors are resolved against the
document's theme part; if the theme is missing or malformed, the result's
partial flag is set and unresolved theme names are returned in place
of hex values.
When target is in a table cell, table-style conditional
formatting (<w:tblStylePr> branches: firstRow, lastRow,
firstCol, lastCol, band1Horz, band1Vert, the four
corners, and wholeTable) is applied on top of the base table
style in ECMA-376 17.7.6.5 precedence order.
Note
Only run- and paragraph-level properties are resolved (the
<w:rPr> / <w:pPr> carried by a style's base and its
<w:tblStylePr> branches). Cell-, row-, and table-level
properties (<w:tcPr> cell shading and margins, <w:trPr>
row heights, <w:tblPr> table defaults) declared by a table
style are not surfaced on :class:ResolvedFormatting — that
belongs to a separate cell-formatting resolver deferred to v0.3+.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
target
|
Paragraph | Run | _Cell
|
A python-docx :class: |
required |
include_provenance
|
bool
|
If True, populate |
False
|
table_context
|
TableContext | None
|
Optional override for the cell's position within
its table. When |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
A |
ResolvedFormatting
|
class: |
Raises:
| Type | Description |
|---|---|
StyleCascadeError
|
If the basedOn chain has a cycle or exceeds Word's depth limit of 11. |
MissingPartError
|
If the target's paragraph references a numbering id
that exists but the |
Example
from docx import Document from docx_plus.styles.inspect import resolve_effective_formatting doc = Document() p = doc.add_paragraph("Hello") resolved = resolve_effective_formatting(p) resolved.font_size # e.g. 11.0 from docDefaults 11.0
Source code in docx_plus/styles/inspect.py
244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 | |