From ac0aa333990b67594d11e23de74b6bbc42f889cc Mon Sep 17 00:00:00 2001 From: Igor20264 Date: Sun, 6 Sep 2026 11:13:41 +0300 Subject: [PATCH] hot-fix Build.bat 0.4.5 --- PROJECT.md | 683 +++++++++++++++++++++++++++++++++++++++++++++ ReadMe.md | 4 +- build-exe-fast.bat | 94 ++++--- build-exe.bat | 65 +++-- docs/README.md | 2 + 5 files changed, 772 insertions(+), 76 deletions(-) create mode 100644 PROJECT.md diff --git a/PROJECT.md b/PROJECT.md new file mode 100644 index 0000000..091adca --- /dev/null +++ b/PROJECT.md @@ -0,0 +1,683 @@ +# 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 / BPMN / 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 через шаблон МИРЭА | + +Общий диалект разметки: спецразделы `# *ВВЕДЕНИЕ`, метки `%id`, ссылки `@Рисунок:id`, таблицы с `^`/`>`, формулы `$$`, библиография `[n]: …`. + +Полнота реализации **не одинаковая**: + +- **md2gost** — полный конвейер (стили, профили, диаграммы, checker, GUI, Word COM). +- **md2fodt** — v1: текст, заголовки, списки, таблицы с merge, картинки, листинги, простые ссылки, библиография. Нет PlantUML, нет «Продолжение таблицы», нет полного паритета PIS/АПИД. +- **md2latex** — тот же диалект → `content.tex` + копия `latex/mirea/`. Таблицы идут в `longtable` с настоящим «Продолжение таблицы N» (это умеет LaTeX, не Word). + +--- + +## 3. Как запустить + +```bash +pip install -e . +python -m md2gost # GUI +python -m md2gost report.md -o report.docx --type coursework --check +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). +Быстрая сборка: `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 | то же | 5–7, ≤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) | +| `--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, выходной путь; +- **Настройки → Диаграммы**: 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` / `bpmn` / `mermaid` | Рисунок; флаги `+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`, `bpmn`. + +Слои загрузки (позже побеждает): встроенный шаблон → `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 — только Kroki. **IDEF0 / DFD конвертер не рисует** — только готовый PNG. + +Кэш картинок: `{каталог_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 — номер ставится на единственную секцию. + +### 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` | итоговый отчёт ПИС | + +В 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. +- `scripts/fetch_plantuml.py` — скачать jar. +- `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`, не пробрасывает исключение наружу. +- `should_launch_gui(filename, gui_flag)`. +- `_CallbackLogHandler` — логи md2gost в GUI/CLI. + +**`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` | уровни 1–9, 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. + +**`md2gost/diagrams/`** — `schemes.json`, `BPMN.puml`, 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`. + +--- + +## 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` | схемы, BPMN, кэш, mermaid, svg, jar, `+listing`/`+landscape` | +| `test_md2fodt.py` | escape, каркас FODT, запись файла | +| `test_md2latex.py` | longtable continuation, генерация проекта | +| `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/BPMN, JSON схем | +| `docs/types.md` | профили работ | +| `docs/cli-gui.md` | флаги ↔ GUI | +| `docs/styles.md` | JSON стилей | +| `docs/prompts.md` | ИИ-промпты | +| `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; для 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` сейчас: «ожидаем BPMN в mermaid» и «оценки с текстом через сам Word, а не предугадывание» — второе частично закрыто режимом `word`. + +--- + +## 13. Чего нет / ограничения (честно) + +- IDEF0 и DFD конвертер не рисует. +- 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 уровни 1–9 у 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, источники 7–20. +5. **UML в отчёте:** `%id Подпись` + ` ```uml-c4 ` / ` ```bpmn ` — 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/`. Этот файл — инвентарь возможностей и кода.* diff --git a/ReadMe.md b/ReadMe.md index c796df0..5fade5e 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -14,7 +14,9 @@ Windows: `build-exe.bat` → `dist\md2gost.exe` (двойной клик — GUI ## Документация -Полное описание — в [`docs/`](docs/): +Полная карта проекта (что уже умеет, пайплайны, модули): [`PROJECT.md`](PROJECT.md). + +Пользовательская документация — в [`docs/`](docs/): | Раздел | Содержание | |--------|------------| diff --git a/build-exe-fast.bat b/build-exe-fast.bat index 0b26dd0..89cb392 100644 --- a/build-exe-fast.bat +++ b/build-exe-fast.bat @@ -1,70 +1,46 @@ @echo off setlocal EnableExtensions -chcp 65001 >nul cd /d "%~dp0" +chcp 65001 >nul 2>&1 set "DO_PAUSE=1" set "PYI_CLEAN=" -for %%A in (%*) do ( - if /i "%%~A"=="nopause" set "DO_PAUSE=0" - if /i "%%~A"=="clean" set "PYI_CLEAN=--clean" -) +if /i "%~1"=="nopause" set "DO_PAUSE=0" +if /i "%~2"=="nopause" set "DO_PAUSE=0" +if /i "%~1"=="clean" set "PYI_CLEAN=--clean" +if /i "%~2"=="clean" set "PYI_CLEAN=--clean" -echo === md2gost: быстрая сборка exe (onefile, шрифты + plantuml.jar) === +echo === md2gost: быстрая сборка exe, onefile, шрифты и plantuml.jar === echo Каталог: %CD% echo Spec: md2gost.fast.spec -echo Кэш: build-fast\ (без --clean, пока не передадите clean) +echo Кэш: build-fast\ без --clean, пока не передадите clean echo. where python >nul 2>&1 -if errorlevel 1 ( - echo Python не найден в PATH. Установите Python 3.10+ и отметьте "Add python.exe to PATH". - goto :fail -) +if errorlevel 1 goto :nopython -python -c "import sys; raise SystemExit(0 if sys.version_info >= (3, 10) else 1)" -if errorlevel 1 ( - echo Нужен Python 3.10 или новее. - python --version - goto :fail -) +python -c "import sys; raise SystemExit(sys.hexversion < 0x030A0000)" +if errorlevel 1 goto :oldpython echo [1/4] Зависимости ... python -c "import PyInstaller, matplotlib, docx, marko, lxml, pygments, PIL, freetype, latex2mathml, requests, docxcompose" 1>nul 2>nul -if errorlevel 1 ( - echo Ставлю проект + PyInstaller + matplotlib ... - python -m pip install -q -e . "pyinstaller>=6.0" "matplotlib>=3.7" - if errorlevel 1 ( - echo Не удалось поставить зависимости. - goto :fail - ) -) else ( - echo Уже стоят, pip пропускаю. -) +if not errorlevel 1 goto :deps_ok +echo Ставлю проект + PyInstaller + matplotlib ... +python -m pip install -q -e . "pyinstaller>=6.0" "matplotlib>=3.7" +if errorlevel 1 goto :pipfail +:deps_ok -if not exist "md2gost\Template.docx" ( - echo Нет md2gost\Template.docx — сборка бессмысленна. - goto :fail -) +if not exist "md2gost\Template.docx" goto :notemplate -echo [2/4] PlantUML jar (обязателен, вшивается в exe) ... +echo [2/4] PlantUML jar, обязателен, вшивается в exe ... python scripts\fetch_plantuml.py -if not exist "md2gost\vendor\plantuml.jar" ( - echo Нет md2gost\vendor\plantuml.jar — portable exe без jar не собираем. - goto :fail -) +if not exist "md2gost\vendor\plantuml.jar" goto :nojar echo [3/4] PyInstaller onefile, кэш build-fast ... python -m PyInstaller --noconfirm %PYI_CLEAN% --workpath build-fast --distpath dist md2gost.fast.spec -if errorlevel 1 ( - echo PyInstaller завершился с ошибкой. - goto :fail -) +if errorlevel 1 goto :pyifail -if not exist "dist\md2gost.exe" ( - echo dist\md2gost.exe не появился. - goto :fail -) +if not exist "dist\md2gost.exe" goto :noexe echo. echo [4/4] Готово: @@ -74,10 +50,38 @@ echo Двойной клик — GUI. Из консоли: md2gost.exe report.md echo Повторно без clean — быстрее за счёт кэша Analysis. echo Полный пересбор: build-exe-fast.bat clean echo. - if "%DO_PAUSE%"=="1" pause exit /b 0 +:nopython +echo Python не найден в PATH. Установите Python 3.10+ и отметьте "Add python.exe to PATH". +goto :fail + +:oldpython +echo Нужен Python 3.10 или новее. +python --version +goto :fail + +:pipfail +echo Не удалось поставить зависимости. +goto :fail + +:notemplate +echo Нет md2gost\Template.docx — сборка бессмысленна. +goto :fail + +:nojar +echo Нет md2gost\vendor\plantuml.jar — portable exe без jar не собираем. +goto :fail + +:pyifail +echo PyInstaller завершился с ошибкой. +goto :fail + +:noexe +echo dist\md2gost.exe не появился. +goto :fail + :fail echo. echo Сборка не удалась. diff --git a/build-exe.bat b/build-exe.bat index 8fc11cf..2d17d1d 100644 --- a/build-exe.bat +++ b/build-exe.bat @@ -1,54 +1,35 @@ @echo off setlocal EnableExtensions -chcp 65001 >nul cd /d "%~dp0" +chcp 65001 >nul 2>&1 echo === md2gost: сборка exe === echo Каталог: %CD% echo. where python >nul 2>&1 -if errorlevel 1 ( - echo Python не найден в PATH. Установите Python 3.10+ и отметьте "Add python.exe to PATH". - goto :fail -) +if errorlevel 1 goto :nopython -python -c "import sys; raise SystemExit(0 if sys.version_info >= (3, 10) else 1)" -if errorlevel 1 ( - echo Нужен Python 3.10 или новее. - python --version - goto :fail -) +python -c "import sys; raise SystemExit(sys.hexversion < 0x030A0000)" +if errorlevel 1 goto :oldpython echo [1/4] Зависимости проекта + PyInstaller + matplotlib ... python -m pip install -q -e . "pyinstaller>=6.0" "matplotlib>=3.7" -if errorlevel 1 ( - echo Не удалось поставить зависимости. - goto :fail -) +if errorlevel 1 goto :pipfail -if not exist "md2gost\Template.docx" ( - echo Нет md2gost\Template.docx — сборка бессмысленна. - goto :fail -) +if not exist "md2gost\Template.docx" goto :notemplate -echo [2/4] PlantUML jar (вшивается в exe, если скачается^) ... +echo [2/4] PlantUML jar, вшивается в exe если скачается ... python scripts\fetch_plantuml.py if not exist "md2gost\vendor\plantuml.jar" ( echo Предупреждение: plantuml.jar нет — в exe диаграммы пойдут через kroki.io. ) -echo [3/4] PyInstaller (один файл, без консоли; CLI подцепит консоль сам^) ... +echo [3/4] PyInstaller, один файл, без консоли; CLI подцепит консоль сам ... python -m PyInstaller --noconfirm --clean md2gost.spec -if errorlevel 1 ( - echo PyInstaller завершился с ошибкой. - goto :fail -) +if errorlevel 1 goto :pyifail -if not exist "dist\md2gost.exe" ( - echo dist\md2gost.exe не появился. - goto :fail -) +if not exist "dist\md2gost.exe" goto :noexe echo. echo [4/4] Готово: @@ -56,11 +37,35 @@ echo %CD%\dist\md2gost.exe echo. echo Двойной клик — GUI. Из консоли: md2gost.exe report.md -o report.docx echo. - if /i "%~1"=="nopause" goto :eof pause exit /b 0 +:nopython +echo Python не найден в PATH. Установите Python 3.10+ и отметьте "Add python.exe to PATH". +goto :fail + +:oldpython +echo Нужен Python 3.10 или новее. +python --version +goto :fail + +:pipfail +echo Не удалось поставить зависимости. +goto :fail + +:notemplate +echo Нет md2gost\Template.docx — сборка бессмысленна. +goto :fail + +:pyifail +echo PyInstaller завершился с ошибкой. +goto :fail + +:noexe +echo dist\md2gost.exe не появился. +goto :fail + :fail echo. echo Сборка не удалась. diff --git a/docs/README.md b/docs/README.md index dd2a772..41fc472 100644 --- a/docs/README.md +++ b/docs/README.md @@ -14,4 +14,6 @@ Исходный код конвертера: [`md2gost/`](../md2gost/). Примеры: [`examples/`](../examples/). +Полная карта репозитория (архитектура, все функции CLI/GUI и модули кода): [`PROJECT.md`](../PROJECT.md). + В GUI: **Справка → Документация** — тот же набор страниц (вшит в exe).