Files
Igor20264 b38661f588
Python application / build (push) Has been cancelled
Update 0.4.0
- Add\Rework UI
- Add Split Table and Listing
- Add Support Customazeble schems
2026-09-04 22:28:39 +03:00

144 lines
4.5 KiB
Python

from copy import deepcopy
from docx.oxml import CT_Tbl
from docx.oxml.ns import qn
from docx.shared import Length, Parented
from docx.table import Table, _Row, _Cell
from md2gost.util import create_element
def _twips(value) -> int:
"""Length or EMU-int → twips.
python-docx ``Length + Length`` returns a bare ``int`` in EMUs, not twips.
"""
if hasattr(value, "twips"):
return int(value.twips)
# bare int from Length arithmetic is EMU (1 twip = 635 EMU)
return int(value) // 635
__all__ = [
"create_table",
"create_table_row",
"create_table_cell",
"apply_cell_merge",
"set_table_box_borders",
]
def _set_table_width(table: Table, width: Length) -> None:
"""Force fixed table width (CT_Tbl.new_tbl leaves tblW auto/0 → Word shrinks table)."""
tbl = table._tbl
tblPr = tbl.tblPr
if tblPr is None:
tblPr = create_element("w:tblPr")
tbl.insert(0, tblPr)
for el in tblPr.findall(qn("w:tblW")):
tblPr.remove(el)
tblPr.append(create_element("w:tblW", {
"w:type": "dxa",
"w:w": str(_twips(width)),
}))
for el in tblPr.findall(qn("w:tblLayout")):
tblPr.remove(el)
tblPr.append(create_element("w:tblLayout", {"w:type": "fixed"}))
for el in tblPr.findall(qn("w:tblInd")):
tblPr.remove(el)
tblPr.append(create_element("w:tblInd", {
"w:type": "dxa",
"w:w": "0",
}))
def _sync_grid_cols(table: Table, cols: int, width: Length) -> None:
tbl = table._tbl
grid = tbl.tblGrid
if grid is None:
grid = create_element("w:tblGrid")
tbl.insert(list(tbl).index(tbl.tblPr) + 1 if tbl.tblPr is not None else 0, grid)
for el in list(grid.findall(qn("w:gridCol"))):
grid.remove(el)
col_w = max(_twips(width) // max(cols, 1), 1)
for _ in range(cols):
grid.append(create_element("w:gridCol", {"w:w": str(col_w)}))
def create_table(parent: Parented, rows: int, cols, width: Length, style="Table Grid"):
table = Table(CT_Tbl.new_tbl(rows, cols, width), parent)
table.style = style
_set_table_width(table, width)
_sync_grid_cols(table, cols, width)
# google docs fix
borders = parent.part.styles[style]._element.xpath("w:tblPr/w:tblBorders")
if borders:
tblPr = table._tbl.tblPr
for el in tblPr.findall(qn("w:tblBorders")):
tblPr.remove(el)
tblPr.append(deepcopy(borders[0]))
for i in range(rows):
for j in range(cols):
cell = table.cell(i, j)
cell._element.remove(cell.paragraphs[0]._element)
table.cell(i, j)._element.tcPr.append(create_element("w:shd", {
"w:fill": "auto", "w:val": "clear"
}))
return table
def create_table_row(parent: Table, *, header: bool = False):
children = [create_element("w:cantSplit")]
if header:
children.append(create_element("w:tblHeader"))
row = _Row(create_element("w:tr"), parent)
row._tr.insert(0, create_element("w:trPr", children))
return row
def create_table_cell(parent: _Row, width: Length):
cell = _Cell(create_element("w:tc"), parent)
cell.width = width
return cell
def apply_cell_merge(cell: _Cell, v_merge: str | None = None, grid_span: int | None = None) -> None:
"""Apply Word OXML vertical merge and/or horizontal gridSpan on a cell.
v_merge: ``restart`` | ``continue`` | None
grid_span: column span (>= 2) or None
"""
tc = cell._tc
tcPr = tc.get_or_add_tcPr()
if v_merge == "restart":
tcPr.append(create_element("w:vMerge", {"w:val": "restart"}))
elif v_merge == "continue":
tcPr.append(create_element("w:vMerge"))
if grid_span and grid_span > 1:
tcPr.append(create_element("w:gridSpan", {"w:val": str(grid_span)}))
def set_table_box_borders(table: Table, *, sz: str = "4", color: str = "000000") -> None:
"""Outer frame only — no inside H/V lines (listings look like text in a box)."""
tbl = table._tbl
tblPr = tbl.tblPr
if tblPr is None:
tblPr = create_element("w:tblPr")
tbl.insert(0, tblPr)
for el in list(tblPr.findall(qn("w:tblBorders"))):
tblPr.remove(el)
solid = {"w:val": "single", "w:sz": sz, "w:space": "0", "w:color": color}
none = {"w:val": "nil"}
borders = create_element("w:tblBorders")
for edge, attrs in (
("w:top", solid),
("w:left", solid),
("w:bottom", solid),
("w:right", solid),
("w:insideH", none),
("w:insideV", none),
):
borders.append(create_element(edge, attrs))
tblPr.append(borders)