콘텐츠로 이동

HTML 파이프라인

Spec: CSS Cascade 5, §6.1 Spec: CSS Display 3, §2 Evidence: Code-backed

NextPDF는 HTML과 CSS를 PHP 프로세스 안에서 PDF로 렌더링합니다 — 기본적으로 브라우저도, 하위 프로세스도 필요하지 않습니다. 이 페이지에서는 변환이 거치는 계층화된 단계, CSS 엔진이 실제로 지원하는 범위, 그리고 실제 브라우저 렌더러에 위임하는 것이 더 타당한 경우를 설명합니다.

“HTML을 PDF로” 변환하는 것은 하나의 작업처럼 들립니다. 실제로는 캐스케이드, 박스 모델, 레이아웃 패스, 페인트 패스로 이어지는 연쇄 과정입니다. 각 단계는 고유한 실패 양상을 지닌, 명확히 명세된 문제입니다. 이들을 하나의 절차로 뒤섞는 엔진은 취약합니다. 색상 파싱을 변경하면 박스가 이동할 수 있으며, 이를 확인할 유일한 방법은 렌더링해서 직접 살펴보는 것뿐입니다.

인프로세스 모델에는 실질적인 이점이 있습니다. 설치할 브라우저도, 운영할 샌드박스도, 데이터를 마샬링해야 할 프로세스 경계도 없습니다. 하지만 이는 변환 과정이 각 관심사를 개별적으로 테스트할 수 있을 만큼 깔끔하게 분해되었을 때에만 효과를 발휘합니다. “PHP에서 HTML을 렌더링한다”는 일을 단지 가능한 수준이 아니라 신뢰할 수 있는 수준으로 만드는 것이 바로 이 아키텍처입니다.

  • HTML/CSS 변환은 writeHtml()를 통해 인프로세스로 실행됩니다. 결과물은 페이지 이미지가 아니라 네이티브 PDF 콘텐츠입니다.
  • 이는 단일 패스이며 스트리밍 방식입니다. 토크나이저가 토큰 목록을 생성하고, 파서는 이를 왼쪽에서 오른쪽으로 소비합니다. 전체 DOM 트리는 유지되지 않습니다(ADR-001). 하드 캡이 요소 개수와 중첩 깊이를 제한합니다.
  • 엔진은 명시적 계층으로 구성됩니다. CSS 파싱과 적용자, 스타일 상태, 레이아웃과 포매팅, 페인트, 페이지드 미디어로 나뉘며, 각 계층이 무엇을 할 수 있는지에 대한 엄격한 규칙을 따릅니다(ADR-010).
  • CSS 엔진은 캐스케이드, 박스 모델, 그리고 일반적인 레이아웃(블록, 인라인, 테이블, 플로트 등)을 지원합니다. 범위는 넓지만, 현대 브라우저가 구현하는 기능 중 정의된 부분집합입니다.
  • 임의의 현대 CSS에 대해 정확한 브라우저 수준 충실도가 필요하다면, NextPDF는 선택적 확장을 통해 헤드리스 브라우저 렌더러에 위임할 수 있습니다. 이는 의도적으로 마련된, 네트워크가 격리된 이음매이며 기본 경로가 아닙니다.

변환은 일련의 단계로 이루어지며, 각 단계는 이전 단계의 타입이 지정된 출력을 입력으로 사용합니다.

  1. Tokenize HTML becomes an ordered token list — no retained DOM tree.
  2. Resolve CSS Parse styles; the cascade and applicators compute typed values.
  3. Style state A push/pop style stack carries computed values per nesting level.
  4. Layout Block, inline, table, and float geometry computed; no paint here.
  5. Paint Borders, backgrounds, text, and decorations emit PDF operators.
  6. Paged media Page-break and @page rules applied as the cursor crosses page bounds.
인프로세스 HTML 파이프라인: 토큰 스트림에 대한 단일 좌-우 패스로, CSS 해석, 스타일 상태, 레이아웃, 페인트를 별개의 계층으로 두며, 커서가 진행함에 따라 페이지드 미디어 분할이 적용됩니다.

두 가지 아키텍처 규칙이 이 흐름을 단순한 절차 이상으로 만듭니다.

계층에는 계약이 있습니다. CSS 텍스트는 적용자 클래스 내부에서만 읽힙니다. 레이아웃 코드는 기하 정보를 계산하지만 어떤 페인트 연산자도 내보내지 않습니다. 페인트 코드는 변경 가능한 레이아웃 추적 상태가 아니라 불변의 계산된 스타일 스냅숏을 읽습니다. 페이지드 미디어 코드는 분할을 트리거하지만 페이지 장식은 페인트 계층에 위임합니다. 이러한 경계는 강제됩니다(ADR-010). 그래서 새로운 CSS 속성은 파서, 레이아웃 디스패치, 페인터 전반에 동시에 파급되는 변경이 아니라 새로운 적용자로 추가됩니다.

DOM이 없습니다. 파이프라인은 의도적으로 단일 패스이며 스트리밍 방식입니다(ADR-001). 요소마다 객체를 두지 않고, 중첩 수준마다 최대 하나의 스타일 상태와 활성 커서만 둡니다. 일부 연산은 실제로 룩어헤드가 필요합니다. 테이블 열 크기 조정, :has(), :last-child가 그렇습니다. 이들은 트리를 유지하지 않고, 평탄한 토큰 목록에 대한 제한된 사전 스캔 인덱스 구조로 처리됩니다. 요소 개수와 중첩 깊이는 하드 캡으로 제한되므로, 병적인 입력은 메모리를 소진하는 대신 빠르게 실패합니다.

CSS 엔진은 겉모습만 흉내 내는 것이 아니라 실제 CSS 시맨틱을 해석합니다. 경합하는 선언들은 출처, 중요도, 레이어, 명시도, 순서에 따라 속성당 하나의 값으로 축약됩니다. 이것이 실제 캐스케이드입니다. 레이아웃은 박스 모델을 따릅니다. 박스의 타입과 그것이 확립하는 포매팅 컨텍스트가 자신과 흐름 내 형제 요소들이 어떻게 배치되는지를 결정합니다. 엔진 소스는 바로 이러한 관심사(캐스케이드, box/display, 플렉스, 플로트, 테이블, 분할)를 중심으로 구성됩니다. 그래서 그 동작을 경험적으로 발견하는 대신 명세에 비추어 추론할 수 있습니다.

이 페이지는 Evidence: Code-backed 기반입니다. 단계와 규칙은 코어 저장소에 매핑됩니다.

  • 인프로세스 진입점은 src/Core/Concerns/HasTextOutput.phpwriteHtml(string $html): static입니다.
  • 요소 캡과 중첩 캡을 갖춘 단일 패스, DOM 비유지 설계는 ADR-001이며, src/Html/의 tokenizer/parser/스타일 스택 코드입니다.
  • 계층화된 엔진 계약(CSS parsing/applicators, 스타일 상태, 레이아웃, 페인트, 페이지드 미디어)은 ADR-010이며, src/Html/ 레이아웃에 반영되어 있습니다(예: Cascade/, Css/, Flex/, Float/, Fragmentation/, 그리고 적용자 클래스들).
  • 브라우저 위임 이음매는 같은 파일의 writeHtmlChrome()이며, 선택적 렌더러 확장과 Chrome/Chromium 바이너리가 필요하다고 문서화되어 있습니다.

표준은 커버리지 주장을 정직하게 뒷받침합니다. 캐스케이드는 경합하는 선언들을 출처, 중요도, 레이어, 명시도, 순서에 따라 속성당 하나의 값으로 축약합니다 Spec: CSS Cascade 5, §6.1 . 흐름 내 배치는 박스 및 포매팅 컨텍스트 규칙을 따릅니다 Spec: CSS Display 3, §2 . 똑같이 중요한 것은 경계입니다. 모든 프로세서가 모든 기능을 지원하는 것은 아니기 때문에 기능 쿼리가 존재합니다 Spec: CSS Conditional 5, §2 . NextPDF의 CSS 엔진은 정의되어 있고 명세에 정렬된 부분집합이며, 그것을 명확히 밝히는 것이 계약의 일부입니다.

인프로세스 렌더링은 한 번의 호출로 이루어집니다. 출력은 래스터화된 페이지가 아니라 선택 가능한 PDF 텍스트입니다.

<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();
$doc->setTitle('HTML Basic');
$doc->addPage();
$html = <<<'HTML'
<h1 style="color: #1E3A8A;">HTML Rendering in NextPDF</h1>
<p>NextPDF renders <strong>HTML and CSS</strong> directly into PDF pages,
<em>in-process</em>.</p>
<ul>
<li>Headings, paragraphs, bold and italic</li>
<li>Lists, tables, inline styles</li>
</ul>
HTML;
$doc->writeHtml($html);
$doc->save(__DIR__ . '/html-basic.pdf');

동일한 문서에 임의의 현대 CSS를 정확한 브라우저 수준 충실도로 렌더링해야 했다면, 호출은 대신 writeHtmlChrome($html)가 되었을 것입니다 — 같은 문서이지만 렌더링 경로가 다르고, 선택적 브라우저 렌더러에 의도적으로 의존합니다.

되풀이되는 오해는 HTML-투-PDF 엔진이 “기본적으로 브라우저다”라는 생각입니다. 그렇지 않으며, NextPDF도 그렇게 주장하지 않습니다. 브라우저는 웹 플랫폼 전체를 끊임없이 갱신하며 구현하는 방대한 구현체입니다. NextPDF의 인프로세스 엔진은 문서 레이아웃에 초점을 맞춘, 명세에 정렬된 부분집합입니다. 정확한 멘탈 모델은 “PHP 속의 Chrome”이 아니라 “유능한 인쇄 문서 CSS 엔진”입니다. 플랫폼 전체가 정말로 필요할 때, 바로 그것을 위한 것이 writeHtmlChrome()입니다. 이는 조용한 폴백이 아니라, 고유한 운영 비용을 지닌 별개의 옵트인 경로입니다.

두 번째 오해는 브라우저 경로를 단지 “네트워크를 통해 페이지를 렌더링하는 것”으로 보는 것입니다. 설계상 정반대입니다. 위임 이음매는 하위 리소스의 네트워크 접근을 무조건 차단한 상태에서 렌더링합니다 — 원격 이미지, 폰트, 스타일시트, 프레임이 없습니다 — 따라서 외부 요청 벡터가 될 수 없습니다. 픽셀 충실도는 제공하지만, 열린 네트워크 송신은 제공하지 않습니다.

이 페이지는 파이프라인의 형태와 인프로세스 / 브라우저 선택을 설명합니다. 이는 CSS 지원 매트릭스가 아닙니다. 인프로세스 엔진이 정확히 어떤 속성, 모듈, 셀렉터를 지원하는지는 이 개요가 아니라 코드와 적합성 테스트로 정의됩니다. 그 커버리지는 진화합니다. 브라우저 위임 경로는 선택적 확장과 Chrome/Chromium 바이너리가 필요합니다. 설정과 운영 특성, 해당 확장의 내부 레이아웃은 여기서는 범위 밖이며 해당 패키지와 함께 문서화되어 있습니다. “인프로세스”는 기본 writeHtml() 경로를 설명합니다. 이는 모든 렌더링 경로가 하위 프로세스를 피한다는 주장이 아닙니다. 아키텍처 주장은 이 페이지의 검토 날짜 기준으로 정확합니다. 권위 있는 출처는 코어 저장소의 src/Html/, ADR-001, 그리고 ADR-010입니다.

인프로세스 CSS 엔진은 코어 능력입니다. 브라우저 위임 이음매는 선택적 확장이며, 여기서는 기능 수준에서만 설명합니다.

HTML rendering paths — edition availability
Edition Availability
Core 코어는 인프로세스 HTML/CSS 엔진(writeHtml)을 제공합니다.
Pro 브라우저 위임 경로는 에디션 등급과 무관한 선택적 추가 확장입니다.
Enterprise 브라우저 위임 경로는 에디션 등급과 무관한 선택적 추가 확장입니다.
  • 인프로세스 렌더링 — 브라우저나 기본 하위 프로세스 없이 PHP 프로세스 내부에서 HTML/CSS를 PDF로 변환하는 것(writeHtml()).
  • 단일 패스 / 스트리밍 — 전체 DOM 트리를 유지하지 않고 토큰 스트림을 왼쪽에서 오른쪽으로 소비하는 것(ADR-001).
  • 캐스케이드 — 경합하는 선언들을 출처, 중요도, 레이어, 명시도, 순서에 따라 속성당 하나의 값으로 해석하는 CSS 과정.
  • 포매팅 컨텍스트 — 박스가 확립하는 레이아웃 환경으로, 흐름 내 콘텐츠가 어떻게 배치되는지를 좌우합니다.
  • 엔진 계층 계약 — 파싱, 스타일, 레이아웃, 페인트, 페이지드 미디어 계층이 각각 무엇을 할 수 있는지를 정의하는 강제된 규칙 집합(ADR-010).
  • 브라우저 위임 이음매 — 하위 리소스의 네트워크 접근이 차단된 헤드리스 브라우저를 통해 렌더링하는 선택적 writeHtmlChrome() 경로.