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/.
171 lines
8.9 KiB
Markdown
171 lines
8.9 KiB
Markdown
# 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`.
|