Writer: PDF 2.0 직렬화기 + xref
한눈에 보기
섹션 제목: “한눈에 보기”Writer 모듈은 문서를 PDF 바이트로 직렬화합니다. 버전 전략을 선택하고 객체 그래프를 작성하며, 상호 참조 구조와 트레일러를 내보냅니다.
composer require nextpdf/core:^3개념 개요
섹션 제목: “개념 개요”PdfWriter가 진입점입니다. write() 메서드는 DocumentData 값 객체를 받아 완전한 PDF를 바이트 문자열로 반환합니다. 작성기는 객체 그래프를 조립하고 객체 번호를 할당하며, 바이트 오프셋을 기록한 뒤 상호 참조 구조를 마지막에 기록합니다.
작성기는 호출할 때마다 하나의 직렬화 전략을 사용합니다. PdfSerializationStrategy 인터페이스는 writeHeader(), getCatalogVersion(), writeXrefAndTrailer(), usesXrefStream() 네 가지 메서드를 정의합니다. 세 가지 전략이 이 인터페이스를 구현합니다. Pdf20StreamStrategy는 %PDF-2.0 헤더를 기록하고, 카탈로그 버전을 /2.0으로 설정하며, 상호 참조 스트림을 내보냅니다. Pdf17TableStrategy는 %PDF-1.7과 전통적인 상호 참조 테이블을 기록합니다. Pdf14TableStrategy는 %PDF-1.4와 상호 참조 테이블을 기록합니다. PdfWriter는 match를 사용해 DocumentData::$outputProfile에 따라 전략을 선택합니다. 기본값은 Pdf20StreamStrategy입니다.
열거형 PdfOutputProfile은 Pdf20, Pdf17, Pdf14라는 세 가지 대상 버전을 담습니다. 이 열거형은 headerVersion(), catalogVersion(), allowsObjectStreams(), usesXrefStream()을 노출합니다. 보존용 적합성 모드는 전략 선택 전에 선택된 프로파일을 재정의합니다. Pdf14FeatureGuard는 프로파일이 Pdf14일 때 PDF 2.0 기능을 거부합니다.
상호 참조 스트림은 각 객체 번호를 해당 바이트 오프셋에 매핑합니다(ISO 32000-2 §7). 증분 업데이트는 새 객체를 파일 끝에 추가합니다(ISO 32000-2 §7.5.6). 작성기는 모든 리터럴 문자열을 표준 PdfStringEscaper::escapeLiteral() 공통 경로를 통해 이스케이프하며, 이 경로는 ISO 32000-2 §7.3.4.2의 규범적 이스케이프 테이블을 따릅니다(ADR-015).
작성기는 결정론적 출력을 지원합니다. setDeterministicMode()는 객체 식별자와 딕셔너리 키 순서를 고정합니다. setReproducibleClock()는 문서 타임스탬프를 고정합니다. 두 설정을 모두 고정하면 같은 입력에서 바이트가 동일한 출력이 생성됩니다. writeChunked() 메서드는 PDF를 고정 크기 청크로 산출하는 제너레이터를 반환합니다. Streaming/StreamingPdfWriter는 메모리 예산을 초과하는 문서를 호출자가 제공한 스트림에 한 번에 한 페이지씩 기록합니다.
Linearizer는 완성된 PDF를 선형화된 레이아웃으로 다시 작성합니다. 첫 페이지를 앞쪽에 배치해 전체 다운로드가 완료되기 전에 뷰어가 해당 페이지를 표시할 수 있게 합니다. shadowValidate()는 입력을 변경하지 않고 재작성 결과를 검사합니다.
주의.
PdfWriter.php와Linearizer.php는 바이트 오프셋 및 객체 그래프에 큰 영향을 미칩니다(매니페스트 위험 구역). Writer 골든 스위트 없이 객체 번호 매기기 또는 xref 오프셋 산술을 변경하지 마십시오.
API 표면
섹션 제목: “API 표면”| 클래스 | 주요 메서드 | 역할 |
|---|---|---|
PdfWriter | write(DocumentData): string, writeChunked(DocumentData, int): Generator, setDeterministicMode(), setReproducibleClock(), setOutputColorProfile(), getLastXrefOffset(), getFileId() | 기본 직렬화기 |
PdfSerializationStrategy (인터페이스) | writeHeader(), getCatalogVersion(), writeXrefAndTrailer(), usesXrefStream() | 버전 전략 계약 |
Pdf20StreamStrategy | writeHeader() → %PDF-2.0, getCatalogVersion() → /2.0, usesXrefStream() → true | PDF 2.0 xref 스트림 전략 |
Pdf17TableStrategy | writeHeader() → %PDF-1.7, xref 테이블 | PDF 1.7 xref 테이블 전략 |
Pdf14TableStrategy | writeHeader() → %PDF-1.4, xref 테이블 | PDF 1.4 xref 테이블 전략 |
PdfOutputProfile (열거형) | Pdf20, Pdf17, Pdf14; headerVersion(), catalogVersion(), allowsObjectStreams() | 대상 버전 선택기 |
PdfXrefWriter | generateFileId(), finalizeTrailerAndXref() | File ID + trailer/xref 마무리 |
Linearizer | linearize(string): string, shadowValidate(string): array | 빠른 웹 보기 재작성 |
Streaming\StreamingPdfWriter | open(), newPage(), close() | 단일 패스 스트리밍 작성기 |
전체 PHPDoc 테이블은 composer docs:generate-api-php -- --module=Writer를 실행해 확인하십시오.
코드 샘플 — 빠른 시작
섹션 제목: “코드 샘플 — 빠른 시작”소스: examples/02-pdf-factory.php.
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Writer\PdfWriter;
$writer = new PdfWriter();$pdfBytes = $writer->write($documentData);
file_put_contents('out.pdf', $pdfBytes);기본 프로파일은 PDF 2.0입니다. 출력은 %PDF-2.0으로 시작하고 상호 참조 스트림으로 끝납니다.
코드 샘플 — 프로덕션
섹션 제목: “코드 샘플 — 프로덕션”이 예제는 바이트가 동일한 출력을 위해 결정론적 동작과 클록을 고정한 다음, 고정 크기 청크로 스트리밍합니다.
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use DateTimeImmutable;use NextPDF\Writer\PdfWriter;use NextPDF\Writer\ReproducibleClock;
$pinned = new DateTimeImmutable('2026-01-01T00:00:00Z');
$writer = new PdfWriter();$writer->setDeterministicMode($pinned, 'nextpdf-fixed-file-id');$writer->setReproducibleClock(new ReproducibleClock($pinned));
$out = fopen('php://output', 'wb');foreach ($writer->writeChunked($documentData, chunkSize: 65536) as $chunk) { fwrite($out, $chunk);}fclose($out);예외 사례 및 주의점
섹션 제목: “예외 사례 및 주의점”- 각
write()호출에는 하나의 전략만 실행됩니다. 작성기는 호출할 때마다 프로파일에서 전략을 다시 설정합니다. 이전 호출의 버전은 누출되지 않습니다. - 보존용 적합성 모드는 요청된 프로파일을 재정의합니다. PDF/A-3 빌드는 PDF 1.7을 강제합니다. PDF/A-4 빌드는 PDF 2.0을 강제합니다.
- 바이트가 동일한 출력에는 두 가지 고정이 모두 필요합니다. 결정론적 모드 및 재현 가능한 클록을 설정하십시오. 한쪽만 고정해서는 충분하지 않습니다.
writeChunked()는 제너레이터를 반환합니다. 끝까지 소비하십시오. 일부만 읽으면 잘린 무효한 PDF가 생성됩니다.Linearizer는 상호 참조 오프셋을 다시 작성합니다. 재작성 실패를 허용할 수 없는 파이프라인에서는 먼저shadowValidate()를 실행하십시오.Pdf14TableStrategy는final readonly입니다. PDF 1.4 경로는 PDF 2.0 기능을 낮춰 처리하지 않고Pdf14FeatureGuard를 통해 거부합니다.
직렬화는 객체 수와 총 바이트 크기에 선형적입니다. 상호 참조 스트림은 객체 테이블을 한 번 더 통과합니다. writeChunked()는 조립된 문서를 보유하되 제한된 조각으로 산출하므로, 최대 메모리는 문서 크기에 청크 하나를 더한 값입니다. Streaming\StreamingPdfWriter는 전체 문서를 보유하지 않으므로 메모리 예산보다 큰 입력에 쓰는 경로입니다. 기준 워크로드 예산은 벽시계 시간 1500 ms와 최대 64 MB입니다. 선형화는 두 번째 전체 패스와 측정 패스를 추가합니다. 이를 명시적으로 예산에 반영하십시오.
보안 참고 사항
섹션 제목: “보안 참고 사항”작성기는 신뢰할 수 있는 인메모리 객체 그래프를 직렬화합니다. 주요 위협은 입력에 있습니다. 모든 리터럴 문자열은 표준 PdfStringEscaper::escapeLiteral()(ADR-015)를 거치므로, 내장된 제어 바이트가 문자열 토큰 밖으로 벗어날 수 없습니다. 암호화는 PdfEncryptionWriter와 /Encrypt 트레일러 항목을 통해 연결됩니다. 공개 키 암호화는 자동으로 다운그레이드되지 않으며, 명시적 예외로 거부됩니다. 결정론적 모드와 재현 가능한 클록 모드는 출력에서 타임스탬프와 순서 기반 부채널을 제거합니다. 문서 위협 모델과 암호화 신뢰 경계는 /modules/core/security/를 참조하십시오.
적합성
섹션 제목: “적합성”Writer는 PDF 2.0 파일 구조를 생성합니다. 여기에는 %PDF-2.0 헤더, /2.0 카탈로그 버전, 상호 참조 스트림, 그리고 ISO 32000-2 §7.3.4.2 이스케이프 테이블에 따른 리터럴 문자열 이스케이프가 포함됩니다. 이는 구현 사실입니다. 근거는 src/Writer/Pdf20StreamStrategy.php, src/Writer/PdfSerializationStrategy.php, 그리고 src/Writer/PdfWriter.php의 전략 선택이며, 이는 tests/Unit/Writer/(Pdf20StreamStrategy, PdfXrefWriter, Linearizer* 스위트를 포함한 192개 테스트)와 tests/Golden/PdfWriter/PdfWriterGoldenBaselineSmokeTest 기준선으로 검증됩니다.
이는 완전한 PDF 2.0 적합성을 주장하는 것이 아닙니다. 완전한 ISO 32000-2 적합성은 직렬화기만의 속성이 아니라, 외부 오라클로 검증된 완전한 문서의 속성입니다. 엔드 투 엔드 적합성은 오라클이 확인한 경우에만 단언됩니다. tests/Integration/Accessibility/VeraPdfUa2GoldenTest는 생성된 픽스처를 PDF/UA-2에 대해 veraPDF로 검증하고, tests/Standards/Profile/PdfRConformanceTest는 PDF/R 프로파일을 다룹니다. veraPDF 골든 테스트는 러너에 veraPDF 바이너리가 없을 때 건너뛰므로, 무조건적인 게이트가 아니라 선택적 오라클 게이트입니다. 실행하려면 VERAPDF_BINARY를 설정하십시오. 보존용 프로파일 선택(PDF/A-3 → PDF 1.7, PDF/A-4 → PDF 2.0)은 ADR-011과 적합성 모드에 의해 결정되며, /modules/core/conformance/의 적합성 스위트로 검증됩니다. 이러한 오라클 기반 프로파일 외부에서는 무조건적인 적합성을 단언하지 말고, Writer가 “PDF 2.0 구조를 생성하며, 적합성은 PDF/UA-2 프로파일에 대해 veraPDF로 검증된다”고 명시하십시오.