Files
md_to_gost/md2gost/renderable/caption.py
T
Igor20264 f7136bd8bd
Example document / build (push) Has been cancelled
Python application / build (push) Has been cancelled
v0.5.3
- Мелкие исправления увеличивающие работу конвертера
2026-09-10 10:50:15 +03:00

95 lines
3.7 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 ParagraphSizerResult, measure_paragraph
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._size_cache: dict = {}
self._docx_paragraph = DocxParagraph(create_element("w:p"), parent)
# 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. Written here
# rather than at the end of render() so that measuring the caption twice
# (fit check, then emit) sees the same paragraph.
self._docx_paragraph.paragraph_format.page_break_before = False
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 measure(self, previous_paragraph: DocxParagraph | None, max_width) -> ParagraphSizerResult:
"""Line count and spacing of the caption line against a given predecessor."""
return measure_paragraph(
self._size_cache, self._docx_paragraph, previous_paragraph, max_width
)
def render(self, previous_rendered: RenderedInfo, layout_state: LayoutState) -> Generator[
"RenderedInfo | Renderable", None, None]:
height_data = self.measure(
previous_rendered.docx_element
if previous_rendered and isinstance(previous_rendered.docx_element, DocxParagraph) else None,
layout_state.max_width
)
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 = self.measure(None, layout_state.max_width)
yield RenderedInfo(self._docx_paragraph, height_data.full + leftover)