Files
second_constantine 9e81d7a3e0 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/.
2026-08-22 15:12:01 +03:00

127 lines
8.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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` по ширине).
- Цвет чернил по умолчанию — синий. Палитры/градиенты намеренно убраны
(раньше были, но усложняли). Не возвращайте мульти-цвет без явной просьбы.