Groups keyboard shortcuts have been updated
Dismiss
See shortcuts

Auto-generating OpenGraph card images with ImageMagick

26 views
Skip to first unread message

S. Qiouyi Lu

unread,
Oct 12, 2023, 9:14:05 PM10/12/23
to nanoc
Hi all,

I'm trying to get Nanoc to auto-generate OpenGraph images that include the page title, summary, and permalink, sort of like how GitHub card previews include metadata about the page.

I've been trying to write both a filter and a compilation rule, but neither seems to be working.

I want cards to be generated for all Markdown pages. The card should be stored in the same folder as the page.

Where the code is currently at:

# Create OpenGraph cards.
compile '/**/*.md', rep: :opengraph do
system(
'magick -size 1200x675 xc:#ffffff \( -page +0+0 ',
@items['/assets/**/opengraph.png'],
' \) \( -page +40+130 -background transparent -fill #690000 -font Fraunces -size 1120x250 -interline-spacing -20 caption:\'',
@item[:title],
' \) \( -page +40+380 -background transparent -fill #333333 -font Bitter -stroke #333333 -strokewidth 4 -size 1120x150 -interline-spacing 0 caption:\'',
@item[:summary],
' \) \( -page +40+580 -background transparent -fill #666666 -strokewidth 2 -font Monaco -size 1120x75 -interline-spacing 0 caption:\'s.qiouyi.lu',
@item.path,
' \) -layers flatten opengraph.png'
)

system(
'mv opengraph.png',
item.identifier.without_ext + '/opengraph.png'
)
end

/assets/images/layouts/opengraph.png is the path to the canvas. Not sure how that gets handled in system().

I'm pretty new to Ruby, so I'm guessing the problem has something to do with how I'm conceptualizing the workflow. I'd appreciate help to reformat this so Nanoc can generate cards and I don't have to use a separate API.

Best,
S. (they/them/theirs)

Denis Defreyne

unread,
Oct 14, 2023, 4:04:46 PM10/14/23
to na...@googlegroups.com
Hey S,

I think you’re well on the way to a solution. The `system()` call needs one argument, so you’ll want to join all the pieces into a single command using + (rather than comma which you’re using now). Maybe that already makes it work?

I’ll post the solution that I use for my own web site. Maybe it can inspire you! It is not the prettiest code, but it works, and that is the most important thing.

First is a filter that generates HTML pages from which to generate the images:

Nanoc::Filter.define(:card_html) do |content, _params = {}|
  content = @item.compiled_content(snapshot: :last)
  doc = Nokogiri::HTML(content)

  doc.search('body').each do |body|
    body['class'] = 'minimal ' + body['class']
  end

  title = doc.at_css('title')
  title.add_next_sibling('<meta name="robots" content="noindex">')

  doc.to_html
end

Then, a `card_png` filter, which creates a PNG by using Chrome’s screenshot functionality:

require 'tempfile'

Class.new(Nanoc::Filter) do
  identifier :card_png
  type :text => :binary

  def run(content, _params = {})
    # Ensure all assets exist (explicitly generate dependencies, because Chrome won’t know)
    @items.find_all('/assets/fonts/**').each { _1.reps[:default].raw_path(snapshot: :last) }

    Tempfile.create(['screenshot', '.png']) do |tmpfile|
      system(
        '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
        '--headless',
        '--screenshot=' + tmpfile.path,
        '--disable-gpu',
        '--hide-scrollbars',
        '--window-size=1200,630',
        @item.reps[:card_html].raw_path(snapshot: :last)
      )

      FileUtils.mv(tmpfile.path, output_filename)
    end
  end
end

In the Rules file, it looks like this:

compile '/articles/*.{md,md.erb,dmark}', rep: :card_html do
  filter :card_html
  filter :relativize_paths, type: :html
  write item.identifier.without_exts + '/card.html'
end

compile '/articles/*.{md,md.erb,dmark}', rep: :card_png do
  filter :card_png
  write item.identifier.without_exts + '/card.png'
end

It is not perfect — it leaves behind a `…/card.html` file (hidden from search engines though). I could fix that by not writing the `…/card.html` file at all, but write it to a temporary location (using Tempfile) in the `card_png`  filter. I’ve not gotten around to fixing that, but it’s not very important.

Hope this helps,

--
You received this message because you are subscribed to the Google Groups "nanoc" group.
To unsubscribe from this group and stop receiving emails from it, send an email to nanoc+un...@googlegroups.com.

Reply all
Reply to author
Forward
Message has been deleted
0 new messages