Przejdź do głównej zawartości

Przekształcanie przestrzeni współrzędnych: obrót, skalowanie, pochylenie i odbicie lustrzane

Przekształcaj przestrzeń współrzędnych rysunku wokół wybranego punktu obrotu. Ten przepis obejmuje obrót, skalowanie, pochylenie i odbicie lustrzane. Każde przekształcenie pozostaje izolowane w bloku zapisanego stanu graficznego, więc nie wpływa na dalszą treść. Przepis opiera się na examples/21-transforms.php.

Okno terminala
composer require nextpdf/core:^3

Pakiety Pro ani Enterprise nie są potrzebne. API przekształceń jest dostarczane razem z Core i działa z PHP od 8.1 do 8.4.

Treść formatu PDF (Portable Document Format) jest rysowana w przestrzeni użytkownika. Domyślnie początek przestrzeni użytkownika znajduje się w lewym dolnym rogu strony, a jedna jednostka odpowiada 1/72 cala (ISO 32000-2 §8.3.2). Przekształcenie mnoży bieżącą macierz przekształcenia (CTM) przez nową macierz za pomocą operatora cm (§8.3.4). Przekształcenia składają się przez konkatenację macierzy, więc kolejność ma znaczenie.

NextPDF pozwala pracować w autorskim układzie współrzędnych z początkiem w lewym górnym rogu. Wewnętrznie przekształca ten układ na natywną przestrzeń użytkownika z początkiem w lewym dolnym rogu za pomocą rzutowania toY() w metodach przekształceń. Pozycje są podawane w jednostkach przestrzeni użytkownika: punktach PDF, gdzie 1 pt odpowiada 1/72 cala. Aby przekształcenie pozostało lokalne, umieść je pomiędzy startTransform() a stopTransform(). Metody te emitują operatory stanu graficznego q (zapis) oraz Q (przywrócenie) (§8.4.2). Wszystko, co zostanie narysowane pomiędzy nimi, dziedziczy to przekształcenie. Po stopTransform() wszystko wraca do poprzedniej macierzy CTM. Każde wywołanie rotate()/scale()/skewX()/mirrorH() przyjmuje jawny punkt obrotu, więc przekształcenie zostaje zakotwiczone w oczekiwanym miejscu, a nie w początku strony.

Powierzchnia API jest generowana z PHPDoc. Główne punkty wejścia udostępnia cecha (trait) \NextPDF\Core\Concerns\HasTransforms:

  • Document::startTransform(): static — emituje q i otwiera blok stanu
  • Document::stopTransform(): static — emituje Q i zamyka blok
  • Document::rotate(float $angle, float $x = 0, float $y = 0): static
  • Document::scale(float $sx, float $sy, float $x = 0, float $y = 0): static
  • Document::skewX(float $angle, float $x = 0, float $y = 0): static / skewY(...)
  • Document::mirrorH(float $x = 0): static / mirrorV(float $y = 0): static
  • Document::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";

Ten samodzielny program działa w środowisku testowym książki kucharskiej. Odzwierciedla sekcję skalowania z examples/21-transforms.php. Każde przekształcenie pozostaje w bloku zapisanego stanu graficznego z jawnym punktem obrotu. Ustawienia koloru i linii są resetowane na końcu, więc nic nie przenika do dalszej części strony.

<?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";

Oczekiwany STDOUT:

Wrote transforms.pdf

Pełny przykład obejmuje wszystkie cztery rodziny przekształceń: obrót, skalowanie, pochylenie i odbicie lustrzane. Uruchom go za pomocą php examples/21-transforms.php; zapisze examples/output/21-transforms.pdf.

  • Zawsze paruj blok. Każde startTransform() musi mieć odpowiadające mu stopTransform(). Niezrównoważona liczba q/Q uszkadza stan graficzny przez resztę strony (ISO 32000-2 §8.4.2). NextPDF śledzi głębokość, ale na poziomie przepisu obowiązuje kontrakt jeden do jednego.
  • Kolejność nie jest przemienna. Przekształcenia składają się przez konkatenację macierzy, więc rotate(), a następnie scale() to nie to samo, co scale(), a następnie rotate(). Zastosuj je wewnątrz jednego bloku w zamierzonej kolejności.
  • Punkt obrotu domyślnie znajduje się w początku układu. Jeśli pominiesz punkt obrotu, przekształcenie obraca się wokół początku strony, a nie wokół kształtu. Zwykle nie o to chodzi, dlatego przekazuj punkt obrotu jawnie.
  • Oś Y należy do przestrzeni autora. Punkt obrotu y oznacza odległość od lewego górnego rogu w przestrzeni autora, a NextPDF rzutuje ją na natywną przestrzeń użytkownika. Mieszanie surowych współrzędnych PDF z autorskim API daje wynik odbity lustrzanie.
  • Wyciek stanu. Kolor, czcionka i grubość linii ustawione wewnątrz bloku przekształcenia utrzymują się po stopTransform(), ponieważ w tej powierzchni API Q przywraca jedynie macierz CTM. Zresetuj te wartości jawnie, jeśli dalsza sekcja nie może ich odziedziczyć, tak jak robi to przykład produkcyjny.

Przekształcenie emituje jeden operator cm oraz parę q/Q. Każdy z tych elementów zajmuje tylko kilka bajtów i nie dodaje mierzalnego kosztu wykonania, więc przepis mieści się w budżecie 1500 ms / 96 MB. Profil odtwarzalności jest strukturalny. Dane wyjściowe zawierają tablicę /ID w trailerze oraz metadane utworzenia, które nie są stabilne między uruchomieniami, dlatego musisz je znormalizować przed porównaniem. Sam strumień przekształcenia jest deterministyczny.

  • Lokalizacja danych i środki ograniczające ryzyko dla danych osobowych (PII). Nie dotyczy. Ten przepis rysuje prymitywy geometryczne i krótkie etykiety. Nie przetwarza żadnych danych zewnętrznych ani osobowych.
  • Bezpieczna telemetria i czyszczenie dzienników. Przepis zapisuje jeden stały wiersz postępu. Nie rejestruje żadnej treści dokumentu.
  • Model zagrożeń. Nie dotyczy. Nie ma parsowania danych wejściowych, kryptografii ani granicy zaufania. Przekształcenie to czysta emisja strumienia treści.
  • Zachowanie w trybie Federal Information Processing Standards (FIPS). Nie dotyczy. Nie są wykonywane żadne operacje kryptograficzne.
StwierdzenieSpecyfikacjaKlauzulareference_id
Przekształcenie łączy macierz z macierzą CTM za pomocą operatora cm.ISO 32000-2§8.3.4
Przekształcenia składają się poprzez konkatenację macierzy, a kolejność ma znaczenie.ISO 32000-2§8.3.4
q zapisuje, a Q przywraca stan graficzny, co ogranicza zasięg przekształcenia.ISO 32000-2§8.4.2
Domyślny początek przestrzeni użytkownika to lewy dolny róg; jedna jednostka to 1/72 cala.ISO 32000-2§8.3.2

Ten przepis jest zgodny z przywołanymi klauzulami ISO 32000-2 dotyczącymi stanu graficznego i przekształceń. Nie deklaruje całkowitej zgodności z ISO 32000-2; przywołane klauzule są jedynymi, które ten przepis wykorzystuje.