콘텐츠로 이동

좌표 공간 변환: 회전, 크기 조정, 기울이기, 미러링

선택한 피벗을 중심으로 그리기 좌표 공간을 변환합니다. 이 레시피에서는 회전, 크기 조정, 기울이기, 미러링을 다룹니다. 각 변환은 저장된 그래픽 상태 블록 안에 격리되어 이후 콘텐츠에 영향을 주지 않습니다. 이 레시피는 examples/21-transforms.php를 기준으로 합니다.

Terminal window
composer require nextpdf/core:^3

Pro 또는 Enterprise 패키지는 필요하지 않습니다. 변환 API는 Core에 포함되어 있으며 PHP 8.1부터 8.4까지에서 실행됩니다.

PDF 콘텐츠는 사용자 공간에 그려집니다. 기본 사용자 공간의 원점은 페이지 왼쪽 아래에 있으며, 1 단위는 1/72 인치와 같습니다 (ISO 32000-2 §8.3.2). 변환은 cm 연산자를 통해 현재 변환 행렬(CTM)에 새 행렬을 곱해 적용합니다 (§8.3.4). 변환은 행렬 연결을 통해 합성되므로 순서가 중요합니다.

NextPDF는 왼쪽 위 기준의 작성자 좌표계를 제공합니다. 내부적으로는 변환 메서드의 toY() 투영을 통해 이를 네이티브 왼쪽 아래 사용자 공간으로 변환합니다. 위치는 사용자 공간 단위를 사용하며, 이는 PDF 포인트입니다. 여기서 1pt는 1/72 인치와 같습니다. 변환을 로컬 범위로 유지하려면 그리기 작업을 startTransform()stopTransform() 사이에 배치하십시오. 두 메서드는 q(저장) 및 Q(복원) 그래픽 상태 연산자를 내보냅니다 (§8.4.2). 그 사이에 그려진 모든 것은 변환을 상속받습니다. stopTransform() 이후의 모든 것은 이전 CTM으로 돌아갑니다. 각 rotate()/scale()/skewX()/mirrorH() 호출은 명시적 피벗을 받으므로, 변환이 페이지 원점이 아니라 의도한 위치에 고정됩니다.

API 표면은 PHPDoc에서 생성됩니다. 핵심 진입점은 \NextPDF\Core\Concerns\HasTransforms 트레이트가 제공합니다:

  • Document::startTransform(): staticq를 내보내 상태 블록을 엽니다
  • Document::stopTransform(): staticQ를 내보내 블록을 닫습니다
  • 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";

다음은 자체 완결형이며 하니스에서 실행할 수 있는 프로그램입니다. 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() 이후에도 유지됩니다. 이 API 표면에서는 Q가 CTM만 복원하기 때문입니다. 이후 섹션이 이 값들을 상속하지 않아야 한다면, 프로덕션 예제에서처럼 명시적으로 재설정하십시오.

변환은 단일 cm 연산자와 q/Q 쌍으로 구성됩니다. 각 요소는 몇 바이트에 불과하며 측정 가능한 런타임 비용을 추가하지 않으므로, 레시피는 1500ms / 96MB 예산 안에 여유롭게 유지됩니다. 재현성 프로파일은 **구조적(structural)**입니다. 출력에는 실행 간에 안정적이지 않은 트레일러 /ID 배열과 생성 메타데이터가 포함되어 있으므로, 비교하기 전에 이를 정규화해야 합니다. 변환 스트림 자체는 결정적입니다.

  • 데이터 레지던시 및 PII 완화 조치. 해당 없음. 이 레시피는 기하학 기본 도형과 짧은 레이블을 그립니다. 외부 데이터나 개인 데이터를 처리하지 않습니다.
  • 안전한 텔레메트리 및 로그 스크러빙. 이 레시피는 고정된 진행 상황 한 줄만 기록합니다. 문서 콘텐츠는 기록되지 않습니다.
  • 위협 모델. 해당 없음. 입력 파싱, 암호화, 신뢰 경계가 없습니다. 변환은 순수한 콘텐츠 스트림 출력입니다.
  • FIPS 모드 동작. 해당 없음. 암호화 작업이 없습니다.
설명사양조항reference_id
변환은 cm 연산자를 통해 CTM에 행렬을 연결합니다.ISO 32000-2§8.3.4
변환은 행렬 연결을 통해 합성되며, 순서가 중요합니다.ISO 32000-2§8.3.4
q는 그래픽 상태를 저장하고 Q는 복원하여 변환의 범위를 지정합니다.ISO 32000-2§8.4.2
기본 사용자 공간의 원점은 왼쪽 아래이며, 1 단위는 1/72 인치입니다.ISO 32000-2§8.3.2

이 레시피는 인용된 ISO 32000-2의 그래픽 상태 및 변환 조항을 구현합니다. ISO 32000-2 전반에 대한 적합성을 주장하지는 않습니다. 인용된 조항은 이 레시피에서 사용하는 범위만 가리킵니다.