v0.5.2
Python application / build (push) Waiting to run

Что то сделал
This commit is contained in:
Igor20264
2026-09-08 19:37:54 +03:00
parent 1a5b35eb54
commit 510f7e7adf
90 changed files with 11720 additions and 5547 deletions
+140
View File
@@ -0,0 +1,140 @@
"""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