Pro 에디션
Writer — 심층 참조
한눈에 보기
섹션 제목: “한눈에 보기”Writer 모듈은 PDF 증분 업데이트(incremental-update) 리비전을 작성하고 작은 객체를 Object Stream으로 패킹합니다. 증분 writer는 실패 시 닫힘(fail-closed) 추가 전용(append-only) 규칙을 강제합니다. 즉, 버퍼가 리비전 이전에 보유했던 모든 바이트는 리비전 이후에도 변경되지 않은 채로 남아야 합니다. Object Stream 빌더는 적격 객체들을 제한된 크기 이내의 하나의 FlateDecode로 압축된 /Type /ObjStm 객체로 그룹화합니다.
가용성 및 라이선싱
섹션 제목: “가용성 및 라이선싱”이 기능은 NextPDF Pro(nextpdf/pro)로 제공되며 Pro 등급 라이선스 봉투(license envelope)로 활성화됩니다. 해당 자격이 없는 배포는 이 기능의 클래스를 로드하지 않습니다. 에디션을 비교하고 라이선스를 받으십시오. 기능별 라이선스 플래그는 없으며, 코드는 Pro 에디션과 함께 제공됩니다.
공개 API 표면
섹션 제목: “공개 API 표면”이 모듈은 NextPDF\Pro\Writer 네임스페이스 아래에 있습니다. 모든 공개 심볼은 아래에 나열되어 있습니다. 값 객체는 불변(immutable) final readonly 클래스입니다.
| 심볼 | 매개변수 | 기본 동작 | 반환 | 예외 또는 실패 | 비고 |
|---|---|---|---|---|---|
IncrementalUpdateWriter::writeRevision | BinaryBuffer $buffer, ObjectRegistry $registry, int $prevXrefOffset, int $catalogObject, array $catalogEntries, array $catalogUpdates, array $newObjectNumbers, string $fileId | 정적. 병합된 항목으로 카탈로그를 다시 작성하고, 새 객체와 수정된 객체에 대한 전통적 상호 참조 테이블을 추가하며, /Size, /Root, /Prev, /ID가 있는 트레일러를 작성합니다. 이후 리비전 이전 접두사(prefix)가 바이트 동일한지 검증합니다. | int — 새 상호 참조 테이블의 바이트 오프셋 | 추가 전용 접두사 검사가 실패하면 \NextPDF\Exception\WriterException; getWriterState()는 dss-append-only-invariant를 반환합니다 | 정적 진입점. 위반 시 사용 가능한 출력 없음. |
ObjectStreamWriter::addObject | int $objectNumber, string $content | 크기 검사 후 대기 중인 스트림에 하나의 객체를 추가합니다. | void | 결합된 인덱스와 본문이 65,536바이트를 초과할 때 OverflowException | $content는 N 0 obj / endobj 래퍼를 제외합니다. |
ObjectStreamWriter::canAccept | string $content | 인덱스 오버헤드를 추정하고 누적 합계를 최대값과 비교하여 검사합니다. | bool | 예외를 던지지 않음 | 순수 술어(predicate); 상태 변경 없음. |
ObjectStreamWriter::build | 없음 | 인덱스를 만들고, 본문을 연결하며, FlateDecode로 압축하고, /Type /ObjStm 딕셔너리로 래핑합니다. | string — 원시 Object Stream 콘텐츠 | 추가된 객체가 없거나 zlib 압축 실패 시 ObjectStreamWriteException | 호출자가 객체 번호를 할당하고 마커를 래핑합니다. |
ObjectStreamWriter::getEntries | 없음 | 누적된 객체들에 대해 본문 상대 오프셋을 다시 계산합니다. | list<ObjectStreamEntry> | 예외를 던지지 않음 | 오프셋은 본문 섹션에 대해 상대적입니다. |
ObjectStreamWriter::count | 없음 | 누적된 객체 수를 보고합니다. | int | 예외를 던지지 않음 | — |
ObjStmCompressor::__construct | int $maxStreamSize = 65536, int $maxObjectsPerStream = 200 | 그룹화에 사용되는 크기 및 객체 수 한계를 저장합니다. | — | 예외를 던지지 않음 | 기본값은 모듈의 Object Stream 튜닝과 일치합니다. |
ObjStmCompressor::groupObjects | list<array{number: int, generation?: int, content: string}> $objects | 부적격 객체를 필터링한 후, 나머지를 크기 및 개수 한계 이내의 writer들로 패킹합니다. | list<ObjectStreamWriter> | 예외를 던지지 않음; 부적격 객체는 건너뜀 | 0이 아닌 세대(generation) 객체는 일반 직렬화로 넘어갑니다. |
ObjStmCompressor::isEligible | string $content, int $generation = 0 | 스트림 객체, /Encrypt, /XRef, /Catalog, 그리고 0이 아닌 모든 세대를 거부합니다. | bool | 예외를 던지지 않음 | /Type 매칭은 공백 및 #xx 이스케이프에 관용적입니다. |
ObjStmCompressor::writeToBuffer | list<ObjectStreamWriter> $streams, BinaryBuffer $buffer, ObjectRegistry $registry | 스트림당 캐리어 객체를 할당하고, type-2 압축 항목을 등록하며, 각 ObjStm 블록을 작성합니다. | list<int> — 캐리어 객체 번호 | 드문 압축 실패 시 build()의 ObjectStreamWriteException를 전파합니다 | 비적격 객체가 작성된 후, 상호 참조가 방출되기 전에 실행합니다. |
ObjStmCompressor::estimateSavings | list<ObjectStreamWriter> $streams, int $originalSize | 각 스트림을 빌드하여 압축 크기를 원본과 비교 측정합니다. | ObjStmCompressionResult | 드문 압축 실패 시 build()의 ObjectStreamWriteException를 전파합니다 | 읽기 전용 측정 헬퍼. |
ObjectStreamEntry::__construct | int $objectNumber, string $content, int $offset | 패킹된 하나의 객체와 그 본문 오프셋의 불변 레코드입니다. | — | 예외를 던지지 않음 | final readonly; 공개 속성. |
ObjStmCompressionResult::__construct | int $originalObjectCount, int $streamCount, int $estimatedOriginalSize, int $estimatedCompressedSize | 불변 메트릭 컨테이너입니다. | — | 예외를 던지지 않음 | final readonly; 공개 속성. |
ObjStmCompressionResult::savedBytes | 없음 | 원본에서 압축 크기를 뺀 값을 반환합니다. | int | 예외를 던지지 않음 | 패킹이 데이터를 확장한 경우 음수일 수 있습니다. |
ObjStmCompressionResult::savedPercent | 없음 | 감소 백분율을 반환합니다. | float | 예외를 던지지 않음 | 원본 크기가 0이면 0.0을 반환합니다. |
ObjStmCompressionResult::compressionRatio | 없음 | 원본 대비 압축 크기를 반환합니다. | float | 예외를 던지지 않음 | 원본 크기가 0이면 1.0을 반환합니다. |
ObjectStreamWriteException | — | Object Stream 빌드 실패를 알립니다. | — | RuntimeException을 확장 | build()가 던집니다; 하위 호환성을 위해 RuntimeException로 캐치 가능합니다. |
진입점 시그니처
섹션 제목: “진입점 시그니처”final class IncrementalUpdateWriter{ public static function writeRevision( BinaryBuffer $buffer, ObjectRegistry $registry, int $prevXrefOffset, int $catalogObject, array $catalogEntries, array $catalogUpdates, array $newObjectNumbers, string $fileId, ): int;}final class ObjectStreamWriter{ public function addObject(int $objectNumber, string $content): void; public function canAccept(string $content): bool; public function build(): string; /** @return list<ObjectStreamEntry> */ public function getEntries(): array; public function count(): int;}final class ObjStmCompressor{ public function __construct( int $maxStreamSize = 65536, int $maxObjectsPerStream = 200, );
/** * @param list<array{number: int, generation?: int, content: string}> $objects * @return list<ObjectStreamWriter> */ public function groupObjects(array $objects): array;
public function isEligible(string $content, int $generation = 0): bool;
/** * @param list<ObjectStreamWriter> $streams * @return list<int> */ public function writeToBuffer(array $streams, BinaryBuffer $buffer, ObjectRegistry $registry): array;
/** @param list<ObjectStreamWriter> $streams */ public function estimateSavings(array $streams, int $originalSize): ObjStmCompressionResult;}동작 계약
섹션 제목: “동작 계약”writeRevision은 하나의 증분 업데이트 리비전을 작성합니다. 작성하기 전에 기존 버퍼 접두사를 스냅샷합니다. 병합된 항목으로 카탈로그를 다시 작성하고, 새 객체 오프셋을 등록하며, 인접한 하위 섹션으로 그룹화된 전통적 상호 참조 테이블을 작성하고, /Size, /Root, /Prev, /ID가 있는 트레일러를 작성합니다. 작성 후 접두사를 다시 비교합니다. 더 앞선 바이트가 하나라도 변경되었다면, 추가 전용 위반(append-only-violation) 상태를 담은 WriterException을 발생시키며 사용 가능한 출력을 반환하지 않습니다. 성공 시 추가 리비전을 체이닝하기 위한 새 상호 참조 테이블의 바이트 오프셋을 반환합니다. 리비전 간에 상호 참조 테이블과 스트림을 혼합하는 것은 허용됩니다.
ObjectStreamWriter는 객체를 누적합니다. addObject는 결합된 인덱스와 본문이 최대값인 비압축 65,536바이트를 초과할 때 오버플로 오류를 발생시킵니다. build는 빈 스트림에서 오류를 발생시킵니다. 그렇지 않으면 인덱스와 본문을 압축하여 /Type /ObjStm, /N, /First, /Length, /Filter /FlateDecode 항목이 있는 Object Stream 콘텐츠를 반환합니다. 호출자가 객체 번호를 할당하고 N 0 obj / endobj 마커를 래핑합니다.
ObjStmCompressor는 어떤 객체를 패킹할지 결정합니다. 스트림 객체, 암호화 딕셔너리, 상호 참조 스트림, 문서 카탈로그, 그리고 0이 아닌 세대 번호를 가진 모든 객체를 제외합니다. writeToBuffer는 스트림당 캐리어 객체를 할당하고, 패킹된 각 객체를 type-2 압축 상호 참조 항목으로 등록하며, 현재 버퍼 오프셋에 ObjStm 블록을 작성합니다. estimateSavings는 버퍼를 변형하지 않고 각 스트림을 빌드하여 크기 메트릭을 계산합니다.
엣지 케이스 및 실패 모드
섹션 제목: “엣지 케이스 및 실패 모드”- 추가 전용 검사는 기존 접두사를 복사합니다. 그 비용은 이미 작성된 문서의 크기에 따라 증가합니다. 이 비용은 의도된 것이며 서명된 바이트를 보호합니다.
- Object Stream 한계는 비압축 인덱스와 본문에 적용됩니다. 암호화 딕셔너리와 기타 제외되는 객체 타입은 직접 간접 객체(direct indirect objects)로 배치하십시오.
/Type제외는 임의의 토큰 간 공백과#xx16진 이스케이프에 관용적입니다./Type /Encrypt,/Type\n/Encrypt,/Type /#45ncrypt같은 형태는 표준 리터럴 철자뿐 아니라 모두 거부됩니다.- 0이 아닌 세대 번호를 가진 모든 객체는 부적격으로 취급되어 일반
N G obj … endobj직렬화로 넘어갑니다. 압축된 객체의 세대는 암묵적으로 0이기 때문입니다. writeToBuffer는 모든 비적격 객체가 작성된 후, 상호 참조가 방출되기 전에 실행되어야 합니다. 패킹된 객체는 별도로 직렬화되어서도 안 됩니다.
FIPS 모드 동작
섹션 제목: “FIPS 모드 동작”Writer 모듈은 어떤 암호 연산도 수행하지 않습니다. 더 앞선 바이트가 변경될 경우 방출을 거부함으로써 서명된 바이트를 보호하는데, 이는 암호적 검사가 아니라 바이트 동일성 검사입니다. 서명 및 해싱을 위한 FIPS 알고리즘 선택은 이 writer가 아니라 서명 모듈이 관장합니다. FIPS 모드를 활성화하거나 비활성화해도 어떤 Writer 메서드의 동작도 바뀌지 않습니다.
적합성
섹션 제목: “적합성”NextPDF는 이 모듈을 ISO 32000-2:2020에 맞추어 구현합니다. 증분 writer는 §7.5.6 증분 업데이트 문법을 따릅니다. 즉, 각 리비전은 새 객체, 변경된 객체, 삭제된 객체만을 다루는 상호 참조 섹션과, /Prev 항목이 이전 상호 참조의 오프셋을 제공하는 트레일러를 추가합니다. Object Stream 빌더는 §7.5.7 Object Stream 모델을 따릅니다. 즉, 객체 번호와 오프셋 쌍의 인덱스—오프셋은 /First 항목으로부터 오름차순으로 측정—가 패킹된 객체 본문 앞에 옵니다. 두 조항 참조 모두 ISO 32000-2:2020 코퍼스에 대해 검증되었습니다. PAdES B-LT 및 B-LTA 워크플로를 위한 리비전 체이닝은 소스에 주석으로 표기된 대로 ETSI EN 319 142-1 §5.4를 따릅니다. 어떤 조항에 대한 지원은 엔지니어링 역량 진술이지 인증이 아닙니다. NextPDF는 어떤 공식 적합성 인증도 보유하고 있지 않습니다.
개발 노트
섹션 제목: “개발 노트”- 패키지를
composer require nextpdf/pro:^3으로 설치하십시오. 클래스는NextPDF\Pro\Writer아래에서 해석됩니다. IncrementalUpdateWriter::writeRevision은 정적 진입점이며, 리비전 간에 인스턴스 상태를 보유하지 않습니다.ObjectStreamEntry,ObjStmCompressionResult,IncrementalUpdateWriter, 그리고 컴프레서가 함께 모듈의 공개 표면을 이룹니다. 저장소는 이에 대한 실행 가능한 예제를 제공하지 않습니다.writeRevision의WriterException은 추가 전용 위반을 나타냅니다. 이를 하드 실패로 취급하고 버퍼를 폐기하십시오.- Object Stream 캐리어는 간접 객체입니다. 호출자가 레지스트리를 통해 그 객체 번호를 할당합니다.
게시 경계
섹션 제목: “게시 경계”이 페이지는 외부에서 관찰 가능한 동작과 지원되는 공개 API 표면만을 문서화합니다. 내부 네임스페이스 경로, 헬퍼 클래스, 메커니즘 테이블, 런북 파일명, 티켓 접두사는 범위에서 제외됩니다.