weasy_rb 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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: f62ee3336120e61a79a3c7eb6eeb857921f051c86fad540b334793b9aed0b102
4
+ data.tar.gz: 8639bbebf6d7f7a17c3ab6627b98873c2a0fa1d63f9d3fbe2af3c21e6cfeef27
5
+ SHA512:
6
+ metadata.gz: 18ffa8aef8c02d62ecf26b7a47bec9cf5e10099ac15e5ace73dfe711d348835d6568486d6788c27e9d3d9571a8ba78290d895fc0163263b26a0d0c6c4a80c6b9
7
+ data.tar.gz: 9b330514e4ef75b6eea4a4593b2ac4eadb21ae2cb2d438720c60e4b113368978f341d598b396a362b9e170c592905f16a193a1d2e1e72b9cd934bccdeeeef91e
data/.rubocop.yml ADDED
@@ -0,0 +1,13 @@
1
+ AllCops:
2
+ TargetRubyVersion: 3.1
3
+
4
+ Style/StringLiterals:
5
+ EnforcedStyle: double_quotes
6
+
7
+ Style/StringLiteralsInInterpolation:
8
+ EnforcedStyle: double_quotes
9
+
10
+ # Ignore method length in test files
11
+ Metrics/MethodLength:
12
+ Exclude:
13
+ - 'test/**/*'
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2025-07-31
4
+
5
+ - Initial release
data/DOCKER.md ADDED
@@ -0,0 +1,92 @@
1
+ # Docker Development Environment for WeasyRb
2
+
3
+ This directory contains Docker configuration files to make development easier by providing a consistent environment with all dependencies pre-installed.
4
+
5
+ ## Prerequisites
6
+
7
+ - Docker
8
+ - Docker Compose
9
+
10
+ ## Quick Start
11
+
12
+ ### Build the Docker image
13
+ ```bash
14
+ docker-compose build
15
+ ```
16
+
17
+ ### Run tests
18
+ ```bash
19
+ docker-compose run test
20
+ ```
21
+
22
+ ### Start interactive development shell
23
+ ```bash
24
+ docker-compose run weasy_rb
25
+ ```
26
+
27
+ ### Start Ruby console with gem loaded
28
+ ```bash
29
+ docker-compose run console
30
+ ```
31
+
32
+ ## Development Workflow
33
+
34
+ 1. **Make changes** to your code in your host environment
35
+ 2. **Run tests** using Docker:
36
+ ```bash
37
+ docker-compose run test
38
+ ```
39
+ 3. **Debug interactively**:
40
+ ```bash
41
+ docker-compose run weasy_rb bash
42
+ # Inside container:
43
+ bundle exec rake test
44
+ bin/console
45
+ ```
46
+
47
+ ## What's Included
48
+
49
+ The Docker environment includes:
50
+ - Ruby 3.4
51
+ - WeasyPrint (Python library)
52
+ - All system dependencies for WeasyPrint
53
+ - All Ruby gem dependencies
54
+ - Development tools
55
+
56
+ ## Container Commands
57
+
58
+ ### Run specific test files
59
+ ```bash
60
+ docker-compose run weasy_rb bundle exec ruby test/test_converter.rb
61
+ ```
62
+
63
+ ### Run RuboCop
64
+ ```bash
65
+ docker-compose run weasy_rb bundle exec rubocop
66
+ ```
67
+
68
+ ### Install new gems
69
+ ```bash
70
+ docker-compose run weasy_rb bundle install
71
+ ```
72
+
73
+ ### Generate a test PDF
74
+ ```bash
75
+ docker-compose run weasy_rb ruby -e "
76
+ require './lib/weasy_rb'
77
+ html = '<html><body><h1>Hello from Docker!</h1></body></html>'
78
+ WeasyRb.html_to_pdf(html, '/tmp/test.pdf')
79
+ puts 'PDF created at /tmp/test.pdf'
80
+ "
81
+ ```
82
+
83
+ ## Troubleshooting
84
+
85
+ ### Permission Issues
86
+ If you encounter permission issues with mounted volumes, you may need to adjust the user in the Dockerfile or run with appropriate permissions.
87
+
88
+ ### WeasyPrint Issues
89
+ The Docker image includes all necessary system dependencies for WeasyPrint. If you encounter issues, check that the container has access to the required fonts and libraries.
90
+
91
+ ### Bundle Cache
92
+ The docker-compose.yml uses a named volume for bundle cache to speed up subsequent builds and runs.
data/Dockerfile ADDED
@@ -0,0 +1,43 @@
1
+ # Use official Ruby image as base
2
+ FROM ruby:3.4-slim
3
+
4
+ # Install system dependencies required for WeasyPrint
5
+ RUN apt-get update && apt-get install -y \
6
+ python3 \
7
+ python3-pip \
8
+ python3-venv \
9
+ build-essential \
10
+ git \
11
+ libpango-1.0-0 \
12
+ libharfbuzz0b \
13
+ libpangoft2-1.0-0 \
14
+ libfontconfig1 \
15
+ libcairo-gobject2 \
16
+ libgdk-pixbuf-2.0-0 \
17
+ shared-mime-info \
18
+ libyaml-dev \
19
+ && rm -rf /var/lib/apt/lists/*
20
+
21
+ # Install WeasyPrint via pip
22
+ RUN pip3 install --break-system-packages weasyprint
23
+
24
+ # Set working directory
25
+ WORKDIR /app
26
+
27
+ # Copy Gemfile and Gemfile.lock
28
+ COPY Gemfile Gemfile.lock weasy_rb.gemspec ./
29
+ COPY lib/weasy_rb/version.rb ./lib/weasy_rb/
30
+
31
+ # Install Ruby dependencies
32
+ RUN bundle install
33
+
34
+ # Copy the rest of the application
35
+ COPY . .
36
+
37
+ # Create a non-root user for development
38
+ RUN useradd -m -s /bin/bash developer && \
39
+ chown -R developer:developer /app
40
+ USER developer
41
+
42
+ # Default command
43
+ CMD ["bundle", "exec", "rake", "test"]
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2025 ElectionBuddy Inc.
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
13
+ all 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
21
+ THE SOFTWARE.
data/Makefile ADDED
@@ -0,0 +1,35 @@
1
+ # WeasyRb Docker Development Scripts
2
+
3
+ ## Build and test
4
+ build-and-test:
5
+ docker-compose build
6
+ docker-compose run test
7
+
8
+ ## Interactive development shell
9
+ dev:
10
+ docker-compose run weasy_rb bash
11
+
12
+ ## Ruby console
13
+ console:
14
+ docker-compose run console
15
+
16
+ ## Run tests
17
+ test:
18
+ docker-compose run test
19
+
20
+ ## Run linter
21
+ lint:
22
+ docker-compose run weasy_rb bundle exec rubocop
23
+
24
+ ## Clean up Docker resources
25
+ clean:
26
+ docker-compose down
27
+ docker system prune -f
28
+
29
+ ## Rebuild from scratch
30
+ rebuild:
31
+ docker-compose down
32
+ docker-compose build --no-cache
33
+ docker-compose run test
34
+
35
+ .PHONY: build-and-test dev console test lint clean rebuild
data/README.md ADDED
@@ -0,0 +1,158 @@
1
+ # WeasyRb
2
+
3
+ A dead simple Ruby wrapper for [WeasyPrint](https://github.com/Kozea/WeasyPrint), the Python library that converts HTML documents to PDF files. WeasyRb provides a clean and Ruby-idiomatic interface to WeasyPrint while handling the complexities of process execution and error handling.
4
+
5
+ ## Features
6
+
7
+ - **Simple API**: Convert HTML to PDF with a single method call
8
+ - **Clean Architecture**: Follows SOLID principles and Clean Code practices
9
+ - **Error Handling**: Comprehensive error handling with meaningful error messages
10
+ - **Flexible Options**: Support for WeasyPrint's various configuration options
11
+ - **Process Safety**: Uses Open3 for safe subprocess execution
12
+
13
+ ## Prerequisites
14
+
15
+ WeasyPrint must be installed on your system. You can install it via pip:
16
+
17
+ ```bash
18
+ pip install weasyprint
19
+ ```
20
+
21
+ For more installation options, see the [WeasyPrint documentation](https://doc.courtbouillon.org/weasyprint/stable/first_steps.html#installation).
22
+
23
+ ## Installation
24
+
25
+ Add this line to your application's Gemfile:
26
+
27
+ ```ruby
28
+ gem "weasy_rb"
29
+ ```
30
+
31
+ And then execute:
32
+
33
+ ```bash
34
+ bundle install
35
+ ```
36
+
37
+ Or install it yourself as:
38
+
39
+ ```bash
40
+ gem install weasy_rb
41
+ ```
42
+
43
+ ## Usage
44
+
45
+ ### Basic Usage
46
+
47
+ ```ruby
48
+ require "weasy_rb"
49
+
50
+ html_content = "<html><body><h1>Hello, PDF!</h1></body></html>"
51
+ output_path = "/tmp/output.pdf"
52
+
53
+ # Convert HTML to PDF
54
+ WeasyRb.html_to_pdf(html_content, output_path)
55
+ ```
56
+
57
+ ### With Options
58
+
59
+ ```ruby
60
+ require "weasy_rb"
61
+
62
+ html_content = File.read("document.html")
63
+ output_path = "output.pdf"
64
+
65
+ options = {
66
+ base_url: "file:///path/to/assets/",
67
+ format: "A4",
68
+ media_type: "print",
69
+ stylesheets: ["style.css"],
70
+ encoding: "utf-8"
71
+ }
72
+
73
+ WeasyRb.html_to_pdf(html_content, output_path, options)
74
+ ```
75
+
76
+ ### Available Options
77
+
78
+ - `base_url`: Base URL for resolving relative URLs in the HTML
79
+ - `format`: Page format (A4, Letter, etc.)
80
+ - `media_type`: CSS media type ("print" or "screen")
81
+ - `stylesheets`: Array of stylesheet paths to include
82
+ - `encoding`: Character encoding for the HTML content
83
+ - `verbose`: Enable verbose output for debugging
84
+
85
+ ### Docker Examples
86
+
87
+ Try the included examples to see WeasyRb in action:
88
+
89
+ ```bash
90
+ # Run the basic example
91
+ docker-compose run --rm weasy_rb ruby examples/basic_example.rb
92
+
93
+ # Run the decorated demo (with subtle emoji support)
94
+ docker-compose run --rm weasy_rb ruby examples/decorated_demo.rb
95
+
96
+ # Interactive exploration
97
+ docker-compose run --rm console
98
+ ```
99
+
100
+ ## Error Handling
101
+
102
+ WeasyRb provides specific error classes for different failure scenarios:
103
+
104
+ ```ruby
105
+ begin
106
+ WeasyRb.html_to_pdf(html_content, output_path)
107
+ rescue WeasyRb::CommandError => e
108
+ # WeasyPrint is not installed or command execution failed
109
+ puts "Command error: #{e.message}"
110
+ rescue WeasyRb::ConversionError => e
111
+ # PDF conversion failed
112
+ puts "Conversion error: #{e.message}"
113
+ rescue ArgumentError => e
114
+ # Invalid input parameters
115
+ puts "Invalid input: #{e.message}"
116
+ end
117
+ ```
118
+
119
+ ## Development
120
+
121
+ ### Local Development
122
+
123
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
124
+
125
+ ### Docker Development (Recommended)
126
+
127
+ For the easiest development experience, use Docker which includes all dependencies:
128
+
129
+ ```bash
130
+ # Build and run tests
131
+ make build-and-test
132
+
133
+ # Interactive development shell
134
+ make dev
135
+
136
+ # Ruby console with gem loaded
137
+ make console
138
+
139
+ # Run tests
140
+ make test
141
+
142
+ # Run linter
143
+ make lint
144
+ ```
145
+
146
+ See [DOCKER.md](DOCKER.md) for detailed Docker development instructions.
147
+
148
+ ### Publishing
149
+
150
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
151
+
152
+ ## Contributing
153
+
154
+ Bug reports and pull requests are welcome on GitHub at https://github.com/Rynaro/weasy_rb.
155
+
156
+ ## License
157
+
158
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "minitest/test_task"
5
+
6
+ Minitest::TestTask.create
7
+
8
+ require "rubocop/rake_task"
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[test rubocop]
@@ -0,0 +1,41 @@
1
+ version: '3.8'
2
+
3
+ services:
4
+ weasy_rb:
5
+ build: .
6
+ volumes:
7
+ - .:/app
8
+ - bundle_cache:/usr/local/bundle
9
+ environment:
10
+ - BUNDLE_PATH=/usr/local/bundle
11
+ working_dir: /app
12
+ command: bash
13
+ stdin_open: true
14
+ tty: true
15
+
16
+ # Service for running tests
17
+ test:
18
+ build: .
19
+ volumes:
20
+ - .:/app
21
+ - bundle_cache:/usr/local/bundle
22
+ environment:
23
+ - BUNDLE_PATH=/usr/local/bundle
24
+ working_dir: /app
25
+ command: bash -c "bundle install && bundle exec rake test"
26
+
27
+ # Service for running console/development
28
+ console:
29
+ build: .
30
+ volumes:
31
+ - .:/app
32
+ - bundle_cache:/usr/local/bundle
33
+ environment:
34
+ - BUNDLE_PATH=/usr/local/bundle
35
+ working_dir: /app
36
+ command: bash -c "bundle install && bin/console"
37
+ stdin_open: true
38
+ tty: true
39
+
40
+ volumes:
41
+ bundle_cache:
@@ -0,0 +1,76 @@
1
+ # WeasyRb Examples
2
+
3
+ This directory contains example scripts demonstrating WeasyRb functionality.
4
+
5
+ # WeasyRb Examples
6
+
7
+ This directory contains two example scripts demonstrating WeasyRb functionality.
8
+
9
+ ## 📁 Files
10
+
11
+ ### `basic_example.rb`
12
+ A simple, minimal demonstration of WeasyRb:
13
+ - Clean, straightforward HTML to PDF conversion
14
+ - Basic CSS styling
15
+ - Simple error handling
16
+ - No decorative elements
17
+
18
+ **Generated Output:** `basic_example.pdf` (~12KB)
19
+
20
+ ### `decorated_demo.rb`
21
+ A more sophisticated demonstration showcasing WeasyPrint's capabilities:
22
+ - Modern CSS features (gradients, shadows, blur effects)
23
+ - Professional layout and typography
24
+ - Subtle emoji support with proper font fallbacks
25
+ - Performance benchmarking
26
+ - Advanced styling techniques
27
+
28
+ **Generated Output:** `decorated_demo.pdf` (~20KB)
29
+
30
+ ## 🚀 Running Examples
31
+
32
+ ### Using Docker (Recommended)
33
+ ```bash
34
+ # Run the basic example
35
+ docker-compose run --rm weasy_rb ruby examples/basic_example.rb
36
+
37
+ # Run the decorated demo
38
+ docker-compose run --rm weasy_rb ruby examples/decorated_demo.rb
39
+
40
+ # Interactive exploration
41
+ docker-compose run --rm console
42
+ ```
43
+
44
+ ### Using Make Commands
45
+ ```bash
46
+ # Run demo with convenient make command
47
+ make dev
48
+ # Then inside container:
49
+ ruby examples/docker_demo.rb
50
+ ```
51
+
52
+ ## 📖 Viewing Generated PDFs
53
+
54
+ The examples generate PDF files that you can open with any PDF viewer:
55
+
56
+ - **Linux:** `xdg-open examples/weasy_rb_demo.pdf`
57
+ - **macOS:** `open examples/weasy_rb_demo.pdf`
58
+ - **Windows:** `start examples/weasy_rb_demo.pdf`
59
+ - **VS Code:** Right-click the PDF file and select "Open with > Default Application"
60
+
61
+ ## ✨ What You'll See
62
+
63
+ ### Basic Example
64
+ - Clean, minimal layout
65
+ - Simple CSS styling
66
+ - Straightforward content structure
67
+ - Professional but understated design
68
+
69
+ ### Decorated Demo
70
+ - Modern CSS gradients and visual effects
71
+ - Subtle emoji support (just one 🚀 in the title)
72
+ - Professional typography and spacing
73
+ - Advanced layout techniques
74
+ - Performance metrics and timing
75
+
76
+ Both examples showcase WeasyPrint's ability to handle different levels of CSS complexity while maintaining excellent PDF output quality.
@@ -0,0 +1,85 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "../lib/weasy_rb"
5
+
6
+ puts "WeasyRb Basic Example"
7
+ puts "=" * 25
8
+
9
+ # Simple HTML content
10
+ html = <<~HTML
11
+ <!DOCTYPE html>
12
+ <html>
13
+ <head>
14
+ <title>Basic WeasyRb Example</title>
15
+ <style>
16
+ body {
17
+ font-family: Arial, sans-serif;
18
+ max-width: 800px;
19
+ margin: 40px auto;
20
+ padding: 20px;
21
+ line-height: 1.6;
22
+ }
23
+ h1 {
24
+ color: #333;
25
+ border-bottom: 2px solid #007acc;
26
+ padding-bottom: 10px;
27
+ }
28
+ .info-box {
29
+ background: #f4f4f4;
30
+ padding: 15px;
31
+ border-left: 4px solid #007acc;
32
+ margin: 20px 0;
33
+ }
34
+ code {
35
+ background: #f0f0f0;
36
+ padding: 2px 4px;
37
+ border-radius: 3px;
38
+ font-family: monospace;
39
+ }
40
+ </style>
41
+ </head>
42
+ <body>
43
+ <h1>Welcome to WeasyRb!</h1>
44
+
45
+ <p>This is a basic example of converting HTML to PDF using WeasyRb.</p>
46
+
47
+ <div class="info-box">
48
+ <strong>Key Features:</strong>
49
+ <ul>
50
+ <li>Simple API: <code>WeasyRb.html_to_pdf(html, output_path)</code></li>
51
+ <li>Full CSS support including modern features</li>
52
+ <li>Docker-ready development environment</li>
53
+ <li>Comprehensive error handling</li>
54
+ </ul>
55
+ </div>
56
+
57
+ <h2>Getting Started</h2>
58
+ <p>This PDF was generated on #{Time.now.strftime("%B %d, %Y at %I:%M %p")} using WeasyRb.</p>
59
+
60
+ <p>To create your own PDFs:</p>
61
+ <ol>
62
+ <li>Install WeasyRb gem</li>
63
+ <li>Create your HTML content</li>
64
+ <li>Call <code>WeasyRb.html_to_pdf(html, "output.pdf")</code></li>
65
+ <li>Enjoy your PDF!</li>
66
+ </ol>
67
+ </body>
68
+ </html>
69
+ HTML
70
+
71
+ # Output path in examples directory
72
+ output_path = File.join(__dir__, "basic_example.pdf")
73
+
74
+ puts "Converting HTML to PDF..."
75
+
76
+ begin
77
+ result = WeasyRb.html_to_pdf(html, output_path)
78
+
79
+ puts "Success! PDF created at: #{File.basename(result)}"
80
+ puts "Full path: #{File.expand_path(result)}"
81
+ puts "File size: #{File.size(result)} bytes"
82
+ rescue StandardError => e
83
+ puts "Error: #{e.message}"
84
+ puts "Make sure to run this in Docker: docker-compose run --rm weasy_rb ruby examples/basic_example.rb"
85
+ end
@@ -0,0 +1,191 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "../lib/weasy_rb"
5
+
6
+ puts "WeasyRb Decorated Demo"
7
+ puts "=" * 25
8
+
9
+ html = <<~HTML
10
+ <!DOCTYPE html>
11
+ <html>
12
+ <head>
13
+ <title>WeasyRb Decorated Demo</title>
14
+ <meta charset="UTF-8">
15
+ <style>
16
+ @page {
17
+ size: A4;
18
+ margin: 20mm;
19
+ }
20
+
21
+ body {
22
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
23
+ margin: 0;
24
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
25
+ color: white;
26
+ line-height: 1.6;
27
+ }
28
+
29
+ .container {
30
+ background: rgba(255, 255, 255, 0.1);
31
+ padding: 30px;
32
+ border-radius: 15px;
33
+ backdrop-filter: blur(10px);
34
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
35
+ }
36
+
37
+ h1 {
38
+ color: #fff;
39
+ margin-bottom: 20px;
40
+ font-size: 2.2em;
41
+ text-align: center;
42
+ text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
43
+ }
44
+
45
+ h2 {
46
+ color: #fff;
47
+ border-bottom: 2px solid rgba(255, 255, 255, 0.3);
48
+ padding-bottom: 10px;
49
+ margin-top: 25px;
50
+ font-size: 1.3em;
51
+ }
52
+
53
+ .feature {
54
+ margin: 12px 0;
55
+ padding: 12px;
56
+ background: rgba(255, 255, 255, 0.15);
57
+ border-radius: 6px;
58
+ border-left: 3px solid #ffd700;
59
+ }
60
+
61
+ .feature strong {
62
+ color: #ffd700;
63
+ }
64
+
65
+ .code-block {
66
+ background: rgba(0, 0, 0, 0.3);
67
+ padding: 15px;
68
+ border-radius: 5px;
69
+ font-family: 'Courier New', monospace;
70
+ margin: 15px 0;
71
+ border: 1px solid rgba(255, 255, 255, 0.2);
72
+ font-size: 0.9em;
73
+ }
74
+
75
+ .stats {
76
+ display: flex;
77
+ justify-content: space-around;
78
+ margin: 20px 0;
79
+ flex-wrap: wrap;
80
+ }
81
+
82
+ .stat {
83
+ text-align: center;
84
+ padding: 12px;
85
+ background: rgba(255, 255, 255, 0.1);
86
+ border-radius: 6px;
87
+ flex: 1;
88
+ margin: 3px;
89
+ min-width: 100px;
90
+ }
91
+
92
+ .footer {
93
+ margin-top: 25px;
94
+ font-size: 0.85em;
95
+ color: #ddd;
96
+ text-align: center;
97
+ padding-top: 15px;
98
+ border-top: 1px solid rgba(255, 255, 255, 0.3);
99
+ }
100
+
101
+ /* Simple emoji support with fallbacks */
102
+ .emoji {
103
+ font-family: 'Apple Color Emoji', 'Segoe UI Emoji', 'Noto Color Emoji', sans-serif;
104
+ }
105
+ </style>
106
+ </head>
107
+ <body>
108
+ <div class="container">
109
+ <h1><span class="emoji">🚀</span> WeasyRb - Ruby + WeasyPrint</h1>
110
+
111
+ <h2>Key Features</h2>
112
+
113
+ <div class="feature">
114
+ <strong>SOLID Principles:</strong> Clean, maintainable architecture following best practices.
115
+ </div>
116
+
117
+ <div class="feature">
118
+ <strong>Docker Ready:</strong> Pre-configured development environment with all dependencies.
119
+ </div>
120
+
121
+ <div class="feature">
122
+ <strong>Simple API:</strong> Just one method call: <code>WeasyRb.html_to_pdf(html, path)</code>
123
+ </div>
124
+
125
+ <div class="feature">
126
+ <strong>Well Tested:</strong> Comprehensive test suite with 23+ tests.
127
+ </div>
128
+
129
+ <h2>Usage Example</h2>
130
+
131
+ <div class="code-block">
132
+ require "weasy_rb"
133
+
134
+ html = "&lt;html&gt;&lt;body&gt;&lt;h1&gt;Hello!&lt;/h1&gt;&lt;/body&gt;&lt;/html&gt;"
135
+ WeasyRb.html_to_pdf(html, "output.pdf")
136
+
137
+ # With options
138
+ options = { format: "A4", media_type: "print" }
139
+ WeasyRb.html_to_pdf(html, "output.pdf", options)
140
+ </div>
141
+
142
+ <h2>Docker Commands</h2>
143
+
144
+ <div class="stats">
145
+ <div class="stat">
146
+ <strong>Build</strong><br>
147
+ <code>make build-and-test</code>
148
+ </div>
149
+ <div class="stat">
150
+ <strong>Dev</strong><br>
151
+ <code>make dev</code>
152
+ </div>
153
+ <div class="stat">
154
+ <strong>Console</strong><br>
155
+ <code>make console</code>
156
+ </div>
157
+ <div class="stat">
158
+ <strong>Test</strong><br>
159
+ <code>make test</code>
160
+ </div>
161
+ </div>
162
+
163
+ <div class="footer">
164
+ Generated on #{Time.now.strftime("%Y-%m-%d at %H:%M:%S")} using WeasyRb<br>
165
+ <small>Demonstrates CSS gradients, shadows and modern layouts in PDF</small>
166
+ </div>
167
+ </div>
168
+ </body>
169
+ </html>
170
+ HTML
171
+
172
+ # Save PDF in examples directory
173
+ output_path = File.join(__dir__, "decorated_demo.pdf")
174
+
175
+ puts "Converting HTML to PDF..."
176
+ start_time = Time.now
177
+
178
+ begin
179
+ result = WeasyRb.html_to_pdf(html, output_path)
180
+
181
+ end_time = Time.now
182
+ duration = ((end_time - start_time) * 1000).round(2)
183
+
184
+ puts "Success! PDF created at: #{File.basename(result)}"
185
+ puts "File size: #{File.size(result)} bytes"
186
+ puts "Conversion time: #{duration}ms"
187
+ puts "Full path: #{File.expand_path(result)}"
188
+ rescue StandardError => e
189
+ puts "Error: #{e.message}"
190
+ puts "Make sure to run this in Docker: docker-compose run --rm weasy_rb ruby examples/decorated_demo.rb"
191
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WeasyRb
4
+ # Responsible for building WeasyPrint command strings
5
+ # Follows Single Responsibility Principle and Open/Closed Principle
6
+ class CommandBuilder
7
+ WEASY_PRINT_COMMAND = "weasyprint"
8
+
9
+ # Build WeasyPrint command with options
10
+ # @param output_path [String] Path where the PDF will be saved
11
+ # @param options [Hash] Additional options for WeasyPrint
12
+ # @return [Array<String>] Command array ready for execution
13
+ def build(output_path, options = {})
14
+ command = [WEASY_PRINT_COMMAND]
15
+ command.concat(build_options(options))
16
+ command << "-" # Read from stdin
17
+ command << output_path
18
+ command
19
+ end
20
+
21
+ private
22
+
23
+ def build_options(options)
24
+ option_flags = []
25
+
26
+ add_simple_option(option_flags, options, :base_url, "--base-url")
27
+ add_simple_option(option_flags, options, :media_type, "--media-type")
28
+ add_simple_option(option_flags, options, :format, "--format")
29
+ add_simple_option(option_flags, options, :encoding, "--encoding")
30
+ add_stylesheets_option(option_flags, options)
31
+ add_verbose_option(option_flags, options)
32
+
33
+ option_flags
34
+ end
35
+
36
+ def add_simple_option(option_flags, options, key, flag)
37
+ option_flags.concat([flag, options[key]]) if options[key]
38
+ end
39
+
40
+ def add_stylesheets_option(option_flags, options)
41
+ return unless options[:stylesheets]
42
+
43
+ Array(options[:stylesheets]).each do |stylesheet|
44
+ option_flags.concat(["--stylesheet", stylesheet])
45
+ end
46
+ end
47
+
48
+ def add_verbose_option(option_flags, options)
49
+ option_flags << "--verbose" if options[:verbose]
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WeasyRb
4
+ # Main converter class responsible for orchestrating HTML to PDF conversion
5
+ # Follows Single Responsibility Principle by focusing only on conversion logic
6
+ class Converter
7
+ def initialize(command_builder: CommandBuilder.new, executor: Executor.new)
8
+ @command_builder = command_builder
9
+ @executor = executor
10
+ end
11
+
12
+ # Converts HTML content to PDF file
13
+ # @param html [String] HTML content to convert
14
+ # @param output_path [String] Path where the PDF will be saved
15
+ # @param options [Hash] Additional options for WeasyPrint
16
+ # @return [String] Path to the generated PDF file
17
+ def convert(html, output_path, options = {})
18
+ validate_inputs(html, output_path)
19
+
20
+ command = @command_builder.build(output_path, options)
21
+ result = @executor.execute(command, html)
22
+
23
+ handle_result(result, output_path)
24
+ end
25
+
26
+ private
27
+
28
+ def validate_inputs(html, output_path)
29
+ raise ArgumentError, "HTML content cannot be empty" if html.nil? || html.strip.empty?
30
+ raise ArgumentError, "Output path cannot be empty" if output_path.nil? || output_path.strip.empty?
31
+ end
32
+
33
+ def handle_result(result, output_path)
34
+ raise ConversionError, "WeasyPrint conversion failed: #{result.error}" unless result.success?
35
+
36
+ raise ConversionError, "PDF file was not created at #{output_path}" unless File.exist?(output_path)
37
+
38
+ output_path
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "open3"
4
+
5
+ module WeasyRb
6
+ # Executes WeasyPrint commands using Open3
7
+ class Executor
8
+ # Result class to encapsulate command execution results
9
+ Result = Struct.new(:success?, :output, :error, :exit_status) do
10
+ def initialize(success:, output: "", error: "", exit_status: nil)
11
+ super(success, output, error, exit_status)
12
+ end
13
+ end
14
+
15
+ # Execute WeasyPrint command with HTML input
16
+ # @param command [Array<String>] Command array to execute
17
+ # @param html_content [String] HTML content to pipe to stdin
18
+ # @return [Result] Execution result
19
+ # rubocop:disable Metrics/MethodLength
20
+ def execute(command, html_content)
21
+ validate_weasyprint_availability
22
+
23
+ stdout, stderr, status = capture_command_output(command, html_content)
24
+
25
+ Result.new(
26
+ success: status.success?,
27
+ output: stdout,
28
+ error: stderr,
29
+ exit_status: status.exitstatus
30
+ )
31
+ rescue Errno::ENOENT
32
+ raise CommandError, "WeasyPrint is not installed or not available in PATH"
33
+ rescue StandardError => e
34
+ raise CommandError, "Failed to execute WeasyPrint: #{e.message}"
35
+ end
36
+ # rubocop:enable Metrics/MethodLength
37
+
38
+ private
39
+
40
+ def capture_command_output(command, html_content)
41
+ Open3.capture3(*command, stdin_data: html_content)
42
+ end
43
+
44
+ def validate_weasyprint_availability
45
+ # Check if weasyprint is available in PATH
46
+ _stdout, _stderr, status = Open3.capture3("which", "weasyprint")
47
+
48
+ return if status.success?
49
+
50
+ raise CommandError, "WeasyPrint is not installed or not available in PATH"
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WeasyRb
4
+ VERSION = "0.1.0"
5
+ end
data/lib/weasy_rb.rb ADDED
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "weasy_rb/version"
4
+ require_relative "weasy_rb/converter"
5
+ require_relative "weasy_rb/command_builder"
6
+ require_relative "weasy_rb/executor"
7
+
8
+ # WeasyRb is a Ruby wrapper for the WeasyPrint Python library.
9
+ # It provides a simple interface to convert HTML documents to PDF files.
10
+ #
11
+ # @example Basic usage
12
+ # html = "<html><body><h1>Hello, PDF!</h1></body></html>"
13
+ # WeasyRb.html_to_pdf(html, "/tmp/output.pdf")
14
+ #
15
+ # @example With options
16
+ # options = { format: "A4", media_type: "print" }
17
+ # WeasyRb.html_to_pdf(html, "/tmp/output.pdf", options)
18
+ module WeasyRb
19
+ class Error < StandardError; end
20
+ class CommandError < Error; end
21
+ class ConversionError < Error; end
22
+
23
+ # Main entry point for HTML to PDF conversion
24
+ # @param html [String] HTML content to convert
25
+ # @param output_path [String] Path where the PDF will be saved
26
+ # @param options [Hash] Additional options for WeasyPrint
27
+ # @return [String] Path to the generated PDF file
28
+ def self.html_to_pdf(html, output_path, options = {})
29
+ Converter.new.convert(html, output_path, options)
30
+ end
31
+ end
data/sig/weasy_rb.rbs ADDED
@@ -0,0 +1,37 @@
1
+ module WeasyRb
2
+ VERSION: String
3
+
4
+ class Error < StandardError
5
+ end
6
+
7
+ class CommandError < Error
8
+ end
9
+
10
+ class ConversionError < Error
11
+ end
12
+
13
+ def self.html_to_pdf: (String html, String output_path, ?Hash[Symbol, untyped] options) -> String
14
+
15
+ class Converter
16
+ def initialize: (?command_builder: CommandBuilder, ?executor: Executor) -> void
17
+ def convert: (String html, String output_path, ?Hash[Symbol, untyped] options) -> String
18
+ end
19
+
20
+ class CommandBuilder
21
+ WEASY_PRINT_COMMAND: String
22
+ def build: (String output_path, ?Hash[Symbol, untyped] options) -> Array[String]
23
+ end
24
+
25
+ class Executor
26
+ class Result
27
+ attr_reader success?: bool
28
+ attr_reader output: String
29
+ attr_reader error: String
30
+ attr_reader exit_status: Integer?
31
+
32
+ def initialize: (success: bool, ?output: String, ?error: String, ?exit_status: Integer?) -> void
33
+ end
34
+
35
+ def execute: (Array[String] command, String html_content) -> Result
36
+ end
37
+ end
metadata ADDED
@@ -0,0 +1,61 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: weasy_rb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Henrique Aparecido Lavezzo
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies: []
12
+ description: WeasyRb provides a clean and simple Ruby interface to the WeasyPrint
13
+ Python library for converting HTML documents to PDF files.
14
+ email:
15
+ - henriquel@electionbuddy.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - ".rubocop.yml"
21
+ - CHANGELOG.md
22
+ - DOCKER.md
23
+ - Dockerfile
24
+ - LICENSE.txt
25
+ - Makefile
26
+ - README.md
27
+ - Rakefile
28
+ - docker-compose.yml
29
+ - examples/README.md
30
+ - examples/basic_example.rb
31
+ - examples/decorated_demo.rb
32
+ - lib/weasy_rb.rb
33
+ - lib/weasy_rb/command_builder.rb
34
+ - lib/weasy_rb/converter.rb
35
+ - lib/weasy_rb/executor.rb
36
+ - lib/weasy_rb/version.rb
37
+ - sig/weasy_rb.rbs
38
+ homepage: https://github.com/Rynaro/weasy_rb
39
+ licenses:
40
+ - MIT
41
+ metadata:
42
+ homepage_uri: https://github.com/Rynaro/weasy_rb
43
+ source_code_uri: https://github.com/Rynaro/weasy_rb
44
+ rdoc_options: []
45
+ require_paths:
46
+ - lib
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: 3.1.0
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ requirements: []
58
+ rubygems_version: 3.6.7
59
+ specification_version: 4
60
+ summary: A simple Ruby wrapper for WeasyPrint HTML to PDF converter
61
+ test_files: []