ironpress 1.4.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. ironpress-1.4.1/.gitignore +12 -0
  2. ironpress-1.4.1/CONTRIBUTING.md +28 -0
  3. ironpress-1.4.1/Cargo.toml +56 -0
  4. ironpress-1.4.1/LICENSE +21 -0
  5. ironpress-1.4.1/PKG-INFO +71 -0
  6. ironpress-1.4.1/README.md +149 -0
  7. ironpress-1.4.1/assets/NotoEmoji-Regular.ttf +0 -0
  8. ironpress-1.4.1/benches/conversion.rs +177 -0
  9. ironpress-1.4.1/bindings/python/Cargo.lock +1455 -0
  10. ironpress-1.4.1/bindings/python/Cargo.toml +14 -0
  11. ironpress-1.4.1/bindings/python/README.md +52 -0
  12. ironpress-1.4.1/bindings/python/src/lib.rs +101 -0
  13. ironpress-1.4.1/codecov.yml +2 -0
  14. ironpress-1.4.1/playground/.gitignore +3 -0
  15. ironpress-1.4.1/playground/index.html +416 -0
  16. ironpress-1.4.1/playground/ironpress.d.ts +60 -0
  17. ironpress-1.4.1/playground/ironpress.js +271 -0
  18. ironpress-1.4.1/playground/ironpress_bg.wasm +0 -0
  19. ironpress-1.4.1/playground/parity/index.html +427 -0
  20. ironpress-1.4.1/pyproject.toml +29 -0
  21. ironpress-1.4.1/scripts/compare-parity.sh +148 -0
  22. ironpress-1.4.1/scripts/generate-references.sh +81 -0
  23. ironpress-1.4.1/scripts/render-fixtures.sh +33 -0
  24. ironpress-1.4.1/src/bidi.rs +181 -0
  25. ironpress-1.4.1/src/bin/cli.rs +74 -0
  26. ironpress-1.4.1/src/cli.rs +463 -0
  27. ironpress-1.4.1/src/error.rs +31 -0
  28. ironpress-1.4.1/src/fonts.rs +859 -0
  29. ironpress-1.4.1/src/layout/block.rs +1411 -0
  30. ironpress-1.4.1/src/layout/context.rs +98 -0
  31. ironpress-1.4.1/src/layout/engine.rs +10174 -0
  32. ironpress-1.4.1/src/layout/flex.rs +1325 -0
  33. ironpress-1.4.1/src/layout/grid.rs +276 -0
  34. ironpress-1.4.1/src/layout/helpers.rs +708 -0
  35. ironpress-1.4.1/src/layout/images.rs +1117 -0
  36. ironpress-1.4.1/src/layout/inline.rs +327 -0
  37. ironpress-1.4.1/src/layout/math.rs +926 -0
  38. ironpress-1.4.1/src/layout/mod.rs +12 -0
  39. ironpress-1.4.1/src/layout/paginate.rs +524 -0
  40. ironpress-1.4.1/src/layout/table.rs +1378 -0
  41. ironpress-1.4.1/src/layout/text.rs +897 -0
  42. ironpress-1.4.1/src/lib.rs +3818 -0
  43. ironpress-1.4.1/src/parser/css/imports.rs +501 -0
  44. ironpress-1.4.1/src/parser/css/inline.rs +931 -0
  45. ironpress-1.4.1/src/parser/css/lightning.rs +68 -0
  46. ironpress-1.4.1/src/parser/css/media.rs +265 -0
  47. ironpress-1.4.1/src/parser/css/mod.rs +42 -0
  48. ironpress-1.4.1/src/parser/css/model.rs +182 -0
  49. ironpress-1.4.1/src/parser/css/page.rs +429 -0
  50. ironpress-1.4.1/src/parser/css/rules.rs +265 -0
  51. ironpress-1.4.1/src/parser/css/selectors.rs +452 -0
  52. ironpress-1.4.1/src/parser/css/selectors_tests.rs +308 -0
  53. ironpress-1.4.1/src/parser/css/values.rs +503 -0
  54. ironpress-1.4.1/src/parser/css/values_tests.rs +250 -0
  55. ironpress-1.4.1/src/parser/dom.rs +578 -0
  56. ironpress-1.4.1/src/parser/html.rs +197 -0
  57. ironpress-1.4.1/src/parser/jpeg.rs +219 -0
  58. ironpress-1.4.1/src/parser/markdown.rs +260 -0
  59. ironpress-1.4.1/src/parser/math.rs +1010 -0
  60. ironpress-1.4.1/src/parser/mod.rs +10 -0
  61. ironpress-1.4.1/src/parser/png.rs +313 -0
  62. ironpress-1.4.1/src/parser/svg.rs +4860 -0
  63. ironpress-1.4.1/src/parser/ttf.rs +1617 -0
  64. ironpress-1.4.1/src/render/background.rs +644 -0
  65. ironpress-1.4.1/src/render/mod.rs +6 -0
  66. ironpress-1.4.1/src/render/pdf/layout_elements.rs +781 -0
  67. ironpress-1.4.1/src/render/pdf.rs +10918 -0
  68. ironpress-1.4.1/src/render/pdf_fonts.rs +893 -0
  69. ironpress-1.4.1/src/render/shading.rs +247 -0
  70. ironpress-1.4.1/src/render/svg_geometry.rs +505 -0
  71. ironpress-1.4.1/src/render/svg_to_pdf.rs +3233 -0
  72. ironpress-1.4.1/src/security/mod.rs +1 -0
  73. ironpress-1.4.1/src/security/sanitizer.rs +466 -0
  74. ironpress-1.4.1/src/style/computed.rs +8217 -0
  75. ironpress-1.4.1/src/style/defaults.rs +353 -0
  76. ironpress-1.4.1/src/style/mod.rs +3 -0
  77. ironpress-1.4.1/src/style/resolve.rs +333 -0
  78. ironpress-1.4.1/src/style/resolve_tests.rs +419 -0
  79. ironpress-1.4.1/src/system_fonts.rs +843 -0
  80. ironpress-1.4.1/src/text.rs +547 -0
  81. ironpress-1.4.1/src/types.rs +173 -0
  82. ironpress-1.4.1/src/util.rs +131 -0
  83. ironpress-1.4.1/tests/fixtures/baseline.json +102 -0
  84. ironpress-1.4.1/tests/fixtures/combined/article.html +110 -0
  85. ironpress-1.4.1/tests/fixtures/combined/dashboard.html +141 -0
  86. ironpress-1.4.1/tests/fixtures/combined/invoice.html +114 -0
  87. ironpress-1.4.1/tests/fixtures/combined/math-paper.html +99 -0
  88. ironpress-1.4.1/tests/fixtures/combined/resume.html +122 -0
  89. ironpress-1.4.1/tests/fixtures/combined/simple-report.html +63 -0
  90. ironpress-1.4.1/tests/fixtures/edge-cases/deep-nesting.html +92 -0
  91. ironpress-1.4.1/tests/fixtures/edge-cases/empty-elements.html +94 -0
  92. ironpress-1.4.1/tests/fixtures/edge-cases/long-table.html +126 -0
  93. ironpress-1.4.1/tests/fixtures/edge-cases/overflow.html +66 -0
  94. ironpress-1.4.1/tests/fixtures/edge-cases/page-breaks.html +66 -0
  95. ironpress-1.4.1/tests/fixtures/edge-cases/unicode.html +101 -0
  96. ironpress-1.4.1/tests/fixtures/expectations/combined_article.json +46 -0
  97. ironpress-1.4.1/tests/fixtures/expectations/combined_dashboard.json +36 -0
  98. ironpress-1.4.1/tests/fixtures/expectations/combined_invoice.json +54 -0
  99. ironpress-1.4.1/tests/fixtures/expectations/combined_math-paper.json +44 -0
  100. ironpress-1.4.1/tests/fixtures/expectations/combined_resume.json +47 -0
  101. ironpress-1.4.1/tests/fixtures/expectations/combined_simple-report.json +39 -0
  102. ironpress-1.4.1/tests/fixtures/expectations/edge-cases_deep-nesting.json +31 -0
  103. ironpress-1.4.1/tests/fixtures/expectations/edge-cases_empty-elements.json +35 -0
  104. ironpress-1.4.1/tests/fixtures/expectations/edge-cases_long-table.json +47 -0
  105. ironpress-1.4.1/tests/fixtures/expectations/edge-cases_overflow.json +35 -0
  106. ironpress-1.4.1/tests/fixtures/expectations/edge-cases_page-breaks.json +41 -0
  107. ironpress-1.4.1/tests/fixtures/expectations/edge-cases_unicode.json +47 -0
  108. ironpress-1.4.1/tests/fixtures/expectations/features_backgrounds-advanced.json +47 -0
  109. ironpress-1.4.1/tests/fixtures/expectations/features_box-model.json +35 -0
  110. ironpress-1.4.1/tests/fixtures/expectations/features_colors-backgrounds.json +36 -0
  111. ironpress-1.4.1/tests/fixtures/expectations/features_flexbox.json +41 -0
  112. ironpress-1.4.1/tests/fixtures/expectations/features_grid.json +36 -0
  113. ironpress-1.4.1/tests/fixtures/expectations/features_images-svg.json +34 -0
  114. ironpress-1.4.1/tests/fixtures/expectations/features_math.json +38 -0
  115. ironpress-1.4.1/tests/fixtures/expectations/features_positioning.json +37 -0
  116. ironpress-1.4.1/tests/fixtures/expectations/features_pseudo-elements.json +38 -0
  117. ironpress-1.4.1/tests/fixtures/expectations/features_tables.json +43 -0
  118. ironpress-1.4.1/tests/fixtures/expectations/features_transforms.json +45 -0
  119. ironpress-1.4.1/tests/fixtures/expectations/features_typography.json +40 -0
  120. ironpress-1.4.1/tests/fixtures/features/backgrounds-advanced.html +81 -0
  121. ironpress-1.4.1/tests/fixtures/features/box-model.html +72 -0
  122. ironpress-1.4.1/tests/fixtures/features/colors-backgrounds.html +89 -0
  123. ironpress-1.4.1/tests/fixtures/features/flexbox.html +124 -0
  124. ironpress-1.4.1/tests/fixtures/features/grid.html +98 -0
  125. ironpress-1.4.1/tests/fixtures/features/images-svg.html +76 -0
  126. ironpress-1.4.1/tests/fixtures/features/math.html +68 -0
  127. ironpress-1.4.1/tests/fixtures/features/positioning.html +78 -0
  128. ironpress-1.4.1/tests/fixtures/features/pseudo-elements.html +90 -0
  129. ironpress-1.4.1/tests/fixtures/features/tables.html +100 -0
  130. ironpress-1.4.1/tests/fixtures/features/transforms.html +81 -0
  131. ironpress-1.4.1/tests/fixtures/features/typography.html +79 -0
  132. ironpress-1.4.1/tests/fixtures/references/combined/.gitkeep +0 -0
  133. ironpress-1.4.1/tests/fixtures/references/edge-cases/.gitkeep +0 -0
  134. ironpress-1.4.1/tests/fixtures/references/features/.gitkeep +0 -0
  135. ironpress-1.4.1/tests/parity_tests.rs +579 -0
  136. ironpress-1.4.1/tests/pdf_smoke_tests.rs +426 -0
  137. ironpress-1.4.1/tests/proptest_properties.rs +144 -0
@@ -0,0 +1,12 @@
1
+ /target
2
+ Cargo.lock
3
+ .claude/
4
+ .DS_Store
5
+ docs/
6
+ examples/
7
+ fuzz/corpus/
8
+ fuzz/artifacts/
9
+ fuzz/target/
10
+ tests/fixtures/references/**/*.png
11
+ bindings/python/target/
12
+ bindings/ruby/target/
@@ -0,0 +1,28 @@
1
+ # Contributing to ironpress
2
+
3
+ Thanks for your interest in contributing!
4
+
5
+ ## Getting Started
6
+
7
+ 1. Fork the repository
8
+ 2. Create a feature branch: `git checkout -b my-feature`
9
+ 3. Make your changes
10
+ 4. Run the checks:
11
+ ```bash
12
+ cargo fmt --check
13
+ cargo clippy -- -D warnings
14
+ cargo test
15
+ ```
16
+ 5. Commit and push your branch
17
+ 6. Open a pull request against `main`
18
+
19
+ ## Guidelines
20
+
21
+ - All code must pass `cargo fmt`, `cargo clippy -- -D warnings`, and `cargo test`
22
+ - Add tests for new functionality
23
+ - Keep PRs focused on a single change
24
+ - Follow existing code style and conventions
25
+
26
+ ## Reporting Issues
27
+
28
+ Open an issue on GitHub with a clear description and, if possible, a minimal reproduction.
@@ -0,0 +1,56 @@
1
+ [package]
2
+ name = "ironpress"
3
+ version = "1.4.1"
4
+ edition = "2024"
5
+ rust-version = "1.85"
6
+ description = "Pure Rust HTML/CSS/Markdown to PDF converter with layout engine, LaTeX math, tables, images, custom fonts, and streaming output. No browser, no system dependencies."
7
+ license = "MIT"
8
+ repository = "https://github.com/gastongouron/ironpress"
9
+ documentation = "https://docs.rs/ironpress"
10
+ readme = "README.md"
11
+ keywords = ["pdf", "html", "css", "markdown", "converter"]
12
+ categories = ["text-processing", "encoding", "template-engine"]
13
+ exclude = [".github/", ".claude/", "target/", "docs/"]
14
+
15
+ [lib]
16
+ crate-type = ["cdylib", "rlib"]
17
+
18
+ [[bin]]
19
+ name = "ironpress"
20
+ path = "src/bin/cli.rs"
21
+
22
+ [features]
23
+ default = []
24
+ async = ["tokio"]
25
+ remote = ["ureq"]
26
+ wasm = ["wasm-bindgen", "js-sys", "getrandom/wasm_js"]
27
+
28
+ [dependencies]
29
+ html5ever = "0.38"
30
+ lightningcss = { version = "1.0.0-alpha.71", default-features = false }
31
+ image = { version = "0.25", default-features = false, features = ["jpeg", "png"] }
32
+ png_decoder = { package = "png", version = "0.18" }
33
+ pulldown-cmark = "0.13"
34
+ markup5ever_rcdom = "0.38"
35
+ fontdb = "0.23"
36
+ flate2 = "1"
37
+ resvg = "0.47"
38
+ rustybuzz = "0.20"
39
+ subsetter = "0.2.3"
40
+ thiserror = "2"
41
+ tokio = { version = "1", features = ["fs", "rt"], optional = true }
42
+ ureq = { version = "3", optional = true }
43
+ wasm-bindgen = { version = "0.2", optional = true }
44
+ js-sys = { version = "0.3", optional = true }
45
+ getrandom = { version = "0.3", optional = true }
46
+ unicode-bidi = "0.3.18"
47
+
48
+ [dev-dependencies]
49
+ tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
50
+ proptest = "1"
51
+ criterion = { version = "0.5", features = ["html_reports"] }
52
+ serde_json = "1"
53
+
54
+ [[bench]]
55
+ name = "conversion"
56
+ harness = false
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Gaston Gouron
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,71 @@
1
+ Metadata-Version: 2.4
2
+ Name: ironpress
3
+ Version: 1.4.1
4
+ Classifier: Development Status :: 4 - Beta
5
+ Classifier: Intended Audience :: Developers
6
+ Classifier: License :: OSI Approved :: MIT License
7
+ Classifier: Programming Language :: Rust
8
+ Classifier: Programming Language :: Python :: Implementation :: CPython
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Topic :: Text Processing :: Markup :: HTML
11
+ Summary: Pure Rust HTML/CSS/Markdown to PDF converter. No browser, no system dependencies.
12
+ Keywords: pdf,html,css,markdown,converter
13
+ License-Expression: MIT
14
+ Requires-Python: >=3.8
15
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
16
+ Project-URL: Homepage, https://github.com/gastongouron/ironpress
17
+ Project-URL: Repository, https://github.com/gastongouron/ironpress
18
+
19
+ # ironpress
20
+
21
+ Pure Rust HTML/CSS/Markdown to PDF converter. No browser, no system dependencies.
22
+
23
+ ## Installation
24
+
25
+ ```bash
26
+ pip install ironpress
27
+ ```
28
+
29
+ ## Quick Start
30
+
31
+ ```python
32
+ import ironpress
33
+
34
+ # Simple conversion
35
+ pdf = ironpress.html_to_pdf("<h1>Hello World</h1><p>Generated with ironpress.</p>")
36
+ with open("output.pdf", "wb") as f:
37
+ f.write(pdf)
38
+
39
+ # Markdown
40
+ pdf = ironpress.markdown_to_pdf("# Hello\n\nGenerated from **Markdown**.")
41
+
42
+ # Advanced options
43
+ converter = ironpress.HtmlConverter()
44
+ converter.page_size("Letter")
45
+ converter.landscape(True)
46
+ converter.margin(36.0) # 0.5 inch margins
47
+ pdf = converter.convert("<h1>Landscape PDF</h1>")
48
+ ```
49
+
50
+ ## API
51
+
52
+ ### `html_to_pdf(html: str) -> bytes`
53
+ Convert an HTML string to PDF bytes.
54
+
55
+ ### `markdown_to_pdf(markdown: str) -> bytes`
56
+ Convert a Markdown string to PDF bytes.
57
+
58
+ ### `HtmlConverter`
59
+ Configurable converter with options:
60
+ - `page_size(name)` — `"A4"`, `"Letter"`, or `"Legal"`
61
+ - `landscape(enabled)` — landscape orientation
62
+ - `margin(points)` — uniform margin in points (72 points = 1 inch)
63
+ - `convert(html)` → PDF bytes
64
+ - `convert_markdown(markdown)` → PDF bytes
65
+
66
+ ## Performance
67
+
68
+ - **10-100x faster** than browser-based solutions (Puppeteer, Playwright)
69
+ - **~5MB** binary, no runtime dependencies
70
+ - Instant startup, no browser process
71
+
@@ -0,0 +1,149 @@
1
+
2
+ <div align="center">
3
+
4
+ <img width="188" alt="4" src="https://github.com/user-attachments/assets/e8b569e6-e74c-4c0f-9e84-05cf37fae3ae" />
5
+
6
+ # Ironpress
7
+
8
+ Pure rust HTML/CSS/Markdown to PDF converter. No browser, no system dependencies.
9
+
10
+
11
+ [![Crates.io](https://img.shields.io/crates/v/ironpress.svg)](https://crates.io/crates/ironpress)
12
+ [![PyPI](https://img.shields.io/pypi/v/ironpress.svg)](https://pypi.org/project/ironpress/)
13
+ [![Gem](https://img.shields.io/gem/v/ironpress.svg)](https://rubygems.org/gems/ironpress)
14
+ [![npm](https://img.shields.io/npm/v/ironpress.svg)](https://www.npmjs.com/package/ironpress)
15
+ [![docs.rs](https://docs.rs/ironpress/badge.svg)](https://docs.rs/ironpress)
16
+ [![CI](https://github.com/gastongouron/ironpress/actions/workflows/ci.yml/badge.svg)](https://github.com/gastongouron/ironpress/actions)
17
+ [![codecov](https://codecov.io/gh/gastongouron/ironpress/branch/main/graph/badge.svg?token=w36XIAwRxG)](https://codecov.io/gh/gastongouron/ironpress)
18
+ [![deps.rs](https://deps.rs/repo/github/gastongouron/ironpress/status.svg)](https://deps.rs/repo/github/gastongouron/ironpress)
19
+ [![MSRV](https://img.shields.io/badge/MSRV-1.85-blue.svg)](https://www.rust-lang.org)
20
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
21
+ [![Downloads](https://img.shields.io/crates/d/ironpress.svg)](https://crates.io/crates/ironpress)
22
+ [![WASM](https://img.shields.io/badge/wasm-ready-blueviolet.svg)](../../wiki/WASM-Playground)
23
+ [![Playground](https://img.shields.io/badge/try_it-playground-blueviolet.svg)](https://gastongouron.github.io/ironpress/)
24
+ [![Parity](https://img.shields.io/badge/parity-dashboard-ff69b4.svg)](https://gastongouron.github.io/ironpress/parity/)
25
+
26
+ **[Try it in your browser](https://gastongouron.github.io/ironpress/)** | **[Parity dashboard](https://gastongouron.github.io/ironpress/parity/)** | **[Wiki](../../wiki)**
27
+
28
+ </div>
29
+
30
+
31
+ ## Performance
32
+
33
+ <!-- AUTO:BENCH - updated by CI -->
34
+ | Document | Time | Pages/sec |
35
+ |----------|------|-----------|
36
+ | Simple HTML (`<h1>` + `<p>`) | **16 us** | 62,500 |
37
+ | Styled HTML (CSS, lists, links) | **71 us** | 14,000 |
38
+ | Markdown (headings, code, lists) | **141 us** | 7,000 |
39
+ | Table (5 rows, styled headers) | **341 us** | 2,900 |
40
+ | Full report (tables, flex, progress bars) | **587 us** | 1,700 |
41
+ <!-- /AUTO:BENCH -->
42
+
43
+ Chrome headless takes ~2,500 ms per page. **ironpress is 4,000x faster**.
44
+
45
+ ## Quick start
46
+
47
+ ```rust
48
+ use ironpress::html_to_pdf;
49
+
50
+ let pdf = html_to_pdf("<h1>Hello</h1><p>World</p>").unwrap();
51
+ std::fs::write("output.pdf", pdf).unwrap();
52
+ ```
53
+
54
+ ```rust
55
+ let pdf = ironpress::markdown_to_pdf("# Hello\n\nWorld").unwrap();
56
+ ```
57
+
58
+ ## CLI
59
+
60
+ ```bash
61
+ cargo install ironpress
62
+
63
+ ironpress input.html output.pdf
64
+ ironpress document.md output.pdf
65
+ ironpress --page-size letter --landscape --margin 54 input.html output.pdf
66
+ ironpress --header "Report" --footer "Page {page} of {pages}" input.html output.pdf
67
+ echo '<h1>Hello</h1>' | ironpress --stdin output.pdf
68
+ ```
69
+
70
+ ## Builder API
71
+
72
+ ```rust
73
+ use ironpress::{HtmlConverter, PageSize, Margin};
74
+
75
+ let pdf = HtmlConverter::new()
76
+ .page_size(PageSize::LETTER)
77
+ .margin(Margin::uniform(54.0))
78
+ .header("My Document")
79
+ .footer("Page {page} of {pages}")
80
+ .convert("<h1>Custom page</h1>")
81
+ .unwrap();
82
+ ```
83
+
84
+ ## Features at a glance
85
+
86
+ | Area | Highlights | Details |
87
+ |------|-----------|---------|
88
+ | **HTML** | 50+ elements: headings, tables, lists, forms, media, `<img>`, inline `<svg>` | [Layout Engine](../../wiki/Layout-Engine) |
89
+ | **CSS** | Flexbox, grid, multi-column, `calc()`, variables, `@media`, `@page`, `@font-face` | [CSS Support](../../wiki/CSS-Support) |
90
+ | **Fonts** | Base-14 PDF fonts, custom TTF embedding with subsetting, system font discovery, Unicode/CJK fallback | [Font System](../../wiki/Font-System) |
91
+ | **Math** | LaTeX via `$...$` / `$$...$$`: fractions, roots, matrices, Greek, operators | [Math Engine](../../wiki/Math-Engine) |
92
+ | **SVG** | Vector rendering: path, shapes, gradients, transforms, clip paths, `viewBox` | [Layout Engine](../../wiki/Layout-Engine) |
93
+ | **Images** | JPEG + PNG, data URIs, local files, remote URLs (`remote` feature) | [Architecture](../../wiki/Architecture) |
94
+ | **PDF** | PDF 1.4, bookmarks, link annotations, headers/footers, gradients, streaming output | [PDF Rendering](../../wiki/PDF-Rendering) |
95
+ | **WASM** | `npm install ironpress` - runs 100% client-side in the browser | [WASM & Playground](../../wiki/WASM-Playground) |
96
+ | **Testing** | 2100+ unit tests, property-based tests, 6 fuzz targets, parity dashboard | [Testing Strategy](../../wiki/Testing-Strategy) |
97
+
98
+ ## Custom fonts
99
+
100
+ ```rust
101
+ let pdf = HtmlConverter::new()
102
+ .add_font("Inter", std::fs::read("Inter.ttf").unwrap())
103
+ .convert(r#"<p style="font-family: Inter">Shaped with HarfBuzz</p>"#)
104
+ .unwrap();
105
+ ```
106
+
107
+ Fonts are shaped with [rustybuzz](https://crates.io/crates/rustybuzz), subset to used glyphs only, and embedded as CIDFontType2. Characters outside WinAnsi (CJK, Arabic, emoji) are rendered via automatic Unicode font fallback. See [Font System](../../wiki/Font-System).
108
+
109
+ ## Math
110
+
111
+ ```markdown
112
+ The equation $E = mc^2$ is famous.
113
+
114
+ $$\sum_{k=1}^{n} k = \frac{n(n+1)}{2}$$
115
+ ```
116
+
117
+ Full LaTeX support: fractions, roots, matrices, Greek letters, operators, delimiters, accents. See [Math Engine](../../wiki/Math-Engine).
118
+
119
+ ## WASM
120
+
121
+ ```bash
122
+ npm install ironpress
123
+ ```
124
+
125
+ ```javascript
126
+ import init, { htmlToPdf, markdownToPdf } from 'ironpress';
127
+ await init();
128
+
129
+ const pdf = htmlToPdf('<h1>Hello</h1>');
130
+ const blob = new Blob([pdf], { type: 'application/pdf' });
131
+ ```
132
+
133
+ See [WASM & Playground](../../wiki/WASM-Playground).
134
+
135
+ ## Security
136
+
137
+ HTML is sanitized by default: `<script>`, `<iframe>`, event handlers, and `javascript:` URLs are stripped. Resources are sandboxed (local-only by default, 10 MB cap). SVG sanitizer strips dangerous elements. PNG decompression capped at 50 MB. Disable with `.sanitize(false)` if you trust the input.
138
+
139
+ ## How it works
140
+
141
+ ```
142
+ HTML/Markdown → Sanitize → Parse (html5ever) → Style cascade → Layout engine → PDF 1.4
143
+ ```
144
+
145
+ See [Architecture](../../wiki/Architecture) for the full pipeline.
146
+
147
+ ## License
148
+
149
+ MIT
@@ -0,0 +1,177 @@
1
+ use criterion::{Criterion, black_box, criterion_group, criterion_main};
2
+
3
+ const SIMPLE_HTML: &str = "<h1>Hello</h1><p>World</p>";
4
+
5
+ const STYLED_HTML: &str = r#"
6
+ <style>
7
+ body { font-size: 11pt; }
8
+ h1 { color: navy; border-bottom: 2pt solid #ccc; }
9
+ .box { background-color: #f5f5f5; padding: 8pt; border-radius: 4pt; }
10
+ </style>
11
+ <h1>Report</h1>
12
+ <p>Some <strong>bold</strong> and <em>italic</em> text with <a href="https://example.com">a link</a>.</p>
13
+ <div class="box"><p>Styled content with background and border-radius.</p></div>
14
+ <ul><li>Item one</li><li>Item two</li><li>Item three</li></ul>
15
+ "#;
16
+
17
+ const TABLE_HTML: &str = r#"
18
+ <style>
19
+ table { border-collapse: collapse; width: 100%; }
20
+ td, th { border: 1pt solid #ddd; padding: 6pt; }
21
+ th { background-color: #336699; color: white; }
22
+ </style>
23
+ <h1>Data Report</h1>
24
+ <table>
25
+ <thead><tr><th>Name</th><th>Role</th><th>Department</th><th>Status</th></tr></thead>
26
+ <tbody>
27
+ <tr><td>Alice</td><td>Engineer</td><td>Backend</td><td>Active</td></tr>
28
+ <tr><td>Bob</td><td>Designer</td><td>Frontend</td><td>Active</td></tr>
29
+ <tr><td>Charlie</td><td>Manager</td><td>Product</td><td>On leave</td></tr>
30
+ <tr><td>Diana</td><td>Engineer</td><td>Infra</td><td>Active</td></tr>
31
+ <tr><td>Eve</td><td>Analyst</td><td>Data</td><td>Active</td></tr>
32
+ </tbody>
33
+ </table>
34
+ "#;
35
+
36
+ const FULL_DOCUMENT: &str = r#"
37
+ <style>
38
+ body { font-size: 11pt; color: #333; }
39
+ h1 { color: navy; border-bottom: 2pt solid #ccc; padding-bottom: 4pt; }
40
+ h2 { color: #336699; margin-top: 12pt; }
41
+ table { border-collapse: collapse; width: 100%; margin: 8pt 0; }
42
+ td, th { border: 1pt solid #ddd; padding: 6pt; }
43
+ th { background-color: #336699; color: white; font-weight: bold; }
44
+ .highlight { background-color: #ffffcc; }
45
+ blockquote { border-left: 3pt solid #ccc; padding-left: 10pt; font-style: italic; color: #666; }
46
+ code { background-color: #f5f5f5; padding: 2pt 4pt; font-size: 10pt; }
47
+ .flex { display: flex; gap: 10pt; }
48
+ .flex > div { flex: 1; padding: 8pt; background-color: #f0f0f0; border-radius: 4pt; }
49
+ </style>
50
+ <h1>Annual Report 2026</h1>
51
+ <p>This is a comprehensive document with <strong>bold</strong>, <em>italic</em>, <code>inline code</code>, and <a href="https://example.com">links</a>.</p>
52
+
53
+ <h2>Financial Summary</h2>
54
+ <table>
55
+ <thead><tr><th>Quarter</th><th>Revenue</th><th>Expenses</th><th>Growth</th></tr></thead>
56
+ <tbody>
57
+ <tr><td>Q1</td><td>$1.2M</td><td>$0.8M</td><td class="highlight">+15%</td></tr>
58
+ <tr><td>Q2</td><td>$1.4M</td><td>$0.9M</td><td class="highlight">+17%</td></tr>
59
+ <tr><td>Q3</td><td>$1.1M</td><td>$0.7M</td><td>-2%</td></tr>
60
+ <tr><td>Q4</td><td>$1.8M</td><td>$1.0M</td><td class="highlight">+22%</td></tr>
61
+ </tbody>
62
+ </table>
63
+
64
+ <h2>Key Metrics</h2>
65
+ <div class="flex">
66
+ <div><strong>Users</strong><br>12,450</div>
67
+ <div><strong>Revenue</strong><br>$5.5M</div>
68
+ <div><strong>Growth</strong><br>+13%</div>
69
+ </div>
70
+
71
+ <h2>Team Updates</h2>
72
+ <ul>
73
+ <li>Engineering: shipped 42 features</li>
74
+ <li>Design: completed brand refresh</li>
75
+ <li>Sales: exceeded Q4 target by 15%</li>
76
+ </ul>
77
+ <ol>
78
+ <li>Expand to European market</li>
79
+ <li>Launch mobile application</li>
80
+ <li>Achieve SOC 2 certification</li>
81
+ </ol>
82
+
83
+ <blockquote>Quality is not an act, it is a habit. — Aristotle</blockquote>
84
+
85
+ <h2>Progress</h2>
86
+ <p>Project Alpha: <progress value="85" max="100"></progress></p>
87
+ <p>Project Beta: <progress value="40" max="100"></progress></p>
88
+
89
+ <hr>
90
+ <p><small>Confidential — Internal use only — Generated with ironpress</small></p>
91
+ "#;
92
+
93
+ const MARKDOWN: &str = r#"# Project Documentation
94
+
95
+ ## Overview
96
+
97
+ This is a **comprehensive** project document with *various* formatting.
98
+
99
+ ## Features
100
+
101
+ - Feature one with `inline code`
102
+ - Feature two with **bold emphasis**
103
+ - Feature three with [a link](https://example.com)
104
+
105
+ ## Code Example
106
+
107
+ ```rust
108
+ fn main() {
109
+ println!("Hello, world!");
110
+ }
111
+ ```
112
+
113
+ ## Roadmap
114
+
115
+ 1. Phase one: Research
116
+ 2. Phase two: Development
117
+ 3. Phase three: Launch
118
+
119
+ > This project represents our commitment to excellence.
120
+
121
+ ---
122
+
123
+ *Last updated: 2026*
124
+ "#;
125
+
126
+ fn bench_simple(c: &mut Criterion) {
127
+ c.bench_function("simple_html", |b| {
128
+ b.iter(|| ironpress::html_to_pdf(black_box(SIMPLE_HTML)).unwrap())
129
+ });
130
+ }
131
+
132
+ fn bench_styled(c: &mut Criterion) {
133
+ c.bench_function("styled_html", |b| {
134
+ b.iter(|| ironpress::html_to_pdf(black_box(STYLED_HTML)).unwrap())
135
+ });
136
+ }
137
+
138
+ fn bench_table(c: &mut Criterion) {
139
+ c.bench_function("table_html", |b| {
140
+ b.iter(|| ironpress::html_to_pdf(black_box(TABLE_HTML)).unwrap())
141
+ });
142
+ }
143
+
144
+ fn bench_full_document(c: &mut Criterion) {
145
+ c.bench_function("full_document", |b| {
146
+ b.iter(|| ironpress::html_to_pdf(black_box(FULL_DOCUMENT)).unwrap())
147
+ });
148
+ }
149
+
150
+ fn bench_markdown(c: &mut Criterion) {
151
+ c.bench_function("markdown", |b| {
152
+ b.iter(|| ironpress::markdown_to_pdf(black_box(MARKDOWN)).unwrap())
153
+ });
154
+ }
155
+
156
+ fn bench_with_header_footer(c: &mut Criterion) {
157
+ c.bench_function("header_footer", |b| {
158
+ b.iter(|| {
159
+ ironpress::HtmlConverter::new()
160
+ .header("Report Title")
161
+ .footer("Page {page} of {pages}")
162
+ .convert(black_box(FULL_DOCUMENT))
163
+ .unwrap()
164
+ })
165
+ });
166
+ }
167
+
168
+ criterion_group!(
169
+ benches,
170
+ bench_simple,
171
+ bench_styled,
172
+ bench_table,
173
+ bench_full_document,
174
+ bench_markdown,
175
+ bench_with_header_footer,
176
+ );
177
+ criterion_main!(benches);