https://github.com/jamedjo/himg
You give it HTML and get back an image - perfect for dynamic OpenGraph social image previews.
https://github.com/jamedjo/himg
browserless image-generation opengraph rails rails-renderer renderer ruby-gem
Last synced: 8 months ago
JSON representation
You give it HTML and get back an image - perfect for dynamic OpenGraph social image previews.
- Host: GitHub
- URL: https://github.com/jamedjo/himg
- Owner: Jamedjo
- License: apache-2.0
- Created: 2025-04-19T05:38:56.000Z (about 1 year ago)
- Default Branch: main
- Last Pushed: 2025-08-21T23:23:55.000Z (10 months ago)
- Last Synced: 2025-10-07T20:44:48.853Z (8 months ago)
- Topics: browserless, image-generation, opengraph, rails, rails-renderer, renderer, ruby-gem
- Language: HTML
- Homepage: https://himg.jamedjo.co.uk/
- Size: 539 KB
- Stars: 88
- Watchers: 3
- Forks: 0
- Open Issues: 3
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE-APACHE.txt
Awesome Lists containing this project
README
You give it HTML and it gives back an image!
Parses HTML/CSS, fetches nested resources, renders an image. No browser, no fuss.
Perfect for OpenGraph images - stop losing clicks to boring links by adding a rich image preview that showcases your content.

## Quickstart
### Command Line
```bash
gem install himg
himg screenshot path/to/your.html screenshot.png
himg screenshot https://himg.jamedjo.co.uk himg.png --width=1024 --verbose --no-truncate
echo '
Hello Image
' | himg screenshot --stdin output.png
```
### Ruby
```ruby
png = Himg.render("")
File.open("blue.png", "wb"){|f| f.write(png) }
```
```ruby
png = Himg.render("
Snapshot
", width: 89, height: 5, truncate: false)
File.open("dynamic_height.png", "wb"){|f| f.write(png) }
```
### Rails
Simply add a `show.himg.erb`!
```erb
<%= @username %>
```
### OpenGraph Meta Tags
OpenGraph tags let messenger apps and social media sites know to use your generated image as a thumbnail card for your website.
```html
```

# Usage
Install the gem and add to the application's Gemfile by executing:
```bash
bundle add himg
```
## Configuration
|Option | Description | Type | Default |
|-|-|-|-|
|width | Sets the width of the rendered content. | integer | 720 |
|height | Sets the desired height of the rendered output. | integer | 405 |
|truncate | Keeps the image height fixed instead of expanding to include the full page | bool | true |
|verbose | Enables detailed logging for debugging and profiling. | bool | false |
|base_url | Where relative paths are relative to for linked resources (stylesheets, images, fonts, etc) | string | nil |
|disable_fetch | Disables fetching linked resources from disk and network| bool | false |
|fetch_timeout | Timeout in seconds for fetching resources | float | 10.0 |
|gpu | Use GPU renderer instead of CPU renderer | bool | false |
|http_headers | Headers sent when the CLI fetches the SOURCE HTML (CLI only) | hash | nil |
|stdin | Read HTML content from stdin instead of a file (CLI only) | bool | false |
### Passing options to a Rails view template
Options can be set at a controller level using the `himg_config` helper method:
```ruby
class UsersController < ActionController::Base
himg_config(verbose: true)
end
```
These can be overridden at a view level:
```ruby
class UsersController < ActionController::Base
def show
himg_config(width: params[:w]) if params[:w]
@user = User.new
end
```
### Rails manual render
If you prefer you could also use `render himg: "
My Data"` instead, but should be careful with untrusted input if constructing HTML manually.
Options can then be passed directly to the manual render:
```ruby
render himg: '', truncate: false
```
Alternatively you can pass in options which have been set with `himg_config`:
```ruby
render himg: '', config: himg_config
```
### Rails `respond_to`
To be explicit in the controller you can also use `respond_to` style:
```ruby
respond_to do |format|
format.html
format.himg
end
```
You can also use this combined with a manual render:
```ruby
respond_to do |format|
format.html
format.himg { render himg: '
Recent Users
' }
format.png { render himg: 'For .png URLs' }
end
```
### Rails caching
You can cache images to save re-generating them for later requests.
To serve the cached response you'll need to use `send_data` to send the png data as binary content.
```ruby
format.png do
cache_key = ["opengraph-your-resource", id_or_data].join("/")
cached_image = Rails.cache.fetch(cache_key, expires_in: 30.days) do
render_to_string(template: 'your_resource/show', formats: [:himg])
end
send_data cached_image, type: 'image/png', disposition: 'inline'
end
```
You can also speed up the render time by minimizing resources to fetch over the network, for example installing fonts locally or ensuring background images are read from disk as a file rather than fetched over http.
# Supported HTML and CSS
Himg supports a large subset of the HTML and CSS you'd need to get by, but not all elements or properties are supported.
For a full list, see our [snapshot of the blitz.is status page](https://github.com/Jamedjo/himg/issues/2).
If something you'd like supported is missing and is available [upstream on Blitz](https://blitz.is/status), please [open an issue](https://github.com/Jamedjo/himg/issues/new).
|HTML|Status|
|-|-|
|`Generic HTML5 elements`|✅ Supported|
|``|✅ Supported|
|`<link rel="stylesheet">`|✅ Supported|
|`<link rel="icon">`|❌ Not supported|
|`<img src="">`|✅ Supported|
|`<img srcset="">`|❌ Not supported|
|`<picture>`|❌ Not supported|
|`<svg>`|⚠️ [Partial support](https://github.com/Jamedjo/himg/issues/2)|
|`<h1>-<h6>`|✅ Supported|
|`<ul>/<ol>`|✅ Supported|
|`<i>/<em>`|✅ Supported|
|`<b>/<strong>`|✅ Supported|
|`<u>`|✅ Supported|
|`<center>`|✅ Supported|
|`<pre>/<blockquote>`|✅ Supported|
|`<a>`|✅ Supported|
|`<br>`|✅ Supported|
|`<hr>`|❌ Not supported|
|`<details>/<summary>`|❌ Not supported|
|`<table>`|⚠️ [Partial support](https://github.com/Jamedjo/himg/issues/2)|
|`Form Controls`|⚠️ [Partial support](https://github.com/Jamedjo/himg/issues/2)|
|CSS|Status|
|-|-|
|`display:inline, display:block, display:inline-block`|✅ Supported|
|`display:none`|✅ Supported|
|`display:flex`|✅ Supported|
|`display:grid`|⚠️ [Partial support](https://github.com/Jamedjo/himg/issues/2)|
|`display:table`|⚠️ [Partial support](https://github.com/Jamedjo/himg/issues/2)|
|`display:contents`|❌ Not supported|
|`position:relative, position:absolute`|✅ Supported|
|`position:static, position:fixed, position:sticky`|❌ Not supported|
|`overflow`|⚠️ [Partial support](https://github.com/Jamedjo/himg/issues/2)|
|`z-index`|⚠️ [Partial support](https://github.com/Jamedjo/himg/issues/2)|
|`box-sizing`|✅ Supported|
|`float`|❌ Not supported|
|`content (::before / ::after)`|⚠️ [Partial support](https://github.com/Jamedjo/himg/issues/2)|
|`opacity, visibility`|✅ Supported|
|`width, height`|✅ Supported|
|`min-width, max-width, min-height, max-height`|✅ Supported|
|`padding, margin, gap`|✅ Supported|
|`border`|⚠️ [Partial support](https://github.com/Jamedjo/himg/issues/2)|
|`@font-face, font-size, font-family`|✅ Supported|
|`font-weight, font-style, font-stretch`|✅ Supported|
|`font-display, font-variant, font-feature-settings`|❌ Not supported|
|`color, text-align, line-height, text-decoration`|✅ Supported|
|`letter-spacing, overlap-wrap, word-wrap, word-break`|✅ Supported|
|`vertical-align, text-transform, text-overflow`|❌ Not supported|
|`border`|⚠️ [Partial support](https://github.com/Jamedjo/himg/issues/2)|
|`background-color, background-image`|✅ Supported|
|`background-size, background-position`|✅ Supported|
|`background-repeat, background-clip, background-origin`|✅ Supported|
|`background-attachment`|❌ Not supported|
|`box-shadow`|✅ Supported|
|`filter`|❌ Not supported|
# How it works
No browser, just basics!
Himg calls through to the amazing [blitz](https://github.com/DioxusLabs/blitz) library, which uses [Stylo](https://github.com/servo/stylo) to parse the CSS, [servo/html5ever](https://github.com/servo/html5ever) to parse the HTML, fetches network resources, builds a scene graph and hands over to [vello](https://github.com/linebender/vello) to render an image.
Interaction between Ruby & Rust is done with the help of `magnus`, `rb_sys` and lots of glue code from the `oxidize-rb` team.
To play nicely with Rails a template handler is registered, which Rails' `default_render` method automatically calls when the corresponding view is found. This can be `show.himg` for a static image, or `show.himg.erb` to use variables from the controller. Additionally a Renderer is available with `render himg: 'content'` in case a view template is not needed.
## CAVEATS
1. This is **pre-alpha** software, don't expect it to work perfectly yet.
2. Performance needs tuning. Both in the underlying [Blitz](https://github.com/DioxusLabs/blitz) library and how data is passed between Rust and Ruby
3. Network requests can be made: don't use this library with untrusted inputs. Use `disable_fetch` if you don't need to fetch any resources.
4. file:// URLs are resolved: this could expose files on your computer. Use `disable_fetch` if you don't need to fetch any resources.
# Development
1. Run `bin/setup` to install dependencies.
2. Run `rake spec` to run the tests with the default development setup
3. Run `appraisal rake spec` to run tests against different versions of rails and to confirm that the gem works in a plain ruby environment
4. Run `bin/console` for an interactive prompt that will allow you to experiment.
5. Run `RAILS_ENV=development bundle exec spec/dummy/dummy_rails server` to check the dummy app.
- http://localhost:3000/users/jamedjo.png will display an opengraph compatible png
- http://localhost:3000/users/jamedjo.himg will also render the same png
- http://localhost:3000/users/jamedjo will render an HTML page with opengraph meta tags
6. To install this gem onto your local machine, run `bundle exec rake install`.
7. To simulate a headless server environment without a GPU, use `WGPU_BACKEND=empty bundle exec rspec`
8. To profile performance with flamegraphs, run `bin/profile spec/fixtures/profile_test.html`
### Run cargo example directly generate image in Rust
```bash
bundle exec cargo run --example file
bundle exec cargo run --example file -- path/to/file.html
```
## Releases
To release a new version:
1. Run `rake bump:patch` to update the version numbers in `version.rb` and `ext/himg/Cargo.toml`.
3. 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).
## Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/jamedjo/himg.
## License
Copyright (c) 2025 James Edwards-Jones
This project is dual licenced under both the MIT and Apache 2.0 terms.
See: [MIT License](https://opensource.org/licenses/MIT) and [APACHE 2.0 License](http://www.apache.org/licenses/LICENSE-2.0)