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 +7 -0
- data/.rubocop.yml +13 -0
- data/CHANGELOG.md +5 -0
- data/DOCKER.md +92 -0
- data/Dockerfile +43 -0
- data/LICENSE.txt +21 -0
- data/Makefile +35 -0
- data/README.md +158 -0
- data/Rakefile +12 -0
- data/docker-compose.yml +41 -0
- data/examples/README.md +76 -0
- data/examples/basic_example.rb +85 -0
- data/examples/decorated_demo.rb +191 -0
- data/lib/weasy_rb/command_builder.rb +52 -0
- data/lib/weasy_rb/converter.rb +41 -0
- data/lib/weasy_rb/executor.rb +53 -0
- data/lib/weasy_rb/version.rb +5 -0
- data/lib/weasy_rb.rb +31 -0
- data/sig/weasy_rb.rbs +37 -0
- metadata +61 -0
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
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
data/docker-compose.yml
ADDED
@@ -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:
|
data/examples/README.md
ADDED
@@ -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 = "<html><body><h1>Hello!</h1></body></html>"
|
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
|
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: []
|