HTML에서 생성
두 가지 진입점이 있습니다.
Pdf::from_html(content)— 기본 구조 HTML (제목, 단락, 목록, 코드, 굵게 / 기울임). 스타일은 없습니다. 모든 바인딩에서 제공됩니다.Pdf::from_html_css(html, css, font_bytes)— v0.3.37에 추가된 순수 Rust HTML+CSS 파이프라인입니다. 수작업으로 짠 CSS 엔진 (L3 + L4 선택자 일부, cascade,calc()/var(),@page/@media print), Taffy 기반 블록 / flex / grid 레이아웃, UAX #14 라인 브레이킹, rustybuzz를 이용한 RTL 셰이핑,::before/::after,page-break-*,<a href>→ 링크 주석,<img>데이터 URI →/XObject, 다중 폰트 cascade를 지원합니다. MPL 전이 의존성이 전혀 없습니다. 모든 바인딩에서 제공됩니다.
빠른 예제
Python
from pdf_oxide import Pdf
pdf = Pdf.from_html("<h1>Hello</h1><p>World</p>")
pdf.save("out.pdf")
WASM
import { WasmPdf } from "pdf-oxide-wasm";
import { writeFileSync } from "fs";
const pdf = WasmPdf.fromHtml("<h1>Hello</h1><p>World</p>");
writeFileSync("out.pdf", pdf.toBytes());
Rust
use pdf_oxide::api::Pdf;
let pdf = Pdf::from_html("<h1>Hello</h1><p>World</p>")?;
pdf.save("out.pdf")?;
HTML + CSS 파이프라인 (v0.3.37)
Pdf::from_html_css(html, css, font_bytes)는 HTML, CSS 스타일시트, TTF/OTF 폰트 바이트를 받아 페이지 분할된 PDF를 돌려줍니다. extract_text는 바이트 수준에서 왕복 일치하므로 생성된 PDF는 기존 테스트 인프라에서 그대로 검증됩니다.
Rust:
use pdf_oxide::api::Pdf;
let font = std::fs::read("DejaVuSans.ttf")?;
let pdf = Pdf::from_html_css(
"<h1>Hello</h1><p>World</p>",
"h1 { color: blue; font-size: 24pt } p { line-height: 1.5 }",
font,
)?;
pdf.save("out.pdf")?;
Python:
from pdf_oxide import Pdf
with open("DejaVuSans.ttf", "rb") as f:
font = f.read()
pdf = Pdf.from_html_css(
"<h1>Hello</h1><p>World</p>",
"h1 { color: blue; font-size: 24pt }",
font,
)
pdf.save("out.pdf")
Node / TypeScript:
import { Pdf } from "pdf-oxide";
import { readFileSync } from "fs";
const font = readFileSync("DejaVuSans.ttf");
const pdf = Pdf.fromHtmlCss(
"<h1>Hello</h1><p>World</p>",
"h1 { color: blue; font-size: 24pt }",
font,
);
pdf.save("out.pdf");
Go:
font, _ := os.ReadFile("DejaVuSans.ttf")
pdf, err := pdfoxide.FromHtmlCss(
"<h1>Hello</h1><p>World</p>",
"h1 { color: blue; font-size: 24pt }",
font,
)
if err != nil { log.Fatal(err) }
defer pdf.Close()
_ = pdf.Save("out.pdf")
C#:
var font = File.ReadAllBytes("DejaVuSans.ttf");
using var pdf = Pdf.FromHtmlCss(
"<h1>Hello</h1><p>World</p>",
"h1 { color: blue; font-size: 24pt }",
font);
pdf.Save("out.pdf");
다중 폰트 cascade
문서에 여러 폰트 패밀리가 섞여 있을 때는 Pdf::from_html_css_with_fonts(html, css, fonts)를 사용하세요. 요소의 CSS font-family는 등록된 패밀리에서 해석됩니다 (대소문자 구분 없음, 따옴표 유무 무관, 여러 단어도 허용). 알 수 없는 패밀리는 첫 번째로 등록된 폰트로 폴백됩니다.
from pdf_oxide import Pdf
fonts = [
("DejaVu Sans", open("DejaVuSans.ttf", "rb").read()),
("Noto Sans CJK", open("NotoSansCJKtc-Regular.otf", "rb").read()),
]
pdf = Pdf.from_html_css_with_fonts(
'<h1 style="font-family: DejaVu Sans">English</h1>'
'<p style="font-family: \'Noto Sans CJK\'">中文段落</p>',
"h1 { font-size: 24pt }",
fonts,
)
pdf.save("multilang.pdf")
CJK 콘텐츠는 출력 시 자동으로 서브셋됩니다 (v0.3.38 #385). 약 17 MB짜리 CJK 폰트에서 5글자만 담은 PDF는 일반적으로 100 KB 이하입니다.
지원되는 CSS 범위
- 선택자 — L3 + L4 부분집합:
:is/:where/:not/:has, 구조적 의사 클래스,i/s플래그를 포함한 속성 매처. - Cascade — 출처 / 구체성 / 소스 순서 정렬, 상속, 인라인 스타일 병합, 사용자 정의 속성 (
var(), 순환 감지 포함). - 함수 —
calc(),min(),max(),clamp(). - @ 규칙 —
@media print(항상 참),(min/max-width),@page :first / :left / :right / :blank와 마진 박스,@font-face,@import,@supports. - 타입 값 — 색상 (명명 색상 약 150개, hex, rgb/rgba, hsl), 길이 (CSS Values L4의 모든 단위), display, font-size / weight / style / family, margin / padding 단축, line-height.
- Counter —
counter/counters,counter-reset/-increment/-set, 로마 / 그리스 / 알파벳 번호 매기기. - 의사 요소 —
::before/::after의 문자열 리터럴,attr(name),open-quote/close-quote. - 레이아웃 — 블록, flex, grid (모두 Taffy 기반), 마진 붕괴, 다중 컬럼 (
column-count/column-width/column-gap), 테이블 (auto 및 fixed 열 알고리즘). - 인라인 — UAX #14 라인 브레이킹,
text-align,white-space모드, 강제 줄 바꿈, 원자성 인라인 박스. - 효과 —
opacity,transform: translate*(),page-break-before: always,page-break-after: always. - HTML — HTML5 토크나이저,
<style>/<link rel="stylesheet">/ 인라인style=""추출,<img>데이터 URI 디코딩 (/XObject),<a href>→/Link주석,<ul>/<ol>목록 마커.
지원 범위 밖
CSS filter, 3D 변환, 애니메이션, HTML 내 SVG (사용 가능한 Rust SVG 크레이트는 모두 MPL), MathML, hyphens: auto, shape-outside, JavaScript 실행, 전체 행렬 transform (scale / rotate), 그라디언트, box-shadow.
라이선스
cargo deny check licenses는 MPL 전이 의존성이 0개인 상태로 통과합니다. Mozilla의 CSS 스택 (cssparser, selectors, html5ever, lightningcss, stylo)은 모두 MPL-2.0입니다. v0.3.37은 pdf_oxide 전체를 MIT/Apache로 유지하기 위해 동등한 기능을 직접 구현했습니다.
지원되는 HTML 요소
| Element | 설명 |
|---|---|
<h1> through <h6> |
Headings (mapped to PDF heading sizes) |
<p> |
자동 간격의 단락 |
<b>, <strong> |
굵은 텍스트 |
<i>, <em> |
기울임꼴 텍스트 |
<ul>, <ol>, <li> |
비순서 및 순서 목록 |
<pre>, <code> |
서식 지정된 코드 및 인라인 코드 |
<blockquote> |
블록 인용문 |
<br> |
줄 바꿈 |
<hr> |
수평선 |
전체 API 레퍼런스
Pdf::from_html(content) (Static 메서드)
기본 설정(Letter 페이지, 72pt 여백, 12pt Helvetica)으로 HTML 콘텐츠에서 PDF를 생성합니다.
Rust:
use pdf_oxide::api::Pdf;
let html = r#"
<h1>Product Specification</h1>
<p>This document describes the <strong>technical requirements</strong>
for the new product line.</p>
<h2>Requirements</h2>
<ul>
<li>Operating temperature: -20C to 60C</li>
<li>Power consumption: <5W</li>
<li>Weight: <200g</li>
</ul>
"#;
let pdf = Pdf::from_html(html)?;
pdf.save("spec.pdf")?;
JavaScript:
import { WasmPdf } from "pdf-oxide-wasm";
import { writeFileSync } from "fs";
const html = `
<h1>Product Specification</h1>
<p>This document describes the <strong>technical requirements</strong>
for the new product line.</p>
`;
const pdf = WasmPdf.fromHtml(html);
writeFileSync("spec.pdf", pdf.toBytes());
Python:
from pdf_oxide import Pdf
html = """
<h1>Product Specification</h1>
<p>This document describes the <strong>technical requirements</strong>
for the new product line.</p>
"""
pdf = Pdf.from_html(html)
pdf.save("spec.pdf")
Python Signature:
Pdf.from_html(
content: str,
title: str | None = None,
author: str | None = None
) -> Pdf
PdfBuilder::new().from_html(content) (Builder Pattern)
페이지 크기, 여백, 폰트 크기, 문서 메타데이터를 제어하려면 PdfBuilder를 사용합니다.
Rust:
use pdf_oxide::api::PdfBuilder;
use pdf_oxide::writer::PageSize;
let pdf = PdfBuilder::new()
.title("Technical Specification")
.author("Engineering")
.page_size(PageSize::A4)
.margin(54.0)
.font_size(11.0)
.from_html("<h1>Spec</h1><p>Version 2.0</p>")?;
pdf.save("spec_a4.pdf")?;
고급 예제
구조화된 보고서
use pdf_oxide::api::Pdf;
let html = r#"
<h1>Incident Report</h1>
<h2>Summary</h2>
<p>On <em>2025-11-15</em>, a service disruption was detected in the
<strong>payment processing</strong> pipeline.</p>
<h2>Timeline</h2>
<ol>
<li>14:32 UTC - Alert triggered for elevated error rates</li>
<li>14:35 UTC - On-call engineer acknowledged</li>
<li>14:48 UTC - Root cause identified: database connection pool exhaustion</li>
<li>15:02 UTC - Fix deployed, services recovering</li>
<li>15:15 UTC - Full recovery confirmed</li>
</ol>
<h2>Root Cause</h2>
<p>A configuration change deployed at 14:00 UTC reduced the maximum
connection pool size from 100 to 10.</p>
<h2>Code Reference</h2>
<pre><code>max_connections: 10 # Should be 100
timeout_seconds: 30
</code></pre>
<h2>Action Items</h2>
<ul>
<li>Add validation for connection pool configuration</li>
<li>Implement canary deployment for config changes</li>
<li>Add alerting for connection pool utilization</li>
</ul>
"#;
let pdf = Pdf::from_html(html)?;
pdf.save("incident_report.pdf")?;
Python에서 동적 HTML
from pdf_oxide import Pdf
rows = [
("Widget A", "$12.99", 150),
("Widget B", "$24.50", 89),
("Widget C", "$7.25", 312),
]
html = "<h1>Inventory Report</h1>"
html += "<p>Generated on 2025-11-20</p>"
html += "<h2>Current Stock</h2><ul>"
for name, price, qty in rows:
html += f"<li><strong>{name}</strong> - {price} ({qty} units)</li>"
html += "</ul>"
pdf = Pdf.from_html(html, title="Inventory Report")
pdf.save("inventory.pdf")
파일에서 HTML 읽기
from pdf_oxide import Pdf
with open("report.html") as f:
html = f.read()
pdf = Pdf.from_html(html, title="Report")
pdf.save("report.pdf")
import { WasmPdf } from "pdf-oxide-wasm";
import { readFileSync, writeFileSync } from "fs";
const html = readFileSync("report.html", "utf-8");
const pdf = WasmPdf.fromHtml(html);
writeFileSync("report.pdf", pdf.toBytes());
use pdf_oxide::api::Pdf;
let html = std::fs::read_to_string("report.html")?;
let pdf = Pdf::from_html(&html)?;
pdf.save("report.pdf")?;
관련 페이지
- Create from Markdown – Convert Markdown to PDF
- PdfBuilder Fluent API – Full builder configuration options
- DocumentBuilder Low-Level API – Programmatic page construction