Files
Igor20264 510f7e7adf
Python application / build (push) Waiting to run
v0.5.2
Что то сделал
2026-09-08 19:37:54 +03:00

141 lines
4.3 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""DOCX table → markdown rows with ^ / > merge markers."""
from __future__ import annotations
from docx.oxml.ns import qn
from docx.table import Table as DocxTable
def _cell_v_merge(tc) -> str:
"""Return none | restart | continue for vertical merge."""
tcPr = tc.tcPr
if tcPr is None:
return "none"
vMerge = tcPr.find(qn("w:vMerge"))
if vMerge is None:
return "none"
val = vMerge.get(qn("w:val"))
if val in (None, "continue"):
return "continue"
return "restart"
def _cell_grid_span(tc) -> int:
tcPr = tc.tcPr
if tcPr is None:
return 1
span = tcPr.find(qn("w:gridSpan"))
if span is None:
return 1
try:
return max(1, int(span.get(qn("w:val")) or "1"))
except ValueError:
return 1
def _cell_text(cell) -> str:
from .text import paragraph_plain
parts: list[str] = []
for p in cell.paragraphs:
t = paragraph_plain(p)
if t:
parts.append(t)
return " ".join(parts).replace("|", "\\|")
def table_to_grid(docx_table: DocxTable) -> tuple[list[list[str]], list[list[tuple[str, str]]]]:
"""
Expand a Word table into a rectangular grid.
merges[r][c] = (v, h) where v/h in {none, restart, continue}.
Horizontally spanned continue cells are filled with '>' semantics.
Vertically continued cells use '^'.
"""
rows_xml = docx_table._tbl.tr_lst
# First pass: determine max columns from grid spans
max_cols = 0
row_infos: list[list[tuple[object, int, str]]] = [] # (tc, span, vmerge)
for tr in rows_xml:
info: list[tuple[object, int, str]] = []
col = 0
for tc in tr.tc_lst:
span = _cell_grid_span(tc)
v = _cell_v_merge(tc)
info.append((tc, span, v))
col += span
max_cols = max(max_cols, col)
row_infos.append(info)
if max_cols == 0:
return [], []
texts: list[list[str]] = [["-" for _ in range(max_cols)] for _ in range(len(row_infos))]
merges: list[list[tuple[str, str]]] = [
[("none", "none") for _ in range(max_cols)] for _ in range(len(row_infos))
]
# Map tc elements to python-docx cells by index
for r, info in enumerate(row_infos):
c = 0
cell_idx = 0
for tc, span, v in info:
try:
cell = docx_table.rows[r].cells[cell_idx]
text = _cell_text(cell)
except Exception:
text = ""
cell_idx += 1
if v == "continue":
merges[r][c] = ("continue", "none")
texts[r][c] = "^"
else:
merges[r][c] = ("restart" if v == "restart" else "none", "none" if span == 1 else "restart")
texts[r][c] = text if text else " "
for k in range(1, span):
if c + k < max_cols:
merges[r][c + k] = ("none", "continue")
texts[r][c + k] = ">"
c += span
return texts, merges
def is_formula_table(docx_table: DocxTable) -> bool:
"""Heuristic: 1×2 table with Formula Content / Formula Numbering styles."""
try:
if len(docx_table.rows) != 1 or len(docx_table.columns) != 2:
return False
except Exception:
return False
styles: list[str] = []
for cell in docx_table.rows[0].cells:
for p in cell.paragraphs:
name = (p.style.name if p.style is not None else "") or ""
styles.append(name)
joined = " ".join(styles).lower()
return "formula" in joined
def formula_table_parts(docx_table: DocxTable) -> tuple[object | None, str | None]:
"""Return (omml_element_or_None, number_text_or_None) from a formula table."""
from .omml import find_omml, omml_to_latex
from .text import paragraph_plain
left = docx_table.rows[0].cells[0]
right = docx_table.rows[0].cells[1]
latex = ""
for p in left.paragraphs:
for om in find_omml(p._element):
latex = omml_to_latex(om)
if latex:
break
if latex:
break
if not latex:
latex = " ".join(paragraph_plain(p) for p in left.paragraphs).strip()
number = " ".join(paragraph_plain(p) for p in right.paragraphs).strip()
number = number.strip("() ").strip() or None
return latex, number