212 lines
8.2 KiB
Python
212 lines
8.2 KiB
Python
#!/usr/bin/env python
|
|
from argparse import ArgumentParser, BooleanOptionalAction
|
|
import os
|
|
import os.path
|
|
import sys
|
|
from getpass import getuser
|
|
|
|
from docx import Document
|
|
|
|
from .converter import Converter
|
|
from .profiles import (
|
|
DOC_TYPES,
|
|
HEADING_NUMBERING_MODES,
|
|
DEFAULT_HEADING_NUMBERING,
|
|
TOC_MODES,
|
|
DEFAULT_TOC_MODE,
|
|
TABLE_CONTINUATION_MODES,
|
|
DEFAULT_TABLE_CONTINUATION,
|
|
get_profile,
|
|
)
|
|
from .checker import check_markdown, format_report
|
|
|
|
|
|
def main():
|
|
parser = ArgumentParser(
|
|
prog="md2gost",
|
|
description=(
|
|
"Генерация DOCX-отчётов из Markdown по ТЗ МИРЭА / ГОСТ. "
|
|
"Типы: coursework/practice/vkr, APID_coursework, PIS_custom. "
|
|
"FODT: python -m md2fodt …"
|
|
),
|
|
)
|
|
parser.add_argument("filename", help="Путь до исходного markdown файла")
|
|
parser.add_argument("-o", "--output", help="Путь до сгенерированного .docx")
|
|
parser.add_argument("-t", "--template", help="Путь до шаблона .docx")
|
|
parser.add_argument(
|
|
"--type", dest="doc_type", choices=DOC_TYPES, default="coursework",
|
|
help="Тип: coursework | practice | vkr | PIS_custom | APID_coursework",
|
|
)
|
|
parser.add_argument(
|
|
"--heading-numbering",
|
|
choices=HEADING_NUMBERING_MODES,
|
|
default=DEFAULT_HEADING_NUMBERING,
|
|
help=(
|
|
"Нумерация заголовков разделов: "
|
|
"manual — цифры из markdown (# 1 … / ## 1.1 …), автонумерация Word отключена; "
|
|
"auto — нумерует Word, цифры в начале заголовка md снимаются. "
|
|
f"По умолчанию: {DEFAULT_HEADING_NUMBERING}."
|
|
),
|
|
)
|
|
parser.add_argument(
|
|
"--toc",
|
|
choices=TOC_MODES,
|
|
default=DEFAULT_TOC_MODE,
|
|
help=(
|
|
"Содержание: native — встроенное поле Word TOC (обновить при открытии); "
|
|
"manual — собрать в md2gost с номерами страниц из layout. "
|
|
f"По умолчанию: {DEFAULT_TOC_MODE}."
|
|
),
|
|
)
|
|
parser.add_argument(
|
|
"--table-continuation",
|
|
choices=TABLE_CONTINUATION_MODES,
|
|
default=DEFAULT_TABLE_CONTINUATION,
|
|
help=(
|
|
"Таблицы длиннее страницы: "
|
|
"off/soft — одна таблица, пагинация Word, без авто«Продолжение» (по умолчанию); "
|
|
"legacy/caption — режем по оценке высоты и вставляем «Продолжение…» "
|
|
"(оценка ≠ Word, возможны артефакты). "
|
|
f"По умолчанию: {DEFAULT_TABLE_CONTINUATION}."
|
|
),
|
|
)
|
|
parser.add_argument(
|
|
"--emdash-to-hyphen",
|
|
action=BooleanOptionalAction,
|
|
default=True,
|
|
help=(
|
|
"Автозамена типографского тире «—» на дефис «-» в тексте и подписях. "
|
|
"Включено по умолчанию; отключить: --no-emdash-to-hyphen."
|
|
),
|
|
)
|
|
parser.add_argument("--title", help="DOCX титульного листа (вставляется перед телом)")
|
|
parser.add_argument("--assignment", help="DOCX бланка задания")
|
|
parser.add_argument("--check", help="Проверить markdown по ТЗ и вывести отчёт",
|
|
action="store_true")
|
|
parser.add_argument("--check-only", help="Только проверка, без генерации документа",
|
|
action="store_true")
|
|
parser.add_argument("--strict", help="Код выхода 1 при ошибках проверки",
|
|
action="store_true")
|
|
parser.add_argument("--syntax-highlighting", help="Подсветка синтаксиса в листингах",
|
|
action=BooleanOptionalAction)
|
|
parser.add_argument(
|
|
"--plantuml-jar",
|
|
help="Путь к plantuml.jar (иначе env PLANTUML_JAR)",
|
|
)
|
|
parser.add_argument(
|
|
"--kroki-url",
|
|
help="URL локального Kroki (иначе env KROKI_URL, default http://localhost:8000)",
|
|
)
|
|
parser.add_argument(
|
|
"--diagram-fallback",
|
|
choices=["local", "remote", "off"],
|
|
default="remote",
|
|
help="Если локальный рендер UML недоступен: remote (kroki.io), local (ошибка), off",
|
|
)
|
|
parser.add_argument("--debug", help="Добавляет отладочные данные в документ",
|
|
action="store_true")
|
|
|
|
args = parser.parse_args()
|
|
filename, output, template, debug = \
|
|
args.filename, args.output, args.template, args.debug
|
|
if args.syntax_highlighting:
|
|
os.environ["SYNTAX_HIGHLIGHTING"] = "1"
|
|
|
|
from .diagram_renderer import configure_diagrams
|
|
configure_diagrams(
|
|
plantuml_jar=args.plantuml_jar,
|
|
kroki_url=args.kroki_url,
|
|
fallback=args.diagram_fallback,
|
|
)
|
|
|
|
if not filename.endswith(".md"):
|
|
print("Error: filename must have md format")
|
|
exit(1)
|
|
|
|
os.environ["WORKING_DIR"] = os.path.dirname(os.path.abspath(filename)) or "."
|
|
|
|
with open(filename, encoding="utf-8") as f:
|
|
md_text = f.read()
|
|
|
|
if args.check or args.check_only:
|
|
issues = check_markdown(md_text, args.doc_type)
|
|
print(format_report(issues))
|
|
errors = [i for i in issues if i.severity == "error"]
|
|
if args.strict and errors:
|
|
sys.exit(1)
|
|
if args.check_only:
|
|
sys.exit(0 if not errors else (1 if args.strict else 0))
|
|
|
|
if not output:
|
|
output = os.path.basename(filename).replace(".md", ".docx")
|
|
elif not output.endswith(".docx"):
|
|
print("Error: output file must have docx format")
|
|
exit(1)
|
|
|
|
if not template:
|
|
from . import package_dir
|
|
template = os.path.join(package_dir(), "Template.docx")
|
|
|
|
converter = Converter(
|
|
filename, output, template, debug,
|
|
doc_type=args.doc_type,
|
|
heading_numbering=args.heading_numbering,
|
|
emdash_to_hyphen=args.emdash_to_hyphen,
|
|
toc_mode=args.toc,
|
|
table_continuation=args.table_continuation,
|
|
)
|
|
converter.convert()
|
|
|
|
document = converter.document
|
|
|
|
# Front matter is appended *into* a shell that already has coursework styles.
|
|
# Never use title.docx as compose base: python-docx default template has
|
|
# Calibri + accent-blue headings and would override ГОСТ стили.
|
|
if args.title or args.assignment:
|
|
try:
|
|
from docxcompose.composer import Composer
|
|
except ImportError:
|
|
print("Error: docxcompose required for --title/--assignment")
|
|
sys.exit(3)
|
|
from .styles import apply_document_styles
|
|
|
|
shell = Document(template)
|
|
apply_document_styles(shell, get_profile(args.doc_type).style_preset)
|
|
body = shell.element.body
|
|
for child in list(body):
|
|
if not child.tag.endswith("}sectPr"):
|
|
body.remove(child)
|
|
|
|
composer = Composer(shell)
|
|
if args.title:
|
|
composer.append(Document(args.title))
|
|
shell.add_page_break()
|
|
if args.assignment:
|
|
composer.append(Document(args.assignment))
|
|
shell.add_page_break()
|
|
composer.append(document)
|
|
document = composer.doc
|
|
apply_document_styles(document, get_profile(args.doc_type).style_preset)
|
|
|
|
document.core_properties.author = getuser()
|
|
document.core_properties.comments = \
|
|
"Создано при помощи md2gost (ТЗ МИРЭА)"
|
|
|
|
document.save(output)
|
|
print(f"Generated document: {os.path.abspath(output)}")
|
|
|
|
if debug:
|
|
import platform
|
|
if platform.system() == 'Darwin':
|
|
import subprocess
|
|
subprocess.call(('open', output))
|
|
elif platform.system() == 'Windows':
|
|
os.startfile(output)
|
|
else:
|
|
import subprocess
|
|
subprocess.call(('xdg-open', output))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|