Собрать .docx из markdown, когда документ пойдет на правки: "сделай docx", "нужен ворд", "отправь на вычитку". Документ на чтение и рассылку - скилл `md-pdf`.
Installs into .claude/skills of the current project.
Are you the author of Md Docx?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/dewil-md-docx)
---
name: md-docx
description: Собрать .docx из markdown, когда документ пойдет на правки: "сделай docx", "нужен ворд", "отправь на вычитку". Документ на чтение и рассылку - скилл `md-pdf`.
---
# md-docx
Конвертация markdown -> `.docx` без внешних зависимостей: `.docx` - это zip с XML, и скрипт собирает его сам через `zipfile`. Скрипт-эталон - `scripts/md-docx.py` (top-level папка `scripts/` в корне проекта). Не нужны python-docx, pandoc и LibreOffice.
Разбор markdown общий с `md-pdf`: `md-docx.py` подтягивает `md_to_html` из соседнего `scripts/md-pdf.py`, поэтому **оба скрипта должны лежать рядом** - без `md-pdf.py` сборка docx остановится с сообщением об этом. Следствие удобное: контракт markdown у обоих форматов один, и правка разбора чинит сразу оба.
## Когда НЕ применять
- **Документ идет на чтение и печать** - причина брать PDF не только в формате: docx выглядит по-разному в разных версиях Word и LibreOffice, а получатель может случайно изменить текст.
- **Нужны колонтитулы с нумерацией страниц** - в docx-ветке их нет, это умеет только PDF.
- **Нужна верстка сложнее нашего контракта** - вложенные списки, выравнивание колонок, сноски, оглавление. Конвертер их не поддерживает намеренно; такому документу нужен не конвертер, а редактор.
- **Исходник не markdown** - скилл собирает документ из md, а не конвертирует произвольные форматы.
## Какой формат когда
| Документ пойдет | Формат | Почему |
| --- | --- | --- |
| на чтение, рассылку, печать | PDF (`md-pdf`) | верстка фиксирована, открывается везде одинаково |
| на правки, вычитку, согласование | docx (`md-docx`) | получатель правит прямо в файле, включает рецензирование, оставляет комментарии |
Спрашивать пользователя, какой формат нужен, не надо, если это ясно из задачи ("на вычитку" -> docx, "отправить клиенту на согласование" -> docx, "распечатать" -> PDF). Неясно - спроси одной строкой.
## CLI
```bash
python3 scripts/md-docx.py тз.md # тз.docx рядом с исходником
python3 scripts/md-docx.py тз.md --out /path/x.docx # свой путь
python3 scripts/md-docx.py тз.md --title "Отчет" # иначе первый H1, иначе имя файла
python3 scripts/md-docx.py тз.md --author "Имя" # иначе DOC_AUTHOR, иначе DOCX_AUTHOR, иначе dwl
```
**Оформление.** Два необязательных флага, симметричных `md-pdf.py` - один markdown собирается в оба формата, поэтому интерфейс общий:
- `--photo <файл>` - фото в правом верхнем углу первой страницы (`--photo-width` 30mm, `--photo-height` 38mm). Кадрируется по рамке, как `object-fit: cover` в PDF-ветке, а не вписывается с искажением пропорций - иначе форматы разъезжаются на глаз.
- `--separators` - горизонтальные разделители: черта под заголовком секции (H2) и тонкая над должностью (H4). По умолчанию выключено: черта под каждым H2 уместна в резюме, но не в ТЗ и не в протоколе встречи.
## Метаданные
Свойства файла проставляются **всегда**, без флагов - по правилу `rules/document-metadata.md`: автор (`--author` -> `DOC_AUTHOR` -> `DOCX_AUTHOR` -> `dwl`), заголовок, текущие даты создания и изменения; `comments`, `keywords`, `category`, `subject` пустые. Следов инструмента сборки в свойствах не остается - именно этот дефект (`author: python-docx`, дата 2013 г.) и породил правило.
Проверка после сборки - часть верификации артефакта:
```bash
python3 - <<'PY'
import zipfile, xml.etree.ElementTree as ET
z = zipfile.ZipFile("тз.docx")
print(z.read("docProps/core.xml").decode()[:400])
W = "{http://schemas.openxmlformats.org/wordprocessingml/2006/main}"
d = ET.fromstring(z.read("word/document.xml"))
print("параграфов:", sum(1 for _ in d.iter(W+"p")), "таблиц:", len(list(d.iter(W+"tbl"))))
PY
```
Открывать Word для проверки не нужно. Проверка грубая, и читать ее надо правильно: если `word/document.xml` парсится и число `w:tbl` совпадает с числом таблиц в исходнике - структура собралась. Точный подсчет `w:p` со своим подсчетом блоков markdown не сверяй: в `w:p` попадают и заголовки, и пункты списков, и параграфы внутри ячеек, и служебный пустой параграф после каждой таблицы, так что число заведомо больше "числа абзацев" в бытовом смысле. Смысл счетчика - поймать явную потерю блока, а не сойтись цифра в цифру.
## Что поддержано и что нет
Блоки кода из трех и более обратных кавычек или тильд используют общий разбор с `md-pdf`: отступ забора до трех пробелов, закрытие тем же символом не короче открывающего и без текста после него. Незакрытый блок идет до конца файла; язык после открывающего забора не меняет оформление. Содержимое выводится буквально абзацами стиля `Code` с моноширинным шрифтом, включая текущий завершающий пустой абзац.
Поддержано: заголовки H1-H4, абзацы с мягким переносом, списки маркированные и нумерованные (настоящие списки Word - получатель дописывает пункт, нумерация продолжается сама; каждый нумерованный список считается с единицы, а не сквозняком через документ), GFM-таблицы с рамками и жирной шапкой, цитаты, `---`, `**bold**`, `*italic*`, `` `code` ``, fenced-блоки, ссылки настоящими гиперссылками, **картинки** (``) и фото в углу первой страницы (`--photo`).
Про картинки: встраиваются локальные файлы PNG, JPEG и GIF; формат определяется по сигнатуре файла, а не по расширению (`.jpg` с PNG внутри - обычное дело после пересохранения, а Word спотыкается на несовпадении). Ширина ужимается до полосы набора. Не встроилось (нет файла, ссылка в сеть, незнакомый формат) - в документ идет `[alt]`: сборка не падает, но получатель видит, что здесь была картинка. Одинаковые файлы кладутся в пакет один раз.
Не поддержано (осознанно):
- **Вложенные списки и выравнивание колонок** - как и в PDF-ветке (контракт разбора общий).
Нужно больше - дорабатывай конвертер, а не меняй формат исходника.
## Грабли
- **Контракт таблицы строгий и общий с `md-pdf`** - нарушишь, и блок молча станет абзацем. Условия целиком перечислены один раз, в `skills/md-pdf/SKILL.md`, раздел "Грабли"; здесь не повторяются, чтобы копия не разошлась с оригиналом.
- **Существующий файл перезаписывается без вопросов** (симметрично `md-pdf.py`). Документ мог быть правлен руками или собран иначе - перед перезаписью сделай копию в scratchpad сам.
- **Word не объясняет, что не так.** Битый XML или ячейка таблицы без параграфа дают одно и то же "содержимое повреждено". Поэтому проверка структуры после сборки (см. выше) - не формальность; регресс-тесты на это в `tests/test_md_docx.py`.
- **Правки исходника после отправки.** Получатель правит уже сам `.docx`, и следующая сборка из markdown его правки затрет. Отдал файл на вычитку - дальше источником истины становится он, а не md; либо переноси правки в markdown руками.
## Специализация под проект
Регулярный документ со своим оформлением (шаблон ТЗ, фирменная шапка, реквизиты) - локальный скилл-обертка или скрипт в проекте поверх канонического. Сам `md-docx` остается generic: фирменный стиль и реквизиты в канон не тащить. Так же, как у `md-pdf`.
Скиллы-соседи по документам: `md-pdf` (PDF), `csv-xlsx` (таблицы Excel), `md-pptx` (презентации).