Files
md_to_gost/tests/test_table_merge.py
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

182 lines
5.9 KiB
Python

"""Tests for table cell merge (^ rowspan, > colspan)."""
from __future__ import annotations
import os
from docx import Document
from docx.shared import Mm, Pt
from md2gost import package_dir
from md2gost.checker import check_table_merge
from md2gost.docx_elements import apply_cell_merge, create_table, create_table_cell, create_table_row
from md2gost.extended_markdown import markdown
from md2gost.extended_markdown.table import Table as MdTable
from md2gost.layout_tracker import LayoutState
from md2gost.renderable.caption import CaptionInfo
from md2gost.renderable_factory import RenderableFactory
SAMPLE_ROWSPAN = """\
%req Требования
| Категория | Описание |
|-----------|----------|
| Производительность | Требование 1 |
| ^ | Требование 2 |
| ^ | Требование 3 |
| Масштабируемость | Требование 4 |
| ^ | Требование 5 |
"""
SAMPLE_COLSPAN = """\
%cspan Demo
| A | B | C |
|---|---|---|
| wide | > | Z |
"""
def test_parse_rowspan_markers():
doc = markdown.parse(SAMPLE_ROWSPAN)
tables = [c for c in doc.children if isinstance(c, MdTable)]
assert len(tables) == 1
t = tables[0]
assert len(t.children) == 6
assert t.children[1].children[0].merge_v == "restart"
assert t.children[2].children[0].merge_v == "continue"
assert t.children[3].children[0].merge_v == "continue"
assert t.children[4].children[0].merge_v == "restart"
assert t.children[5].children[0].merge_v == "continue"
def test_parse_colspan_markers():
doc = markdown.parse(SAMPLE_COLSPAN)
tables = [c for c in doc.children if isinstance(c, MdTable)]
assert len(tables) == 1
t = tables[0]
data = t.children[1].children
assert data[0].merge_h == "restart"
assert data[1].merge_h == "continue"
assert data[2].merge_h == "none"
def test_apply_cell_merge_oxml():
doc = Document()
table = create_table(doc, 0, 2, doc.sections[0].page_width)
assert 'w:type="dxa"' in table._tbl.xml
row = create_table_row(table)
cell = create_table_cell(row, doc.sections[0].page_width / 2)
apply_cell_merge(cell, v_merge="restart", grid_span=2)
xml = cell._tc.xml
assert "vMerge" in xml
assert "restart" in xml
assert "gridSpan" in xml
def test_create_table_fixed_width():
doc = Document()
width = Mm(170)
table = create_table(doc, 0, 3, width)
xml = table._tbl.xml
assert 'w:type="dxa"' in xml
assert 'w:type="fixed"' in xml
assert xml.count("gridCol") == 3
# ~170 mm ≈ 9638 twips, not EMU (~6e6)
from md2gost.docx_elements import _twips
assert 9000 < _twips(width) < 11000
import re
m = re.search(r'w:tblW[^>]*w:w="(\d+)"', xml) or re.search(r'w:w="(\d+)"[^>]*w:type="dxa"', xml)
# tblW attrs order may vary
w_attr = re.search(r'<w:tblW[^/]*/>', xml)
assert w_attr
tw = int(re.search(r'w:w="(\d+)"', w_attr.group(0)).group(1))
assert 9000 < tw < 11000, f"tblW too large (EMU mistaken for twips?): {tw}"
def test_twips_from_length_sum():
"""Length+Length yields EMU int — must convert to twips, not treat as twips."""
from md2gost.docx_elements import _twips
from docx.shared import Twips as T
summed = Mm(170) + T(108) # bare EMU int
assert isinstance(summed, int)
assert 9000 < _twips(summed) < 12000
def test_factory_sets_merge_and_oxml_row():
"""Factory passes merge metadata; physical row has vMerge without full convert."""
parsed = markdown.parse(SAMPLE_ROWSPAN)
md_table = next(c for c in parsed.children if isinstance(c, MdTable))
doc = Document(os.path.join(package_dir(), "Template.docx"))
factory = RenderableFactory(doc._body)
table = factory.create(md_table, CaptionInfo("req", "Требования"))
assert table._merge[1][0][0] == "restart"
assert table._merge[2][0][0] == "continue"
for r in range(len(table._rows)):
for c in range(table._cols):
table._rows[r][c] = []
docx_table = create_table(doc, 0, table._cols, Mm(170))
layout = LayoutState(Mm(250), Mm(170))
row_el, _ = table._build_docx_row(docx_table, 1, True, layout)
docx_table._element.append(row_el._element)
row2, _ = table._build_docx_row(docx_table, 2, True, layout)
docx_table._element.append(row2._element)
xml = docx_table._tbl.xml
assert "vMerge" in xml
assert "restart" in xml
def test_checker_merge_header_error():
bad = """\
| ^ | B |
|---|---|
| 1 | 2 |
"""
issues = check_table_merge(bad)
assert any(i.id == "table.merge_header" for i in issues)
def test_checker_merge_col_error():
bad = """\
| A | B |
|---|---|
| > | 2 |
"""
issues = check_table_merge(bad)
assert any(i.id == "table.merge_col" for i in issues)
def test_checker_merge_ok():
issues = check_table_merge(SAMPLE_ROWSPAN)
assert issues == []
def test_table_continuation_modes():
from md2gost.profiles import TABLE_CONTINUATION_MODES, DEFAULT_TABLE_CONTINUATION
assert DEFAULT_TABLE_CONTINUATION == "word"
assert TABLE_CONTINUATION_MODES == ("off", "legacy", "caption", "soft", "word")
doc = Document(os.path.join(package_dir(), "Template.docx"))
from md2gost.renderable.table import Table as RTable
from md2gost.renderable.caption import CaptionInfo
t = RTable(doc._body, 1, 2, CaptionInfo("x", "y"))
assert t._continuation_mode == "word"
t.set_continuation_mode("legacy")
assert t._continuation_mode == "legacy"
t.set_continuation_mode("caption")
assert t._continuation_mode == "caption"
t.set_continuation_mode("soft")
assert t._continuation_mode == "soft"
assert not t._should_split_fragment(Pt(1000), LayoutState(Mm(250), Mm(170)), 3)
t.set_continuation_mode("caption")
assert t._should_split_fragment(Pt(1000), LayoutState(Mm(50), Mm(170)), 3)
try:
t.set_continuation_mode("nope")
assert False
except ValueError:
pass