- Add\Rework UI - Add Split Table and Listing - Add Support Customazeble schems
This commit is contained in:
@@ -0,0 +1,200 @@
|
||||
"""Tests for landscape section around wide figures/tables."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
from docx import Document
|
||||
from docx.enum.section import WD_ORIENT
|
||||
from PIL import Image as PILImage
|
||||
|
||||
from md2gost import package_dir
|
||||
from md2gost.page_geometry import content_size
|
||||
from md2gost.parser_ import Parser
|
||||
from md2gost.renderable.caption import CaptionInfo
|
||||
from md2gost.renderable.image import Image
|
||||
from md2gost.renderable.table import Table
|
||||
from md2gost.renderer import Renderer
|
||||
|
||||
|
||||
def test_content_size_landscape_wider():
|
||||
h_p, w_p = content_size(landscape=False)
|
||||
h_l, w_l = content_size(landscape=True)
|
||||
assert w_l > w_p
|
||||
assert h_l < h_p
|
||||
|
||||
|
||||
def test_renderer_landscape_section_for_table():
|
||||
doc = Document(os.path.join(package_dir(), "Template.docx"))
|
||||
body = doc._body._element
|
||||
for child in list(body):
|
||||
if child.tag.endswith("}sectPr"):
|
||||
continue
|
||||
body.remove(child)
|
||||
|
||||
renderer = Renderer(doc, skip_numbering=False)
|
||||
table = Table(doc._body, 2, 4, CaptionInfo("t1", "Wide", landscape=True))
|
||||
for r in range(2):
|
||||
for c in range(4):
|
||||
table.add_paragraph_to_cell(r, c).add_run(f"{r},{c}")
|
||||
|
||||
before = len(doc.sections)
|
||||
renderer.process([table])
|
||||
after = len(doc.sections)
|
||||
assert after >= before + 2
|
||||
|
||||
land = [s for s in doc.sections if s.orientation == WD_ORIENT.LANDSCAPE]
|
||||
assert land, "no landscape section"
|
||||
assert int(land[0].page_width) > int(land[0].page_height)
|
||||
|
||||
from md2gost.styles import apply_document_styles
|
||||
apply_document_styles(doc, "mirea")
|
||||
land2 = [s for s in doc.sections if s.orientation == WD_ORIENT.LANDSCAPE]
|
||||
assert land2
|
||||
assert int(land2[0].page_width) > int(land2[0].page_height)
|
||||
|
||||
assert doc.sections[-1].orientation == WD_ORIENT.PORTRAIT
|
||||
assert int(doc.sections[-1].page_width) < int(doc.sections[-1].page_height)
|
||||
|
||||
|
||||
def test_parser_propagates_landscape_to_table():
|
||||
doc = Document(os.path.join(package_dir(), "Template.docx"))
|
||||
md = "%t1 Широкая +landscape\n\n|A|B|C|\n|---|---|---|\n|1|2|3|\n"
|
||||
renderables = list(Parser(doc, md).parse())
|
||||
assert len(renderables) == 1
|
||||
assert isinstance(renderables[0], Table)
|
||||
assert renderables[0].landscape is True
|
||||
|
||||
|
||||
def test_landscape_image_not_queued_for_soft_pagebreak(tmp_path, monkeypatch):
|
||||
"""Portrait fit-check must not queue +landscape images (empty page before figure)."""
|
||||
monkeypatch.chdir(tmp_path)
|
||||
png = tmp_path / "wide.png"
|
||||
# Wide image that is tall when forced into portrait width
|
||||
PILImage.new("RGB", (2000, 1400), color=(0, 128, 255)).save(png)
|
||||
os.environ["WORKING_DIR"] = str(tmp_path)
|
||||
|
||||
doc = Document(os.path.join(package_dir(), "Template.docx"))
|
||||
body = doc._body._element
|
||||
for child in list(body):
|
||||
if child.tag.endswith("}sectPr"):
|
||||
continue
|
||||
body.remove(child)
|
||||
|
||||
from md2gost.renderable.paragraph import Paragraph as RParagraph
|
||||
from md2gost.sub_renderable import SubRenderable
|
||||
|
||||
para = RParagraph(doc._body)
|
||||
para.add_run("Before text that fills some space. " * 20)
|
||||
img = Image(
|
||||
doc._body,
|
||||
str(png),
|
||||
CaptionInfo("deploy", "Диаграмма", landscape=True),
|
||||
)
|
||||
# Mimic factory: image attached to paragraph
|
||||
para._images.append(img)
|
||||
|
||||
from md2gost.layout_tracker import LayoutState
|
||||
from md2gost.page_geometry import content_size
|
||||
from docx.shared import Length
|
||||
|
||||
max_h, max_w = content_size(landscape=False)
|
||||
state = LayoutState(max_h, max_w)
|
||||
# Pretend most of the page is already used so a portrait fit-check would fail
|
||||
state.add_height(Length(int(max_h) - 100000))
|
||||
|
||||
infos = list(para.render(None, state))
|
||||
subs = [i for i in infos if isinstance(i, SubRenderable)]
|
||||
assert len(subs) == 1
|
||||
assert subs[0].renderable is img
|
||||
assert subs[0].add_to_new_page is False
|
||||
|
||||
|
||||
def test_landscape_places_figure_inside_landscape_section(tmp_path, monkeypatch):
|
||||
"""Content must sit BEFORE body sectPr or Word ignores landscape."""
|
||||
monkeypatch.chdir(tmp_path)
|
||||
png = tmp_path / "x.png"
|
||||
PILImage.new("RGB", (40, 20), color=(0, 128, 255)).save(png)
|
||||
|
||||
doc = Document(os.path.join(package_dir(), "Template.docx"))
|
||||
body = doc._body._element
|
||||
for child in list(body):
|
||||
if child.tag.endswith("}sectPr"):
|
||||
continue
|
||||
body.remove(child)
|
||||
|
||||
os.environ["WORKING_DIR"] = str(tmp_path)
|
||||
img = Image(
|
||||
doc._body,
|
||||
str(png),
|
||||
CaptionInfo("fig", "wide", landscape=True),
|
||||
)
|
||||
renderer = Renderer(doc, skip_numbering=False)
|
||||
renderer.process([img])
|
||||
|
||||
A = "{http://schemas.openxmlformats.org/drawingml/2006/main}"
|
||||
W = "{http://schemas.openxmlformats.org/wordprocessingml/2006/main}"
|
||||
section_idx = 0
|
||||
image_section = None
|
||||
for child in doc.element.body:
|
||||
tag = child.tag.split("}")[-1]
|
||||
if child.findall(f".//{A}blip"):
|
||||
image_section = section_idx
|
||||
sect = None
|
||||
if tag == "sectPr":
|
||||
sect = child
|
||||
elif tag == "p":
|
||||
pPr = child.find(f"{W}pPr")
|
||||
if pPr is not None:
|
||||
sect = pPr.find(f"{W}sectPr")
|
||||
if sect is not None:
|
||||
section_idx += 1
|
||||
|
||||
assert any(s.orientation == WD_ORIENT.LANDSCAPE for s in doc.sections)
|
||||
assert image_section is not None
|
||||
assert image_section == 1
|
||||
|
||||
|
||||
def test_landscape_listing_deferred_after_section(tmp_path, monkeypatch):
|
||||
"""+landscape +listing → listing must not sit in the landscape section."""
|
||||
from unittest.mock import patch
|
||||
|
||||
from md2gost.renderable.caption import CaptionInfo
|
||||
from md2gost.renderable.diagram import DiagramFigure
|
||||
from md2gost.renderer import Renderer
|
||||
from md2gost.styles import apply_mirea_styles
|
||||
|
||||
monkeypatch.chdir(tmp_path)
|
||||
png = tmp_path / "d.png"
|
||||
PILImage.new("RGB", (40, 20), color=(0, 128, 255)).save(png)
|
||||
|
||||
doc = Document(os.path.join(package_dir(), "Template.docx"))
|
||||
body = doc._body._element
|
||||
for child in list(body):
|
||||
if child.tag.endswith("}sectPr"):
|
||||
continue
|
||||
body.remove(child)
|
||||
apply_mirea_styles(doc)
|
||||
|
||||
class FakeResult:
|
||||
png_path = str(png)
|
||||
svg_path = None
|
||||
pixel_scale = 1.0
|
||||
|
||||
fig = DiagramFigure(
|
||||
doc._body,
|
||||
"uml",
|
||||
"@startuml\nA->B\n@enduml",
|
||||
CaptionInfo("d1", "Схема", with_listing=True, landscape=True),
|
||||
with_listing=True,
|
||||
)
|
||||
assert fig.listing is not None
|
||||
renderer = Renderer(doc, skip_numbering=False)
|
||||
with patch("md2gost.renderable.diagram.render_diagram", return_value=FakeResult()):
|
||||
renderer.process([fig])
|
||||
|
||||
assert any(s.orientation == WD_ORIENT.LANDSCAPE for s in doc.sections)
|
||||
texts = [p.text for p in doc.paragraphs]
|
||||
listing_caps = [t for t in texts if t.startswith("Листинг")]
|
||||
assert listing_caps, f"no listing caption in {texts!r}"
|
||||
Reference in New Issue
Block a user