neuron-explorer
Qwen3.5‑4B 전용 조향형 뉴런 탐색기, 모든 MLP 뉴런을 밀어 결과를 보관·탐색
Blazing fast HTML to PDF engine built on blitz and krilla
저장소 README에서 가져옴
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
A modern, lightweight alternative to wkhtmltopdf. Converts HTML/CSS to PDF without a browser engine.
Built in Rust for server-side workloads where memory footprint and startup time matter.README 전문 보기 ↗A modern, lightweight alternative to wkhtmltopdf. Converts HTML/CSS to PDF without a browser engine.
Built in Rust for server-side workloads where memory footprint and startup time matter.
break-before, break-after, break-inside, orphans/widows)Run directly with npx (no install needed):
npx @fulgur-rs/cli render -o output.pdf input.html
Or install globally via npm:
npm install -g @fulgur-rs/cli
fulgur render -o output.pdf input.html
Or via Cargo:
cargo install fulgur-cli
From source:
cargo install --path crates/fulgur-cli
# Basic conversion
fulgur render -o output.pdf input.html
# Read from stdin
cat input.html | fulgur render --stdin -o output.pdf
# Page options
fulgur render -o output.pdf -s Letter -l --margin "20 30" input.html
# Custom fonts and CSS
fulgur render -o output.pdf -f fonts/NotoSansJP.ttf --css style.css input.html
# Images
fulgur render -o output.pdf -i logo.png=assets/logo.png input.html
# Template + JSON data
fulgur render -o invoice.pdf -d data.json template.html
# Tagged PDF (accessibility / PDF/UA-1)
fulgur render --tagged --language en -o report.pdf report.html
fulgur render --pdf-ua --language en -o report.pdf report.html
template.html:
<h1>Invoice #{{ invoice_number }}</h1>
<p>{{ customer_name }}</p>
<table>
{% for item in items %}
<tr><td>{{ item.name }}</td><td>{{ item.price }}</td></tr>
{% endfor %}
</table>
data.json:
{
"invoice_number": "2026-001",
"customer_name": "Acme Corp",
"items": [
{ "name": "Widget", "price": "$10.00" },
{ "name": "Gadget", "price": "$25.00" }
]
}
| Option | Description | Default |
|---|---|---|
-o, --output | Output PDF file path (required, use - for stdout) | — |
-s, --size | Page size (A4, Letter, A3) | A4 |
-l, --landscape | Landscape orientation | false |
--margin | Page margins in mm (CSS shorthand: "20", "20 30", "10 20 30", "10 20 30 40") | — |
--title | PDF title metadata | — |
--language | Document language tag (BCP 47, e.g. en, ja). Required for PDF/UA-1 unless the document sets <html lang>. | — |
-f, --font | Font files to bundle (repeatable) | — |
--css | CSS files to include (repeatable) | — |
-i, --image | Image files to bundle as name=path (repeatable) | — |
-d, --data | JSON data file for template mode (use - for stdin) | — |
--bookmarks | Generate PDF bookmarks (outline) from h1-h6 headings | false |
--tagged | Enable Tagged PDF output (structure tree for accessibility) | false |
--pdf-ua | Enable PDF/UA-1 conformance (implies --tagged and --bookmarks) | false |
--stdin | Read HTML from stdin | false |
use fulgur::engine::Engine;
use fulgur::config::{PageSize, Margin};
// Basic conversion
let engine = Engine::builder().build();
let pdf = engine.render("<h1>Hello</h1>")?;
// With page options
let engine = Engine::builder()
.page_size(PageSize::A4)
.margin(Margin::uniform_mm(20.0))
.title("My Document")
.build();
let pdf = engine.render(html)?;
engine.render_file(html, "output.pdf")?;
// Template + JSON
let engine = Engine::builder()
.template("invoice.html", template_str)
.data(serde_json::json!({
"invoice_number": "2026-001",
"customer_name": "Acme Corp",
}))
.build();
let pdf = engine.render_template()?;
// Tagged PDF (accessibility / PDF/UA-1)
let engine = Engine::builder()
.tagged(true) // structure tree only
.build();
// or: .pdf_ua(true) for full PDF/UA-1 conformance (implies tagged + bookmarks)
let pdf = engine.render(html)?;
Engine::layout() exposes the shared parse → style → layout stage as a
renderer-agnostic LayoutOutput, letting out-of-core consumers (image
rasterization, OCR label generation) reuse Fulgur's layout without pulling PDF
serialization into the core crate.
use fulgur::engine::{Engine, LayoutOutput};
use fulgur::config::PageSize;
let html = "<h1>Hello</h1>";
let engine = Engine::builder().page_size(PageSize::A4).build();
let out: LayoutOutput = engine.layout(html)?;
// `LayoutOutput` is `#[non_exhaustive]`: read its fields, do not destructure it.
let drawables = &out.drawables; // per-node draw payloads — coordinates in PDF pt
let geometry = &out.geometry; // pagination fragments — coordinates in CSS px
layout() runs the same two-pass resolution as render(), so target-counter()
/ target-text() cross-references are already resolved in the returned
drawables.
Limitations: LayoutOutput carries body layout only. @page margin
boxes and position: running() headers/footers are painted render-side and are
not included, and the resolved page box (after @page { size / margin }
overrides) is not yet surfaced — a consumer must not assume Engine::config()
alone describes the canvas. See the design doc for the full contract and the
consumer split (core owns unit conversion; consumers own composition):
docs/plans/2026-06-27-engine-layout-api-design.md.
Fulgur can embed a semantic structure tree in the PDF output, required for accessibility tools and PDF/UA-1 conformance.
# Structure tree only (--tagged)
fulgur render --tagged --language en -o report.pdf report.html
# Full PDF/UA-1 conformance (implies --tagged and --bookmarks)
fulgur render --pdf-ua --language en -o report.pdf report.html
| HTML element | PDF structure tag |
|---|---|
h1–h6 | H1–H6 |
p, div | P, Div |
span | Span |
ul | L (unordered) |
ol | L (ordered) |
li | LI → Lbl + LBody |
table | Table |
thead / tbody / tfoot | THead / TBody / TFoot |
tr | TR |
th | TH (scope: Row/Column/Both) |
td | TD |
img | Figure (alt text propagated) |
a | Link |
<img alt> attributes only. CSS background images are not tagged.--language (CLI) or .lang() (Rust API), or set <html lang> in the document; rendering fails when neither is present.tagged / pdf_ua. Follow-up work is tracked separately.Fulgur integrates Blitz (HTML parsing, CSS style resolution, layout) with Krilla (PDF generation) through a pagination-aware layout abstraction.
HTML/CSS input
↓
Blitz (HTML parse → DOM → style resolution → Taffy layout)
↓
DOM → Pageable conversion (BlockPageable / ParagraphPageable / ImagePageable)
↓
Pagination (split Pageable tree at page boundaries)
↓
Krilla rendering (Pageable.draw() per page → PDF Surface)
↓
PDF bytes
crates/
├── fulgur/ # Core library (conversion, layout, rendering)
└── fulgur-cli/ # CLI tool
# Build
cargo build
# Test
cargo test
# Run CLI directly
cargo run -p fulgur-cli -- render -o output.pdf input.html
Fulgur follows the ZeroVer convention while still in
the 0.x line. The number after the leading zero — the minor — is
what we treat as a release boundary; the patch field exists only to ship
hotfixes between minors.
0.5.x → 0.6 → 0.7). We
refer to it as "Fulgur 0.6" externally; internal artefacts (Cargo.toml,
PyPI, RubyGems, npm, git tag) carry the full semver string 0.6.0
because those registries require it.0.6.1 to fix a regression in 0.6.0). They are not used for
routine releases.fulgur, fulgur-cli, fulgur-wasm, fulgur-ruby, pyfulgur share
the same version. Independent binding versioning is tracked as future
work.0.x. Each minor may introduce
breaking changes. The 1.0 line will be cut once the public surface
stabilises (criteria TBD).If you depend on Fulgur from another Rust crate or a binding, pin the
exact minor (fulgur = "0.6" resolves to >=0.6.0, <0.7.0 under Cargo's
default caret behaviour) until you are ready to absorb a breaking change.
Releases are automated with release-plz: a push to
main opens a Release PR (minor version bump + a PR-based changelog rendered
from release-notes:* labels). Approving and merging it publishes to crates.io,
creates the vX.Y.Z tag, and cascades to the platform binaries, npm, PyPI and
RubyGems. See docs/RELEASE_SETUP.md for the full flow
and PyPI / RubyGems Trusted Publisher setup.
Fulgur aims for byte-identical PDF output from identical input. The core pipeline (Blitz → Taffy → Parley → Krilla) is deterministic, but there is one known environment dependency you should be aware of:
blitz-dom 0.2.4 uses a process-global
fontdb::Database::load_system_fonts() call for parsing inline <svg>
elements. Fulgur cannot currently override it, so the font chosen for
<text> elements inside SVG — and the default fallback for HTML text when
no bundled fonts are supplied — depends on which .ttf/.otf files are
installed on the host. The same HTML can therefore produce different
PDFs on two machines if their system font sets differ.To get reproducible output today, pin the font environment via fontconfig:
# Point fontconfig at a controlled set of font files.
export FONTCONFIG_FILE="$PWD/my-fonts.conf"
fulgur render -o output.pdf input.html
The repository ships a pinned Noto Sans bundle under examples/.fonts/
together with a matching examples/.fontconfig/fonts.conf, which is what
mise run update-examples and the GitHub Actions regen workflows use to
keep examples/*/index.pdf byte-identical across environments. See
examples/.fonts/README.md for the exact font list and re-fetch
instructions. Making this configurable at the library API level (so
fulgur::Engine callers get determinism without touching fontconfig) is
tracked as a follow-up — once landed, library callers will be able to
supply their own font database directly.
If you plan to accept untrusted HTML templates or JSON data (e.g. in a multi-tenant SaaS), see the threat model (日本語) for the full analysis of attack vectors and mitigations.
Contributions are welcome! Please read the Contributing Guide before opening a pull request. All contributors must sign the Contributor License Agreement — a bot will guide you through the one-time sign-off on your first PR.
Fulgur is dual-licensed under either of:
at your option.