Skip to content
Back to skills

Md Docx

ASecurity

Собрать .docx из markdown, когда документ пойдет на правки: "сделай docx", "нужен ворд", "отправь на вычитку". Документ на чтение и рассылку - скилл `md-pdf`.

  • 8 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 29, 2026
developmentpythongobash

Works with

  • cli

Security analysis

A100/100

Scanned October 3, 2026

npx -y skills add dewil/claude-toolkit --skill md-docx --agent claude-code

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.

Security grade badge for Md Docx
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/dewil-md-docx/badge)](https://www.skillsdirectory.com/skills/dewil-md-docx)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
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-блоки, ссылки настоящими гиперссылками, **картинки** (`![alt](путь)`) и фото в углу первой страницы (`--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` (презентации).

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.