forme-ruby 0.1.0-arm64-darwin → 0.2.0-arm64-darwin

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c028032d19dfaa10a56c45a093f7f3958c6a2618ad2f5ea686baf5c2e50b79b3
4
- data.tar.gz: a073be319ad6cf02f450bf610f84a2c794add9ae86a588be45eb87aa40db5e83
3
+ metadata.gz: 314d2407a6841b5e70b97b7bde1522f2fe318aba2ce5f67bdbda15ad6e55510e
4
+ data.tar.gz: 1b99dc2f56f3d98e1fff301801144c5c6acefaf580fac3dbc669cdf4dd7f9aa3
5
5
  SHA512:
6
- metadata.gz: 2f45c57418aea8d6aea3abf3839a95428e88b8eb5316967b1a845c22edba8a1cc3d2ad4fd8f7612688ab98c19c7322d86118d4d585e7347723aaa420900d1103
7
- data.tar.gz: a4da16d8206dd13065e14cae65c99a0186562752d356c8cd68011d4d2359e01d4c83adeb21ac88fe7ce7166166a28f1100a2c9f6ade7d1b7c8885477222564c7
6
+ metadata.gz: dec3a04d02ac8cc81c66934902461b297db33b7fa272f29592317349f8de5033fc24a41849918a5153976b40dc551c6b3aa47183bc41196dff4f352b77a77e39
7
+ data.tar.gz: 003164fafe49d5fdeda448aa8f1e5f8a9957c989fc61743bd7df399646722fcb3e909b8c1574e5540f79263975fac9a40864969449b4ea0e9e93c8a715b86d2a
data/CHANGELOG.md CHANGED
@@ -1,6 +1,48 @@
1
1
  # Changelog
2
2
 
3
- ## 0.1.0 (unreleased)
3
+ ## 0.2.0 (2026-10-20)
4
+
5
+ ### Breaking
6
+
7
+ - File image sources (`/`, `./`, `../`) are omitted with a warning unless the new
8
+ `image_root:` option names a directory containing them. Allowed files are
9
+ inlined after checking they are regular files of at most 64 MiB.
10
+ - Font `weight` must be an Integer from 1 to 1000 and `italic` true or false;
11
+ invalid values and non-UTF-8 `css:` or font families raise `ArgumentError`.
12
+ - `FormePDF::Rails#render_forme_pdf` is private, so Rails does not expose it as
13
+ a controller action. Call it from your own actions as before.
14
+
15
+ ### Fixed
16
+
17
+ - Rendering deeply nested HTML on a non-main Ruby thread no longer overflows the
18
+ thread's stack and hangs the process. Renders run on a native thread with a
19
+ 64 MiB stack, and documents nested deeper than 512 elements raise
20
+ `FormePDF::RenderError`.
21
+ - Failed source builds show Cargo's diagnostics instead of only
22
+ "Forme native build failed".
23
+
24
+ ### Changed
25
+
26
+ - Require Ruby 3.3 or later. The Ubuntu 24.04 package job installs a pinned
27
+ Ruby 3.3 build in its clean container.
28
+ - State only platform requirements in the README and installation guide.
29
+ - Update the native engine to Forme HTML 0.28.0 at
30
+ `b97dc101b705ea5d7fea6fb1b20fabfb5bd2d63d`; running headers and footers
31
+ no longer inherit `<body>` color and font.
32
+ - Allow the `json` 3.x runtime dependency.
33
+ - Refresh locked Rust and development dependencies and regenerate third-party notices.
34
+ - Publish on a `v*` tag push instead of a manual dispatch. The release promotes
35
+ the tagged commit's CI packages unchanged, attaches them with `SHA256SUMS` to
36
+ the GitHub release, and pushes each to RubyGems with a Sigstore attestation.
37
+ - Re-review native audit exceptions against the new engine revision.
38
+ - CI installs the source gem on macOS as well as Linux, verifies the rustup
39
+ installer checksum and pins the Ubuntu test images by digest.
40
+
41
+ ## 0.1.0 (2026-09-18)
42
+
43
+ Released on [GitHub](https://github.com/clearstackio/forme-ruby/releases/tag/v0.1.0)
44
+ and [RubyGems](https://rubygems.org/gems/forme-ruby/versions/0.1.0)
45
+ with identical source and native gems.
4
46
 
5
47
  ### Added
6
48
 
@@ -23,7 +65,9 @@
23
65
 
24
66
  - Forme HTML 0.24.0 at `f408920e632c59da0651b5b6d32f8c1397477673`.
25
67
  - Build toolchain: Rust 1.94.0; target minimums macOS 15 and glibc 2.28.
26
- - RubyGems publication and qualification of the final release commit are pending.
68
+ - Release commit `266628a` passed the full hosted platform matrix and CodeQL.
69
+ - Published all three packages through trusted publishing; registry downloads
70
+ match the qualified CI and GitHub release artifacts byte-for-byte.
27
71
 
28
72
  ### Release preparation
29
73
 
data/CONTRIBUTING.md CHANGED
@@ -5,7 +5,7 @@ Use synthetic documents and assets that can be redistributed.
5
5
 
6
6
  ## Set up a checkout
7
7
 
8
- Use CRuby 3.2+ and Rust/Cargo 1.94.0, a native linker and make. From the repository:
8
+ Use CRuby 3.3+ and Rust/Cargo 1.94.0, a native linker and make. From the repository:
9
9
 
10
10
  ```bash
11
11
  bundle install
data/NOTICE CHANGED
@@ -1,5 +1,5 @@
1
1
  forme-ruby is an independent binding, not an official Forme project.
2
2
  Forme: https://github.com/danmolitor/forme
3
- Pinned revision: f408920e632c59da0651b5b6d32f8c1397477673
3
+ Pinned revision: b97dc101b705ea5d7fea6fb1b20fabfb5bd2d63d
4
4
  Forme is MIT licensed. Native distributions must include upstream and dependency
5
5
  license notices generated during their build (see third-party notices).
data/README.md CHANGED
@@ -9,18 +9,25 @@ WebAssembly or separate rendering service is needed.
9
9
 
10
10
  This is an independent MIT-licensed binding maintained by
11
11
  [ClearStack](https://github.com/clearstackio), not an official upstream project.
12
- **0.1.0 is in preparation; RubyGems publication is pending.**
12
+ **[0.2.0 is available on RubyGems](https://rubygems.org/gems/forme-ruby/versions/0.2.0).**
13
13
 
14
14
  ## Getting started
15
15
 
16
- Until the first RubyGems release, use the Git repository and commit the resolved
17
- lockfile. Git installation compiles the native extension and requires Rust/Cargo
18
- 1.94.0, a linker and make:
16
+ Add to your Gemfile and run `bundle install`:
19
17
 
20
18
  ```ruby
21
- gem "forme-ruby", github: "clearstackio/forme-ruby", branch: "main", require: "forme_pdf"
19
+ gem "forme-ruby", "~> 0.2.0", require: "forme_pdf"
22
20
  ```
23
21
 
22
+ Or install directly:
23
+
24
+ ```sh
25
+ gem install forme-ruby -v 0.2.0
26
+ ```
27
+
28
+ RubyGems selects a matching native package where available. The same packages
29
+ and checksums are available in the [GitHub release](https://github.com/clearstackio/forme-ruby/releases/tag/v0.2.0).
30
+
24
31
  ```ruby
25
32
  require "forme_pdf"
26
33
 
@@ -28,21 +35,14 @@ pdf = FormePDF.render_html("<h1>Hello from Ruby</h1>")
28
35
  File.binwrite("hello.pdf", pdf)
29
36
  ```
30
37
 
31
- Ruby 3.2 or later is required. Planned native packages target Apple Silicon macOS
32
- 15+ and Linux AMD64 glibc 2.28+ and need no compiler. Source installs require
33
- network access to locked upstream dependencies. See [installation](docs/installation.md).
34
-
35
- ## Native platform targets
38
+ ## Requirements
36
39
 
37
- | Native gem | Qualification environments |
38
- | --- | --- |
39
- | `arm64-darwin` | Apple Silicon macOS 15 and 26 |
40
- | `x86_64-linux-gnu` | Red Hat UBI 8, 9 and 10; Ubuntu 24.04 and 26.04; Arch Linux rolling |
40
+ - Ruby 3.3 or later.
41
+ - Native gems: macOS 15+ on Apple Silicon, or Linux AMD64 with glibc 2.28+.
42
+ - Source gem: Rust/Cargo 1.94.0, a C linker, make and network access to the
43
+ locked upstream dependencies.
41
44
 
42
- These are the release CI targets; qualification of this prepared commit is
43
- pending. Red Hat testing uses official UBI images, not RHEL certification.
44
- Linux ARM64, Windows and Alpine/musl binaries are not provided. See the
45
- [installation guide](docs/installation.md) for Ruby and source-build requirements.
45
+ See the [installation guide](docs/installation.md).
46
46
 
47
47
  ## Using Rails
48
48
 
@@ -65,7 +65,7 @@ Rails is optional. The [Rails guide](docs/rails.md) also covers the opt-in
65
65
  - [Release guide](docs/releasing.md): package qualification and future publication.
66
66
  - [Changelog](CHANGELOG.md), [license](LICENSE) and [third-party notices](THIRD_PARTY_LICENSES.txt).
67
67
 
68
- Use trusted HTML and assets. The engine can read local image paths and is not a
69
- sandbox for untrusted documents. See [security](SECURITY.md). Report reproducible
68
+ Use trusted HTML and assets. Local image files are read only inside an explicit
69
+ `image_root:`, and the engine is not a sandbox for untrusted documents. See [security](SECURITY.md). Report reproducible
70
70
  bugs through [GitHub issues](https://github.com/clearstackio/forme-ruby/issues)
71
71
  with synthetic input, Ruby/platform versions and renderer diagnostics.
data/SECURITY.md CHANGED
@@ -1,7 +1,9 @@
1
1
  # Security
2
2
 
3
- Render trusted HTML and caller-selected assets only. Forme can read local image
4
- paths and consumes CPU/memory in the host process. It is not an HTML sandbox;
3
+ Render trusted HTML and caller-selected assets only. Local image files are read
4
+ only inside a caller-supplied `image_root:`, at most 64 MiB each. Rendering
5
+ consumes CPU/memory in the host process, and some layouts, such as deeply nested
6
+ tables, can take far longer than their size suggests. It is not an HTML sandbox;
5
7
  thread cancellation is not a reliable rendering timeout.
6
8
 
7
9
  Keep confidential documents out of logs and public issue attachments. Use a
@@ -9,9 +11,6 @@ minimal synthetic reproduction when reporting a problem. Send potential
9
11
  security vulnerabilities privately to ajaya@clearstack.io rather than posting
10
12
  sensitive details publicly. No response-time guarantee is offered.
11
13
 
12
- The initial 0.1.0 release is still in preparation. Before deployment, qualify the
13
- exact package and pinned native dependencies on your application's target.
14
-
15
14
  Dependency audit exceptions and their call-path evidence are recorded in
16
15
  [the security review](docs/security-exceptions.md). They must be re-reviewed
17
16
  when the pinned engine or public native interface changes.
@@ -3236,7 +3236,7 @@ DEALINGS IN THE SOFTWARE.
3236
3236
 
3237
3237
 
3238
3238
 
3239
- crc32fast 1.5.2 — MIT OR Apache-2.0
3239
+ crc32fast 1.5.3 — MIT OR Apache-2.0
3240
3240
  https://github.com/srijs/rust-crc32fast
3241
3241
 
3242
3242
  Apache License
@@ -6631,7 +6631,7 @@ DEALINGS IN THE SOFTWARE.
6631
6631
 
6632
6632
 
6633
6633
 
6634
- forme-pdf 0.24.0 — MIT
6634
+ forme-pdf 0.28.0 — MIT
6635
6635
  https://github.com/formepdf/forme
6636
6636
 
6637
6637
  MIT License
@@ -6658,7 +6658,7 @@ SOFTWARE.
6658
6658
 
6659
6659
 
6660
6660
 
6661
- forme-pdf-html 0.24.0 — MIT
6661
+ forme-pdf-html 0.28.0 — MIT
6662
6662
  https://github.com/formepdf/forme
6663
6663
 
6664
6664
  MIT License
@@ -8240,7 +8240,7 @@ SOFTWARE.
8240
8240
 
8241
8241
 
8242
8242
 
8243
- lazy_static 1.5.0 — MIT OR Apache-2.0
8243
+ lazy_static 1.5.1 — MIT OR Apache-2.0
8244
8244
  https://github.com/rust-lang-nursery/lazy-static.rs
8245
8245
 
8246
8246
  Apache License
@@ -8446,8 +8446,6 @@ See the License for the specific language governing permissions and
8446
8446
  limitations under the License.
8447
8447
 
8448
8448
 
8449
- Copyright (c) 2010 The Rust Project Developers
8450
-
8451
8449
  Permission is hereby granted, free of charge, to any
8452
8450
  person obtaining a copy of this software and associated
8453
8451
  documentation files (the "Software"), to deal in the
@@ -8474,7 +8472,7 @@ DEALINGS IN THE SOFTWARE.
8474
8472
 
8475
8473
 
8476
8474
 
8477
- libc 0.2.189 — MIT OR Apache-2.0
8475
+ libc 0.2.191 — MIT OR Apache-2.0
8478
8476
  https://github.com/rust-lang/libc
8479
8477
 
8480
8478
  Apache License
@@ -16370,7 +16368,7 @@ DEALINGS IN THE SOFTWARE.
16370
16368
 
16371
16369
 
16372
16370
 
16373
- serde_json 1.0.151 — MIT OR Apache-2.0
16371
+ serde_json 1.0.154 — MIT OR Apache-2.0
16374
16372
  https://github.com/serde-rs/json
16375
16373
 
16376
16374
  Apache License
@@ -17074,7 +17072,7 @@ SOFTWARE.
17074
17072
 
17075
17073
 
17076
17074
 
17077
- siphasher 1.0.3 — MIT/Apache-2.0
17075
+ siphasher 1.0.4 — MIT OR Apache-2.0
17078
17076
  https://github.com/jedisct1/rust-siphash
17079
17077
 
17080
17078
  Copyright 2012-2016 The Rust Project Developers.
@@ -17087,7 +17085,230 @@ option.
17087
17085
 
17088
17086
 
17089
17087
 
17090
- smallvec 1.16.1 — MIT OR Apache-2.0
17088
+ Apache License
17089
+ Version 2.0, January 2004
17090
+ http://www.apache.org/licenses/
17091
+
17092
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
17093
+
17094
+ 1. Definitions.
17095
+
17096
+ "License" shall mean the terms and conditions for use, reproduction,
17097
+ and distribution as defined by Sections 1 through 9 of this document.
17098
+
17099
+ "Licensor" shall mean the copyright owner or entity authorized by
17100
+ the copyright owner that is granting the License.
17101
+
17102
+ "Legal Entity" shall mean the union of the acting entity and all
17103
+ other entities that control, are controlled by, or are under common
17104
+ control with that entity. For the purposes of this definition,
17105
+ "control" means (i) the power, direct or indirect, to cause the
17106
+ direction or management of such entity, whether by contract or
17107
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
17108
+ outstanding shares, or (iii) beneficial ownership of such entity.
17109
+
17110
+ "You" (or "Your") shall mean an individual or Legal Entity
17111
+ exercising permissions granted by this License.
17112
+
17113
+ "Source" form shall mean the preferred form for making modifications,
17114
+ including but not limited to software source code, documentation
17115
+ source, and configuration files.
17116
+
17117
+ "Object" form shall mean any form resulting from mechanical
17118
+ transformation or translation of a Source form, including but
17119
+ not limited to compiled object code, generated documentation,
17120
+ and conversions to other media types.
17121
+
17122
+ "Work" shall mean the work of authorship, whether in Source or
17123
+ Object form, made available under the License, as indicated by a
17124
+ copyright notice that is included in or attached to the work
17125
+ (an example is provided in the Appendix below).
17126
+
17127
+ "Derivative Works" shall mean any work, whether in Source or Object
17128
+ form, that is based on (or derived from) the Work and for which the
17129
+ editorial revisions, annotations, elaborations, or other modifications
17130
+ represent, as a whole, an original work of authorship. For the purposes
17131
+ of this License, Derivative Works shall not include works that remain
17132
+ separable from, or merely link (or bind by name) to the interfaces of,
17133
+ the Work and Derivative Works thereof.
17134
+
17135
+ "Contribution" shall mean any work of authorship, including
17136
+ the original version of the Work and any modifications or additions
17137
+ to that Work or Derivative Works thereof, that is intentionally
17138
+ submitted to Licensor for inclusion in the Work by the copyright owner
17139
+ or by an individual or Legal Entity authorized to submit on behalf of
17140
+ the copyright owner. For the purposes of this definition, "submitted"
17141
+ means any form of electronic, verbal, or written communication sent
17142
+ to the Licensor or its representatives, including but not limited to
17143
+ communication on electronic mailing lists, source code control systems,
17144
+ and issue tracking systems that are managed by, or on behalf of, the
17145
+ Licensor for the purpose of discussing and improving the Work, but
17146
+ excluding communication that is conspicuously marked or otherwise
17147
+ designated in writing by the copyright owner as "Not a Contribution."
17148
+
17149
+ "Contributor" shall mean Licensor and any individual or Legal Entity
17150
+ on behalf of whom a Contribution has been received by Licensor and
17151
+ subsequently incorporated within the Work.
17152
+
17153
+ 2. Grant of Copyright License. Subject to the terms and conditions of
17154
+ this License, each Contributor hereby grants to You a perpetual,
17155
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
17156
+ copyright license to reproduce, prepare Derivative Works of,
17157
+ publicly display, publicly perform, sublicense, and distribute the
17158
+ Work and such Derivative Works in Source or Object form.
17159
+
17160
+ 3. Grant of Patent License. Subject to the terms and conditions of
17161
+ this License, each Contributor hereby grants to You a perpetual,
17162
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
17163
+ (except as stated in this section) patent license to make, have made,
17164
+ use, offer to sell, sell, import, and otherwise transfer the Work,
17165
+ where such license applies only to those patent claims licensable
17166
+ by such Contributor that are necessarily infringed by their
17167
+ Contribution(s) alone or by combination of their Contribution(s)
17168
+ with the Work to which such Contribution(s) was submitted. If You
17169
+ institute patent litigation against any entity (including a
17170
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
17171
+ or a Contribution incorporated within the Work constitutes direct
17172
+ or contributory patent infringement, then any patent licenses
17173
+ granted to You under this License for that Work shall terminate
17174
+ as of the date such litigation is filed.
17175
+
17176
+ 4. Redistribution. You may reproduce and distribute copies of the
17177
+ Work or Derivative Works thereof in any medium, with or without
17178
+ modifications, and in Source or Object form, provided that You
17179
+ meet the following conditions:
17180
+
17181
+ (a) You must give any other recipients of the Work or
17182
+ Derivative Works a copy of this License; and
17183
+
17184
+ (b) You must cause any modified files to carry prominent notices
17185
+ stating that You changed the files; and
17186
+
17187
+ (c) You must retain, in the Source form of any Derivative Works
17188
+ that You distribute, all copyright, patent, trademark, and
17189
+ attribution notices from the Source form of the Work,
17190
+ excluding those notices that do not pertain to any part of
17191
+ the Derivative Works; and
17192
+
17193
+ (d) If the Work includes a "NOTICE" text file as part of its
17194
+ distribution, then any Derivative Works that You distribute must
17195
+ include a readable copy of the attribution notices contained
17196
+ within such NOTICE file, excluding those notices that do not
17197
+ pertain to any part of the Derivative Works, in at least one
17198
+ of the following places: within a NOTICE text file distributed
17199
+ as part of the Derivative Works; within the Source form or
17200
+ documentation, if provided along with the Derivative Works; or,
17201
+ within a display generated by the Derivative Works, if and
17202
+ wherever such third-party notices normally appear. The contents
17203
+ of the NOTICE file are for informational purposes only and
17204
+ do not modify the License. You may add Your own attribution
17205
+ notices within Derivative Works that You distribute, alongside
17206
+ or as an addendum to the NOTICE text from the Work, provided
17207
+ that such additional attribution notices cannot be construed
17208
+ as modifying the License.
17209
+
17210
+ You may add Your own copyright statement to Your modifications and
17211
+ may provide additional or different license terms and conditions
17212
+ for use, reproduction, or distribution of Your modifications, or
17213
+ for any such Derivative Works as a whole, provided Your use,
17214
+ reproduction, and distribution of the Work otherwise complies with
17215
+ the conditions stated in this License.
17216
+
17217
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
17218
+ any Contribution intentionally submitted for inclusion in the Work
17219
+ by You to the Licensor shall be under the terms and conditions of
17220
+ this License, without any additional terms or conditions.
17221
+ Notwithstanding the above, nothing herein shall supersede or modify
17222
+ the terms of any separate license agreement you may have executed
17223
+ with Licensor regarding such Contributions.
17224
+
17225
+ 6. Trademarks. This License does not grant permission to use the trade
17226
+ names, trademarks, service marks, or product names of the Licensor,
17227
+ except as required for reasonable and customary use in describing the
17228
+ origin of the Work and reproducing the content of the NOTICE file.
17229
+
17230
+ 7. Disclaimer of Warranty. Unless required by applicable law or
17231
+ agreed to in writing, Licensor provides the Work (and each
17232
+ Contributor provides its Contributions) on an "AS IS" BASIS,
17233
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
17234
+ implied, including, without limitation, any warranties or conditions
17235
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
17236
+ PARTICULAR PURPOSE. You are solely responsible for determining the
17237
+ appropriateness of using or redistributing the Work and assume any
17238
+ risks associated with Your exercise of permissions under this License.
17239
+
17240
+ 8. Limitation of Liability. In no event and under no legal theory,
17241
+ whether in tort (including negligence), contract, or otherwise,
17242
+ unless required by applicable law (such as deliberate and grossly
17243
+ negligent acts) or agreed to in writing, shall any Contributor be
17244
+ liable to You for damages, including any direct, indirect, special,
17245
+ incidental, or consequential damages of any character arising as a
17246
+ result of this License or out of the use or inability to use the
17247
+ Work (including but not limited to damages for loss of goodwill,
17248
+ work stoppage, computer failure or malfunction, or any and all
17249
+ other commercial damages or losses), even if such Contributor
17250
+ has been advised of the possibility of such damages.
17251
+
17252
+ 9. Accepting Warranty or Additional Liability. While redistributing
17253
+ the Work or Derivative Works thereof, You may choose to offer,
17254
+ and charge a fee for, acceptance of support, warranty, indemnity,
17255
+ or other liability obligations and/or rights consistent with this
17256
+ License. However, in accepting such obligations, You may act only
17257
+ on Your own behalf and on Your sole responsibility, not on behalf
17258
+ of any other Contributor, and only if You agree to indemnify,
17259
+ defend, and hold each Contributor harmless for any liability
17260
+ incurred by, or claims asserted against, such Contributor by reason
17261
+ of your accepting any such warranty or additional liability.
17262
+
17263
+ END OF TERMS AND CONDITIONS
17264
+
17265
+ APPENDIX: How to apply the Apache License to your work.
17266
+
17267
+ To apply the Apache License to your work, attach the following
17268
+ boilerplate notice, with the fields enclosed by brackets "[]"
17269
+ replaced with your own identifying information. (Don't include
17270
+ the brackets!) The text should be enclosed in the appropriate
17271
+ comment syntax for the file format. We also recommend that a
17272
+ file or class name and description of purpose be included on the
17273
+ same "printed page" as the copyright notice for easier
17274
+ identification within third-party archives.
17275
+
17276
+ Copyright [yyyy] [name of copyright owner]
17277
+
17278
+ Licensed under the Apache License, Version 2.0 (the "License");
17279
+ you may not use this file except in compliance with the License.
17280
+ You may obtain a copy of the License at
17281
+
17282
+ http://www.apache.org/licenses/LICENSE-2.0
17283
+
17284
+ Unless required by applicable law or agreed to in writing, software
17285
+ distributed under the License is distributed on an "AS IS" BASIS,
17286
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17287
+ See the License for the specific language governing permissions and
17288
+ limitations under the License.
17289
+
17290
+
17291
+ Permission is hereby granted, free of charge, to any person obtaining a copy
17292
+ of this software and associated documentation files (the "Software"), to deal
17293
+ in the Software without restriction, including without limitation the rights
17294
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17295
+ copies of the Software, and to permit persons to whom the Software is
17296
+ furnished to do so, subject to the following conditions:
17297
+
17298
+ The above copyright notice and this permission notice shall be included in all
17299
+ copies or substantial portions of the Software.
17300
+
17301
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17302
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17303
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17304
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17305
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
17306
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
17307
+ SOFTWARE.
17308
+
17309
+
17310
+
17311
+ smallvec 1.16.3 — MIT OR Apache-2.0
17091
17312
  https://github.com/servo/rust-smallvec
17092
17313
 
17093
17314
  Apache License
@@ -18291,7 +18512,7 @@ DEALINGS IN THE SOFTWARE.
18291
18512
 
18292
18513
 
18293
18514
 
18294
- syn 3.0.6 — MIT OR Apache-2.0
18515
+ syn 3.0.9 — MIT OR Apache-2.0
18295
18516
  https://github.com/dtolnay/syn
18296
18517
 
18297
18518
  Apache License
@@ -18732,7 +18953,7 @@ DEALINGS IN THE SOFTWARE.
18732
18953
 
18733
18954
 
18734
18955
 
18735
- thiserror 2.0.20 — MIT OR Apache-2.0
18956
+ thiserror 2.0.21 — MIT OR Apache-2.0
18736
18957
  https://github.com/dtolnay/thiserror
18737
18958
 
18738
18959
  Apache License
@@ -18939,7 +19160,7 @@ DEALINGS IN THE SOFTWARE.
18939
19160
 
18940
19161
 
18941
19162
 
18942
- thiserror-impl 2.0.20 — MIT OR Apache-2.0
19163
+ thiserror-impl 2.0.21 — MIT OR Apache-2.0
18943
19164
  https://github.com/dtolnay/thiserror
18944
19165
 
18945
19166
  Apache License
@@ -20768,7 +20989,7 @@ SOFTWARE.
20768
20989
 
20769
20990
 
20770
20991
 
20771
- unicode-ident 1.0.26 — (MIT OR Apache-2.0) AND Unicode-3.0
20992
+ unicode-ident 1.0.27 — (MIT OR Apache-2.0) AND Unicode-3.0
20772
20993
  https://github.com/dtolnay/unicode-ident
20773
20994
 
20774
20995
  Apache License
@@ -23307,7 +23528,7 @@ DEALINGS IN THE SOFTWARE.
23307
23528
 
23308
23529
 
23309
23530
 
23310
- zerocopy 0.8.57 — BSD-2-Clause OR Apache-2.0 OR MIT
23531
+ zerocopy 0.8.63 — BSD-2-Clause OR Apache-2.0 OR MIT
23311
23532
  https://github.com/google/zerocopy
23312
23533
 
23313
23534
  Apache License
@@ -23569,7 +23790,7 @@ DEALINGS IN THE SOFTWARE.
23569
23790
 
23570
23791
 
23571
23792
 
23572
- zerocopy-derive 0.8.57 — BSD-2-Clause OR Apache-2.0 OR MIT
23793
+ zerocopy-derive 0.8.63 — BSD-2-Clause OR Apache-2.0 OR MIT
23573
23794
  https://github.com/google/zerocopy
23574
23795
 
23575
23796
  Apache License
@@ -23831,7 +24052,7 @@ DEALINGS IN THE SOFTWARE.
23831
24052
 
23832
24053
 
23833
24054
 
23834
- zeroize 1.9.0 — Apache-2.0 OR MIT
24055
+ zeroize 1.9.1 — Apache-2.0 OR MIT
23835
24056
  https://github.com/RustCrypto/utils
23836
24057
 
23837
24058
 
data/docs/github-setup.md CHANGED
@@ -9,7 +9,7 @@ RubyGems settings a maintainer must enable. These instructions prepare
9
9
  The repository includes an MIT license, contributor and security guides, issue
10
10
  forms, a pull-request template, CODEOWNERS, Dependabot, CodeQL, dependency review,
11
11
  and build/release workflows. Actions are pinned to full commit SHAs; Dependabot
12
- proposes updates. CI runs on pull requests, main pushes and a weekly schedule.
12
+ proposes updates. CI runs on pull requests, master pushes and a weekly schedule.
13
13
  PR jobs do not receive publishing credentials. Checkout does not persist tokens.
14
14
 
15
15
  ## Configure GitHub after an authorized push
@@ -20,7 +20,7 @@ PR jobs do not receive publishing credentials. Checkout does not persist tokens.
20
20
  graph, Dependabot alerts, secret scanning and push protection where available.
21
21
  CodeQL is provided as an advanced workflow for Ruby, Rust and GitHub Actions; do not also enable
22
22
  CodeQL default setup.
23
- 3. After the first successful run, protect `main` with a ruleset requiring pull
23
+ 3. After the first successful run, protect `master` with a ruleset requiring pull
24
24
  requests, one approving review, resolved conversations, current branches and
25
25
  the `CI required`, CodeQL matrix and dependency-review checks. Select the
26
26
  actual check names from GitHub. Block deletion and force pushes. Require code
@@ -28,26 +28,28 @@ PR jobs do not receive publishing credentials. Checkout does not persist tokens.
28
28
  pull requests can be reviewed. Do not silently bypass review to unblock them.
29
29
  4. Allow only reviewed Actions, keep the default workflow token read-only, and
30
30
  disable Actions creating or approving pull requests. Keep fork approval on.
31
- 5. Create environment `rubygems`, restrict deployment to `main`, and configure a
31
+ 5. Create environment `rubygems`, restrict deployment to `v*` tags, and configure a
32
32
  trusted maintainer as required reviewer with self-review prevented. Confirm
33
33
  an eligible second reviewer exists before enabling this requirement.
34
34
  6. Configure RubyGems trusted publishing exactly as [releasing](releasing.md)
35
35
  specifies. Do not store a long-lived publishing key in repository secrets.
36
36
 
37
- The existing remote has older history. The prepared local root commit replaces
38
- that history only after explicit authorization and consumer coordination. Enable
39
- force-push protection after that authorized initial transition. A backup local
40
- branch preserves the prior history. Never force-push just to make CI pass.
37
+ The authorized initial history consolidation is complete. Remote branch
38
+ `archive/pre-0.1.0-squash` preserves the old consumer pin. The `rubygems`
39
+ environment deploys only from `v*` tags (it allowed the default branch for the manual 0.1.0
40
+ release). RubyGems trusted publishing successfully published 0.1.0. A second reviewer remains a
41
+ separate repository setup task.
42
+ Never force-push just to make CI pass.
41
43
 
42
44
  ## Release evidence
43
45
 
44
- `CI required` fails if any Ruby test, package build, distribution installation or
46
+ `CI required` fails if any Ruby test, package build, package installation or
45
47
  audit fails or is skipped. Linux packages are built against glibc 2.28 and checked
46
48
  for newer symbol requirements. Compatibility jobs install the same artifact,
47
- not a newly rebuilt library. Rolling Arch is deliberately retested weekly.
49
+ not a newly rebuilt library.
48
50
 
49
- The manual release workflow accepts only a successful CI run from this repository,
50
- for the exact main commit. It validates package names, versions, platforms and
51
+ The tag-triggered release workflow accepts only a successful CI run from this
52
+ repository, for the exact master commit the tag points to. It validates package names, versions, platforms and
51
53
  license files before trusted publication, and records an attestation for the
52
54
  promotion workflow. Keep the successful CI URL with release notes. GitHub
53
55
  settings, hosted matrix success and RubyGems setup must be confirmed separately;
data/docs/installation.md CHANGED
@@ -5,49 +5,33 @@ source and native packages, and how to check the installed renderer.
5
5
 
6
6
  ## Requirements
7
7
 
8
- Use CRuby 3.2 or later. CI covers Ruby 3.2, 3.3, 3.4 and 4.0 on Linux AMD64 and
9
- macOS ARM64. The core has no Rails dependency. JRuby, Windows, musl and other
10
- architectures are not qualified release targets.
8
+ CRuby 3.3 or later. Rails is optional.
11
9
 
12
- | Package | Requirement |
10
+ | Package | Needs |
13
11
  | --- | --- |
14
- | Git checkout or `ruby` source gem | Rust/Cargo 1.94.0 (qualified toolchain), linker, make, network access to pinned Git and crates.io dependencies. |
15
- | `arm64-darwin` | Apple Silicon, macOS 15 or later. |
16
- | `x86_64-linux-gnu` | AMD64 Linux, glibc 2.28 or later. |
12
+ | `arm64-darwin` | macOS 15 or later on Apple Silicon. |
13
+ | `x86_64-linux-gnu` | Linux AMD64 with glibc 2.28 or later. |
14
+ | `ruby` source gem or Git checkout | Rust/Cargo 1.94.0, a C linker, make and network access to the pinned Git and crates.io dependencies. |
17
15
 
18
- ## Native qualification
16
+ Native gems need no compiler and no system Forme library.
19
17
 
20
- The Linux library is built inside pinned Red Hat UBI 8 with a baseline x86-64
21
- CPU target. CI rejects symbols requiring glibc newer than 2.28. The same gem
22
- is installed and renders a PDF in UBI 8, 9 and 10, Ubuntu 24.04 and 26.04,
23
- and current Arch Linux, without Rust installed. UBI testing exercises the
24
- RHEL userspace baseline; it is not a certification of every RHEL deployment.
25
- Use a Ruby 3.2+ installation even when a distribution defaults to older Ruby.
18
+ ## Install from GitHub
26
19
 
27
- The macOS gem is built on Apple Silicon macOS 15 with a 15.0 deployment target
28
- and installed on macOS 15 and 26. Both native artifacts are also installed
29
- across CRuby 3.2, 3.3, 3.4 and 4.0. Hosted qualification of this prepared commit
30
- is pending. These tests establish installation/rendering compatibility, not
31
- identical pagination for every font and document.
20
+ Download the matching native gem and `SHA256SUMS` from the
21
+ [0.2.0 release](https://github.com/clearstackio/forme-ruby/releases/tag/v0.2.0).
22
+ Check its digest with `shasum -a 256 <filename>` against the checksum file, then
23
+ run `gem install ./<filename>`.
32
24
 
33
- ## Install before the first release
25
+ ## Install from RubyGems
34
26
 
35
- Version 0.1.0 is not yet published to RubyGems. Add the Git dependency shown in
36
- [README](../README.md) and run `bundle install`. Commit `Gemfile.lock` so builds
37
- use a reviewed revision. Compilation happens during installation; no compiler
38
- or dependency download runs during `require` or rendering.
39
-
40
- ## Install a released version later
41
-
42
- After publication is confirmed, use:
27
+ Add to your Gemfile and run `bundle install`:
43
28
 
44
29
  ```ruby
45
- gem "forme-ruby", "~> 0.1.0", require: "forme_pdf"
30
+ gem "forme-ruby", "~> 0.2.0", require: "forme_pdf"
46
31
  ```
47
32
 
48
- Bundler selects a matching native package where available. A source fallback
49
- requires the build tools above; it is not a promise of support for every platform.
50
- Native packages load their own shared library without a system Forme install.
33
+ Bundler selects a matching native package where available and otherwise
34
+ installs the source gem, which needs the build tools above.
51
35
 
52
36
  ## Verify the installation
53
37
 
data/docs/releasing.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Preparing and Releasing Forme Ruby
2
2
 
3
3
  After reading this guide, you will know how to qualify source/native artifacts
4
- and which steps publish externally. **Current status: 0.1.0 is unreleased.**
4
+ and which steps publish externally. **Published versions are listed in the [changelog](../CHANGELOG.md).**
5
5
  Local preparation does not authorize a push, tag or RubyGems publication.
6
6
 
7
7
  ## Prepare locally
@@ -18,22 +18,20 @@ Local preparation does not authorize a push, tag or RubyGems publication.
18
18
  Previously successful CI does not qualify the new commit. Consumers must not
19
19
  lock to an unpublished SHA. Do not change remote history during local preparation.
20
20
 
21
- ## Qualify the release commit later
21
+ ## Qualify the release commit
22
22
 
23
23
  After a separately authorized GitHub push, require successful `ci.yml` for that
24
- exact `main` commit. CI tests CRuby 3.2, 3.3, 3.4 and 4.0 on Ubuntu 24.04 and
25
- macOS 15, builds source/native packages and clean-installs them. The same Linux
26
- artifact renders on UBI 8/9/10, Ubuntu 24.04/26.04 and Arch rolling; the macOS
27
- artifact renders on macOS 15/26. See [installation](installation.md). Review the three intended artifacts: `ruby`,
28
- `arm64-darwin`, and `x86_64-linux-gnu`. No Windows or musl binary is claimed.
24
+ exact `master` commit, including every package installation job. Review the three
25
+ intended artifacts: `ruby`, `arm64-darwin` and `x86_64-linux-gnu`. The
26
+ [installation requirements](installation.md) must match what CI qualifies.
29
27
 
30
- The repository already has published history. Replacing it after a local root
31
- squash would be a remote history rewrite, requiring separate authorization and
32
- coordination with consumers. Keep the existing reachable consumer pin until then.
28
+ The initial history consolidation is complete. Branch `archive/pre-0.1.0-squash`
29
+ preserves the earlier history and existing consumer pin. Future releases should
30
+ use normal commits without rewriting published history.
33
31
 
34
32
  Review [GitHub repository setup](github-setup.md) before enabling releases.
35
33
 
36
- ## Configure trusted publishing later
34
+ ## Configure trusted publishing
37
35
 
38
36
  Under the `ajaya` RubyGems account, configure a pending publisher for the new gem:
39
37
 
@@ -52,32 +50,48 @@ for setup. No long-lived RubyGems API key is required by this workflow.
52
50
 
53
51
  ## Publish only with explicit authorization
54
52
 
55
- The manual `release.yml` workflow takes a successful CI run ID and verifies its
56
- commit, branch and workflow path against the release invocation. It downloads
57
- those tested artifacts and publishes them; it does not rebuild them at release
58
- time. Package validation requires exactly the three intended platforms and
59
- matching names/versions. A provenance attestation records the release workflow
60
- that promotes those artifacts; it does not replace the linked CI build evidence.
61
- Treat dispatching it as publication, not a dry run.
62
-
63
- Before dispatch, confirm the version is available, the package inventory is
64
- correct and all three tested artifacts belong to the intended commit. Afterward,
65
- verify all platforms on RubyGems, install from RubyGems in clean environments and
66
- record the release date/tag. Partial publication cannot be overwritten: inspect
67
- which platforms succeeded before deciding how to recover. Update consumer
68
- Gemfiles only after the version is available and qualified.
69
-
70
- ## Evidence and remaining gates
71
-
72
- The earlier `b6d287c` commit passed hosted Linux/macOS CI, including AlmaLinux 10.
73
- That is historical evidence, not approval of a new local squashed SHA. Hosted CI,
74
- trusted-publisher configuration and actual publication remain pending for the
75
- prepared release. No release workflow is run by local package tasks.
76
-
77
- Local preparation on 2026-09-18 verified macOS source/native installations and
78
- native Linux rendering in UBI 8/9, Ubuntu 24.04/26.04 and Arch. The UBI 8 build
79
- passed the glibc 2.28 symbol check. Arch's local QEMU run needed pacman's sandbox
80
- disabled; the committed hosted job uses its normal sandbox. UBI 10 could not
81
- start under the local emulator because it requires an x86-64-v3 CPU. Its hosted
82
- x86 check remains mandatory. These local results do not qualify the complete
83
- hosted matrix or authorize publication.
53
+ Pushing a `v*` tag publishes. `release.yml` runs on the tag and:
54
+
55
+ 1. checks the tag equals `v` plus `FormePDF::VERSION`;
56
+ 2. finds the successful `ci.yml` run on `master` for the tagged commit and
57
+ downloads its three packages, rebuilding nothing;
58
+ 3. validates names, versions, platforms, license files and build hooks;
59
+ 4. records a GitHub build-provenance attestation for those packages;
60
+ 5. creates the GitHub release with the packages and `SHA256SUMS`, or, when the
61
+ release already exists, requires its packages to match byte-for-byte;
62
+ 6. signs each package with Sigstore and pushes it to RubyGems through trusted
63
+ publishing with that attestation, then waits until RubyGems serves it.
64
+
65
+ This follows the RubyGems [trusted publishing release guide](https://guides.rubygems.org/trusted-publishing/releasing-gems/)
66
+ and our other gems:
67
+
68
+ 1. Update `VERSION` in `lib/forme_pdf/version.rb`.
69
+ 2. Move the Unreleased CHANGELOG entries under the new version and date.
70
+ 3. Commit to `master` and wait for CI to pass on that commit.
71
+ 4. Tag and push: `git tag vX.Y.Z && git push origin master vX.Y.Z`.
72
+
73
+ Treat the tag push as publication, not a dry run. Afterward, verify all three
74
+ platforms and their attestations on RubyGems and install from RubyGems in a clean
75
+ environment. Partial publication cannot be overwritten: inspect which platforms
76
+ succeeded before deciding how to recover. Update consumer Gemfiles only after
77
+ the version is available and qualified.
78
+
79
+ ## Release evidence
80
+
81
+ [GitHub release 0.1.0](https://github.com/clearstackio/forme-ruby/releases/tag/v0.1.0)
82
+ contains the source gem, both native gems and SHA-256 checksums from
83
+ [CI run 35415089757](https://github.com/clearstackio/forme-ruby/actions/runs/35415089757).
84
+ Every job passed on commit `266628a20a0a52bad222c8f8e6d2277b08122896`, including
85
+ every package installation job. CodeQL also
86
+ passed. Packages were downloaded and validated before release; the macOS native
87
+ gem was additionally installed and rendered locally outside the checkout.
88
+
89
+ The GitHub `rubygems` environment exists.
90
+ [Registry publication run 35415885728](https://github.com/clearstackio/forme-ruby/actions/runs/35415885728)
91
+ succeeded through trusted publishing for all three platforms. Downloads from
92
+ RubyGems matched the CI and GitHub release artifacts byte-for-byte. A fresh
93
+ macOS ARM registry installation loaded the native library and rendered a PDF.
94
+
95
+ The publisher is configured; do not recreate it for subsequent versions.
96
+ A published version must never be pushed again. Use a new
97
+ version for future releases and qualify its packages before publication.
data/docs/rendering.md CHANGED
@@ -20,8 +20,22 @@ ASCII-8BIT String. Always write with `File.binwrite`.
20
20
 
21
21
  Forme supports a subset of HTML and print CSS, not browser layout or JavaScript.
22
22
  Use dedicated print styles and inspect the resulting pages. Inline CSS or pass
23
- `css:`; embed images as data URIs. The binding does not fetch remote stylesheets
24
- or fonts. Local image paths are possible, so use trusted input only.
23
+ `css:`; embed images as data URIs. The binding does not fetch remote stylesheets,
24
+ fonts or images.
25
+
26
+ Image sources starting with `/`, `./` or `../` are files. They are omitted with a
27
+ warning unless you pass `image_root:`, a directory that must contain them:
28
+
29
+ ```ruby
30
+ pdf = FormePDF.render_html(
31
+ '<img src="./logo.png" width="120">',
32
+ image_root: Rails.root.join("app/assets/images")
33
+ )
34
+ ```
35
+
36
+ Relative paths resolve against `image_root`; absolute paths and symlinks must
37
+ resolve inside it. Each file must be a regular PNG, JPEG or WebP file of at most
38
+ 64 MiB. Warnings name the reason, never the path.
25
39
 
26
40
  Supply TTF font bytes explicitly:
27
41
 
@@ -32,9 +46,10 @@ pdf = FormePDF.render_html(
32
46
  )
33
47
  ```
34
48
 
35
- `css:` and `fonts:` are the supported rendering keywords. Font entries accept
36
- `family`, `data`, `weight` (default 400) and `italic` (default false). Unknown
37
- options fail. Register the weights/styles your document needs and check the PDF.
49
+ `css:`, `fonts:` and `image_root:` are the supported rendering keywords. Font
50
+ entries accept `family`, `data`, `weight` (an Integer from 1 to 1000, default
51
+ 400) and `italic` (true or false, default false). Unknown or invalid options
52
+ raise `ArgumentError`. Register the weights/styles your document needs and check the PDF.
38
53
 
39
54
  ## Inspect diagnostics
40
55
 
@@ -52,12 +67,16 @@ send sensitive document content or diagnostics to public logs.
52
67
  ## Handle errors and concurrency
53
68
 
54
69
  `FormePDF::LoadError` and `FormePDF::RenderError` inherit `FormePDF::Error`.
55
- Invalid caller arguments can raise `ArgumentError` or encoding errors. Avoid
70
+ Invalid caller arguments, including text that is not valid UTF-8, raise
71
+ `ArgumentError`. Documents nested more than 512 elements deep raise
72
+ `FormePDF::RenderError` instead of rendering. Avoid
56
73
  rescuing everything and returning an HTML success response as if it were a PDF.
57
74
 
58
75
  Each render owns independent native result storage. Ruby copies the bytes and
59
76
  frees that result even when copying or decoding fails. Native calls release the
60
- Ruby lock; concurrent callers do not share output buffers. Ruby interruption is
77
+ Ruby lock and render on a native thread with its own 64 MiB stack, so deep
78
+ documents behave the same on Puma, Sidekiq and `Thread.new` threads as on the
79
+ main thread. Concurrent callers do not share output buffers. Ruby interruption is
61
80
  masked during native ownership, so thread cancellation is not a render deadline.
62
81
  Use process-level resource limits for large or hostile inputs. The API is not a
63
82
  sandbox and exposes no per-render timeout.
@@ -1,12 +1,12 @@
1
1
  # Reviewing Native Dependency Advisories
2
2
 
3
3
  After reading this guide, you will understand the current audit exceptions and
4
- when they must be reconsidered. Review date: 2026-09-18.
4
+ when they must be reconsidered. Review date: 2026-10-20.
5
5
 
6
6
  ## Scope
7
7
 
8
8
  These exceptions acknowledge vulnerable dependencies; they do not patch them.
9
- They apply only to Forme revision `f408920e632c59da0651b5b6d32f8c1397477673`,
9
+ They apply only to Forme 0.28.0 revision `b97dc101b705ea5d7fea6fb1b20fabfb5bd2d63d`,
10
10
  the locked dependency graph and the HTML-only Ruby API. Rendering is not a
11
11
  sandbox for arbitrary HTML. See [SECURITY](../SECURITY.md).
12
12
 
@@ -18,8 +18,11 @@ sandbox for arbitrary HTML. See [SECURITY](../SECURITY.md).
18
18
 
19
19
  ## Verify the boundaries
20
20
 
21
- `ext/forme_pdf/src/lib.rs` accepts only CSS and fonts, rejects unknown options,
22
- and calls `forme_pdf_html::render_html` with default HTML options. In the pinned
21
+ `ext/forme_pdf/src/lib.rs` accepts only CSS, fonts and an image root, rejects
22
+ unknown options, and reproduces `forme_pdf_html::render_html` with default HTML
23
+ options: `html_to_document`, then `forme::render_with_options`. Between the two
24
+ it replaces file image sources with data URIs or empties them, so the engine
25
+ never reads files itself; image bytes still reach only the image loader. In the pinned
23
26
  upstream tree, `html/src/dom.rs` parses with html5ever. `html/src/map.rs` has no
24
27
  `NodeKind::Svg` construction and initializes `certification: None`.
25
28
  `engine/src/image_loader.rs` accepts JPEG, PNG and WebP, so an SVG image cannot
@@ -5,6 +5,8 @@ require "forme_pdf"
5
5
  module FormePDF
6
6
  # Explicitly include this module in an ActionController; no Railtie or middleware.
7
7
  module Rails
8
+ private
9
+
8
10
  # Render a caller-selected Rails template into binary PDF bytes.
9
11
  # The controller retains responsibility for authorization and send_data.
10
12
  def render_forme_pdf(template:, layout:, locals: {}, **options)
@@ -6,19 +6,12 @@ module FormePDF
6
6
  # One native render call with caller-owned input and independently owned output.
7
7
  class Renderer
8
8
  # Return PDF bytes, renderer warnings and layout pass count.
9
- def self.call(html, css: nil, fonts: [])
9
+ def self.call(html, css: nil, fonts: [], image_root: nil)
10
10
  fail ArgumentError, "HTML must be a String" unless html.is_a?(String)
11
- html = html.dup
12
- html.force_encoding(Encoding::UTF_8) if html.encoding == Encoding::BINARY
13
- fail ArgumentError, "HTML must be valid UTF-8" unless html.valid_encoding?
14
- html = html.encode(Encoding::UTF_8)
11
+ html = utf8(html, "HTML")
15
12
  fail ArgumentError, "CSS must be a String or nil" unless css.nil? || css.is_a?(String)
16
13
  fail ArgumentError, "fonts must be an Array" unless fonts.is_a?(Array)
17
- options = JSON.generate(css: css, fonts: fonts.map { |font|
18
- fail ArgumentError, "font must contain family and data" unless font.is_a?(Hash) && font[:family].is_a?(String) && font[:data].is_a?(String)
19
- fail ArgumentError, "unknown font option" unless (font.keys - %i[family data weight italic]).empty?
20
- {family: font.fetch(:family), data: Base64.strict_encode64(font.fetch(:data)), weight: font.fetch(:weight, 400), italic: font.fetch(:italic, false)}
21
- })
14
+ options = JSON.generate(css: css && utf8(css, "CSS"), fonts: fonts.map { |font| font_option(font) }, image_root: image_root_option(image_root))
22
15
  Native.load!
23
16
  input = FFI::MemoryPointer.from_string(html)
24
17
  opts = FFI::MemoryPointer.from_string(options)
@@ -36,5 +29,32 @@ module FormePDF
36
29
  Native.forme_result_destroy(result) if result && !result.null?
37
30
  end
38
31
  end
32
+
33
+ # Binary text is read as UTF-8; other valid encodings are transcoded.
34
+ def self.utf8(text, name)
35
+ text = text.dup
36
+ text.force_encoding(Encoding::UTF_8) if text.encoding == Encoding::BINARY
37
+ fail ArgumentError, "#{name} must be valid UTF-8" unless text.valid_encoding?
38
+ text.encode(Encoding::UTF_8)
39
+ end
40
+
41
+ def self.font_option(font)
42
+ fail ArgumentError, "font must contain family and data" unless font.is_a?(Hash) && font[:family].is_a?(String) && font[:data].is_a?(String)
43
+ fail ArgumentError, "unknown font option" unless (font.keys - %i[family data weight italic]).empty?
44
+ weight = font.fetch(:weight, 400)
45
+ italic = font.fetch(:italic, false)
46
+ fail ArgumentError, "font weight must be an Integer from 1 to 1000" unless weight.is_a?(Integer) && weight.between?(1, 1000)
47
+ fail ArgumentError, "font italic must be true or false" unless [true, false].include?(italic)
48
+ {family: utf8(font[:family], "font family"), data: Base64.strict_encode64(font[:data]), weight: weight, italic: italic}
49
+ end
50
+
51
+ def self.image_root_option(root)
52
+ return if root.nil?
53
+ fail ArgumentError, "image_root must be a String or Pathname" unless root.is_a?(String) || root.respond_to?(:to_path)
54
+ root = File.expand_path(root.respond_to?(:to_path) ? root.to_path : root)
55
+ fail ArgumentError, "image_root must be an existing directory" unless File.directory?(root)
56
+ utf8(root, "image_root")
57
+ end
58
+ private_class_method :utf8, :font_option, :image_root_option
39
59
  end
40
60
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module FormePDF
4
- VERSION = "0.1.0"
5
- ENGINE_REVISION = "f408920e632c59da0651b5b6d32f8c1397477673"
4
+ VERSION = "0.2.0"
5
+ ENGINE_REVISION = "b97dc101b705ea5d7fea6fb1b20fabfb5bd2d63d"
6
6
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: forme-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: arm64-darwin
6
6
  authors:
7
7
  - Ajaya Agrawalla
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-09-19 00:00:00.000000000 Z
11
+ date: 2026-10-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ffi
@@ -47,7 +47,7 @@ dependencies:
47
47
  version: '2.0'
48
48
  - - "<"
49
49
  - !ruby/object:Gem::Version
50
- version: '3'
50
+ version: '4'
51
51
  type: :runtime
52
52
  prerelease: false
53
53
  version_requirements: !ruby/object:Gem::Requirement
@@ -57,7 +57,7 @@ dependencies:
57
57
  version: '2.0'
58
58
  - - "<"
59
59
  - !ruby/object:Gem::Version
60
- version: '3'
60
+ version: '4'
61
61
  description: Independent Ruby bindings for the Rust Forme HTML renderer. No browser,
62
62
  Node.js, WebAssembly or separate rendering service.
63
63
  email:
@@ -95,9 +95,9 @@ licenses:
95
95
  - MIT
96
96
  metadata:
97
97
  source_code_uri: https://github.com/clearstackio/forme-ruby
98
- documentation_uri: https://github.com/clearstackio/forme-ruby/blob/main/docs/README.md
98
+ documentation_uri: https://github.com/clearstackio/forme-ruby/blob/master/docs/README.md
99
99
  bug_tracker_uri: https://github.com/clearstackio/forme-ruby/issues
100
- changelog_uri: https://github.com/clearstackio/forme-ruby/blob/main/CHANGELOG.md
100
+ changelog_uri: https://github.com/clearstackio/forme-ruby/blob/master/CHANGELOG.md
101
101
  rubygems_mfa_required: 'true'
102
102
  allowed_push_host: https://rubygems.org
103
103
  post_install_message:
@@ -108,7 +108,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
108
108
  requirements:
109
109
  - - ">="
110
110
  - !ruby/object:Gem::Version
111
- version: '3.2'
111
+ version: '3.3'
112
112
  required_rubygems_version: !ruby/object:Gem::Requirement
113
113
  requirements:
114
114
  - - ">="