Files
Igor20264 510f7e7adf
Python application / build (push) Waiting to run
v0.5.2
Что то сделал
2026-09-08 19:37:54 +03:00

740 lines
47 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# GhostEditor / md2gost — полное описание проекта
Версия пакета: **0.1.0**. Python **≥ 3.10**.
Репозиторий: конвертер учебных отчётов **Markdown → DOCX** по методичке РТУ МИРЭА (ГОСТ 7.32-2017) и профилям **ПИС / АПИД**. Рядом — два дополнительных пайплайна: **FODT** (LibreOffice) и **LaTeX → PDF**.
Этот файл — карта всего репозитория: что уже работает, как устроен код, какие функции есть в CLI/GUI и какие модули за что отвечают. Пользовательская документация по разделам лежит в [`docs/`](docs/).
---
## 1. Зачем проект существует
Студент пишет отчёт в расширенном Markdown (диалект md2gost). Конвертер сам:
- оформляет DOCX стилями Times New Roman, полями 30/10/20/20 мм, интервалом 1.5;
- нумерует рисунки, таблицы, листинги, формулы;
- собирает содержание и список источников;
- рисует UML / C4 / Mermaid;
- проверяет структуру по ТЗ (`--check`);
- при необходимости режет длинные таблицы и листинги через Microsoft Word и пишет «Продолжение…».
Исходный форк: [benzlokzik/md2gost](https://github.com/benzlokzik/md2gost). Здесь — своя линия: профили МИРЭА/ПИС/АПИД, GUI, схемы, стили JSON, Word COM, FODT, LaTeX.
---
## 2. Три продукта в одном репозитории
| Пакет | Entry point | Результат | Когда |
|--------|-------------|-----------|--------|
| **md2gost** | `python -m md2gost` / `md2gost-gui` | `.docx` | основной путь, Word |
| **md2fodt** | `python -m md2fodt` | `.fodt` | LibreOffice, без Word |
| **md2latex** | `python -m md2latex` | каталог XeLaTeX (+ `--pdf`) | PDF через шаблон МИРЭА |
| **word2md** | `python -m word2md` / `md2gost файл.docx` | `.md` | импорт DOCX → диалект md2gost |
Общий диалект разметки: спецразделы `# *ВВЕДЕНИЕ`, метки `%id`, ссылки `@Рисунок:id`, таблицы с `^`/`>`, формулы `$$`, библиография `[n]: …`.
Полнота реализации **не одинаковая**:
- **md2gost** — полный конвейер (стили, профили, диаграммы, checker, GUI, Word COM).
- **md2fodt** — v1: текст, заголовки, списки, таблицы с merge, картинки, листинги, простые ссылки, библиография. Нет PlantUML, нет «Продолжение таблицы», нет полного паритета PIS/АПИД.
- **md2latex** — тот же диалект → `content.tex` + копия `latex/mirea/`. Таблицы идут в `longtable` с настоящим «Продолжение таблицы N» (это умеет LaTeX, не Word).
- **word2md** — эвристический DOCX → диалект md2gost (стили Caption/Heading, подписи, таблицы, Code, формулы OMML). Не восстанавливает UML/Mermaid и исходные `%id`.
---
## 3. Как запустить
```bash
pip install -e .
python -m md2gost # GUI
python -m md2gost report.md -o report.docx --type coursework --check
python -m word2md report.docx -o report.md
python -m md2fodt report.md -o report.fodt
python -m md2latex report.md -o report_latex --pdf
```
Windows exe: `build-exe.bat``dist\md2gost.exe` (двойной клик — GUI; из консоли работает как CLI; перед сборкой тянет plantuml.jar и portable Graphviz).
Быстрая сборка: `build-exe-fast.bat` + `md2gost.fast.spec`.
Для режима продолжения таблиц/листингов `word` (по умолчанию): Windows + Microsoft Word + `pip install pywin32` (опциональная группа `[word]`).
---
## 4. Архитектура md2gost (главный конвейер)
Один движок на CLI и GUI. GUI только собирает `ConvertRequest` и зовёт `convert()`.
```
.md
├─ checker.check_markdown() # если --check / --check-only
├─ profiles.preprocess_markdown() # «ёлочки», тире; не трогает UML/Mermaid
├─ Parser (marko + extended_markdown)
│ └─ RenderableFactory → список Renderable
├─ biblio_processor.fold_bibliography()
├─ Heading / ToC / Table / Listing: режимы нумерации и continuation
├─ label_pass.assign_numbers() + resolve @Тип:id
├─ Renderer.process() # LayoutTracker, секции, альбом, колонтитулы
├─ TocProcessor.process() # native TOC field или ручная сборка
├─ [опционально] docxcompose: титул + задание + тело
├─ document.save()
├─ [если word] word_fix.fix_continuations() # COM: реальные разрывы страниц
└─ [если --check-pages] page_fill_check # эвристика полупустых страниц
```
Ключевые типы:
- `ConvertRequest` / `ConvertResult` — контракт CLI↔GUI (`md2gost/pipeline.py`).
- `DocProfile` — правила типа работы (`md2gost/profiles.py`).
- `StyleConfig` — поля страницы + стили абзацев (`md2gost/style_config.py`).
- `Renderable` — элемент, который умеет себя нарисовать в DOCX с учётом высоты страницы.
---
## 5. Что уже умеет пользователь (функции продукта)
### 5.1. Типы документов (`--type`)
| Тип | Стили | Нумерация объектов | Источники | Структура |
|-----|--------|-------------------|-----------|-----------|
| `practice` | mirea | по разделам `1.1`, `2.1` | 5–7, ≤5 лет | Введение, разделы, заключение, список, приложения |
| `coursework` | mirea | то же | 57, ≤5 лет | то же |
| `vkr` | mirea | то же | ≥10 **в каждом** разделе списка, ссылки `[1.5]` | + приложение «Графический материал» |
| `PIS_custom` | pis_custom | сквозная `1, 2, 3` | не обязательны | H1 = «Практическая работа №N. …»; H1 по центру ПРОПИСНЫМИ; H2 с абзацным отступом |
| `APID_coursework` | mirea | по разделам | 7–20, ≤5 лет | обязательные главы «Теоретические…» / «Прикладные…» и пункты 2.1–2.4 |
Реализация: `PROFILES` и `get_profile()` в `profiles.py`. Стили пресетов: `preset_mirea()`, `preset_pis_custom()`.
### 5.2. CLI (все флаги)
Точка входа: `md2gost/__main__.py``build_parser()`, `request_from_args()`, `main()`.
| Флаг | По умолчанию | Что делает |
|------|----------------|------------|
| `filename` | нет → GUI | `.md` или `.docx` с `--check-pages` |
| `--gui` | | открыть окно (можно сразу передать `.md`) |
| `-o` / `--output` | рядом с md, то же имя | выходной DOCX |
| `-t` / `--template` | `md2gost/Template.docx` | шаблон Word |
| `--type` | `practice` | профиль документа |
| `--heading-numbering` | `manual` | `manual` — цифры из md, автонумерация Word выкл.; `auto` — нумерует Word, цифры в начале заголовка снимаются |
| `--toc` | `native` | `native` — поле Word `TOC`; `manual` — сборка по LayoutTracker |
| `--table-continuation` | `word` | `word` / `off` / `soft` / `legacy` / `caption` |
| `--listing-continuation` | `word` | то же для листингов |
| `--table-repeat-header` | выкл. | шапка на фрагментах после word-split |
| `--emdash-to-hyphen` | выкл. | «—» → «-» |
| `--hr-pagebreak` | выкл. | `---` / `***` / `___` → разрыв страницы |
| `--title` / `--assignment` | | DOCX титула и бланка задания (склейка через docxcompose) |
| `--auto-title` / `--no-auto-title` | | автотитул из yaml/` ```title ` (по умолчанию выкл.) |
| `--student` / `--group` | | ФИО и группа для автотитула (иначе `md2gost.user.json`) |
| `--doc-author-from` / `--doc-author` | `os` / профиль | автор в свойствах DOCX: `os` (имя ОС), `student`, `custom` |
| `--doc-title` / `--doc-subject` / `--doc-keywords` / `--doc-comments` / `--doc-category` / `--doc-last-modified-by` | см. профиль | остальные поля File → сведения; пустые comments — без штампа md2gost |
| `--check` / `--check-only` / `--strict` | | проверка ТЗ; `--strict` → код выхода 1 при ошибках |
| `--check-pages` | | полупустые страницы через Word COM (эвристика, не влияет на `--strict`) |
| `--syntax-highlighting` | | Pygments в листингах |
| `--plantuml-jar` / `--kroki-url` | | локальный рендер диаграмм |
| `--diagram-fallback` | `remote` | `remote` / `local` / `off` |
| `--diagram-format` | `png` | `png` или `svg` (svgBlip + PNG-запасной) |
| `--diagram-scale` | `2.0` | качество PNG PlantUML (размер на странице как при 1) |
| `--schemes` | авто | путь к `md2gost.schemes.json` |
| `--styles` | авто | оверлей `md2gost.styles.json` |
| `--debug` | | отладочная сетка в документе + открыть файл |
Занятый выходной файл: `timestamped_output_path()` пишет `имя_ГГГГ-ММ-ДД-ЧЧ-ММ.docx`.
Windowed exe: `_argv_needs_console()` + `_enable_windows_console()` — консоль появляется только для CLI/`--help`, не для GUI.
### 5.3. GUI (Tkinter)
Модуль `md2gost/gui.py`, класс `Md2GostApp`. Entry: `md2gost-gui` / `python -m md2gost` без файла / `--gui`.
Уже есть:
- зона Drop: перетаскивание `.md` (tkinterdnd2, иначе Win32 `WM_DROPFILES`, иначе клик → диалог);
- основные параметры: тип, нумерация заголовков, TOC, continuation таблиц/листингов, тире, `---` → разрыв, проверки ТЗ, вёрстка, открыть после сборки;
- **Настройки → Файлы**: шаблон, титул, задание, стили JSON, выходной путь;
- **Настройки → Студент…**: ФИО и группа (первый запуск);
- **Настройки → Метаданные…**: свойства DOCX (автор, название, тема, теги, примечание) в `md2gost.user.json`;
- **Настройки → Диаграммы**: jar, Kroki, fallback, формат, масштаб, статус движка, скачать PlantUML, сброс кэша includes;
- **Шаблоны UML**: CRUD схем (`md2gost.schemes.json`) — добавить / дублировать / удалить / сохранить / открыть JSON;
- **Справка**: Инструкция, Документация (вшитые `docs/*.md`), Схемы, Промпт для ИИ (каталог `prompts/` + кнопки схем);
- конвертация в фоне (`threading`), лог в окне, тултипы, меню «Дебаг» на одну следующую сборку;
- если выходной DOCX занят — timestamp-имя, как в CLI.
Конвертация не блокирует окно: `_convert()` → поток → `_on_done()`.
### 5.4. Диалект Markdown
Реализовано в `extended_markdown/` (расширение Marko + GFM) и `RenderableFactory`.
| Элемент | Синтаксис | Поведение |
|---------|-----------|-----------|
| Спецраздел без номера | `# *ВВЕДЕНИЕ` | ПРОПИСНЫЕ, без автономера раздела |
| Содержание | `# *СОДЕРЖАНИЕ` + `[TOC]` | native поле Word или ручная сборка |
| Рисунок-файл | `![…](file.png "%id Подпись")` | нумерация + `@Рисунок:id` |
| Таблица | `%id Подпись` перед таблицей | подпись «Таблица N — …» |
| Merge ячеек | `^` rowspan, `>` colspan | OOXML `vMerge` / `gridSpan`; через разрыв страницы merge не переносится |
| Листинг | `%id` + code fence | Courier New, опционально Pygments |
| Диаграмма | `%id` + ` ```uml ` / `uml-c4` / `mermaid` / `idef0` / `dfd` | Рисунок; флаги `+listing`, `+landscape` |
| Формула | `%eq1` + `$$…$$` + `@Формула:eq1` | номер только если есть ссылка |
| Инлайн-формула | `$…$` | OMML в абзаце |
| Ссылка на объект | `@Рисунок:id`, `@Таблица:id`, `@Листинг:id`, `@Формула:id` | подстановка номера |
| Источник | `[1]` в тексте, `[1]: …` в списке | ВКР: `[1.5]`, цитата `[2.18, c. 21-25]` |
| Разрыв страницы | `---` + `--hr-pagebreak` | иначе HR игнорируется |
| Жирный / курсив / зачёркивание / ссылки / списки | обычный MD | поддерживается |
Автоправки текста (`preprocess_markdown`):
- `"..."` → «…» (эвристика, не внутри `%…`);
- `слово - слово``слово — слово` (если не `--emdash-to-hyphen`);
- внутри оград UML/Mermaid/схем **не** трогает кавычки и тире;
- в списке источников каждый `[n]:` выносится в отдельный абзац (`separate_biblio_lines`).
### 5.5. Схемы и диаграммы
Встроенные id (`md2gost/diagrams/schemes.json`): `c4`, `c4context`, `c4component`, `usecase`.
Слои загрузки (позже побеждает): встроенный шаблон → `md2gost.schemes.json` рядом с приложением (создаётся при первом запуске, не перезаписывается) → рядом с `.md``--schemes`.
Рендер UML:
1. Java + `plantuml.jar` (вшитый / `%LOCALAPPDATA%\md2gost\` / `--plantuml-jar`);
2. локальный Kroki (`KROKI_URL` / `--kroki-url`);
3. `https://kroki.io` при `--diagram-fallback remote`.
Mermaid — локально (браузер / QuickJS) или Kroki. **IDEF0** — оградка ` ```idef0 `, локальный Pillow (рамка NIST). **DFD** — оградка ` ```dfd `, пакет [data-flow-diagram](https://github.com/pbauermeister/dfd) → вшитый/кэш/PATH Graphviz (`scripts/fetch_graphviz.py`, GUI «Скачать Graphviz») или Kroki.
Кэш картинок: `{каталог_md}/.md2gost-cache/`.
Кэш `!include` URL: `md2gost.include-cache.json` + `include-cache/`.
Альбомная страница: флаг `+landscape` у подписи — отдельная секция A4 landscape с вертикальным центрированием, затем снова книжная. Один section break (без пустой книжной страницы перед альбомом).
### 5.6. Кастомные стили JSON
Файл `md2gost.styles.json` (рядом с `.md` и/или `--styles`). Deep-merge поверх пресета типа. Неизвестный ключ или имя стиля — ошибка (`StyleConfigError`).
Можно менять поля `page` (мм) и поля стилей абзацев: шрифт, кегль, жирный/курсив/прописные, выравнивание, отступы, интервал 1.0/1.5, `page_break_before`, `keep_with_next`, `widow_control`.
Не в JSON: табы оглавления (считаются от ширины полосы), отступы списков, цвет (всегда чёрный), формулы из шаблона.
### 5.7. Проверка ТЗ (`--check`)
Модуль `md2gost/checker/__init__.py`. Возвращает `Issue(id, severity, message, line)`.
Уже проверяется:
- обязательные разделы по профилю (СОДЕРЖАНИЕ, ВВЕДЕНИЕ, ЗАКЛЮЧЕНИЕ, список; для ПИС — практические работы; для ВКР — «Графический материал»; для АПИД — 6 обязательных заголовков);
- `[TOC]`;
- спецразделы должны быть `# *…`, лучше ПРОПИСНЫМИ;
- запрет «рис.», «табл.», сносок `[^1]`, графы «№ п/п»;
- ссылки `[n]` во введении/заключении запрещены;
- число и возраст источников; для ВКР — по разделам списка; порядок первого упоминания;
- висячие / неиспользованные метки `%id` / `@Тип:id`;
- запрещённые буквы приложений (Ё З Й О Ч Ь Ы Ъ);
- перечень приложений после `# *ПРИЛОЖЕНИЯ`, если приложений ≥ 2;
- маркеры merge `^`/`>` в шапке и `>` в первом столбце;
- подсказка про «Продолжение…», если continuation = `off`/`soft`.
`--strict` падает только на `severity == "error"`. `--check-pages` — отдельная эвристика, в strict не входит.
### 5.8. Продолжение таблиц и листингов
DOCX сам не умеет писать «Продолжение Таблицы N» только со 2-й страницы.
| Режим | Поведение |
|--------|-----------|
| **`word`** (по умолчанию) | После save Word COM режет по реальной пагинации, вставляет подпись |
| **`off` / `soft`** | Одна таблица/листинг, пагинацию делает Word, без автоподписи |
| **`legacy`** | Резка по оценке высоты LayoutTracker + `page_break_before` (могут быть дыры) |
| **`caption`** | То же + явный PageBreak |
Оценка высоты ≠ вёрстка Word — поэтому `legacy`/`caption` помечены как неточные.
### 5.9. Титул, задание, колонтитулы
`--title` / `--assignment`: пустая оболочка из шаблона + docxcompose (титул, разрыв, задание, разрыв, тело).
На секциях титула/задания колонтитул без номера страницы; дальше — сквозная нумерация, поле `PAGE`, TNR 12 по центру. После TOC начинается секция тела с номерами.
Если в документе нет TOC — номер ставится на единственную секцию.
**Автотитул** (по умолчанию **выкл.**; CLI `--auto-title` / галочка в GUI; явный `--title` отключает генератор): см. [`docs/title.md`](docs/title.md). Кратко: `md2gost/title_page.py` заполняет bundled `TitleTemplate.docx` из `info_conv.yaml` и/или блока ` ```title `, ФИО/группа — `md2gost.user.json` или `--student`/`--group`. GUI: первый запуск и **Настройки → Студент…**.
**Свойства DOCX** (после склейки титула, перед save): `doc_metadata.apply_document_metadata`. По умолчанию автор = `getuser()`, примечание «Создано при помощи md2gost (ТЗ МИРЭА)», created/modified = сейчас, revision = 1. GUI **Настройки → Метаданные…** / флаги `--doc-*`. Профиль — блок `metadata` в `md2gost.user.json`.
### 5.10. Приложения
- Буквы: А Б В Г Д Е Ж И К … (без Ё З Й О Ч Ь Ы Ъ) — `APPENDIX_LETTERS` в `numberer.py`.
- `## Приложение А Название` оформляется как приложение.
- Если после `# *ПРИЛОЖЕНИЯ` нет перечня, а приложений несколько — Renderer **сам вставляет** список «Приложение А — …». Если автор уже написал перечень — не дублирует.
### 5.11. Промпты для ИИ
| Файл | Назначение |
|------|------------|
| `prompts/generate-md.md` | диалект md2gost vs обычный MD |
| `prompts/generate-mirea-report.md` | полный отчёт ГОСТ / МИРЭА |
| `prompts/generate-pis-custom-report.md` | итоговый отчёт ПИС |
| `prompts/emulate-student.md` | голос: человечный текст, не отчёт ИИ |
В GUI: выбор промпта + чекбоксы схем → в конец дописываются `ai-prompt` и макросы из `md2gost.schemes.json` (`compose_prompt()`).
### 5.12. Сборка и поставка
- `pyproject.toml`: hatchling, пакеты `md2gost`, `md2latex`, `md2fodt`; в wheel вшиваются Template.docx, mml2omml, diagrams, latex/mirea, prompts, docs.
- `md2gost.spec` / `md2gost.fast.spec` — PyInstaller (вшивают `plantuml.jar` и при наличии `vendor/graphviz`).
- `scripts/fetch_plantuml.py` — скачать jar.
- `scripts/fetch_graphviz.py` — скачать portable Graphviz в `md2gost/vendor/graphviz` (для DFD в exe).
- `scripts/md2gost_exe.py` — обёртка exe.
- `scripts/pyi_rth_mplbackend.py` — runtime hook matplotlib для PyInstaller.
- `scripts/check_page_fill.bas` — макрос Word для той же эвристики вёрстки.
- `scripts/md2pdf.ps1` / `md2pdf.sh` — обёртки LaTeX→PDF.
- CI: `.github/workflows/pytest.yml` (Python 3.11, flake8, pytest); `example-generator.yml`.
---
## 6. Карта модулей md2gost и функции кода
Ниже — публичные и важные внутренние функции **уже реализованные**. Приватные `_foo` указаны, если без них не понять модуль.
### 6.1. Точки входа и пайплайн
**`md2gost/__init__.py`**
- `package_dir()` — каталог пакета (и `_MEIPASS` в frozen exe).
**`md2gost/__main__.py`**
- `build_parser()` — argparse.
- `request_from_args(args)``ConvertRequest`.
- `_argv_needs_console(argv)`, `_enable_windows_console()`.
- `main()`.
**`md2gost/pipeline.py`**
- `ConvertRequest`, `ConvertResult`.
- `default_output_path(filename)`, `timestamped_output_path(path)`, `default_template_path()`.
- `open_document(path)` — macOS `open` / Windows `startfile` / `xdg-open`.
- `_fix_front_matter_after_compose(...)` — сброс PAGE на титуле/задании, сквозная нумерация.
- `convert(req, log=None)` — весь конвейер, ошибки в `ConvertResult`, не пробрасывает исключение наружу. Перед save — `apply_document_metadata`.
- `should_launch_gui(filename, gui_flag)`.
- `_CallbackLogHandler` — логи md2gost в GUI/CLI.
**`md2gost/doc_metadata.py`**
- `DocumentMetadata`, `DEFAULT_DOC_COMMENTS`, `AUTHOR_SOURCES`.
- `os_user()`, `clip_core()` (лимит 255 символов python-docx).
- `resolve_document_metadata(...)` — профиль + CLI/request.
- `apply_document_metadata(document, meta)` — core properties + даты + revision=1.
**`md2gost/user_profile.py`**
- `UserProfile` (student, group, metadata), `load_user_profile` / `save_user_profile``md2gost.user.json`.
**`md2gost/converter.py`**
- класс `Converter`: читает md, применяет стили, парсит, нумерует, рендерит.
- `convert()`, свойства `document`, `style_config`, `raw_markdown`, `doc_type`, `heading_numbering`.
### 6.2. Профили и стили
**`md2gost/profiles.py`**
- константы: `DOC_TYPES`, `HEADING_NUMBERING_MODES`, `TOC_MODES`, `TABLE_CONTINUATION_MODES`, `LISTING_CONTINUATION_MODES`, `NUMBERING_SCOPES`, `STYLE_PRESETS`.
- `DocProfile` — поля min/max источников, sectional_biblio, require_graphic_appendix, style_preset, numbering_scope, флаги структуры, `require_apid_kr_structure`.
- `get_profile(doc_type)`.
- `fix_russian_quotes`, `fix_dashes`, `replace_emdash_with_hyphen`.
- `set_emdash_to_hyphen`, `emdash_to_hyphen_enabled`, `dash_separator`.
- `preprocess_markdown`, `separate_biblio_lines`.
- `find_formula_refs``@Формула:` / `@Formula:`.
- `current_year()`.
**`md2gost/style_config.py`**
- `StyleConfigError`.
- `PageSpec.merge/to_dict/require_complete`.
- `ParagraphStyleSpec.merge/to_dict`.
- `StyleConfig.merge/to_dict`.
- `preset_mirea()`, `preset_pis_custom()`, `get_preset(name)`.
- `style_config_from_dict`, `load_styles_file`, `resolve_style_config` (пресет ← рядом с md ← `--styles`).
**`md2gost/styles.py`**
- `apply_style_config(document, config)` — поля, секции, табы TOC, стили абзацев.
- `apply_mirea_styles`, `apply_pis_custom_styles`.
- `apply_document_styles(...)` — пресет + оверлей, возвращает итоговый `StyleConfig`.
- Внутри: `_set_run_font` (блокирует тему Word: Calibri/синий), `_ensure_style`, `_fix_toc_tab_stops`.
**`md2gost/page_geometry.py`**
- глобальные поля `MARGIN_*`, `MARGIN_PRESETS`, `apply_margin_preset`.
- `is_landscape_section`, `apply_section_geometry`, `content_size`.
- `apply_centered_page_footer`, `clear_section_footer`, `ensure_continuous_page_numbers`.
### 6.3. Разбор Markdown
**`md2gost/extended_markdown/`**
- `__init__.py` — Marko + GFM + Extension: Equation, Reference, Caption, Table, TOC, Heading, InlineEquation, Image.
- `heading.py``# *` → ненумерованный.
- `caption.py` — строка `%id текст +listing +landscape`.
- `table.py` — GFM-таблица + `^`/`>` + `_resolve_merge_restarts`.
- `image.py``%id` в title картинки.
- `equation.py` — блочная `$$`.
- `inline_formula.py``$…$`.
- `reference.py``@Тип:id`.
- `toc.py``[TOC]`.
**`md2gost/parser_.py`**
- `Parser.parse()` — пропускает пустые строки; копит `CaptionInfo`; HR без флага игнорирует; иначе `factory.create`.
**`md2gost/renderable_factory.py`**
- `RenderableFactory.create` (singledispatch): Paragraph, Heading, FencedCode/CodeBlock → Listing или DiagramFigure, Equation, List, Table, TOC, ThematicBreak → PageBreak.
- `_create_runs` — жирный/курсив/зачёркивание, код, картинки, инлайн-формулы, `@ссылки`, гиперссылки.
### 6.4. Нумерация и ссылки
**`md2gost/numberer.py`**
- `Numberer(mode="section"|"continuous")`: `enter_section`, `enter_appendix`, `next_number`, `resolve`, `register_label`.
- Режим section: `1.1` / `Б.1`, счётчики сбрасываются на разделе/приложении.
- Режим continuous: `1, 2, 3` на весь документ (ПИС).
**`md2gost/label_pass.py`**
- `LabelRegistry.put/get/get_display` (алиасы Рисунок/Figure/Таблица/…).
- `assign_numbers(renderables, numbered_equations, numbering_scope)`.
- `resolve_pending_in_renderables`, `set_active_registry`, `resolve_reference`.
Формулы нумеруются **только** если на них есть `@Формула:…` в исходнике.
### 6.5. Рендер в DOCX
**`md2gost/renderer.py` — класс `Renderer`**
- `process(renderables)` — основной цикл.
- Секции: фронт без номеров → после TOC тело с PAGE; `_enter_landscape` / `_exit_landscape`.
- `_ensure_appendix_index` — автоперечень приложений.
- Нумерация объектов через `Numberer` (если не `skip_numbering` — в Converter нумерация уже в label_pass).
- Спецзаголовки: СОДЕРЖАНИЕ / СПИСОК — по центру; Введение / Заключение / ПРИЛОЖЕНИЯ — слева как H1.
**`md2gost/layout_tracker.py`**
- `LayoutState` — текущая/оставшаяся высота, номер страницы.
- `LayoutTracker``add_height`, `can_fit_to_page`, `new_page`, `set_page_size` (книга ↔ альбом).
**`md2gost/renderable/`**
| Класс | Файл | Роль |
|--------|------|------|
| `Renderable` | `renderable.py` | абстрактный `render()` |
| `Paragraph` / `Link` | `paragraph.py` | абзац, runs, картинки, ссылки, инлайн-формулы |
| `Heading` | `heading.py` | уровни 19, manual/auto, снятие Word numbering, strip ведущих цифр |
| `Table` | `table.py` | сетка, merge, continuation, `+landscape` |
| `Listing` | `listing.py` | код, Pygments (`DocxParagraphPygmentsFormatter`), continuation |
| `Image` | `image.py` | файл-рисунок + подпись |
| `DiagramFigure` | `diagram.py` | рендер схемы + опциональный Listing исходника |
| `Equation` | `equation.py` | блочная формула OMML + номер справа |
| `Caption` / `CaptionInfo` | `caption.py` | «Рисунок 1.1 — …» |
| `List` | `list.py` | маркированные/нумерованные, вложенность |
| `ToC` | `toc.py` | native field `TOC \o "1-3" \h \z \u` или ручные строки с табами |
| `PageBreak` | `page_break.py` | явный разрыв |
| `RequiresNumbering` | `requires_numbering.py` | интерфейс нумеруемых объектов |
| `ParagraphSizer` / шрифты | `paragraph_sizer.py`, `find_font.py` | оценка высоты абзаца (FreeType) |
**`md2gost/docx_elements.py`**
- `create_table`, `create_table_row`, `create_table_cell`, `apply_cell_merge`, `set_table_box_borders`, фиксация ширины в twips.
**`md2gost/docx_svg.py`**
- `attach_svg_blip(run, inline_shape, svg_path)` — вектор + PNG fallback.
**`md2gost/latex_math.py`**
- `latex_to_omml(latex)` — latex2mathml + XSLT `mml2omml`.
- `inline_omml(omml)` — дроби в линейный вид для инлайна.
**`md2gost/toc_processor.py`**
- `TocProcessor.process` — native: `updateFields` при открытии Word; manual: страницы из `Heading.rendered_page` (СОДЕРЖАНИЕ в оглавление не попадает).
**`md2gost/debugger.py`**
- плавающая сетка страниц в документе (`--debug`): `Debugger`, `add_float_picture`, `new_pic_anchor`.
**`md2gost/util.py`**
- `create_element` — низкоуровневый OOXML.
### 6.6. Библиография
**`md2gost/bibliography.py`**
- `BiblioEntry`, `extract_year`, `parse_biblio_block`, `find_citations`.
- `Bibliography(...)` — ленивая обёртка над renderable (чтобы checker не тянул docx).
**`md2gost/biblio_processor.py`**
- `entries_from_text` — несколько `[n]:` внутри одного абзаца (Marko склеивает строки).
- `extract_bibliography_from_markdown` — плоский список или секции для ВКР.
- `fold_bibliography(renderables, parent, raw_markdown)` — заменяет абзацы списка на `Bibliography`.
**`md2gost/bibliography_renderable.py`** — отрисовка пунктов списка стилем Bibliography.
### 6.7. Диаграммы
**`md2gost/diagram_schemes.py`**
- `DiagramScheme` (id, title, docs, ai-prompt, includes, prefix/postfix, theme).
- `bundled_schemes_path`, `user_schemes_path`, `ensure_user_schemes`.
- `load_schemes_from_path`, `save_schemes`, `load_merged_schemes`.
- `scheme_id_from_lang`, `is_diagram_lang`, `get_scheme`, `get_schemes`, `reload_schemes`, `configure_schemes`.
- `apply_scheme`, `prepare_with_schemes`.
**`md2gost/diagram_includes.py`**
- кэш HTTP `!include`: `resolve_url`, `resolve_include_ref`, `rewrite_http_includes_in_source`, `reset_include_cache`, `cache_entry_count`.
**`md2gost/diagram_renderer.py`**
- `configure_diagrams(...)`, `DiagramConfig`, `DiagramRenderResult`.
- `prepare_source`, `render_diagram`.
- `resolve_plantuml_jar`, `fetch_plantuml_jar`, `iter_plantuml_candidates`, `diagram_engine_status`.
- рендер jar / Kroki / пара png+svg; масштаб `scale` в исходник PlantUML.
- локальные типы `LOCAL_ONLY_TYPES` (`idef0`, `dfd`): Pillow, без jar/Kroki.
**`md2gost/idef0.py`** — DSL IDEF0 + Pillow/SVG.
**`md2gost/dfd.py`** — обёртка [pbauermeister/dfd](https://github.com/pbauermeister/dfd): DSL → DOT → vendor/system Graphviz / Kroki (`write_dfd_outputs`, `fetch_graphviz`).
**`md2gost/diagrams/`** — `schemes.json`, C4 `*.puml`.
### 6.8. Word COM и вёрстка
**`md2gost/word_fix.py`**
- `parse_caption_text`, `find_page_break_row`, `continuation_label`, `caption_style_name`.
- `fix_continuations(path, tables, listings, repeat_header)``FixResult`.
- Внутри: до 5 проходов `Repaginate` + `Split`, рамки листингов, опциональный повтор шапки.
**`md2gost/page_fill_check.py`**
- `PageMetric`, `PageFillIssue`.
- `evaluate_page_fill(pages)` — чистая эвристика (порог пустого низа 40%, пропускает альбом, последнюю страницу, фронт, конец раздела, «в основном рисунок/таблица»).
- `check_docx_page_fill(path)`, `format_page_fill_report`.
### 6.9. Checker (функции проверок)
`md2gost/checker/__init__.py`:
- `check_structure`, `check_apid_kr_structure`, `check_pis_headings`, `check_special_headings`.
- `check_forbidden_abbreviations`, `check_citations_in_intro_conclusion`.
- `check_bibliography`, `check_object_refs`, `check_appendices`.
- `check_continuation_hints`, `check_table_merge`.
- `check_markdown(...)` — оркестратор.
- `format_report(issues)`.
### 6.10. GUI, DnD, справка
**`md2gost/gui.py`**
- `_make_root`, `_combo_values/_combo_key/_display`, `_Tooltip`, `_tip`.
- `Md2GostApp`: меню, окна файлов/студента/метаданных/диаграмм/схем/справки, браузер docs, сборка промпта, DnD, `_collect()``ConvertRequest`, `_convert`.
- `run_gui(initial)`, `main()`.
**`md2gost/dnd.py`**
- `parse_tkdnd_files`, `normalize_drop_paths`, `first_markdown`, `enable_file_drop`.
- Win32: `_WinDropHook`, `_Win32DropApi`, `_query_drop_files`.
**`md2gost/help_content.py`**
- константы `USAGE_HELP`, `SCHEMES_HELP`.
- `prompt_search_dirs`, `load_prompt_catalog`, `scheme_prompt_block`, `compose_prompt`.
- `docs_search_dirs`, `load_docs_catalog`.
---
## 7. md2fodt — что уже есть
Пакет `md2fodt/`.
| Файл | Функции |
|------|---------|
| `__main__.py` | `main()` — CLI: файл, `-o`, `--emdash-to-hyphen` |
| `converter.py` | `convert_md_to_fodt(md, output, emdash_to_hyphen)` |
| `emitter.py` | `emit_body`, `emit_fodt`; эмиттеры heading/paragraph/image/table/code/list/equation; merge `^`/`>` |
| `styles.py` | `styles_xml()`, `automatic_styles_xml()` — TNR 14, поля ГОСТ |
| `escape.py` | `escape_text`, `xml_id` |
Картинки встраиваются base64. Нет диаграмм PlantUML и нет «Продолжение таблицы».
---
## 8. md2latex — что уже есть
Пакет `md2latex/` + шаблон `latex/mirea/` (вендор [mirea-ninja/Latex-Template-for-Report-Diploma-Thesis](https://github.com/mirea-ninja/Latex-Template-for-Report-Diploma-Thesis)).
| Файл | Функции |
|------|---------|
| `__main__.py` | `main()`, `_run_xelatex` (latexmk или 2 прохода xelatex) |
| `converter.py` | `package_template_dir()`, `convert_md_to_latex_project(md, out, titlepage)` — копирует шаблон, пишет `content.tex` |
| `emitter.py` | `emit_document`; `_emit_longtable` с `\endfirsthead` / `\endhead` и «Продолжение таблицы N» |
| `escape.py` | `escape_text`, `escape_verbatim_for_listing`, `latex_label` |
Флаги: `-o` каталог, `--titlepage` PDF титула, `--pdf`, `--xelatex-passes`.
---
## 8.1. word2md — импорт DOCX
Пакет `word2md/`. Эвристика: стили md2gost, иначе ГОСТ-текст подписей/заголовков.
| Файл | Роль |
|------|------|
| `__main__.py` | CLI: `.docx`, `-o`, `--media-dir`, `--keep-toc-pages`, `--pagebreaks` |
| `pipeline.py` | `ImportRequest` / `ImportResult` / `convert_docx()` |
| `walker.py` | обход body, склейка Code/списков, `postprocess_blocks` (продолжения) |
| `classify.py` | Heading / Caption / Code / библио / спецразделы |
| `captions.py` | `Рисунок` / `Таблица` / `Листинг` + `parse_caption_text` из md2gost |
| `tables.py` | merge → `^`/`>`, детект формульной таблицы |
| `omml.py` | OMML → читаемая формула |
| `media.py` | выгрузка `word/media` |
| `emit.py` | IR → диалект md2gost |
| `refs.py` | `Рисунок 1.1``@Рисунок:fig1_1` |
Точки входа также: `python -m md2gost report.docx`, GUI «Импорт DOCX→MD».
---
## 9. Тесты — что покрыто
Каталог `tests/`, CI гоняет `pytest`.
| Файл | Что проверяет |
|------|----------------|
| `test_pipeline.py` | GUI/CLI развилка, пути, DnD, argparse, HR→pagebreak, промпты, docs catalog, listing continuation |
| `test_mirea_tz.py` | Numberer, checker (coursework/PIS/ВКР/АПИД), препроцесс, TOC, заголовки, приложения, библио, page-fill эвристика, стили H1 |
| `test_style_config.py` | merge оверлея, неизвестные ключи, CLI побеждает файл рядом с md, применение к Document |
| `test_table_merge.py` | `^`/`>`, OOXML merge, checker merge, режимы continuation |
| `test_word_fix.py` | разбор подписей, индекс разрыва, COM smoke (если нет Word — мягко) |
| `test_landscape.py` | геометрия альбома, секции, картинки/таблицы/листинги, один section break |
| `test_layout_tracker.py` | страницы и остаток высоты |
| `test_paragraph.py` / `test_paragraph_sizer.py` | абзацы и измерение шрифта |
| `test_diagram_renderer.py` | схемы, кэш, mermaid, svg, jar, `+listing`/`+landscape` |
| `test_idef0.py` | DSL IDEF0, PNG/SVG, интеграция `render_diagram` |
| `test_dfd.py` | DFD data-flow-diagram, mock Graphviz/Kroki, интеграция `render_diagram` |
| `test_md2fodt.py` | escape, каркас FODT, запись файла |
| `test_md2latex.py` | longtable continuation, генерация проекта |
| `test_word2md.py` | подписи, спецзаголовки, @ссылки, продолжения таблиц, DOCX→MD, round-trip |
| `extended_mardown/formula.py` | формулы (unittest) |
Нет полного e2e «открыть Word и сравнить каждую страницу» в Linux CI (шрифты ставятся, Word COM — нет).
---
## 10. Документация и примеры
Пользовательские страницы (те же вшиты в exe, GUI: Справка → Документация):
| Файл | Содержание |
|------|------------|
| `ReadMe.md` | краткий старт репозитория |
| `docs/README.md` | оглавление docs |
| `docs/quickstart.md` | установка, exe, типы |
| `docs/markdown.md` | диалект |
| `docs/schemes.md` | UML/Mermaid, JSON схем |
| `docs/types.md` | профили работ |
| `docs/cli-gui.md` | флаги ↔ GUI |
| `docs/styles.md` | JSON стилей |
| `docs/prompts.md` | ИИ-промпты |
| `docs/word2md.md` | импорт DOCX → Markdown |
| `md2gost/README.md` | справка пакета (подробнее про continuation и диаграммы) |
| `prompts/README.md` | как копировать промпты |
| `examples/example.md`, `examples/pis_custom.md` | образцы |
| `examples/md2gost.styles.json` | пример оверлея стилей |
---
## 11. Зависимости
Обязательные (`pyproject.toml`): python-docx, marko, docxcompose, freetype-py, pillow, requests, latex2mathml, pygments, lxml.
Опционально: pywin32 (Word COM), matplotlib (сборка exe / отладка), tkinterdnd2 (удобный DnD в GUI).
Внешние рантаймы: Java + plantuml.jar **или** Kroki; Graphviz (вшитый/кэш/PATH) для DFD; для PDF — XeLaTeX / latexmk и шрифт Times New Roman.
---
## 12. Что в репозитории, но не продукт
Черновики и чужой код рядом с пакетами. Это **не** часть `pip install` / exe:
- корень: `main.py`, `main12.py`, `mm2.py``mm5.py`, `asd.py`, `parser.py`, `font.py`, `_audit_tmp.py`, `tmp_listing_word.md`, `todo.txt`;
- `ad/` — черновые отчёты;
- `_paco_extract/` — выгрузки методичек ПАЦО;
- `Other_Code/` — справка OOXML, docx-js, скрипты pack/unpack/validate (не импортируется md2gost);
- артефакты сборки `build-fast/`, кэши диаграмм.
`todo.txt` сейчас: «оценки с текстом через сам Word, а не предугадывание» — частично закрыто режимом `word`.
---
## 13. Чего нет / ограничения (честно)
- DFD: пакет `data-flow-diagram` в зависимостях; картинка — вшитый/кэш/PATH Graphviz или Kroki; балансировка уровней не проверяется.
- FODT и LaTeX не догоняют md2gost по GUI, checker, схемам, профилям PIS/АПИД, Word COM.
- «Продолжение таблицы» в чистом DOCX без Word невозможно точно (нет longtable). Без Word — `off` или неточный `legacy`/`caption`.
- `--check-pages` даёт ложные срабатывания; это эвристика.
- Merge ячеек не живёт через границу разрезанной таблицы.
- Цвет текста в стилях не настраивается (всегда чёрный).
- Профиль `paco_custom` упомянут в комментарии `page_geometry`, отдельного типа в `DOC_TYPES` нет.
- Нумерация заголовков H4+ в стилях JSON не описана (в Word уровни 19 у Heading есть).
---
## 14. Типичные сценарии «уже работает»
1. **Практика / курсовая МИРЭА:** `# *ВВЕДЕНИЕ``# 1 …` … список `[1]:``--type practice|coursework --check --strict`.
2. **ВКР:** раздельный список, ссылки `[1.5]`, приложение «Графический материал».
3. **ПИС:** `# Практическая работа №1. …``--type PIS_custom`, сквозные Рисунок 1, 2, 3.
4. **АПИД:** обязательные 2 главы и 2.1–2.4, источники 720.
5. **UML в отчёте:** `%id Подпись` + ` ```uml-c4 ` — PNG/SVG в Word, опционально `+listing` и `+landscape`.
6. **Широкая таблица:** `%id … +landscape`.
7. **Длинная таблица на Windows:** режим `word` → COM режет и пишет «Продолжение Таблицы N».
8. **Свой вуз/кафедра:** `md2gost.styles.json` меняет поля и H1 без правки кода.
9. **Своя схема:** GUI «Шаблоны UML» или JSON с `includes`/`prefix`/`ai-prompt`.
10. **Черновик через ИИ:** промпт из GUI + схемы → `.md` → конвертер.
11. **Только проверить готовый DOCX:** `python -m md2gost report.docx --check-pages`.
12. **Без Word:** `python -m md2fodt` или `python -m md2latex --pdf`.
---
## 15. Поток данных одним абзацем
Пользователь кладёт `.md` (и опционально титул/задание/JSON стилей/схем). `convert()` читает текст, при необходимости гоняет checker, препроцессит кавычки/тире, парсит Marko-расширением в дерево, фабрика делает Renderable, библиография сворачивается в особые абзацы, объектам раздаются номера по профилю, `@ссылки` заменяются на «Рисунок 2.1», Renderer кладёт OOXML в очищенный Template.docx с оценкой высоты страницы, TocProcessor дописывает содержание, pipeline склеивает титул, сохраняет DOCX, при режиме `word` открывает файл в Word и режет таблицы/листинги по настоящей пагинации. GUI делает ровно то же, только параметры собирает из формы.
---
## 16. Где править, если добавлять функцию
| Хочешь | Куда |
|--------|------|
| Новый тип работы | `profiles.PROFILES` + checker + стили пресета |
| Новое поле CLI/GUI | `__main__.build_parser`, `ConvertRequest`, `gui._collect/_apply_request` |
| Новый элемент Markdown | `extended_markdown/` + `RenderableFactory` + класс в `renderable/` |
| Новая проверка ТЗ | `checker/__init__.py` → вызов из `check_markdown` |
| Новая UML-схема «из коробки» | `md2gost/diagrams/schemes.json` (+ `.puml` при необходимости) |
| Новое поле стиля | `style_config.STYLE_FIELD_KEYS` + `ParagraphStyleSpec` + `_apply_paragraph_spec` |
| Паритет FODT/LaTeX | `md2fodt/emitter.py` / `md2latex/emitter.py` |
---
*Файл сгенерирован по состоянию исходников репозитория (пакет 0.1.0). Пользовательские howto — в `docs/`. Этот файл — инвентарь возможностей и кода.*