콘텐츠로 이동
getnextpdf.com

Pro 에디션

Webview

Webview는 선형화된(Fast Web View) PDF를 HTTP를 통해 전달하므로, 클라이언트는 파일의 나머지가 아직 전송 중인 동안 작은 선행 접두사로부터 1페이지 렌더링을 시작할 수 있습니다. 원시 바이트를 LinearizedDocument로 감싸고, PSR-7 ByteRangeResponder를 통해 Range 요청에 RFC 9110 부분 콘텐츠 응답으로 답하며, (FirstPageProber를 통해) 첫 페이지가 접두사 안에 자체 완결되어 있음을 증명할 수 있습니다.

이 기능은 NextPDF Pro(nextpdf/pro)에 포함되어 있으며 Pro 계층 라이선스 봉투로 활성화됩니다. 해당 권한이 없는 배포는 이 기능의 클래스를 로드하지 않습니다. 에디션 비교 및 라이선스 받기.

별도의 기능별 라이선스 플래그는 없습니다. responder는 런타임에 사용자 자신의 PSR-17 팩토리 — ResponseFactoryInterfaceStreamFactoryInterface — 에 연결되며, 미디어 타입은 라이선스 스위치가 아니라 생성자 인수로서 기본값 application/pdf을 가집니다.

Terminal window
composer require nextpdf/pro

코드는 NextPDF\Pro\Webview 네임스페이스 아래에 있습니다.

선형화된 PDF는 문서의 첫 페이지 — 선형화 매개변수 딕셔너리, 기본 힌트 스트림, 그리고 1페이지의 객체 — 가 /E 오프셋에서 끝나는 선행 섹션에 놓이도록 배치됩니다. Webview는 그 레이아웃을 점진적 전달로 전환합니다.

LinearizedDocument::fromBytes()는 Core의 읽기 측 LinearizationView를 통해 바이트를 파싱하고(Pro는 선형화 파싱을 절대 다시 구현하지 않습니다), 사용 가능한 선형화 문서가 아닌 것은 무엇이든 거부합니다. 즉, 전혀 선형화되지 않았거나, 선언된 /L 길이가 실제 바이트 길이와 일치하지 않거나, /E 첫 페이지 끝 오프셋이 파일 내부의 양수 오프셋이 아닌 경우입니다. 따라서 생성은 전역적(total)입니다 — 일단 LinearizedDocument를 보유하면, 그것이 노출하는 모든 오프셋은 신뢰할 수 있습니다.

그런 다음 ByteRangeResponder가 HTTP 요청에 답합니다. 이는 프레임워크 결합 없이 PSR-7 / PSR-17에 대해서만 구현됩니다. 항상 Accept-Ranges: bytes와 강하고 결정적인 SHA-256 ETag를 광고하고, 클라이언트의 Range 헤더를 RFC 9110 §14에 따라 파싱하며, 전체 200 OK, 단일 범위의 206 Partial Content, 여러 범위에 대한 206 multipart/byteranges 응답, 또는 416 Range Not Satisfiable 중 하나를 반환합니다.

FirstPageProber는 구조적 증명 측입니다. 첫 페이지 접두사, 그 접두사가 전체 파일에서 차지하는 비율, 그리고 기본 힌트 스트림이 그 안에 완전히 들어 있는지 — 리더가 접두사만으로 1페이지 객체를 찾을 수 있게 해 주는 속성 — 를 정량화합니다.

Webview는 선형화를 스스로 다시 파싱하지 않습니다. Core의 읽기 측 LinearizationView를 빌려 쓰므로, 전달 계층은 두 번째로 드리프트하는 사본이 아니라 감사된 하나의 파서를 상속합니다. 생성은 의도적으로 전역적입니다. LinearizedDocument::fromBytes()는 잘못된 레이아웃을 처음에 거부하므로, Range 응답이 신뢰하는 모든 오프셋은 먼저 검증되었습니다. responder는 오직 PSR-7과 PSR-17만 사용하므로, 동일한 코드가 어떤 HTTP 스택에서든 선형화된 PDF를 제공합니다. 그러한 규율이 바로 점진적 범위 전달을 대량으로 신뢰할 수 없는 클라이언트에게 안전하게 노출할 수 있게 하는 것입니다.

설계 배경: 고용량 문서 생성.

점진적 바이트 범위 제공이 작동하는 방식

섹션 제목: “점진적 바이트 범위 제공이 작동하는 방식”
  1. 렌더링된 PDF 바이트로 LinearizedDocument를 빌드합니다. 잘못된 입력은 처음에 UnsupportedDocumentException을 발생시킵니다.
  2. 문서와 들어오는 PSR-7 ServerRequestInterfaceByteRangeResponder::respond()에 넘깁니다. responder는 Range(그리고 선택적인 If-Range 전제 조건)를 읽고, 올바른 PSR-7 ResponseInterface를 생성합니다.
  3. 클라이언트는 먼저 선행 접두사를 요청하고(또는 firstPageResponse()로 푸시하고), 1페이지를 렌더링한 다음, 사용자가 스크롤할 때 나머지 범위를 요청합니다.

바이트 범위 모델은 RFC 9110 §14.1.2에 따라 포함(inclusive) 오프셋을 사용합니다. ByteRangecontentLength의 표현 위에서 firstBytelastByte이며, 그 Content-Range 필드는 bytes first-last/length입니다.

  • LinearizedDocument::fromBytes()는 전역적입니다. 비선형화 문서, /L 불일치, 또는 양수가 아니거나 파일 밖의 /E 오프셋은 각각 안전하지 않은 문서를 생성하는 대신 UnsupportedDocumentException을 발생시킵니다.
  • ETag는 정확한 바이트에 대한 강한 SHA-256 엔티티 태그이며, 생성 시 한 번 메모이즈됩니다. 동일한 렌더 입력은 동일한 바이트를, 따라서 동일한 ETag를 산출하므로, 캐시와 If-Range가 예측 가능하게 동작합니다.
  • 적용 가능한 Range가 없는 요청은 전체 본문과 함께 200 OK를 반환합니다. 현재의 강한 ETag와 일치하지 않는 If-RangeRange를 무시하게 하고 전체 200을 반환하게 합니다(RFC 9110 §13.1.5). If-Range의 강한 엔티티 태그 형식만 존중됩니다. HTTP 날짜 If-Range는 비일치로 취급됩니다.
  • 인식되지 않는 범위 단위 또는 구문적으로 유효하지 않은 Range는 무시되고 전체 200이 반환됩니다(RFC 9110 §14.2).
  • 충족 가능한 범위 하나는 Content-Range와 함께 206 Partial Content를 반환합니다. 충족 가능한 범위 여러 개는 206 multipart/byteranges를 반환합니다. 충족 가능한 것이 하나도 없는 유효한 바이트 범위는 Content-Range: bytes */length와 함께 416을 반환합니다(RFC 9110 §15.3.7).
  • firstPageResponse()는 정확히 첫 페이지의 바이트 범위 [0, /E - 1]를 담은 206을 방출합니다 — “전체 다운로드 전 첫 페이지”의 서버 푸시 형식입니다.

다음은 문서화된 공개 API를 반영합니다. 리포지터리는 이 모듈에 대한 실행 가능한 예제를 제공하지 않습니다.

use NextPDF\Pro\Webview\LinearizedDocument;
use NextPDF\Pro\Webview\ByteRangeResponder;
$document = LinearizedDocument::fromBytes($pdfBytes);
$responder = new ByteRangeResponder($responseFactory, $streamFactory);
$response = $responder->respond($document, $request);

코드 샘플 — 첫 페이지 푸시 및 프로빙

섹션 제목: “코드 샘플 — 첫 페이지 푸시 및 프로빙”
use NextPDF\Pro\Webview\LinearizedDocument;
use NextPDF\Pro\Webview\ByteRangeResponder;
use NextPDF\Pro\Webview\FirstPageProber;
use NextPDF\Pro\Webview\Exception\UnsupportedDocumentException;
try {
$document = LinearizedDocument::fromBytes($pdfBytes);
} catch (UnsupportedDocumentException $e) {
// Not a usable linearized document — fall back to plain full delivery.
// ...
return;
}
$prober = new FirstPageProber($document);
if ($prober->isFirstPageSelfContained()) {
// Push exactly the first page's bytes for an instant render.
$response = (new ByteRangeResponder($responseFactory, $streamFactory))
->firstPageResponse($document);
}
  • Webview는 진정으로 선형화된 PDF를 요구합니다. 렌더링된 문서가 선형화되지 않았다면, 렌더 시점에 선형화를 활성화하거나, 전체를 그대로 전달하십시오 — 첫 페이지 시맨틱이 아니라 범위 지원만 필요할 때, respondToBytes()는 임의의(비선형화) 바이트에 대해서도 범위를 제공할 수 있습니다.
  • 증분 업데이트가 중요합니다. 선언된 /L을 넘어 추가된 문서는 길이 불일치로 거부됩니다. 바이트 범위 오프셋이 더 이상 신뢰할 수 없게 되기 때문입니다.
  • responder는 요청당 존중하는 별개 범위의 수를 제한합니다. 상한보다 더 많은 병합된 범위를 요청하거나, 전체 표현보다 더 많은 총 바이트를 요청하는 요청은 그 Range가 무시되고 전체 200이 제공됩니다.

첫 페이지 접두사는 파일 길이로 클램핑된 /E 첫 페이지 끝 오프셋이므로, FirstPageProber::prefixFraction()은 초기 페치가 전체 파일에 비해 얼마나 작은지를 보고합니다 — 다중 페이지 문서의 경우 이것이 바로 Fast Web View의 핵심입니다. 응답 빌드는 인메모리 바이트 문자열을 슬라이스합니다. 비용은 선택된 바이트에 비례합니다. ETag는 문서당 한 번 계산됩니다. 대표적인 문서로 측정하십시오.

입력을 신뢰할 수 없는 것으로 취급하십시오. LinearizedDocument::fromBytes()는 어떤 오프셋이 사용되기 전에 선형화 불변식을 검증합니다. responder는 헤더 주입을 방지하기 위해 제어 문자를 포함하는 contentType을 거부하고, 본문 내부에 절대 나타나지 않도록 보장된 멀티파트 경계를 도출하며, 멀티파트 범위 증폭 부류의 서비스 거부(Apache HTTPD CVE-2011-3192)를 방어하기 위해 겹치는 범위를 병합하고 그 개수와 총 크기를 제한합니다. 이 모듈은 문서 콘텐츠를 로깅하지 않습니다.

바이트 범위 전달은 RFC 9110(HTTP Semantics)을 따릅니다 — 범위 요청은 §14, If-Range는 §13.1.5, 416은 §15.3.7. 선형화된 문서 모델은 ISO 32000-2 Annex F가 설명하는 Fast Web View 레이아웃입니다. 이 모듈은 자신의 테스트로 검증된 동작을 넘어서는 추가 외부 조항 식별자를 주장하지 않습니다.

Enterprise는 Webview 동작을 변경하지 않습니다. Enterprise는 별도로 문서화된 상위 계층의 규정 준수 및 보관 기능을 추가합니다. 이는 선형화된 PDF를 바이트 범위로 제공하는 데 필요하지 않습니다.

이 페이지는 외부에서 관찰 가능한 동작과 지원되는 공개 API 표면만 문서화합니다. 내부 네임스페이스 경로, 헬퍼 클래스, 메커니즘 표, 런북 파일 이름, 티켓 접두사는 범위 밖입니다.