handwrite.py: рукописный рендер кириллицы синими чернилами

CLI-утилита (Pillow) для превращения текста в картинку рукописного текста. Полная поддержка кириллицы, синие чернила с имитацией письма ручкой (дрейф давления, полупрозрачность, неровный край), корректное выравнивание (б/й/н на baseline, р/у/д с хвостами, точки внизу, спецсимволы), перенос по словам + сохранение переносов строк, тетрадная бумага, --list-fonts. run.sh: команды-обёртки (init/upd/render/lined/big/calm/plain/poetry/render-file/render-stdin/list-fonts/examples). Документация: README.md, AGENTS.md. Примеры в examples/.
This commit is contained in:
2026-08-22 15:12:01 +03:00
commit 9e81d7a3e0
11 changed files with 1122 additions and 0 deletions
+170
View File
@@ -0,0 +1,170 @@
# handwrite.py
Python-скрипт, превращающий текст в картинку рукописного текста с **поддержкой
кириллицы** (полный русский алфавит, включая `Ё`, `щ`, `ъ`, `э`, `ю`, `я`).
Чернила — **синие**, с имитацией письма ручкой.
## Возможности
- 🖋️ **Синие чернила «как рукой»**: имитация письма ручкой — дрейф «давления»
(коррелированные тёмные/светлые участки), полупрозрачные чернила, неровный
край мазка
- ✅ Полная поддержка кириллицы (автоподбор рукописного шрифта с проверкой
наличия всех глифов; приоритет — `Marker Felt`)
- 📐 Корректное выравнивание: обычные буквы (`а`, `о`, `н`, `б`, `й`) на одной
базовой линии, буквы с нижними выносными (`р`, `у`, `д`, `ц`, `щ`) — с
хвостами ниже, точки/запятые — внизу строки, спецсимволы (`$ ~ % @ # & …`) —
по дизайну шрифта
- 📝 Перенос по словам + сохранение `\n` из входного файла/STDIN
- 📓 «Тетрадная» линейная бумага (`--lined`)
- 🎨 Настройка цвета чернил/бумаги/линий, размера шрифта, полей
- 📥 Ввод: аргументом, из файла (`-i`, с переносами строк), через STDIN
- 🔒 Воспроизводимый результат через `--seed`
## Установка
```bash
pip install -r requirements.txt # нужна только Pillow
```
или через обёртку (создаст venv):
```bash
./run.sh init
```
## Быстрый старт
```bash
# текст аргументом
python3 handwrite.py "Привет, мир!" -o hello.png
# тетрадная бумага
python3 handwrite.py "Привет, мир!" -o hello.png --lined
# из файла с переносами строк (каждая строка файла → строка на картинке)
python3 handwrite.py -i text.txt -o letter.png --lined
# через STDIN
echo "Мама мыла раму" | python3 handwrite.py -o out.png
```
## Параметры
| Параметр | По умолчанию | Описание |
|---|---|---|
| `text` | — | Текст для рендера (позиционный). |
| `-i, --input` | — | Файл с текстом (UTF-8); переносы строк сохраняются. `-` = STDIN, иначе при отсутствии текста — STDIN. |
| `-o, --output` | `handwriting.png` | Выходной PNG. |
| `--font` | авто | Путь к шрифту (`path` или `path:index` в `.ttc`). Иначе авто-подбор рукописного с кириллицей. |
| `--font-size` | `44` | Размер шрифта (pt). |
| `--line-spacing` | `1.6` | Множитель межстрочного интервала. |
| `--margin` | `64` | Поля вокруг текста (px). |
| `--chars-per-line` | `48` | Примерная ширина строки в символах для переноса. |
| `--ink` | `#1a3b8c` | Цвет чернил (`#RRGGBB`). По умолчанию — синий. |
| `--paper` | `#ffffff` | Цвет бумаги (`#RRGGBB`). |
| `--lined` | выкл. | Рисовать «тетрадные» линии. |
| `--lined-color` | `#cfe8ff` | Цвет линий бумаги. |
| `--jitter` | `1.0` | Сила «рукописного» дрожания (`0` — ровный текст). |
| `--no-rotate-lines` | выкл. | Отключить лёгкий поворот строк. |
| `--seed` | случайный | Зерно ГПСЧ для воспроизводимости. |
## Примеры использования
```bash
# крупный текст на тёплой бумаге
python3 handwrite.py "Стихи на полях" -o poem.png --font-size 56 --paper "#fdf6e3" --lined
# «спокойный» почерк — меньше колебаний давления, фиксированный seed
python3 handwrite.py "Ровный аккуратный текст" -o neat.png --jitter 0.3 --seed 42
# полностью ровный текст (без дрожания и поворота)
python3 handwrite.py "Как печатный" -o plain.png --jitter 0 --no-rotate-lines
# другой синий оттенок чернил
python3 handwrite.py "Глубокий синий" -o deep.png --ink "#0f2d78"
# широкий формат (длинные строки)
python3 handwrite.py -i long_text.txt -o wide.png --chars-per-line 80 --margin 40
# другой рукописный шрифт (Snell Roundhand)
python3 handwrite.py "Текст" --font "/System/Library/Fonts/Supplemental/SnellRoundhand.ttc:0"
# многострочный файл через STDIN
cat text.txt | python3 handwrite.py -o out.png --lined
# тёмная бумага, светлые чернила (например, для презентации)
python3 handwrite.py "Белым по тёмному" -o dark.png --ink "#cfe8ff" --paper "#1a2238"
```
## Команды run.sh
Удобные обёртки для типовых задач (вывод — в `out/`):
| Команда | Описание |
|---|---|
| `./run.sh init` | Создать venv и установить зависимости |
| `./run.sh upd` | Обновить зависимости |
| `./run.sh render "текст"` | Рендер текста → `out/handwriting.png` |
| `./run.sh lined "текст"` | Рендер на тетрадной бумаге → `out/handwriting.png` |
| `./run.sh big "текст"` | Крупный шрифт (64pt) на тёплой бумаге |
| `./run.sh calm "текст"` | «Спокойный» почерк (jitter 0.3, seed 42) |
| `./run.sh plain "текст"` | Ровный текст (без дрожания/поворота) |
| `./run.sh render-file file` | Рендер файла (с переносами строк) |
| `./run.sh poetry file` | Многострочный файл → тетрадь, крупный шрифт |
| `./run.sh render-stdin` | Чтение из STDIN (`echo "..." \| ./run.sh render-stdin`) |
| `./run.sh list-fonts` | Показать доступные рукописные шрифты с кириллицей |
| `./run.sh examples` | Перегенерировать примеры в `examples/` |
Доп. опции `handwrite.py` передаются после `--` (или просто в конце):
```bash
./run.sh render "Текст" -- --jitter 0.4 --seed 7
./run.sh render-file text.txt --chars-per-line 60
./run.sh lined "Текст" -- --ink "#0f2d78"
```
## Примеры
```
examples/
├── blue_pen.png # синий текст «от руки»
├── blue_pen_lined.png # синий текст на тетради
├── multiline.png # из файла с переносами строк
└── sample_input.txt # пример входного файла
```
Перегенерировать: `./run.sh examples`.
## Подбор шрифта
Скрипт автоматически ищет рукописный шрифт с **полным покрытием кириллицы**:
1. Проверяет приоритетные системные шрифты (`Marker Felt`, затем `Snell Roundhand`).
2. Сканирует системные каталоги, отбирая «рукописные» имена и проверяя все
глифы кириллицы.
3. В крайнем случае берёт любой шрифт с поддержкой кириллицы.
Проверка глифов — без сторонних библиотек (только Pillow): каждый символ
рендерится и сравнивается с формой `notdef`-глифа. Узкие буквы (например «г»)
неотличимы от `notdef` по ширине, поэтому сравнение делается по форме, а не по
ширине.
## Требования
- Python 3.8+
- Pillow (`pip install pillow`)
## Файлы проекта
```
hand-writter/
├── handwrite.py # основной скрипт
├── requirements.txt # Pillow
├── run.sh # команды-обёртки
├── README.md # этот файл
├── AGENTS.md # руководство для агентов/разработчиков
└── examples/ # примеры вывода
```
Подробности о внутреннем устройстве и правилах изменения кода — в `AGENTS.md`.