Cluster · разработчикам

Сложный пример конструктора изображений

Drawing Workshop собирает один макет в трёх вариантах: исходный, изменённая копия и расположение в четыре колонки. В нём 38 слоёв, вложенные группы, сетки, изображения разных форматов и текст с переносами.

Описание пакета задаёт только права на собственные файлы и вызов Image Generator. Доступ к Telegram, сети или чужим файлам этому модулю не нужен. Подробный справочник конструктора находится в Image Generator.

Пакет

В корне архива должны быть:

Изображения учебные, созданы для этого примера. PNG используется в шапке, JPEG показывает разные способы вписывания, WebP содержит прозрачные области. Модуль читает их из собственного пакета, сохраняет временные файлы и загружает их генератору через SDK.

Для своей копии замени ID генератора одновременно в GENERATOR, dependencies и ограничении modules.call. Генератор должен быть установлен и включён. Pillow требуется только ему.

Сцена

Класс Poster задаёт холст 1200 × 940. Он состоит из трёх частей:

  1. Шапка: изображение, полупрозрачное затемнение, значок, заголовок, текст и пометка черновика.
  2. Группа карточек: сетка в две колонки. У каждой карточки своя группа с фоном, изображением, заголовком и описанием.
  3. Нижние подписи: три группы, распределённые в строку.

Сначала grid задаёт размеры групп карточек, затем внутри каждой группы располагаются изображения и текст. Это нужно потому, что сетка изменяет область дочерней группы, но не масштабирует её содержимое.

Координаты сетки могут быть дробными. При отрисовке округляются обе границы области. Соседние полупрозрачные ячейки без зазора не перекрываются из-за округления ширины.

Для текста используются overflow="wrap", явные переносы, ограничение числа строк и автоматическое многоточие. measure получает размеры итоговых строк с теми же шрифтами и правилами, которые применяются при отрисовке.

Изменения

Poster.edit делает независимую копию холста и меняет её:

Poster.compact восстанавливает холст из словаря и перестраивает карточки в четыре колонки. После изменения сетки он отдельно задаёт размеры и положения вложенных элементов. Исходная сцена при этом не меняется.

Действия модуля

compose загружает три изображения и создаёт четыре PNG: before.png, after.png, compact.png и повтор исходной сцены repeat.png. SHA-256 первого и последнего должен совпадать. Три разных макета должны иметь разные хеши.

Ответ содержит список файлов с ID, именами, размерами и SHA-256, число слоёв и результат измерения текста. Файлы принадлежат Drawing Workshop. Через HTTP их может получить овнер с доступом к его файлам. Они хранятся 120 секунд; после скачивания их можно удалить раньше.

reject намеренно передаёт испорченное изображение. Генератор должен вернуть invalid_input, временная загрузка удаляется. Действие возвращает {"rejected": true}. После этого обычная сборка должна продолжать работать.

При ошибке во время создания нескольких PNG модуль удаляет уже созданные результаты. Загруженные изображения и исходные временные файлы освобождаются при выходе из AsyncExitStack, в том числе при исключении.

Для загрузки SDK использует части по 3072 исходных байта: в base64 это 4096 символов, предел строкового поля ядра. Части готового PNG при скачивании могут быть больше, поскольку ответ функции передаётся в другом формате. Полный файл не помещается в результат одного вызова модуля.

Проверка в отдельной среде

tests/test_drawing_workshop.py проверяет обычную сборку и отказ генератора на втором результате. Проверяются содержимое PNG, расположение зазоров, совпадение повторной сборки и очистка файлов. tests/test_drawing.py отдельно проверяет округление дробных сеток, порядок слоёв, вложенность, копирование и переносы текста. Загрузка крупного изображения байтами и через File проверяется в tests/test_image_generator.py.

scripts/probe_drawing.py запускает генератор и Drawing Workshop в gVisor с новым профилем и отдельным хранилищем. Pillow собирается из PyPI через службу сборки Cluster. Рабочие профили и Telegram-сессии не используются, сам модуль не публикуется в маркете.

Нужны настроенные gVisor, Podman, образ Python из конфигурации и возможность создавать тестовое хранилище:

python scripts/probe_drawing.py /tmp/cluster-drawing-results

Сценарий выполняет две полные сборки, проверяет испорченную картинку между ними, сравнивает размеры и хеши PNG и удаляет результаты из тестового хранилища. В указанной папке остаются четыре PNG для просмотра и probe.json с результатами проверки.