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