febf62c257
Python application / build (push) Has been cancelled
- fix Не появляется пустая странициа перед названием таблицы - fix Не появляется пустая страница перед альбомной ориентацией - fix При отказе от изменений создаётся новый файл - fix Замена "..." на «...» больше не затрагивает uml и mermaid блоки
90 lines
3.4 KiB
Python
90 lines
3.4 KiB
Python
from copy import copy
|
|
from dataclasses import dataclass
|
|
from typing import Generator
|
|
|
|
from docx.shared import Parented
|
|
from docx.text.paragraph import Paragraph as DocxParagraph
|
|
from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
|
|
|
|
from md2gost.layout_tracker import LayoutState
|
|
from md2gost.renderable import Renderable
|
|
from md2gost.rendered_info import RenderedInfo
|
|
from .paragraph_sizer import ParagraphSizer
|
|
from ..util import create_element
|
|
|
|
|
|
# Map category → Word style name
|
|
CAPTION_STYLES = {
|
|
"Рисунок": "Caption Figure",
|
|
"Таблица": "Название таблицы",
|
|
"Листинг": "Caption Listing",
|
|
}
|
|
|
|
|
|
@dataclass
|
|
class CaptionInfo:
|
|
unique_name: str | None
|
|
text: str | None
|
|
with_listing: bool = False
|
|
landscape: bool = False
|
|
|
|
|
|
class Caption(Renderable):
|
|
def __init__(self, parent: Parented, category: str, caption_info: CaptionInfo | None,
|
|
number: str | None = None, before: bool = True):
|
|
self._parent = parent
|
|
self._before = before
|
|
self._category = category
|
|
self._docx_paragraph = DocxParagraph(create_element("w:p"), parent)
|
|
|
|
style_name = CAPTION_STYLES.get(category, "Caption")
|
|
try:
|
|
self._docx_paragraph.style = style_name
|
|
except KeyError:
|
|
try:
|
|
self._docx_paragraph.style = "Caption Table" if category == "Таблица" else "Caption"
|
|
except KeyError:
|
|
self._docx_paragraph.style = "Caption"
|
|
|
|
# Format: «Рисунок 1.1 — Название» (or « - » if --emdash-to-hyphen)
|
|
from ..profiles import dash_separator
|
|
self._docx_paragraph.add_run(f"{category} ")
|
|
self._numbering_run = self._docx_paragraph.add_run(str(number) if number else "?")
|
|
if caption_info and caption_info.text:
|
|
text = caption_info.text.strip().rstrip(".")
|
|
if text:
|
|
self._docx_paragraph.add_run(f"{dash_separator()}{text}")
|
|
|
|
def center(self):
|
|
self._docx_paragraph.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
|
|
|
|
def render(self, previous_rendered: RenderedInfo, layout_state: LayoutState) -> Generator[
|
|
"RenderedInfo | Renderable", None, None]:
|
|
height_data = ParagraphSizer(
|
|
self._docx_paragraph,
|
|
previous_rendered.docx_element
|
|
if previous_rendered and isinstance(previous_rendered.docx_element, DocxParagraph) else None,
|
|
layout_state.max_width
|
|
).calculate_height()
|
|
|
|
# Table/listing captions use keep_with_next. pageBreakBefore + keepWithNext
|
|
# on a paragraph that Word already wraps to a new page produces a blank
|
|
# page that the layout tracker / debug overlay never sees.
|
|
leftover = 0
|
|
need_new_page = (
|
|
self._before
|
|
and layout_state.current_page_height > 0
|
|
and ((height_data.lines + 2 - 1) * height_data.line_spacing + 1) * height_data.line_height
|
|
> layout_state.remaining_page_height
|
|
)
|
|
if need_new_page:
|
|
leftover = layout_state.remaining_page_height
|
|
height_data = ParagraphSizer(
|
|
self._docx_paragraph,
|
|
None,
|
|
layout_state.max_width
|
|
).calculate_height()
|
|
|
|
self._docx_paragraph.paragraph_format.page_break_before = False
|
|
yield RenderedInfo(self._docx_paragraph, height_data.full + leftover)
|