Преобразование координатного пространства: поворот, масштабирование, наклон и зеркальное отражение
Преобразуйте координатное пространство рисунка вокруг выбранной точки. В этом рецепте рассматриваются поворот, масштабирование, наклон и зеркальное отражение. Каждое преобразование изолировано в блоке с сохранённым графическим состоянием, поэтому не влияет на последующее содержимое. Рецепт основан на examples/21-transforms.php.
Установка
Заголовок раздела «Установка»composer require nextpdf/core:^3Пакет Pro или Enterprise не требуется. API преобразований входит в Core и работает на PHP 8.1—8.4.
Концептуальный обзор
Заголовок раздела «Концептуальный обзор»Содержимое Portable Document Format (PDF) выводится в пользовательском пространстве. По умолчанию начало координат пользовательского пространства находится в левом нижнем углу страницы, а одна единица равна 1/72 дюйма (ISO 32000-2 §8.3.2). Преобразование умножает текущую матрицу преобразования (CTM) на новую матрицу с помощью оператора cm (§8.3.4). Преобразования объединяются конкатенацией матриц, поэтому порядок важен.
NextPDF позволяет работать в авторской системе координат с началом в левом верхнем углу. Внутри она преобразуется в собственное пользовательское пространство с началом в левом нижнем углу через проекцию toY() в методах преобразования. Позиции задаются в единицах пользовательского пространства — пунктах PDF, где 1 pt равен 1/72 дюйма. Чтобы преобразование оставалось локальным, заключите его между startTransform() и stopTransform(). Эти методы выводят операторы графического состояния q (сохранение) и Q (восстановление) (§8.4.2). Всё, что нарисовано между ними, наследует преобразование. Всё, что следует после stopTransform(), возвращается к прежней CTM. Каждый вызов rotate()/scale()/skewX()/mirrorH() принимает явную точку, поэтому преобразование привязывается к ожидаемому месту, а не к началу координат страницы.
Поверхность API
Заголовок раздела «Поверхность API»Поверхность API формируется из PHPDoc. Основные точки входа предоставляет трейт \NextPDF\Core\Concerns\HasTransforms:
Document::startTransform(): static— выводитqи открывает блок состоянияDocument::stopTransform(): static— выводитQи закрывает блокDocument::rotate(float $angle, float $x = 0, float $y = 0): staticDocument::scale(float $sx, float $sy, float $x = 0, float $y = 0): staticDocument::skewX(float $angle, float $x = 0, float $y = 0): static/skewY(...)Document::mirrorH(float $x = 0): static/mirrorV(float $y = 0): staticDocument::translateCtm(float $dx, float $dy): static
Пример кода — быстрый старт
Заголовок раздела «Пример кода — быстрый старт»<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();$doc->setTitle('Coordinate Transforms');$doc->addPage();
$cx = 60.0;$cy = 60.0;
// Rotate 30° around (cx, cy). The transform is scoped to this block.$doc->startTransform();$doc->rotate(30, $cx, $cy);$doc->setFont('helvetica', '', 14);$doc->text($cx, $cy, 'Rotated 30 degrees');$doc->stopTransform();
// Back to the untransformed CTM — this text is upright.$doc->setFont('helvetica', '', 10);$doc->text($cx, $cy + 20, 'Not rotated');
$doc->save(__DIR__ . '/transforms.pdf');
echo "Created: transforms.pdf\n";Пример кода — производственный
Заголовок раздела «Пример кода — производственный»Эта самодостаточная программа запускается в тестовом окружении cookbook. Она воспроизводит раздел масштабирования из examples/21-transforms.php. Каждое преобразование остаётся в блоке с сохранённым графическим состоянием и явно заданной точкой. В конце состояние цвета и линий сбрасывается, поэтому ничто не переносится на следующую страницу.
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();$doc->setTitle('Coordinate Transforms');$doc->addPage();
$doc->setFont('helvetica', 'B', 13);$doc->cell(0, 8, 'Scaling a reference square at 0.5x, 1.0x, 1.5x, 2.0x', newLine: true);$doc->ln(6);
$scaleBaseY = $doc->getY();$scaleFactors = [0.5, 1.0, 1.5, 2.0];
$doc->setDrawColor(30, 58, 138);$doc->setLineWidth(0.4);
foreach ($scaleFactors as $idx => $factor) { $cx = 25.0 + $idx * 45; $cy = $scaleBaseY + 5;
$doc->startTransform(); $doc->scale($factor, $factor, $cx, $cy); // scale about (cx, cy)
$doc->setFillColor(220, 230, 241); $doc->rect($cx, $cy, 15, 15, 'DF'); $doc->line($cx, $cy, $cx + 15, $cy + 15);
$doc->stopTransform(); // CTM restored here
// Drawn AFTER the block — at the original scale, untransformed. $doc->setFont('helvetica', '', 8); $doc->setTextColor(0); $doc->text($cx, $scaleBaseY + 38, sprintf('%.1fx', $factor));}
// Explicit state reset so nothing carries into the next section.$doc->setTextColor(0);$doc->setFillColor(255);$doc->setDrawColor(0);
// The harness sets NEXTPDF_COOKBOOK_OUTPUT and runs this script twice under// the structural profile (the transform stream itself is deterministic).$out = getenv('NEXTPDF_COOKBOOK_OUTPUT');$doc->save($out !== false && $out !== '' ? $out : __DIR__ . '/transforms.pdf');
echo "Wrote transforms.pdf\n";Ожидаемый STDOUT:
Wrote transforms.pdfПолный пример охватывает все четыре семейства преобразований: поворот, масштабирование, наклон и зеркальное отражение. Запустите его командой php examples/21-transforms.php; она создаст examples/output/21-transforms.pdf.
Граничные случаи и подводные камни
Заголовок раздела «Граничные случаи и подводные камни»- Всегда закрывайте блок парой. Каждому
startTransform()должен соответствовать парныйstopTransform(). Несбалансированное количествоq/Qповреждает графическое состояние до конца страницы (ISO 32000-2 §8.4.2). NextPDF отслеживает глубину вложенности, но в рецепте контракт остаётся один к одному. - Порядок не коммутативен. Преобразования объединяются конкатенацией матриц, поэтому
rotate(), за которым следуетscale(), не то же самое, чтоscale(), за которым следуетrotate(). Применяйте их внутри одного блока в нужном порядке. - По умолчанию точка совпадает с началом координат. Если опустить точку, преобразование выполняется вокруг начала координат страницы, а не фигуры. Обычно это не то, что нужно, поэтому передавайте точку явно.
- Ось Y задаётся в авторском пространстве. Координата
yточки — это расстояние от левого верхнего угла авторской системы, и NextPDF проецирует её в собственное пользовательское пространство. Если смешивать необработанные координаты PDF с авторским API, результат получится зеркально отражённым. - Утечка состояния. Цвет, шрифт и толщина линии, заданные внутри блока преобразования, сохраняются после
stopTransform(), поскольку в этой поверхности APIQвосстанавливает только CTM. Сбрасывайте эти значения явно, если следующий раздел не должен их наследовать, как в производственном примере.
Производительность
Заголовок раздела «Производительность»Преобразование выводит один оператор cm и пару q/Q. Каждая часть занимает всего несколько байтов и не добавляет заметных затрат во время выполнения, поэтому рецепт укладывается в бюджет 1500 мс / 96 МБ. Профиль воспроизводимости — структурный. Вывод содержит массив /ID в трейлере и метаданные создания, которые меняются между запусками, поэтому перед сравнением их необходимо нормализовать. Сам поток преобразования детерминирован.
Замечания по безопасности
Заголовок раздела «Замечания по безопасности»- Локализация данных и меры по защите персонально идентифицируемой информации (PII). Неприменимо. Этот рецепт рисует геометрические примитивы и короткие подписи. Он не обрабатывает внешние или персональные данные.
- Безопасная телеметрия и очистка журналов. Рецепт записывает одну фиксированную строку прогресса. Он не записывает в журнал содержимое документа.
- Модель угроз. Неприменимо. Здесь нет разбора входных данных, криптографии и границы доверия. Преобразование — это чистый вывод в поток содержимого.
- Поведение в режиме Federal Information Processing Standards (FIPS). Неприменимо. Криптографических операций нет.
Соответствие
Заголовок раздела «Соответствие»| Утверждение | Спецификация | Пункт | reference_id (идентификатор ссылки) |
|---|---|---|---|
Преобразование присоединяет матрицу к CTM с помощью оператора cm. | ISO 32000-2 | §8.3.4 | |
| Преобразования объединяются конкатенацией матриц, и порядок важен. | ISO 32000-2 | §8.3.4 | |
q сохраняет, а Q восстанавливает графическое состояние, что ограничивает область действия преобразования. | ISO 32000-2 | §8.4.2 | |
| По умолчанию начало координат пользовательского пространства находится в левом нижнем углу; одна единица равна 1/72 дюйма. | ISO 32000-2 | §8.3.2 |
Этот рецепт следует процитированным пунктам ISO 32000-2 о графическом состоянии и преобразованиях. Он не заявляет полного соответствия ISO 32000-2; процитированные пункты — единственные, которые задействует этот рецепт.