본문으로 건너뛰기

© 2026 Molayo

HN요약2026. 05. 17. 23:33

Show HN: 비트 완벽한 (bit-perfect) PDF를 위한 제로 브라우저, 순수 JS 타이포그래피 엔진을 만들었습니다

요약

VMPrint는 PDF 라이브러리나 렌더러가 아닌, 문서의 레이아웃을 계산하는 전용 '레이아웃 엔진'입니다. 이 엔진은 글리프 위치 결정, 페이지 분할 협상, 공간 의존성 해결 등을 담당하며, 모든 요소에 대한 정확한 X/Y 좌표를 평탄한 배열로 출력합니다. 핵심 아키텍처는 문서 레이아웃을 파이프라인 방식이 아닌 '결정론적 시공간 시뮬레이션'으로 모델링하는 것입니다. 이 접근 방식을 통해 여러 번의 패스가 필요한 복잡한 작업(예: 목차, 형제 기하학)도 단 한 번의 순방향 시뮬레이션만으로 정확하게 해결할 수 있습니다. 이 엔진은 Canvas/WebGL 기반 앱 개발자나 대량 리포트 생성기 등 다양한 분야에서 기존 방식의 비효율성(높은 메모리 사용량, 콜드 스타트 오버헤드)을 개선하는 대안을 제시합니다.

핵심 포인트

  • VMPrint는 레이아웃 계산에 특화된 엔진으로, 렌더링이나 PDF 라이브러리가 아닙니다.
  • 문서 레이아웃을 '결정론적 시공간 시뮬레이션' 모델로 접근하여 정확성을 높였습니다.
  • 단 한 번의 순방향 시뮬레이션 패스로 복잡한 문서 구조(예: 목차, 동적 영역)를 오류 없이 처리합니다.
  • Canvas/WebGL 기반 앱이나 대량 리포트 생성기 등에서 기존 방식 대비 성능 및 안정성 우위를 제공합니다.

VMPrint

여러분이 직접 만들 필요 없도록, 제가 레이아웃 엔진을 만들었습니다.

VMPrint는 레이아웃 엔진 (layout engine)입니다. PDF 라이브러리가 아닙니다. 렌더러 (renderer)도 아닙니다. 레이아웃 엔진입니다. 즉, 모든 글리프 (glyph)가 어디에 위치할지 결정하고, 페이지 분할 (page breaks)을 협상하며, 동적 영역 (dynamic regions) 간의 상호 공간 의존성 (spatial dependencies)을 해결하고, 문서 내의 모든 박스 (box)와 텍스트 런 (text run)에 대한 정확한 X/Y 좌표의 평탄한 배열 (flat array)을 생성하는 컴포넌트입니다.

그 출력물을 가지고 무엇을 할지는 여러분의 결정입니다. PDF로 렌더링하거나, Canvas에 다시 재생하거나, 워드 프로세서의 디스플레이 레이어 (display layer)를 구동하거나, WebGL 파이프라인 (pipeline)에 공급할 수 있습니다. 엔진의 역할은 수학적 계산이 완료되면 끝납니다.

이 아키텍처는 현재 특허 출원 중입니다. 핵심 통찰은 다음과 같습니다: 문서 레이아웃은 파이프라인 (pipeline)이 아니라 결정론적 시공간 시뮬레이션 (deterministic spatiotemporal simulation)이라는 점입니다. 문서 요소들은 영구적인 월드 좌표 공간 (world coordinate space)에 거주하는 자율적인 액터 (actors)입니다. 페이지는 해당 월드에 대한 뷰포트 투영 (viewport projections)이며, 콘텐츠가 할당되는 컨테이너 (containers)가 아닙니다. 액터들은 이웃과 기하학적 구조 (geometry)를 협상하고, 배치가 확정되면 확정된 사실을 게시하며, 다른 액터들은 그 사실을 관찰하고 반응합니다. 이 모든 과정은 단 한 번의 순방향 시뮬레이션 패스 (forward simulation pass) 내에서 이루어집니다. 출력은 월드가 평형 상태 (equilibrium)에 도달했을 때 캡처됩니다.

이것은 비유가 아닙니다. 문자 그대로의 실행 모델 (execution model)입니다. 정확한 페이지 번호가 필요한 목차 (table of contents)나 형제 기하학 (sibling geometry)에 의존하는 동적 영역과 같이, 다른 모든 시스템에서 여러 번의 패스 (multiple passes)가 필요한 작업들이 왜 이곳에서는 단 한 번의 패스만으로 올바르게 해결되는지에 대한 이유입니다.

<p align="center"> </p> <p align="center"><em style="display: block;">왼쪽: 렌더링된 출력물. 오른쪽: <code style="display: inline;">--debug</code> 모드가 적용된 동일한 문서; 모든 액터 경계와 박스에 라벨이 붙어 있습니다. 레이아웃은 완전히 검사 가능한 데이터입니다.</em></p>

누가 레이아웃 엔진을 필요로 하는가

누가 레이아웃 엔진을 필요로 하는가

Canvas 및 WebGL 도구 제작자. Figma급 앱, 무한 화이트보드, 또는 데이터 시각화 플랫폼을 구축하는 팀들은 일반적으로 DOM을 포기하고 Canvas, WebGL, 또는 WebGPU를 통해 모든 것을 렌더링합니다. 사용자가 아랍어 텍스트를 입력하거나 여러 스크립트를 혼용할 때, 단순한 단어 줄바꿈 (word-wrap) 방식은 깨지기 마련입니다. VMPrint는 타이포그래피 마이크로서비스 (typographic microservice) 역할을 합니다. 텍스트, 폰트, 경계 제약 조건 (bounding constraints)이 포함된 JSON 문서를 전달하면, 각 줄에 대한 정확한 글리프 (glyph) X/Y 좌표의 평탄한 배열 (flat array)을 반환합니다. 당신은 그리기만 하면 됩니다. 수학적 계산은 VMPrint가 수행합니다.

대량 리포트 생성기. 매일 수천 개의 복잡한 PDF를 생성하는 FinTech, LegalTech, MedTech 플랫폼: 재무 감사, 개인화된 계약서, 현지화된 서류 등. Puppeteer를 통해 헤드리스 Chrome (headless Chrome)을 실행하는 기본 방식은 워커(worker)당 전체 브라우저 프로세스를 필요로 합니다. 이는 약 300 MB의 RAM, 수백 밀리초의 콜드 스타트 (cold-start) 오버헤드, 서브프로세스 (subprocess) 권한, 그리고 동시성 (concurrency)에 따라 선형적으로 증가하는 비용을 요구합니다. VMPrint는 기존의 Node.js 프로세스 또는 V8 isolate 내부에서 실행됩니다. 326페이지, 웜 스타트 (warm): 718 ms. 브라우저도, 서브프로세스도 필요 없습니다.

<p align="center"> <img src="documents/assets/report.png" width="48%" alt="Financial report: complex multi-column layout with large display type and tables"> <img src="documents/assets/console.jpg" width="48%" alt="Terminal: 324-page manuscript compiled to PDF in 2.32s end-to-end"> </p> <p align="center"><em>왼쪽: 재무 보고서 출력물. 오른쪽: 324페이지 분량의 원고, Markdown에서 PDF로 엔드 투 엔드(end-to-end) 2.32초 만에 컴파일됨.</em></p>

주문형 인쇄 (Print-on-demand) 및 자동 출판. 포토북 생성기, 카탈로그 편집기, 자동 교과서 시스템, 그리고 다이렉트 메일 플랫폼. CSS @pagebreak-inside: avoid는 브라우저마다 여전히 신뢰할 수 없습니다. VMPrint는 결정론적 (deterministic)입니다. 42페이지는 모든 기기, 모든 OS, 모든 실행에서 동일한 42페이지입니다. 페이지 매기기 (Pagination)는 브라우저의 휴리스틱 (heuristic)이 아닌, 시뮬레이션 엔진에 의해 해결되는 일급 물리적 제약 조건 (first-class physical constraint)입니다.

워드 프로세서 및 문서 편집기 빌더. 협업 편집기, 시나리오 작성 도구, 학술 논문 편집기 또는 SOP(표준 작업 절차서) 빌더를 만드는 팀들을 위한 도구입니다. contenteditable은 프로그래밍 가능한 레이아웃 상태 (layout state)를 제공하지 않습니다. 비용이 많이 드는 범위 측정 (range measurements) 없이는 DOM에 3행이 어디서 끝나는지 물어볼 수 없습니다. VMPrint는 레이아웃을 검사 가능한 데이터로 제공합니다. 수학적으로 결정된 모든 줄 바꿈 (line break)과 페이지 바꿈 (page break)이 diff, 직렬화 (serialize), 재생 (replay)이 가능한 평면적인 JSON 구조로 제공됩니다. 그뿐만 아니라, VMPrint는 개별 글리프 (glyph)가 어디에 있는지, 그리고 그것이 무엇인지 알고 있습니다. 정확한 커서 배치, 텍스트 선택, 히트 테스팅 (hit-testing)은 사후 고려 사항이 아닙니다. 이는 레이아웃 데이터로부터 직접 도출됩니다.

엣지 런타임 (Edge runtime) 및 서버리스 (serverless). Headless Chrome을 사용할 수 없는 Cloudflare Workers, Deno Deploy 또는 Lambda@Edge에 배포하는 경우를 위한 솔루션입니다. 서브프로세스(subprocess)가 없고, 엄격한 번들 제한이 있으며, 네이티브 바이너리 (native binary) 의존성 문제가 발생합니다. VMPrint는 네이티브 셰이핑 (shaping) 의존성이나 바이너리 요구 사항이 없는 순수 TypeScript입니다. 이는 V8 isolate에서 실행됩니다.


기존 시스템이 할 수 없는 것들

단일 패스 (Single-pass) 목차 (TOC), 색인 및 참고문헌. 목차는 렌더링하기 전에 페이지 번호를 알아야 하지만, 후속 콘텐츠의 페이지 번호는 목차가 차지하는 공간에 따라 달라집니다. 기존의 모든 시스템은 두 번째 레이아웃 패스 (second layout pass), 근사치, 또는 외부 보조 파일을 통해 이를 해결합니다. VMPrint는 이를 단 한 번의 패스로 해결합니다. 헤딩 액터 (heading actors)는 기하학적 구조가 확정됨에 따라 확정된 신호 (committed signals)를 방출하며, 목차 액터 (TOC actor)는 동일한 실행 시뮬레이션 내에서 해당 신호를 관찰하여 정확한 항목을 조립합니다. 숫자는 정확합니다. 두 번째 패스는 없습니다.

외부 셰이핑 엔진 없는 다중 스크립트 및 양방향 (bidi) 레이아웃. HarfBuzz가 필요 없습니다. ICU도 필요 없습니다. 시스템 수준의 바이너리도 필요 없습니다. 아랍어, 히브리어, 태국어, 데바나가리, CJK(한중일) 및 라틴어가 동일한 줄에 위치하며, 각 스크립트 세그먼트는 자체 폰트 메트릭 (font metrics)에 따라 측정되고, 양방향 재정렬 (bidi-reordered)되며, 지배적인 라인 메트릭에 맞춰 베이스라인 정렬 (baseline-aligned)됩니다. 이 모든 것이 순수 JS로 이루어집니다.

<p align="center"> <img src="documents/assets/languages.jpg" width="80%" alt="세계의 문자 체계: 라틴(Latin), CJK, 아랍어 RTL, 태국어 및 밀집된 혼합 문자가 모두 정확하게 배치됨"> </p>

공간적 재배치(spatial resettlement)를 유발하지 않는 콘텐츠 전용 업데이트. 페이지 카운터가 11에서 12로 업데이트될 때, 기존의 모든 레이아웃 시스템은 부분적으로 또는 전체적으로 레이아웃을 재계산함으로써 대응합니다. VMPrint는 액터(actor) 업데이트 결과를 세 가지 단계로 분류합니다: 변경 없음(no-change), 콘텐츠 전용(content-only), 그리고 기하학적 구조 변경(geometry-changing). 표시되는 숫자가 바뀌는 카운터는 제자리 다시 그리기(in-place redraw) 비용만 지불합니다. 하위 단계의 그 어떤 것도 영향을 받지 않습니다.

롤백(rollback) 기능이 있는 결정론적 투기적 레이아웃(Deterministic speculative layout). 외톨이 줄(Widow/orphan) 제어, 다음 요소와 함께 유지(keep-with-next) 규칙, 그리고 응집성 정책(cohesion policies)은 투기적 브랜치(speculative branch)를 배치하고, 이를 연속성 정책(continuity policy)에 따라 점수를 매긴 뒤, 커밋(commit)하거나 비트 단위로 동일한 커널 스냅샷(kernel snapshot)으로 롤백함으로써 평가됩니다. 롤백은 원자적(atomic)이며 완전합니다: 활성 액터 상태(active actor state), 신호 버스 스테이징 버퍼(signal bus staging buffers), 그리고 월드 공간 좌표(world-space coordinates)가 모두 정확하게 되돌아갑니다.

평면적이고 추적 가능하며 직렬화 가능한 데이터로서의 레이아웃 출력. 엔진은 모든 박스(box)에 의미론적 출처(semantic provenance)를 가진 절대 위치 지정 프리미티브(absolutely positioned primitives)인 Box[]Page[]를 생성합니다. 레이아웃 변경 사항을 JSON으로 차이(diff)를 낼 수 있습니다. 레이아웃을 사전 컴파일(pre-compile)하고 캐싱한 뒤, 나중에 렌더링하십시오. 이 평면적 기하 구조(flat geometry)를 GPU 드로우 파이프라인(draw pipeline)에 직접 공급하십시오. 이것이 Canvas 및 WebGL 소비자들에게 필요한 형식입니다.

스스로 프로그래밍되는 문서. VMPrint 문서는 사후 처리 훅(post-processing hooks)이 아니라 레이아웃 시뮬레이션의 일급 참여자(first-class participants)로서 실행되는 스크립트 메서드를 포함할 수 있습니다. onReady() 핸들러는 레이아웃이 완전히 안정된 후에 실행되며, 실제 페이지 번호, 실제 요소 위치, 실제 콘텐츠 개수를 쿼리(query)한 다음 이에 대응하여 문서 구조를 변경(mutate)할 수 있습니다. 스크립트가 구조를 변경하면, 시뮬레이션은 처음부터 다시 시작하는 것이 아니라 영향을 받은 가장 이른 지점부터 다시 안정화(resettle)됩니다.

특허 출원 중인 마이크로커널 (microkernel) 아키텍처. 단일 패스 순환 의존성 해결 (single-pass cyclic dependency resolution), 결정론적 롤백 (deterministic rollback)을 포함한 투기적 경로 탐색 (speculative pathfinding), 분기 인식 트랜잭션 신호 격리 (branch-aware transactional signal isolation), 3단계 업데이트 결과 분류 (three-tier update outcome classification), 뷰포트 기반 페이지네이션 (viewport-based pagination)을 갖춘 월드 맵 공간 모델 (world-map spatial model), 그리고 커널 소유 시뮬레이션 클록 (kernel-owned simulation clock)을 포함한 엔진의 동작은 청구된 메커니즘에 대한 구현 증거 (reduction-to-practice evidence)와 함께 특허 출원 중입니다.


시작하기

npm install @vmprint/engine @vmprint/local-fonts @vmprint/context-pdf

시작 경로:


패키지

본 리포지토리

패키지용도
@vmprint/engine레이아웃 엔진 (Layout engine) 및 기본 API
...

동반 리포지토리

패키지 / 리포지토리용도
@vmprint/context-pdfPDF 출력 컨텍스트 (PDF output context)
...

FAQ

브라우저에서 문서를 미리 보기만 하고 싶나요?
vmprint-preview는 엔진을 실시간 미리 보기, 확대/축소, 히트 테스팅 (hit-testing) 및 내보내기 워크플로를 갖춘 브라우저 캔버스 (canvas)로 패키징합니다. 이를 위해 엔진 코드를 직접 작성할 필요는 없습니다.

왜 Typst를 사용하지 않나요?
Typst는 작성된 문서에 매우 뛰어나며 아름다운 결과물을 만들어냅니다. 하지만 Typst는 Rust 바이너리입니다. Node 프로세스에 임포트하거나, 브라우저에서 실행하거나, Cloudflare Worker에 배포하거나, TypeScript 애플리케이션에서 라이브러리로 호출할 수 없습니다. JS 또는 TS 런타임에 내장된 레이아웃 엔진이 필요한 경우, Typst는 사용할 수 없습니다.

왜 Puppeteer / headless Chrome을 사용하지 않나요?
Puppeteer는 브라우저 자동화 도구입니다. 이는 렌더링된 웹 페이지를 인쇄하는 과정의 부수 효과로 PDF를 생성합니다. 각 동시 작업자(concurrent worker)는 전체 Chrome 인스턴스, 상당한 RAM, 콜드 스타트(cold-start) 오버헤드 및 서브프로세스 권한을 필요로 합니다. 일회성 문서 생성에는 적절할 수 있습니다. 하지만 대량의 프로덕션 워크로드(production workloads)의 경우 리소스 비용이 상당하며 동시성에 따라 선형적으로 증가합니다. VMPrint는 기존 애플리케이션 프로세스 내부에서 실행됩니다.

이것이 react-pdf인가요?
아니요. react-pdf는 React 트리를 통해 PDF 프리미티브(primitives)를 조립하는 선언적 PDF 컴포넌트 렌더러입니다. VMPrint는 레이아웃 엔진(layout engine)입니다. 즉, 요소들이 어디에 위치할지를 계산하고, 그 결과를 검사 가능한 데이터로 생성하여 출력 컨텍스트(output context)에 전달합니다. 레이아웃 계산과 렌더링은 분리된 명시적 단계입니다.

왜 LaTeX를 사용하지 않나요?
LaTeX는 뛰어난 타이포그래피(typographic) 결과물을 만들어냅니다. 하지만 이는 외부 바이너리를 감싼 매크로 시스템이며, 일반적으로 상호 참조(cross-references)를 해결하기 위해 여러 번의 컴파일 패스(compilation passes)와 보조 파일이 필요합니다. 또한 실행 중인 TypeScript 애플리케이션 내에 라이브러리 형태로 임베드(embed)할 수 없습니다.

브라우저에서도 작동하나요?
네. 이 엔진은 네이티브 바이너리 의존성이 없는 순수 TypeScript로 작성되었습니다. 브라우저 호환 컨텍스트 및 폰트 매니저와 결합하면 전체 레이아웃 패스(layout pass)를 클라이언트 측에서 실행할 수 있습니다.

아랍어, 히브리어, 태국어, CJK(한중일)를 처리할 수 있나요?
네. 양방향 재정렬(Bidi reordering), 오른쪽에서 왼쪽으로 흐르는 문단 흐름(right-to-left paragraph flow), 복잡한 스크립트 분절(complex script segmentation), 다중 스크립트 베이스라인 정렬(multi-script baseline alignment) 기능이 HarfBuzz나 ICU를 통해 덧붙여진 것이 아니라 엔진 자체에 내장되어 있습니다.

레이아웃 엔진을 올바르게 구축하는 것은 매우 어려운 것으로 알려져 있습니다. 왜 이 엔진을 신뢰할 수 있나요?
이 아키텍처는 새로운 메커니즘에 대한 실증적 근거(reduction-to-practice evidence)를 포함하여 특허 출원 중이며, 회귀 테스트 스위트(regression suite)는 광범위한 레이아웃 구성을 테스트합니다. 대규모 벤치마크 문서들은 플로트(floats), 표(tables), 드롭 캡(drop caps), 다단 스토리 흐름(multi-column story flow), 헤더(headers), 푸터(footers), 그리고 상호 참조(cross-references)를 개별적으로가 아닌 동시에 테스트합니다.

스크립팅 시스템이 단순한 템플릿 엔진인가요?
아니요. 템플릿 엔진은 레이아웃(layout)이 실행되기 전에 변수를 치환하여 정적인 문서를 생성합니다. VMPrint 스크립팅은 시뮬레이션(simulation)의 일부로 실행됩니다. 핸들러(Handlers)는 특정 라이프사이클(lifecycle) 시점에 실행되며, 확정된 레이아웃 정보(settled layout facts)를 쿼리(query)할 수 있고, 영향을 받은 지점부터 시뮬레이션이 다시 재설정(resettle)되도록 문서 구조를 변경(mutate)할 수 있습니다.

출력 포맷이 안정적인가요?
문서 AST(Abstract Syntax Tree)는 버전 관리가 됩니다("documentVersion": "1.1"). 직렬화된 레이아웃 출력(AnnotatedLayoutStream)은 --render-from-layout을 사용하여 나중에 방출(emit), 차이 비교(diff), 캐싱(cache) 및 재렌더링(re-render)할 수 있을 만큼 충분히 안정적입니다.

AI 자동 생성 콘텐츠

본 콘텐츠는 HN Show HN (AI)의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
1

댓글

0