Generating Open Graph Images with SVG partial and Vips

Curated ago by @jeremysmithco

Description

I found a way to generate custom Open Graph images for Sample files on this site using an SVG partial that is rendered and converted to PNG by ImageProcessing::Vips.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<?xml version="1.0" encoding="UTF-8"?>
<svg width="1200px" height="600px" viewBox="0 0 1200 600" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
  <title><%= @sample_file.sample.title %></title>

  <defs>
    <clipPath id="editor-clip">
      <rect id="Rectangle" x="100" y="310" width="1000" height="383" rx="6"></rect>
    </clipPath>
  </defs>

  <g id="body" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
    <rect id="Rectangle" fill="#E2E0E0" x="0" y="0" width="1200" height="600"></rect>
    <rect id="Rectangle" fill="#EF4444" x="0" y="86" width="1200" height="36"></rect>
    <rect id="Rectangle" fill="#950000" x="0" y="0" width="1200" height="86"></rect>
    <rect id="Rectangle" fill="#FFFFFF" x="60" y="162" width="1080" height="549" rx="6"></rect>
    <rect id="Rectangle" fill="#272C35" x="100" y="310" width="1000" height="383" rx="6"></rect>

    <text id="title" font-family="Recursive-Regular, Recursive" font-size="30" font-weight="normal" fill="#950000" text-decoration="underline">
      <tspan x="100" y="230"><%= @sample_file.sample.title %></tspan>
    </text>

    <text id="description" font-family="Recursive-Regular, Recursive" font-size="26" font-weight="normal" fill="#404040">
      <tspan x="100" y="283"><%= strip_markdown(@sample_file.sample.description).truncate(75) %></tspan>
    </text>

    <g id="editor" clip-path="url(#editor-clip)">
      <text id="contents" font-family="RecMonoLinear-Regular, Rec Mono Linear" font-size="24" font-weight="normal" line-spacing="32" fill="#FFFFFF" xml:space="preserve">
        <% syntax_higlight(@sample_file.contents, @sample_file.path, "svg_inline").each_line.with_index do |line, i| %>
          <tspan x="190" y="<%= 364 + (i * 32) %>"><%= line.html_safe %></tspan>
        <% end %>
      </text>

      <text id="lines" font-family="RecMonoLinear-Regular, Rec Mono Linear" font-size="24" font-weight="normal" line-spacing="32" fill="#485362">
        <% (1..@sample_file.line_count_or_default).each do |line| %>
          <tspan x="140.6" y="<%= 364 + ((line - 1) * 32) %>"><%= line %></tspan>
        <% end %>
      </text>
    </g>
  </g>
</svg>

The SVG ERB file renders Sample file contents in the SVG. To use custom fonts on Heroku, you need to add them to the .fonts directory of your app.

This SVG uses a few techniques to control text display. First, the description is truncated to 75 characters. Second, the full file contents are loaded in the editor area, but it’s using a <clipPath> to ensure code doesn’t overrun the editor box. Third, the xml:space="preserve" attribute is used to ensure white space doesn’t collapse (similar to a <pre> tag).