forme-ruby 0.1.0

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.
data/docs/README.md ADDED
@@ -0,0 +1,18 @@
1
+ # Forme Ruby Guides
2
+
3
+ These guides introduce native PDF rendering, then cover application integration,
4
+ development and release. Each guide states its prerequisites and includes runnable
5
+ examples. Begin with [installation](installation.md).
6
+
7
+ | Guide | You will learn |
8
+ | --- | --- |
9
+ | [Installation](installation.md) | Runtime requirements, Git/source installs and native packages. |
10
+ | [Rendering](rendering.md) | Binary results, CSS, fonts, diagnostics and errors. |
11
+ | [Rails](rails.md) | Explicit controller rendering and ownership of responses. |
12
+ | [Contributing](../CONTRIBUTING.md) | Build, test and review changes. |
13
+ | [GitHub setup](github-setup.md) | Repository protections, security and publishing setup. |
14
+ | [Security exceptions](security-exceptions.md) | Reviewed, inaccessible upstream advisory paths. |
15
+ | [Releasing](releasing.md) | Qualify packages and prepare trusted publishing. |
16
+
17
+ See [README](../README.md) for a quick example and [CHANGELOG](../CHANGELOG.md)
18
+ for version-specific changes. The gem remains independent of consumer applications.
@@ -0,0 +1,60 @@
1
+ # Setting Up the GitHub Repository
2
+
3
+ After reading this guide, you will know what is checked in and which GitHub and
4
+ RubyGems settings a maintainer must enable. These instructions prepare
5
+ `clearstackio/forme-ruby`; they do not authorize pushing or publishing.
6
+
7
+ ## Repository files
8
+
9
+ The repository includes an MIT license, contributor and security guides, issue
10
+ forms, a pull-request template, CODEOWNERS, Dependabot, CodeQL, dependency review,
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.
13
+ PR jobs do not receive publishing credentials. Checkout does not persist tokens.
14
+
15
+ ## Configure GitHub after an authorized push
16
+
17
+ 1. Set the description to “Native HTML and print CSS to PDF for Ruby and Rails,
18
+ powered by Forme.” Use topics `ruby`, `rails`, `pdf`, `rust`, `html-to-pdf`.
19
+ 2. Enable Issues and private vulnerability reporting. Enable the dependency
20
+ graph, Dependabot alerts, secret scanning and push protection where available.
21
+ CodeQL is provided as an advanced workflow for Ruby, Rust and GitHub Actions; do not also enable
22
+ CodeQL default setup.
23
+ 3. After the first successful run, protect `main` with a ruleset requiring pull
24
+ requests, one approving review, resolved conversations, current branches and
25
+ the `CI required`, CodeQL matrix and dependency-review checks. Select the
26
+ actual check names from GitHub. Block deletion and force pushes. Require code
27
+ owner review for changes; add another trusted maintainer so the sole owner's
28
+ pull requests can be reviewed. Do not silently bypass review to unblock them.
29
+ 4. Allow only reviewed Actions, keep the default workflow token read-only, and
30
+ disable Actions creating or approving pull requests. Keep fork approval on.
31
+ 5. Create environment `rubygems`, restrict deployment to `main`, and configure a
32
+ trusted maintainer as required reviewer with self-review prevented. Confirm
33
+ an eligible second reviewer exists before enabling this requirement.
34
+ 6. Configure RubyGems trusted publishing exactly as [releasing](releasing.md)
35
+ specifies. Do not store a long-lived publishing key in repository secrets.
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.
41
+
42
+ ## Release evidence
43
+
44
+ `CI required` fails if any Ruby test, package build, distribution installation or
45
+ audit fails or is skipped. Linux packages are built against glibc 2.28 and checked
46
+ for newer symbol requirements. Compatibility jobs install the same artifact,
47
+ not a newly rebuilt library. Rolling Arch is deliberately retested weekly.
48
+
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
+ license files before trusted publication, and records an attestation for the
52
+ promotion workflow. Keep the successful CI URL with release notes. GitHub
53
+ settings, hosted matrix success and RubyGems setup must be confirmed separately;
54
+ local tests do not establish any of them.
55
+
56
+ See GitHub's [secure Actions guidance](https://docs.github.com/en/actions/reference/security/secure-use)
57
+ and RubyGems' [trusted publishing guide](https://guides.rubygems.org/trusted-publishing/).
58
+
59
+ Rust scanning uses CodeQL’s supported `none` build mode with the pinned Rust
60
+ toolchain; see [GitHub’s Rust scanning requirements](https://docs.github.com/en/code-security/reference/code-scanning/codeql/build-options-for-compiled-languages#building-rust).
@@ -0,0 +1,60 @@
1
+ # Installing Forme Ruby
2
+
3
+ After reading this guide, you will know how installation differs between Git,
4
+ source and native packages, and how to check the installed renderer.
5
+
6
+ ## Requirements
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.
11
+
12
+ | Package | Requirement |
13
+ | --- | --- |
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. |
17
+
18
+ ## Native qualification
19
+
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.
26
+
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.
32
+
33
+ ## Install before the first release
34
+
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:
43
+
44
+ ```ruby
45
+ gem "forme-ruby", "~> 0.1.0", require: "forme_pdf"
46
+ ```
47
+
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.
51
+
52
+ ## Verify the installation
53
+
54
+ ```bash
55
+ bundle exec ruby -rforme_pdf -e 'FormePDF.verify!; abort "Invalid PDF" unless FormePDF.render_html("<h1>Hello</h1>").start_with?("%PDF-")'
56
+ ```
57
+
58
+ `verify!` checks loading and ABI compatibility; the render exercises the engine.
59
+ For `FormePDF::LoadError`, check the installed platform and library permissions.
60
+ Reinstall the matching gem instead of copying a library from another platform.
data/docs/rails.md ADDED
@@ -0,0 +1,56 @@
1
+ # Rendering PDFs in Rails
2
+
3
+ After reading this guide, you will know how to render ERB to PDF while retaining
4
+ control of permissions, templates and HTTP responses. The core gem does not
5
+ require Rails; these examples assume an existing Rails application.
6
+
7
+ ## Render explicitly
8
+
9
+ Select and authorize records in your application's controller before rendering.
10
+ Supply a dedicated HTML template and layout:
11
+
12
+ ```ruby
13
+ html = render_to_string(template: "invoices/pdf", layout: "pdf", formats: [:html])
14
+ pdf = FormePDF.render_html(html)
15
+ send_data pdf, type: "application/pdf", disposition: "inline", filename: "invoice.pdf"
16
+ ```
17
+
18
+ Use `attachment` instead of `inline` when the application's download contract
19
+ requires it. Application code owns routes, record scopes, filenames, asset
20
+ selection and member-data masking. Keep browser print templates separate if
21
+ PDF styling needs different behavior.
22
+
23
+ ## Use the optional adapter
24
+
25
+ ```ruby
26
+ require "forme_pdf/rails"
27
+
28
+ class InvoicesController < ApplicationController
29
+ include FormePDF::Rails
30
+ end
31
+ ```
32
+
33
+ From an already authorized action:
34
+
35
+ ```ruby
36
+ pdf = render_forme_pdf(template: "invoices/pdf", layout: "pdf", locals: {invoice: @invoice})
37
+ send_data pdf, type: "application/pdf", disposition: "inline", filename: "invoice.pdf"
38
+ ```
39
+
40
+ `render_forme_pdf` returns bytes; it does not send a response. It passes
41
+ `formats: [:html]` to Rails and forwards rendering options to FormePDF. It does
42
+ not prepare assets, select records, install middleware, add routes or register
43
+ a Railtie. The host application supplies Rails and controls its lifecycle.
44
+
45
+ ## Verify at startup
46
+
47
+ Applications that require PDF availability can add this initializer:
48
+
49
+ ```ruby
50
+ FormePDF.verify!
51
+ ```
52
+
53
+ Load failure then prevents startup instead of silently disabling PDF reports.
54
+ Test actual rendering in the deployment image too. The adapter spec uses an
55
+ isolated controller double; consumers must test their actual Rails stack and
56
+ report routes. No specific Rails version matrix is claimed by this gem.
data/docs/releasing.md ADDED
@@ -0,0 +1,83 @@
1
+ # Preparing and Releasing Forme Ruby
2
+
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.**
5
+ Local preparation does not authorize a push, tag or RubyGems publication.
6
+
7
+ ## Prepare locally
8
+
9
+ 1. Review README, guides, AGENTS, CHANGELOG, gemspec metadata, LICENSE, NOTICE
10
+ and generated third-party notices. Keep public examples free of private data.
11
+ 2. Run the contributor checks and package-install checks. Inspect package files:
12
+ source gems contain the locked Rust sources/build hook; native gems contain
13
+ their library and no build hook. Confirm guides and all notices are included.
14
+ 3. Keep `lib/forme_pdf/version.rb` and CHANGELOG aligned. Do not date an unreleased
15
+ entry as if it were published. Review the tested Forme revision and platform
16
+ minimums whenever native dependencies change.
17
+ 4. If history is explicitly squashed, record old/new refs and keep a backup.
18
+ Previously successful CI does not qualify the new commit. Consumers must not
19
+ lock to an unpublished SHA. Do not change remote history during local preparation.
20
+
21
+ ## Qualify the release commit later
22
+
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.
29
+
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.
33
+
34
+ Review [GitHub repository setup](github-setup.md) before enabling releases.
35
+
36
+ ## Configure trusted publishing later
37
+
38
+ Under the `ajaya` RubyGems account, configure a pending publisher for the new gem:
39
+
40
+ | Field | Value |
41
+ | --- | --- |
42
+ | Gem | `forme-ruby` |
43
+ | GitHub owner | `clearstackio` |
44
+ | Repository | `forme-ruby` |
45
+ | Workflow | `release.yml` |
46
+ | Environment | `rubygems` |
47
+
48
+ Use the matching GitHub environment and review its protections. A pending
49
+ publisher becomes associated with the gem after its first successful upload.
50
+ See the [official RubyGems trusted publishing guide](https://guides.rubygems.org/trusted-publishing/)
51
+ for setup. No long-lived RubyGems API key is required by this workflow.
52
+
53
+ ## Publish only with explicit authorization
54
+
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.
data/docs/rendering.md ADDED
@@ -0,0 +1,63 @@
1
+ # Rendering PDFs
2
+
3
+ After reading this guide, you will know how to render trusted HTML, register
4
+ fonts and inspect diagnostics. Install the gem before running these examples.
5
+
6
+ ## Render HTML
7
+
8
+ ```ruby
9
+ require "forme_pdf"
10
+
11
+ pdf = FormePDF.render_html("<h1>Invoice</h1>", css: "@page { size: Letter; margin: 0.75in; }")
12
+ File.binwrite("invoice.pdf", pdf)
13
+ ```
14
+
15
+ Input must be a String containing valid text. Valid non-UTF-8 encodings are
16
+ transcoded; binary input is interpreted as UTF-8. The result is a frozen binary
17
+ ASCII-8BIT String. Always write with `File.binwrite`.
18
+
19
+ ## Style pages and supply assets
20
+
21
+ Forme supports a subset of HTML and print CSS, not browser layout or JavaScript.
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.
25
+
26
+ Supply TTF font bytes explicitly:
27
+
28
+ ```ruby
29
+ pdf = FormePDF.render_html(
30
+ '<p style="font-family: Report">A document</p>',
31
+ fonts: [{family: "Report", data: File.binread("report.ttf"), weight: 400, italic: false}]
32
+ )
33
+ ```
34
+
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.
38
+
39
+ ## Inspect diagnostics
40
+
41
+ ```ruby
42
+ result = FormePDF.render_html_result("<p>Report</p>")
43
+ File.binwrite("report.pdf", result.pdf)
44
+ puts result.passes
45
+ result.warnings.each { |warning| warn warning }
46
+ ```
47
+
48
+ The result and its warning array/strings are frozen. Unsupported CSS can produce
49
+ warnings without a failed render. Review warnings using synthetic input; do not
50
+ send sensitive document content or diagnostics to public logs.
51
+
52
+ ## Handle errors and concurrency
53
+
54
+ `FormePDF::LoadError` and `FormePDF::RenderError` inherit `FormePDF::Error`.
55
+ Invalid caller arguments can raise `ArgumentError` or encoding errors. Avoid
56
+ rescuing everything and returning an HTML success response as if it were a PDF.
57
+
58
+ Each render owns independent native result storage. Ruby copies the bytes and
59
+ 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
61
+ masked during native ownership, so thread cancellation is not a render deadline.
62
+ Use process-level resource limits for large or hostile inputs. The API is not a
63
+ sandbox and exposes no per-render timeout.
@@ -0,0 +1,41 @@
1
+ # Reviewing Native Dependency Advisories
2
+
3
+ After reading this guide, you will understand the current audit exceptions and
4
+ when they must be reconsidered. Review date: 2026-09-18.
5
+
6
+ ## Scope
7
+
8
+ These exceptions acknowledge vulnerable dependencies; they do not patch them.
9
+ They apply only to Forme revision `f408920e632c59da0651b5b6d32f8c1397477673`,
10
+ the locked dependency graph and the HTML-only Ruby API. Rendering is not a
11
+ sandbox for arbitrary HTML. See [SECURITY](../SECURITY.md).
12
+
13
+ | Advisory | Locked package | Why the affected path is inaccessible |
14
+ | --- | --- | --- |
15
+ | [RUSTSEC-2026-0195](https://rustsec.org/advisories/RUSTSEC-2026-0195.html) | quick-xml 0.37.5 | The engine uses plain `Reader`, never `NsReader` or its namespace resolver. |
16
+ | [RUSTSEC-2026-0194](https://rustsec.org/advisories/RUSTSEC-2026-0194.html) | quick-xml 0.37.5 | Duplicate-attribute handling is vulnerable in upstream SVG parsing, but the HTML mapper never constructs SVG nodes. |
17
+ | [RUSTSEC-2023-0071](https://rustsec.org/advisories/RUSTSEC-2023-0071.html) | rsa 0.9.10 | Private-key operations occur only in certification, which HTML conversion leaves disabled. |
18
+
19
+ ## Verify the boundaries
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
23
+ upstream tree, `html/src/dom.rs` parses with html5ever. `html/src/map.rs` has no
24
+ `NodeKind::Svg` construction and initializes `certification: None`.
25
+ `engine/src/image_loader.rs` accepts JPEG, PNG and WebP, so an SVG image cannot
26
+ enter the XML parser through an image URL. Font registration does not parse XML.
27
+ Only `forme-pdf` consumes quick-xml and rsa in the locked dependency graph.
28
+
29
+ The exceptions are configured in `.cargo/audit.toml`. Scheduled CI continues to
30
+ report other advisories, including unmaintained dependency warnings. Re-review
31
+ all exceptions when the engine pin, options schema, Cargo features or exports
32
+ change, especially SVG, raw document/JSON, imports or signing. Check the exact
33
+ new call paths; do not carry exceptions forward automatically.
34
+
35
+ ## Remove exceptions when possible
36
+
37
+ Prefer an upstream update to quick-xml 0.41+ or feature-gating unused SVG and
38
+ certification dependencies. The current upstream 0.37 constraint prevents a
39
+ lockfile-only upgrade. RustSec lists no patched RSA version at review time;
40
+ a version bump alone is not remediation. Track upstream fixes during the weekly
41
+ dependency review and remove an exception once the dependency is fixed or removed.
data/examples/hello.rb ADDED
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "forme_pdf"
4
+
5
+ pdf = FormePDF.render_html("<h1>Hello from Ruby</h1>", css: "@page { size: Letter; }")
6
+ File.binwrite(ARGV.fetch(0, "hello.pdf"), pdf)