b38661f588
Python application / build (push) Has been cancelled
- Add\Rework UI - Add Split Table and Listing - Add Support Customazeble schems
182 lines
5.9 KiB
Python
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
|