Tumblr, RMagick and a Photo Frame!

Over the course of the past year or so, I got myself into a habit of using Tumblr for storing insightful snippets and quotes from my daily endeavours on the internet. Usually, this entails saving a paragraph (too long for tweet, and not enough for del.icio.us) so I can revisit it later.

However, having Tumblr to save the quotes is nice, but I rarely (if ever) went back to read them. Pondering this dilemma, I spotted my Phillips digital photo frame, and a quick weekend project was born: Tumblr API, RMagick to render the images, and an SD card full of wisdom and quotes for easy consumption!

Retrieving quotes from Tumblr API

Retrieving your content from Tumblr is about as easy as it can get: one GET request, with a few query parameters. In return you get an XML file with the quotes, the source link, publication time, and much more. To parse the XML response, HPricot comes to the rescue:

require 'open-uri'
require 'hpricot'
require 'cgi'

# read last 5 quotes
doc = Hpricot.XML(open("http://username.tumblr.com/api/read?type=quote&num=5"))

# iterate and clean up (unescape HTML, and remove HTML entities)
(doc/'post').each do |post|
    text = CGI::unescapeHTML((post/'quote-text').inner_html).gsub(/&[^;]*;?/,'')
    source = CGI::unescapeHTML((post/'quote-source').inner_html).gsub(/\<.*?\>/, '').gsub(/&[^;]*;?/,'').strip

    puts "Processing: #{post['id']}\n"
end

Rendering wisdom with RMagick

Once the data is cleaned up, we can get to the rendering step. Installing RMagick can be a dreadful experience, but it appears that as of 2.x release, this process has been vastly improved - everything worked on the first try!

To render the final image, I've decided to adopt the simplest possible route: render each component as a separate image (quote text, background, source link, etc), and then merge the images as layers into a single file. Easier said than done! RMagick is not for the faint of heart, and to my surprise, did not have the concept of a text box (it can only render contiguous lines), unless you use the annotate method:

require 'rmagick'
include Magick

text = "A goal is a dream with deadline"

# courtesy of http://blog.choonkeat.com/weblog/2007/02/rmagick-renderi.html
# - render the text in a 500x430 box, and truncate if needed
quote = render_cropped_text(text, 500, 430) do |img|
   img.fill = "#ffffff" # font color
   img.background_color = "transparent"
   img.pointsize = 23
   img.antialias = true
   img.font = "Helvetica"
end

quote.write "text.png"
tumblr.zip - full implementation

It's not optimized, but it got the job done: ~160 quotes on an SD card, rotating at random intervals for my enjoyment. Next iteration, automated updates of quotes on my photo frame via RSS and WiFi - any volunteers to write a Sinatra app? I can't wait until D-Link DSM-210 hits the streets!


Ilya Grigorik

Ilya Grigorik is a web performance engineer and developer advocate at Google, where his focus is on making the web fast and driving adoption of performance best practices at Google and beyond.