"); abort "bad pdf" unless pdf.start_with?("%PDF"); puts "ok"'
- uses: actions/upload-artifact@v4
with:
diff --git a/AGENTS.md b/AGENTS.md
index 42ded4c..f218b87 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -4,7 +4,7 @@
`page_print` is a Ruby gem with a native C extension that renders HTML strings to PDF files using the `plutobook` C library.
-The public Ruby API is intentionally small. `PagePrint.html_to_pdf` is the primary entry point.
+The public Ruby API is intentionally small. `PagePrint.render` is the primary entry point.
## Repository Layout
@@ -59,7 +59,7 @@ Use `bin/console` for a local development console. It compiles the extension bef
## Development Guidelines
- Keep the Ruby-facing API minimal and documented in `README.md`.
-- When changing `PagePrint.html_to_pdf`, update tests for both successful output and validation/error behavior.
+- When changing `PagePrint.render` or `PagePrint.render_to_file`, update tests for both successful output and validation/error behavior.
- Prefer inline Ruby calls for readability; only break Ruby argument lists across lines when the line would exceed 120 characters.
- Format Ruby private sections with `private` at the class indentation level and private method definitions indented beneath it, without a blank line between `private` and the first private method.
- Validate Ruby argument types before passing data into `plutobook`.
diff --git a/README.md b/README.md
index f7a12ef..059c994 100644
--- a/README.md
+++ b/README.md
@@ -33,7 +33,7 @@ Render a PDF from a controller:
class PrintsController < ApplicationController
def pdf
html = render_to_string(template: "prints/pdf", formats: [:html], layout: "pdf")
- pdf = PagePrint.html_to_pdf_string(
+ pdf = PagePrint.render(
html,
page_size: :a4,
margins: :normal,
@@ -58,7 +58,7 @@ During controller actions, PagePrint defaults `base_url` to `request.base_url`.
You can still override `base_url` explicitly:
```ruby
-pdf = PagePrint.html_to_pdf_string(html, base_url: "https://example.com")
+pdf = PagePrint.render(html, base_url: "https://example.com")
```
Override the fetcher only when needed:
@@ -87,19 +87,19 @@ HTML
output_path = File.join(Dir.tmpdir, "page_print-output.pdf")
-PagePrint.html_to_pdf(html, output_path, base_url: "https://example.com")
+PagePrint.render_to_file(html, output_path, base_url: "https://example.com")
```
To get the generated PDF as a binary string instead of writing directly to a file:
```ruby
-pdf = PagePrint.html_to_pdf_string(html, base_url: "https://example.com")
+pdf = PagePrint.render(html, base_url: "https://example.com")
```
Use custom page dimensions and margins when a preset is not enough:
```ruby
-pdf = PagePrint.html_to_pdf_string(
+pdf = PagePrint.render(
html,
page_size: { width: 100, height: 150, unit: :mm },
margins: { top: 5, right: 6, bottom: 7, left: 8, unit: :mm }
@@ -181,7 +181,7 @@ Native gems bundle PlutoBook and required non-system shared libraries. Optional
## Notes
-- The extension supports writing to a file path with `html_to_pdf` or returning PDF bytes with `html_to_pdf_string`.
+- The extension supports writing to a file path with `render_to_file` or returning PDF bytes with `render`.
- JavaScript execution is intentionally unsupported.
- Native gems disable PlutoBook's optional curl, TurboJPEG, and WebP features.
@@ -213,13 +213,13 @@ require "tmpdir"
output_path = File.join(Dir.tmpdir, "page_print-output.pdf")
-PagePrint.html_to_pdf("
", output_path, page_size: :letter, margins: :narrow, media: :screen)
```
You can also do a quick one-shot smoke test from the shell:
```sh
-bundle exec ruby -Ilib -e 'require "tmpdir"; require "page_print"; output_path = File.join(Dir.tmpdir, "page_print-output.pdf"); p PagePrint.html_to_pdf("
',
base_url: 'http://explicit.example',
resource_fetcher: lambda { |url|
@@ -180,53 +180,53 @@ def test_html_to_pdf_string_does_not_override_explicit_base_url
assert_equal '%PDF', pdf.byteslice(0, 4)
end
- def test_html_to_pdf_requires_html_to_be_a_string
+ def test_render_to_file_requires_html_to_be_a_string
error = assert_raises(TypeError) do
- PagePrint.html_to_pdf(123, 'output.pdf')
+ PagePrint.render_to_file(123, 'output.pdf')
end
assert_equal 'html must be a String', error.message
end
- def test_html_to_pdf_requires_path_to_be_a_string
+ def test_render_to_file_requires_path_to_be_a_string
error = assert_raises(TypeError) do
- PagePrint.html_to_pdf('
Hello
', 123)
+ PagePrint.render_to_file('
Hello
', 123)
end
assert_equal 'path must be a String', error.message
end
- def test_html_to_pdf_rejects_empty_html
+ def test_render_to_file_rejects_empty_html
error = assert_raises(ArgumentError) do
- PagePrint.html_to_pdf('', 'output.pdf')
+ PagePrint.render_to_file('', 'output.pdf')
end
assert_equal 'html must not be empty', error.message
end
- def test_html_to_pdf_rejects_empty_path
+ def test_render_to_file_rejects_empty_path
error = assert_raises(ArgumentError) do
- PagePrint.html_to_pdf('
Hello
', '')
+ PagePrint.render_to_file('
Hello
', '')
end
assert_equal 'path must not be empty', error.message
end
- def test_html_to_pdf_writes_output_file
+ def test_render_to_file_writes_output_file
Dir.mktmpdir do |dir|
output_path = File.join(dir, 'output.pdf')
- assert PagePrint.html_to_pdf('
', metadata: 'title')
end
assert_equal 'metadata must be a Hash or nil', error.message
end
- def test_html_to_pdf_string_requires_metadata_keys_to_be_symbols
+ def test_render_requires_metadata_keys_to_be_symbols
error = assert_raises(TypeError) do
- PagePrint.html_to_pdf_string('
', resource_fetcher: Object.new)
end
assert_equal 'resource_fetcher must respond to call or be nil/false', error.message
end
- def test_html_to_pdf_string_requires_resource_fetcher_result_to_be_a_hash_or_nil
+ def test_render_requires_resource_fetcher_result_to_be_a_hash_or_nil
error = assert_raises(TypeError) do
- PagePrint.html_to_pdf_string(
+ PagePrint.render(
'
Hello
',
resource_fetcher: ->(_url) { [] }
)
@@ -443,9 +443,9 @@ def test_html_to_pdf_string_requires_resource_fetcher_result_to_be_a_hash_or_nil
assert_equal 'resource_fetcher must return a Hash or nil', error.message
end
- def test_html_to_pdf_string_requires_resource_fetcher_content_to_be_a_string
+ def test_render_requires_resource_fetcher_content_to_be_a_string
error = assert_raises(TypeError) do
- PagePrint.html_to_pdf_string(
+ PagePrint.render(
'
Hello
',
resource_fetcher: ->(_url) { { content: nil, mime_type: 'text/css' } }
)
@@ -454,12 +454,12 @@ def test_html_to_pdf_string_requires_resource_fetcher_content_to_be_a_string
assert_equal 'resource_fetcher result content must be a String', error.message
end
- def test_html_to_pdf_string_rejects_resource_fetcher_content_larger_than_uint_max
+ def test_render_rejects_resource_fetcher_content_larger_than_uint_max
html = ''
content = oversized_string(2**32)
error = assert_raises(ArgumentError) do
- PagePrint.html_to_pdf_string(
+ PagePrint.render(
html,
resource_fetcher: ->(_url) { { content: content, mime_type: 'text/css' } }
)
@@ -468,9 +468,9 @@ def test_html_to_pdf_string_rejects_resource_fetcher_content_larger_than_uint_ma
assert_equal 'resource_fetcher result content is too large', error.message
end
- def test_html_to_pdf_string_requires_resource_fetcher_mime_type_to_be_a_string
+ def test_render_requires_resource_fetcher_mime_type_to_be_a_string
error = assert_raises(TypeError) do
- PagePrint.html_to_pdf_string(
+ PagePrint.render(
'
Hello
',
resource_fetcher: ->(_url) { { content: 'body {}', mime_type: nil } }
)
@@ -479,9 +479,9 @@ def test_html_to_pdf_string_requires_resource_fetcher_mime_type_to_be_a_string
assert_equal 'resource_fetcher result mime_type must be a String', error.message
end
- def test_html_to_pdf_string_reraises_resource_fetcher_errors
+ def test_render_reraises_resource_fetcher_errors
error = assert_raises(RuntimeError) do
- PagePrint.html_to_pdf_string(
+ PagePrint.render(
'
Hello
',
resource_fetcher: ->(_url) { raise 'fetch failed' }
)
@@ -490,11 +490,11 @@ def test_html_to_pdf_string_reraises_resource_fetcher_errors
assert_equal 'fetch failed', error.message
end
- def test_html_to_pdf_string_denies_network_fetch_when_resource_fetcher_returns_nil
+ def test_render_denies_network_fetch_when_resource_fetcher_returns_nil
html = ''
urls = []
- pdf = PagePrint.html_to_pdf_string(
+ pdf = PagePrint.render(
html,
resource_fetcher: ->(url) {
urls << url
@@ -507,82 +507,82 @@ def test_html_to_pdf_string_denies_network_fetch_when_resource_fetcher_returns_n
assert_equal '%PDF', pdf.byteslice(0, 4)
end
- def test_html_to_pdf_string_denies_network_fetch_without_resource_fetcher
+ def test_render_denies_network_fetch_without_resource_fetcher
html = ''
- pdf = PagePrint.html_to_pdf_string(html)
+ pdf = PagePrint.render(html)
assert_operator pdf.bytesize, :>, 0
assert_equal '%PDF', pdf.byteslice(0, 4)
end
- def test_html_to_pdf_string_requires_html_to_be_a_string
+ def test_render_requires_html_to_be_a_string
error = assert_raises(TypeError) do
- PagePrint.html_to_pdf_string(123)
+ PagePrint.render(123)
end
assert_equal 'html must be a String', error.message
end
- def test_html_to_pdf_string_rejects_empty_html
+ def test_render_rejects_empty_html
error = assert_raises(ArgumentError) do
- PagePrint.html_to_pdf_string('')
+ PagePrint.render('')
end
assert_equal 'html must not be empty', error.message
end
- def test_html_to_pdf_string_rejects_html_larger_than_int_max
+ def test_render_rejects_html_larger_than_int_max
html = oversized_string(2**31)
error = assert_raises(ArgumentError) do
- PagePrint.html_to_pdf_string(html)
+ PagePrint.render(html)
end
assert_equal 'html is too large', error.message
end
- def test_html_to_pdf_string_rejects_invalid_options
+ def test_render_rejects_invalid_options
error = assert_raises(TypeError) do
- PagePrint.html_to_pdf_string('
Hello
', :options)
+ PagePrint.render('
Hello
', :options)
end
assert_equal 'options must be a Hash', error.message
end
- def test_html_to_pdf_string_rejects_unknown_keyword
+ def test_render_rejects_unknown_keyword
error = assert_raises(ArgumentError) do
- PagePrint.html_to_pdf_string('
Hello
', foo: :bar)
+ PagePrint.render('
Hello
', foo: :bar)
end
assert_equal 'unknown keyword: :foo', error.message
end
- def test_html_to_pdf_string_rejects_invalid_media
+ def test_render_rejects_invalid_media
error = assert_raises(ArgumentError) do
- PagePrint.html_to_pdf_string('
Hello
', media: :speech)
+ PagePrint.render('
Hello
', media: :speech)
end
assert_equal 'media must be one of: :print, :screen', error.message
end
- def test_html_to_pdf_includes_path_when_pdf_write_fails
+ def test_render_to_file_includes_path_when_pdf_write_fails
Dir.mktmpdir do |dir|
output_path = File.join(dir, 'missing', 'output.pdf')
error = assert_raises(RuntimeError) do
- PagePrint.html_to_pdf('
Hello
', output_path)
+ PagePrint.render_to_file('
Hello
', output_path)
end
assert_match(/\Afailed to write PDF to #{Regexp.escape(output_path)}/, error.message)
end
end
- def test_html_to_pdf_accepts_nil_base_url
+ def test_render_to_file_accepts_nil_base_url
Dir.mktmpdir do |dir|
output_path = File.join(dir, 'output.pdf')
- assert PagePrint.html_to_pdf(
+ assert PagePrint.render_to_file(
'
Hello
',
output_path,
base_url: nil
@@ -593,129 +593,129 @@ def test_html_to_pdf_accepts_nil_base_url
end
end
- def test_html_to_pdf_requires_options_to_be_a_hash
+ def test_render_to_file_requires_options_to_be_a_hash
error = assert_raises(TypeError) do
- PagePrint.html_to_pdf('
', 'output.pdf', :options)
end
assert_equal 'options must be a Hash', error.message
end
- def test_html_to_pdf_requires_base_url_to_be_a_string_or_nil
+ def test_render_to_file_requires_base_url_to_be_a_string_or_nil
error = assert_raises(TypeError) do
- PagePrint.html_to_pdf('
', 'output.pdf', base_url: 123)
end
assert_equal 'base_url must be a String or nil', error.message
end
- def test_html_to_pdf_rejects_invalid_page_size
+ def test_render_to_file_rejects_invalid_page_size
error = assert_raises(ArgumentError) do
- PagePrint.html_to_pdf('
', 'output.pdf', page_size: 'letter')
end
assert_equal 'page_size must be a Symbol or Hash', error.message
end
- def test_html_to_pdf_requires_custom_page_size_width
+ def test_render_to_file_requires_custom_page_size_width
error = assert_raises(ArgumentError) do
- PagePrint.html_to_pdf('
', 'output.pdf', margins: :compact)
end
assert_equal 'margins must be one of: :none, :normal, :narrow, :moderate, :wide', error.message
end
- def test_html_to_pdf_requires_margins_to_be_a_symbol_or_hash
+ def test_render_to_file_requires_margins_to_be_a_symbol_or_hash
error = assert_raises(TypeError) do
- PagePrint.html_to_pdf('
', 'output.pdf', margins: 'narrow')
end
assert_equal 'margins must be a Symbol or Hash', error.message
end
- def test_html_to_pdf_requires_custom_margins_unit
+ def test_render_to_file_requires_custom_margins_unit
error = assert_raises(ArgumentError) do
- PagePrint.html_to_pdf('
', 'output.pdf', media: :speech)
end
assert_equal 'media must be one of: :print, :screen', error.message
end
- def test_html_to_pdf_requires_media_to_be_a_symbol
+ def test_render_to_file_requires_media_to_be_a_symbol
error = assert_raises(TypeError) do
- PagePrint.html_to_pdf('
', 'output.pdf', media: 'screen')
end
assert_equal 'media must be a Symbol', error.message
end
- def test_html_to_pdf_rejects_unknown_keyword
+ def test_render_to_file_rejects_unknown_keyword
error = assert_raises(ArgumentError) do
- PagePrint.html_to_pdf('