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:
@@ -0,0 +1,126 @@
|
||||
# AGENTS.md — hand-writter
|
||||
|
||||
Руководство для агентов (и людей), работающих с репозиторием `hand-writter`.
|
||||
|
||||
## О проекте
|
||||
|
||||
`handwrite.py` — CLI-утилита на Python, превращающая текст в картинку рукописного
|
||||
текста с **поддержкой кириллицы** (полный русский алфавит, включая `Ё`, `щ`, `ъ`,
|
||||
`э`, `ю`, `я`). Единственная зависимость — **Pillow** (`PIL`).
|
||||
|
||||
Ключевые особенности:
|
||||
- Автоподбор рукописного шрифта из системы с проверкой покрытия кириллицы
|
||||
(без сторонних библиотек — только Pillow: рендер символа и сравнение с
|
||||
`notdef`-глифом). Приоритет: `Marker Felt`, затем `Snell Roundhand`, затем
|
||||
любой шрифт с кириллицей.
|
||||
- Синие чернила по умолчанию (`#1a3b8c`) с имитацией письма ручкой: коррелированный
|
||||
дрейф «давления» (тёмные/светлые участки), полупрозрачные чернила, неровный край мазка.
|
||||
- Выравнивание глифов — стандартное PIL ``text((x, line_top))`` (через маску в
|
||||
``_stamp_glyph``). Все обычные буквы (а, о, н, б, й) стоят на одной baseline,
|
||||
буквы с нижними выносными (р, у, д, ц, щ) опускают хвосты ниже — строчная
|
||||
«р» ниже обычных букв, точки/запятые внизу, спецсимволы ($ ~ % @ …) — по
|
||||
дизайну шрифта. Не вводите ручное смещение «по ink_bottom» — оно ломает
|
||||
б/й/н и обрезает глифы.
|
||||
- Перенос по словам + сохранение `\n` из входного файла/STDIN.
|
||||
- Тетрадная «линейная» бумага (`--lined`), цвета чернил/бумаги/линий,
|
||||
синие палитры и градиенты оттенков.
|
||||
|
||||
## Структура
|
||||
|
||||
```
|
||||
hand-writter/
|
||||
├── handwrite.py # основной скрипт (точка входа)
|
||||
├── requirements.txt # Pillow>=9.0.0
|
||||
├── run.sh # обёртка-команды: init/upd/render/...
|
||||
├── README.md # пользовательская документация
|
||||
├── AGENTS.md # этот файл
|
||||
└── examples/ # примеры вывода
|
||||
├── blue_pen.png
|
||||
├── blue_pen_lined.png
|
||||
├── multiline.png
|
||||
└── sample_input.txt
|
||||
```
|
||||
|
||||
## Окружение и запуск
|
||||
|
||||
- Python 3.8+ (в `run.sh` предполагается `python3.13`; если его нет —
|
||||
отредактируйте `PY` в `run.sh` или используйте системный `python3`).
|
||||
- Активация venv: `source .venv/bin/activate` (через `./run.sh init`).
|
||||
- Прямой запуск: `python3 handwrite.py "Текст" -o out.png`.
|
||||
- Команды-обёртки: `./run.sh render "Текст"` и др. (см. ниже).
|
||||
|
||||
## Правила работы с кодом
|
||||
|
||||
1. **Не добавляйте новых зависимостей** без крайней необходимости. Скрипт
|
||||
намеренно зависит только от Pillow — это упрощает установку в
|
||||
изолированных средах. Любая новая зависимость должна быть обоснована
|
||||
и добавлена в `requirements.txt`.
|
||||
|
||||
2. **Сохраняйте поддержку кириллицы.** Любое изменение рендера/шрифтов
|
||||
должно проверяться на полном русском алфавите (см. `CYRILLIC_TEST` в
|
||||
`handwrite.py`). Не полагайтесь на «визуальную» проверку — шрифт может
|
||||
отрисовывать `notdef` для отсутствующих глифов; используйте
|
||||
`_font_covers_cyrillic()`.
|
||||
|
||||
3. **Выравнивание — стандартное PIL.** В ``_stamp_glyph`` глиф рисуется маской
|
||||
с origin в канвасе и композируется так, чтобы совпадать с ``text((x, line_top))``.
|
||||
НЕ прижимайте ink_bottom вручную к baseline и НЕ сдвигайте глифы по bbox —
|
||||
это ломает б/й (обрезает) и делает их «слишком низкими». Канвас берётся по
|
||||
абсолютным границам bbox (включая отрицательные bbox[0]/bbox[1] для Ё, р).
|
||||
Знаки препинания рисуются с jitter=0 (чёткие точки), но тем же выравниванием.
|
||||
|
||||
4. **Сохраняйте переносы строк.** `wrap_text` использует `splitlines()` —
|
||||
каждая строка входа становится отдельной строкой на картинке. Не
|
||||
«схлоптывайте» пустые строки без явного флага.
|
||||
|
||||
5. **Не ломайте CLI-совместимость.** Новые параметры добавляйте с значениями
|
||||
по умолчанию, не меняйте семантику существующих.
|
||||
|
||||
6. **Внесение изменений:** после правок проверяйте
|
||||
- `python3 -c "import ast; ast.parse(open('handwrite.py').read())"` — синтаксис;
|
||||
- запуск на кириллице с точками/запятыми и многострочным файлом;
|
||||
- что точки-знаки остаются в нижней зоне строки.
|
||||
|
||||
## Команды (run.sh)
|
||||
|
||||
| Команда | Описание |
|
||||
|---|---|
|
||||
| `./run.sh init` | Создать venv и установить зависимости |
|
||||
| `./run.sh upd` | Обновить зависимости |
|
||||
| `./run.sh render "текст"` | Рендер текста-аргумента → `out/handwriting.png` |
|
||||
| `./run.sh lined "текст"` | Рендер на тетрадной бумаге |
|
||||
| `./run.sh big "текст"` | Крупный шрифт (64pt) на тёплой бумаге |
|
||||
| `./run.sh calm "текст"` | «Спокойный» почерк (jitter 0.3, seed 42) |
|
||||
| `./run.sh plain "текст"` | Ровный текст (без дрожания/поворота строк) |
|
||||
| `./run.sh render-file file.txt` | Рендер файла (с переносами строк) |
|
||||
| `./run.sh poetry file.txt` | Многострочный файл → тетрадь, крупный шрифт → `out/poetry.png` |
|
||||
| `./run.sh render-stdin` | Чтение из STDIN (echo "..." \| ./run.sh render-stdin) |
|
||||
| `./run.sh list-fonts` | Показать доступные рукописные шрифты с кириллицей |
|
||||
| `./run.sh examples` | Перегенерировать примеры в `examples/` |
|
||||
|
||||
Все команды рендера (`render`, `lined`, `big`, `calm`, `plain`, `poetry`,
|
||||
`render-file`, `render-stdin`) принимают доп. опции `handwrite.py` после `--`,
|
||||
например: `./run.sh render "Текст" -- --jitter 0.4 --seed 7 --lined`.
|
||||
|
||||
## Тестовые сценарии (обязательно прогонять при правках)
|
||||
|
||||
```bash
|
||||
# кириллица с б/й/р, точками и спецсимволами (б/й на baseline, р ниже, точки внизу)
|
||||
python3 handwrite.py "байты йогурт река. \$ ~ % @ # конец!" -o /tmp/t1.png
|
||||
|
||||
# многострочный файл (каждая строка файла → строка на картинке)
|
||||
printf 'Строка 1.\nСтрока 2.\nСтрока 3.\n' > /tmp/m.txt
|
||||
python3 handwrite.py -i /tmp/m.txt -o /tmp/t2.png --lined
|
||||
|
||||
# синий (по умолчанию) + тетрадь
|
||||
python3 handwrite.py "Текст" --lined -o /tmp/t3.png
|
||||
```
|
||||
|
||||
## Чего не делать
|
||||
|
||||
- Не коммитьте `.DS_Store`, `.venv/`, временные `*.png` вне `examples/`.
|
||||
- Не удаляйте `examples/` — это демонстрация возможностей.
|
||||
- Не «упрощайте» проверку глифов кириллицы до `textbbox`/ширины — это
|
||||
ненадёжно (узкие буквы, например «г», неотличимы от `notdef` по ширине).
|
||||
- Цвет чернил по умолчанию — синий. Палитры/градиенты намеренно убраны
|
||||
(раньше были, но усложняли). Не возвращайте мульти-цвет без явной просьбы.
|
||||
Reference in New Issue
Block a user