[AI]
ГОСТ
This commit is contained in:
@@ -67,4 +67,4 @@ styles:
|
|||||||
line_spacing: 1.0
|
line_spacing: 1.0
|
||||||
|
|
||||||
colors:
|
colors:
|
||||||
LinkColor: "0000FF"
|
LinkColor: "000"
|
||||||
@@ -14,6 +14,13 @@ def process_document(
|
|||||||
template_path: Optional[Path] = None, theme_name: Optional[str] = None,
|
template_path: Optional[Path] = None, theme_name: Optional[str] = None,
|
||||||
plugins_dir: Optional[Path] = None, dry_run: bool = False
|
plugins_dir: Optional[Path] = None, dry_run: bool = False
|
||||||
):
|
):
|
||||||
|
if theme_name:
|
||||||
|
theme_path = Path("resources/themes") / f"{theme_name}.yaml"
|
||||||
|
if theme_path.exists():
|
||||||
|
style_config_path = theme_path
|
||||||
|
else:
|
||||||
|
logger.warning(f"Тема {theme_name} не найдена, используем стандартный стиль")
|
||||||
|
|
||||||
if plugins_dir and plugins_dir.exists():
|
if plugins_dir and plugins_dir.exists():
|
||||||
load_plugins_from_path(plugins_dir)
|
load_plugins_from_path(plugins_dir)
|
||||||
|
|
||||||
@@ -42,6 +49,7 @@ def process_document(
|
|||||||
|
|
||||||
style_reg = StyleRegistry(style_config_path)
|
style_reg = StyleRegistry(style_config_path)
|
||||||
style_reg.ensure_styles_in_doc(doc)
|
style_reg.ensure_styles_in_doc(doc)
|
||||||
|
style_reg.apply_page_layout(doc) # ✅ применяем поля
|
||||||
render_markdown_to_docx(doc, md_content, images_dir, style_reg)
|
render_markdown_to_docx(doc, md_content, images_dir, style_reg)
|
||||||
|
|
||||||
output_path = output_dir / f"{doc_name}.docx"
|
output_path = output_dir / f"{doc_name}.docx"
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import yaml
|
import yaml
|
||||||
from docx import Document
|
from docx import Document
|
||||||
from docx.enum.style import WD_STYLE_TYPE
|
from docx.enum.style import WD_STYLE_TYPE
|
||||||
from docx.shared import Pt, RGBColor
|
from docx.shared import Pt, Cm, RGBColor
|
||||||
from docx.enum.text import WD_LINE_SPACING, WD_ALIGN_PARAGRAPH
|
from docx.enum.text import WD_LINE_SPACING, WD_ALIGN_PARAGRAPH
|
||||||
from docx.styles.style import ParagraphStyle
|
|
||||||
|
|
||||||
class StyleRegistry:
|
class StyleRegistry:
|
||||||
def __init__(self, config_path):
|
def __init__(self, config_path):
|
||||||
@@ -20,6 +19,24 @@ class StyleRegistry:
|
|||||||
style = doc.styles.add_style(name, WD_STYLE_TYPE.PARAGRAPH)
|
style = doc.styles.add_style(name, WD_STYLE_TYPE.PARAGRAPH)
|
||||||
self.apply_style(style, name)
|
self.apply_style(style, name)
|
||||||
|
|
||||||
|
def apply_page_layout(self, doc: Document):
|
||||||
|
"""Применяет настройки страницы (поля)."""
|
||||||
|
page_cfg = self.config.get("page", {})
|
||||||
|
margins = page_cfg.get("margins", {})
|
||||||
|
if not margins:
|
||||||
|
return
|
||||||
|
|
||||||
|
sections = doc.sections
|
||||||
|
for section in sections:
|
||||||
|
if "top" in margins:
|
||||||
|
section.top_margin = Cm(margins["top"])
|
||||||
|
if "bottom" in margins:
|
||||||
|
section.bottom_margin = Cm(margins["bottom"])
|
||||||
|
if "left" in margins:
|
||||||
|
section.left_margin = Cm(margins["left"])
|
||||||
|
if "right" in margins:
|
||||||
|
section.right_margin = Cm(margins["right"])
|
||||||
|
|
||||||
def apply_style(self, style, style_name: str):
|
def apply_style(self, style, style_name: str):
|
||||||
cfg = self.config["styles"].get(style_name)
|
cfg = self.config["styles"].get(style_name)
|
||||||
if not cfg:
|
if not cfg:
|
||||||
@@ -50,6 +67,9 @@ class StyleRegistry:
|
|||||||
align = alignment_map.get(para_cfg["alignment"])
|
align = alignment_map.get(para_cfg["alignment"])
|
||||||
if align:
|
if align:
|
||||||
pf.alignment = align
|
pf.alignment = align
|
||||||
|
if "first_line_indent" in para_cfg:
|
||||||
|
from docx.shared import Mm
|
||||||
|
pf.first_line_indent = Mm(para_cfg["first_line_indent"])
|
||||||
|
|
||||||
def get_color(self, key: str):
|
def get_color(self, key: str):
|
||||||
hex_color = self.config.get("colors", {}).get(key, "000000")
|
hex_color = self.config.get("colors", {}).get(key, "000000")
|
||||||
|
|||||||
145
tests/test_page_layout.py
Normal file
145
tests/test_page_layout.py
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
import pytest
|
||||||
|
from pathlib import Path
|
||||||
|
from docx import Document
|
||||||
|
from src.core.style_registry import StyleRegistry
|
||||||
|
|
||||||
|
def test_apply_page_layout_all_margins():
|
||||||
|
"""Тест: все поля заданы."""
|
||||||
|
yaml_content = """
|
||||||
|
page:
|
||||||
|
margins:
|
||||||
|
top: 2.0
|
||||||
|
bottom: 2.0
|
||||||
|
left: 3.0
|
||||||
|
right: 1.0
|
||||||
|
styles:
|
||||||
|
Normal:
|
||||||
|
font:
|
||||||
|
name: "Arial"
|
||||||
|
size: 12
|
||||||
|
"""
|
||||||
|
yaml_path = Path("tests/fixtures/test_layout_all.yaml")
|
||||||
|
yaml_path.parent.mkdir(exist_ok=True)
|
||||||
|
yaml_path.write_text(yaml_content, encoding="utf-8")
|
||||||
|
|
||||||
|
doc = Document()
|
||||||
|
style_reg = StyleRegistry(yaml_path)
|
||||||
|
style_reg.apply_page_layout(doc)
|
||||||
|
|
||||||
|
section = doc.sections[0]
|
||||||
|
assert abs(section.top_margin.cm - 2.0) < 0.01
|
||||||
|
assert abs(section.bottom_margin.cm - 2.0) < 0.01
|
||||||
|
assert abs(section.left_margin.cm - 3.0) < 0.01
|
||||||
|
assert abs(section.right_margin.cm - 1.0) < 0.01
|
||||||
|
|
||||||
|
yaml_path.unlink(missing_ok=True)
|
||||||
|
|
||||||
|
def test_apply_page_layout_partial_margins():
|
||||||
|
"""Тест: заданы не все поля."""
|
||||||
|
yaml_content = """
|
||||||
|
page:
|
||||||
|
margins:
|
||||||
|
top: 1.5
|
||||||
|
left: 2.5
|
||||||
|
styles:
|
||||||
|
Normal:
|
||||||
|
font:
|
||||||
|
name: "Arial"
|
||||||
|
size: 12
|
||||||
|
"""
|
||||||
|
yaml_path = Path("tests/fixtures/test_layout_partial.yaml")
|
||||||
|
yaml_path.parent.mkdir(exist_ok=True)
|
||||||
|
yaml_path.write_text(yaml_content, encoding="utf-8")
|
||||||
|
|
||||||
|
doc = Document()
|
||||||
|
style_reg = StyleRegistry(yaml_path)
|
||||||
|
style_reg.apply_page_layout(doc)
|
||||||
|
|
||||||
|
section = doc.sections[0]
|
||||||
|
assert abs(section.top_margin.cm - 1.5) < 0.01
|
||||||
|
assert abs(section.left_margin.cm - 2.5) < 0.01
|
||||||
|
# Остальные поля не должны измениться (остаются по умолчанию)
|
||||||
|
# У docx по умолчанию: top=2.54, bottom=2.54, left=3.17, right=3.17
|
||||||
|
# Но мы их не трогаем, если в YAML нет
|
||||||
|
|
||||||
|
yaml_path.unlink(missing_ok=True)
|
||||||
|
|
||||||
|
def test_apply_page_layout_no_page_section():
|
||||||
|
"""Тест: в YAML нет секции page."""
|
||||||
|
yaml_content = """
|
||||||
|
styles:
|
||||||
|
Normal:
|
||||||
|
font:
|
||||||
|
name: "Arial"
|
||||||
|
size: 12
|
||||||
|
"""
|
||||||
|
yaml_path = Path("tests/fixtures/test_layout_no_page.yaml")
|
||||||
|
yaml_path.parent.mkdir(exist_ok=True)
|
||||||
|
yaml_path.write_text(yaml_content, encoding="utf-8")
|
||||||
|
|
||||||
|
doc = Document()
|
||||||
|
style_reg = StyleRegistry(yaml_path)
|
||||||
|
style_reg.apply_page_layout(doc)
|
||||||
|
|
||||||
|
section = doc.sections[0]
|
||||||
|
# Поля не должны измениться (остаются по умолчанию)
|
||||||
|
# top=2.54 cm по умолчанию
|
||||||
|
assert abs(section.top_margin.cm - 2.54) < 0.01
|
||||||
|
|
||||||
|
yaml_path.unlink(missing_ok=True)
|
||||||
|
|
||||||
|
def test_apply_page_layout_no_margins():
|
||||||
|
"""Тест: в YAML есть page, но нет margins."""
|
||||||
|
yaml_content = """
|
||||||
|
page:
|
||||||
|
other_option: value
|
||||||
|
styles:
|
||||||
|
Normal:
|
||||||
|
font:
|
||||||
|
name: "Arial"
|
||||||
|
size: 12
|
||||||
|
"""
|
||||||
|
yaml_path = Path("tests/fixtures/test_layout_no_margins.yaml")
|
||||||
|
yaml_path.parent.mkdir(exist_ok=True)
|
||||||
|
yaml_path.write_text(yaml_content, encoding="utf-8")
|
||||||
|
|
||||||
|
doc = Document()
|
||||||
|
style_reg = StyleRegistry(yaml_path)
|
||||||
|
style_reg.apply_page_layout(doc)
|
||||||
|
|
||||||
|
section = doc.sections[0]
|
||||||
|
# Поля не должны измениться
|
||||||
|
assert abs(section.top_margin.cm - 2.54) < 0.01
|
||||||
|
|
||||||
|
yaml_path.unlink(missing_ok=True)
|
||||||
|
|
||||||
|
def test_apply_page_layout_zero_margins():
|
||||||
|
"""Тест: нулевые поля."""
|
||||||
|
yaml_content = """
|
||||||
|
page:
|
||||||
|
margins:
|
||||||
|
top: 0.0
|
||||||
|
bottom: 0.0
|
||||||
|
left: 0.0
|
||||||
|
right: 0.0
|
||||||
|
styles:
|
||||||
|
Normal:
|
||||||
|
font:
|
||||||
|
name: "Arial"
|
||||||
|
size: 12
|
||||||
|
"""
|
||||||
|
yaml_path = Path("tests/fixtures/test_layout_zero.yaml")
|
||||||
|
yaml_path.parent.mkdir(exist_ok=True)
|
||||||
|
yaml_path.write_text(yaml_content, encoding="utf-8")
|
||||||
|
|
||||||
|
doc = Document()
|
||||||
|
style_reg = StyleRegistry(yaml_path)
|
||||||
|
style_reg.apply_page_layout(doc)
|
||||||
|
|
||||||
|
section = doc.sections[0]
|
||||||
|
assert abs(section.top_margin.cm - 0.0) < 0.01
|
||||||
|
assert abs(section.bottom_margin.cm - 0.0) < 0.01
|
||||||
|
assert abs(section.left_margin.cm - 0.0) < 0.01
|
||||||
|
assert abs(section.right_margin.cm - 0.0) < 0.01
|
||||||
|
|
||||||
|
yaml_path.unlink(missing_ok=True)
|
||||||
Reference in New Issue
Block a user