All original content is created in Ukrainian. Not all content has been translated yet. Some posts may only be available in Ukrainian.Learn more

Open Graph Image Generation in Rails Using SVG Templates

This content has been automatically translated from Ukrainian.
As a template for generating JPG images, I chose SVG due to its simplicity and speed of working with this format. For example, HTML as a template offers more possibilities but is more complex to implement and maintain.
The task is to generate a JPG image for OpenGraph markup (this is the image/preview for some social networks). The Rails application is hosted on Heroku. The amount of RAM is not very large, so the image generation task needs to be offloaded somewhere. It doesn't make sense to set up and pay for Sidekiq for a pet project. A good option for me turned out to be Heroku Scheduler. This standard (free) addon will run our rake task on a schedule (once a day). The task itself will find posts that do not yet have an OG image and will generate and attach it (using ActiveStorage, the image will be uploaded to AWS) to the model.
The first thing to do is to create the SVG template. First, we play around with SVG. The OG image should be 1200px x 630px.
Here is approximately how the SVG template will look:
<svg width="1200" height="630" viewBox="0 0 1200 630" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
  <!-- Background -->
  <rect width="1200" height="630" fill="black"/>
  <rect x="56" y="56" width="1088" height="518" rx="13" stroke="white" fill="black" stroke-width="13"/>
  
  <!-- Title -->
  <text x="160" y="150" fill="white" font-size="50px" font-weight="bold" line-height="1.2" margin="0">
    Article Title
  </text>

  <!-- Author -->
  <text x="160" y="510" fill="white" font-size="30px">
    Author Name
  </text>

  <!-- Website URL -->
  <text x="860" y="510" fill="white" font-size="30px">
    site url
  </text>
</svg>
This template has no variables. The variables will be interpreted in the .erb template. We will place this SVG template into our Rails application and add the .erb extension, for example lib/templates/og_image_template.svg.erb.
I am using the mini_magick library. So I already have in the Gemfile:
gem "mini_magick"
Next is the code itself lib/cover_image_generator.rb.
require 'mini_magick'
require 'erb'

module CoverImageGenerator
  class Generator
    def generate_and_attach(entry, entry_title)
      begin
        # Dynamically fill the SVG template
        @entry_title = entry_title.truncate(160)
        @blog_name = entry.blog.slug
        svg_template = File.read(File.expand_path('./templates/cover_image_template.svg.erb', __dir__))
        svg_content = ERB.new(svg_template).result(binding)
        
        # Create a MiniMagick image from the SVG content
        cover_image = MiniMagick::Image.read(svg_content)

        # Convert to JPG
        cover_image.format('jpg')
        temp_file_path = entry.slug + '.jpg'
        cover_image.write(temp_file_path)

        # Attach the image to the entry
        entry.cover_image.attach(io: File.open(temp_file_path), filename: "#{entry.slug}.jpg", content_type: 'image/jpeg')

        # Clean up the temporary file
        File.delete(temp_file_path)
      rescue => e
        # There will likely be issues with text that has specific characters that cannot be converted to JPG.
        # For now, I simply ignore such titles - a solution for this problem needs to be found.
        # Currently, if the model does not have an attached OG image, I show a default image.
        puts "Error on: #{@entry_title}, ID: #{entry.id}"
        puts e
        puts "=========================================="
      end
      puts "Image attached to: #{@entry_title}, ID: #{entry.id}"
      puts "=========================================="
    end
  end
end
AWS is already set up, and all that needs to be done is to add cover_image to our model (for example, Topic)
# app/models/topic.rb
has_one_attached :cover_image
Our SVG template needs to render our variables. Also, considering the specifics of the SVG file styles, we need to break long titles into several lines.
<svg width="1200" height="630" viewBox="0 0 1200 630" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
  <!-- Background -->
  <rect width="1200" height="630" fill="black"/>
  <rect x="56" y="56" width="1088" height="518" rx="13" stroke="white" fill="black" stroke-width="13"/>
  
  <!-- Multi-line title -->
  <text x="160" y="110" fill="white" font-size="50px" font-weight="bold" line-height="1.2" margin="0">
    <% lines = @entry_title.scan(/\S.{0,34}\S(?=\s|\z)/) %>
    <% lines.each_with_index do |line, index| %>
      <tspan x="160" dy="<%= index == 0 ? '1.5em' : '1.2em' %>"><%= line.strip %></tspan>
    <% end %>
  </text>

  <!-- Blog name -->
  <text x="160" y="510" fill="white" font-size="30px">
    <%= @blog_name %>
  </text>

  <!-- Website URL -->
  <text x="860" y="510" fill="white" font-size="30px">
    tseivo.com
  </text>
</svg>
And finally, to generate the image:
topic = Topic.last
CoverImageGenerator::Generator.new.generate_and_attach(topic, topic.name)
This code needs to be added to a rake task that will run and generate OG images for all necessary objects. This code can also be used to generate an image after creating or updating (the title) an object.
Result:
Приклад створеного JPEG з SVG шаблону
Приклад створеного JPEG з SVG шаблону
This example of code/concept is not a final version. There is still work to be done.

This post doesn't have any additions from the author yet.

29 Dec 10:22

What is Automatic Speech Recognition (ASR)?

meme code
meme code@memecode
29 Dec 10:30

What is NLP? What is Natural Language Processing used for?

meme code
meme code@memecode
29 Dec 10:37

What is Chat GPT? What is it used for and how does it work?

meme code
meme code@memecode
29 Dec 10:48

What is AI? What does "generated by AI" mean?

meme code
meme code@memecode
03 Jan 13:16

Fixing An error occurred while installing tiny_tds (2.1.5), and Bundler cannot continue on Mac with M1

meme code
meme code@memecode
10 Jan 19:41

[Personal Experience] MacBook froze. It doesn't turn on at all or the screen turns off immediately.

meme code
meme code@memecode
10 Feb 16:08

What is Hot Potato in software development?

meme code
meme code@memecode
05 Mar 19:17

What is Scalability?

meme code
meme code@memecode
05 Mar 19:18

What does HA (High Availability) mean?

meme code
meme code@memecode
05 Mar 19:29

What is the difference between High Availability and Scalability?

meme code
meme code@memecode
05 Mar 19:38

What is Service Discovery in IT?

meme code
meme code@memecode
07 Mar 18:36

What is Clustering in IT?

meme code
meme code@memecode