- fix Не собиралось само список всех приложений
This commit is contained in:
@@ -42,7 +42,9 @@ CLI (тот же движок)
|
|||||||
# *ЗАКЛЮЧЕНИЕ
|
# *ЗАКЛЮЧЕНИЕ
|
||||||
# *СПИСОК ИСПОЛЬЗОВАННЫХ ИСТОЧНИКОВ
|
# *СПИСОК ИСПОЛЬЗОВАННЫХ ИСТОЧНИКОВ
|
||||||
# *ПРИЛОЖЕНИЯ
|
# *ПРИЛОЖЕНИЯ
|
||||||
(после ПРИЛОЖЕНИЯ — перечень основным текстом, затем ## Приложение А Название)
|
(перечень «Приложение А — …» собирается сам из ## Приложение А Название;
|
||||||
|
можно написать вручную — тогда автосписок не дублируется)
|
||||||
|
## Приложение А Название
|
||||||
|
|
||||||
Нумерованные разделы: # 1 Название ## 1.1 Подраздел
|
Нумерованные разделы: # 1 Название ## 1.1 Подраздел
|
||||||
Точку в конце заголовка не ставить.
|
Точку в конце заголовка не ставить.
|
||||||
|
|||||||
+72
-2
@@ -121,8 +121,9 @@ class Renderer:
|
|||||||
self._layout_tracker.set_page_size(max_height, max_width)
|
self._layout_tracker.set_page_size(max_height, max_width)
|
||||||
|
|
||||||
def process(self, renderables: list[Renderable]):
|
def process(self, renderables: list[Renderable]):
|
||||||
for i in range(len(renderables)):
|
items = self._ensure_appendix_index(list(renderables))
|
||||||
self.render(renderables[i])
|
for i in range(len(items)):
|
||||||
|
self.render(items[i])
|
||||||
|
|
||||||
self._flush_to_new_screen()
|
self._flush_to_new_screen()
|
||||||
# If document had no TOC, still add page numbers to the only section
|
# If document had no TOC, still add page numbers to the only section
|
||||||
@@ -132,6 +133,75 @@ class Renderer:
|
|||||||
if self._debugger:
|
if self._debugger:
|
||||||
self._debugger.after_rendered()
|
self._debugger.after_rendered()
|
||||||
|
|
||||||
|
def _ensure_appendix_index(self, renderables: list[Renderable]) -> list[Renderable]:
|
||||||
|
"""Insert «Приложение X — Title» list after # *ПРИЛОЖЕНИЯ if missing (method guide §6)."""
|
||||||
|
sec_i = None
|
||||||
|
first_app_i = None
|
||||||
|
entries: list[tuple[str, str]] = []
|
||||||
|
for i, r in enumerate(renderables):
|
||||||
|
if not isinstance(r, Heading):
|
||||||
|
continue
|
||||||
|
text = (r.text or "").strip()
|
||||||
|
upper = text.upper().replace("Ё", "Е")
|
||||||
|
if upper in ("ПРИЛОЖЕНИЯ", "ПРИЛОЖЕНИЕ") and sec_i is None:
|
||||||
|
sec_i = i
|
||||||
|
continue
|
||||||
|
m = re.match(r"(?i)^Приложение\s+([А-ЯA-ZЁ])(?:\s+(.+))?$", text)
|
||||||
|
if not m:
|
||||||
|
continue
|
||||||
|
letter = m.group(1).upper().replace("Ё", "Е")
|
||||||
|
title = (m.group(2) or "").strip()
|
||||||
|
if first_app_i is None:
|
||||||
|
first_app_i = i
|
||||||
|
entries.append((letter, title))
|
||||||
|
|
||||||
|
if sec_i is None or first_app_i is None or not entries:
|
||||||
|
return renderables
|
||||||
|
if self._has_appendix_list_between(renderables, sec_i, first_app_i):
|
||||||
|
return renderables
|
||||||
|
|
||||||
|
from .profiles import dash_separator
|
||||||
|
dash = dash_separator()
|
||||||
|
index_paras: list[Renderable] = []
|
||||||
|
for letter, title in entries:
|
||||||
|
line = f"Приложение {letter}"
|
||||||
|
if title:
|
||||||
|
line = f"{line} {dash} {title}"
|
||||||
|
index_paras.append(self._make_appendix_index_paragraph(line))
|
||||||
|
|
||||||
|
return renderables[: sec_i + 1] + index_paras + renderables[sec_i + 1 :]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _has_appendix_list_between(
|
||||||
|
renderables: list[Renderable], sec_i: int, first_app_i: int
|
||||||
|
) -> bool:
|
||||||
|
"""True if author already put a list/prose between ПРИЛОЖЕНИЯ and first appendix."""
|
||||||
|
for r in renderables[sec_i + 1 : first_app_i]:
|
||||||
|
if isinstance(r, Heading):
|
||||||
|
continue
|
||||||
|
text = ""
|
||||||
|
if isinstance(r, Paragraph):
|
||||||
|
text = (r._docx_paragraph.text or "").strip()
|
||||||
|
elif hasattr(r, "text"):
|
||||||
|
text = (getattr(r, "text", None) or "").strip()
|
||||||
|
if not text:
|
||||||
|
continue
|
||||||
|
if re.search(r"(?i)приложение\s+[А-ЯA-ZЁ]", text):
|
||||||
|
return True
|
||||||
|
if re.search(r"[А-Яа-яA-Za-z]{3,}", text):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _make_appendix_index_paragraph(self, text: str) -> Paragraph:
|
||||||
|
"""Body-text line in the appendix list under ПРИЛОЖЕНИЯ (no first-line indent)."""
|
||||||
|
p = Paragraph(self._document._body)
|
||||||
|
p.add_run(text)
|
||||||
|
p.style = "Normal"
|
||||||
|
p.first_line_indent = Cm(0)
|
||||||
|
pf = p._docx_paragraph.paragraph_format
|
||||||
|
pf.left_indent = Cm(0)
|
||||||
|
return p
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _rewrite_heading_text(heading: Heading, text: str) -> None:
|
def _rewrite_heading_text(heading: Heading, text: str) -> None:
|
||||||
runs = heading._docx_paragraph.runs
|
runs = heading._docx_paragraph.runs
|
||||||
|
|||||||
@@ -428,6 +428,44 @@ def test_appendix_item_becomes_h3_plus_title():
|
|||||||
assert title_p._docx_paragraph.alignment == WD_ALIGN_PARAGRAPH.CENTER
|
assert title_p._docx_paragraph.alignment == WD_ALIGN_PARAGRAPH.CENTER
|
||||||
|
|
||||||
|
|
||||||
|
def test_appendix_index_auto_inserted():
|
||||||
|
import docx
|
||||||
|
from md2gost.renderable.heading import Heading
|
||||||
|
from md2gost.renderer import Renderer
|
||||||
|
from md2gost.styles import apply_mirea_styles
|
||||||
|
|
||||||
|
doc = docx.Document(r"md2gost/Template.docx")
|
||||||
|
doc._body.clear_content()
|
||||||
|
apply_mirea_styles(doc)
|
||||||
|
renderer = Renderer(doc, skip_numbering=True)
|
||||||
|
|
||||||
|
sec = Heading(doc._body, 1, False)
|
||||||
|
sec.add_run("ПРИЛОЖЕНИЯ")
|
||||||
|
a = Heading(doc._body, 2, True)
|
||||||
|
a.add_run("Приложение А Графический материал")
|
||||||
|
b = Heading(doc._body, 2, True)
|
||||||
|
b.add_run("Приложение Б Листинг")
|
||||||
|
|
||||||
|
out = renderer._ensure_appendix_index([sec, a, b])
|
||||||
|
assert len(out) == 5 # sec + 2 index lines + a + b
|
||||||
|
assert out[0] is sec
|
||||||
|
t0 = out[1]._docx_paragraph.text
|
||||||
|
t1 = out[2]._docx_paragraph.text
|
||||||
|
assert t0.startswith("Приложение А")
|
||||||
|
assert "Графический материал" in t0
|
||||||
|
assert t1.startswith("Приложение Б")
|
||||||
|
assert "Листинг" in t1
|
||||||
|
assert out[3] is a
|
||||||
|
assert out[4] is b
|
||||||
|
|
||||||
|
# If list already present — do not duplicate
|
||||||
|
from md2gost.renderable.paragraph import Paragraph
|
||||||
|
manual = Paragraph(doc._body)
|
||||||
|
manual.add_run("Приложение А — вручную")
|
||||||
|
out2 = renderer._ensure_appendix_index([sec, manual, a, b])
|
||||||
|
assert out2 == [sec, manual, a, b]
|
||||||
|
|
||||||
|
|
||||||
def test_caption_table_keep_with_next_and_toc_styles():
|
def test_caption_table_keep_with_next_and_toc_styles():
|
||||||
import docx
|
import docx
|
||||||
from docx.enum.text import WD_ALIGN_PARAGRAPH, WD_LINE_SPACING
|
from docx.enum.text import WD_ALIGN_PARAGRAPH, WD_LINE_SPACING
|
||||||
|
|||||||
Reference in New Issue
Block a user