Searching for the perfect presentation toolchain

I’ve spent the last few years trying to find the holy grail of FOSS toolchains for producing and displaying my presentations.

I started with OpenOffice.org Impress (which I have sworn never to use again after it ate several presentations), dallied with Clutter’s opt, seriously used KeyJNote, before moving to Mac OS X last year and settling on Apple’s Keynote.

While travelling this year without a Mac, i’ve resumed my search for the perfect toolchain, and I think i’ve found a setup that works pretty well.

It’s uses Inkscape to build the slides, a text file to order slides, KeyJNote to display them, and Rake to tie it all together. Oh, and it’s versioned with Bazaar.

Each slide goes on a new line in order.txt:

/home/auxesis/Desktop/devopsdays/slides/blank.png
# http://www.flickr.com/photos/tim_norris/2600844073/
/home/auxesis/Desktop/devopsdays/slides/scalable.png
# http://www.flickr.com/photos/barbour/404053639/
/home/auxesis/Desktop/devopsdays/slides/distributed.png
# http://www.flickr.com/photos/numstead/535460927/
/home/auxesis/Desktop/devopsdays/slides/nagios-plugin-format.png

You can easily add comments between slides to keep track of image sources or write down ideas.

Then there’s a Rake task for building the KeyJNote command line and setting up displays:

desc "setup external displays"
task :displays do
  system("xrandr --output VGA --mode 1024x768")
  system("xrandr --output VGA --same-as LVDS")
end

desc "perform presentation"
task :perform => :displays do
  options = "--transition Crossfade --transtime 250 -c persistent"
  command = "keyjnote #{options} @#{File.dirname(__FILE__)}/order.txt"
  system(command)
end

Finally, there’s a Rake task for building the PNGs from the SVGs created through Inkscape:

desc "build pngs from svgs"
task :build do
  Dir.glob("sources/*.svg").each do |file|
    if modified?(file)
      basename = File.basename(file, '.svg')
      slide = "slides/#{basename}.png"
      system("inkscape -e #{slide} -f #{file}")
    end
  end
end

def modified?(filename)
  index_filename = File.join(File.dirname(__FILE__), "cache", "index")
  # read or initialise index
  if File.exists?(index_filename)
    @index = File.open(index_filename, 'r') { |f| Marshal.load(f) }
  else
    @index = {}
  end

  # check if modified
  if @index[filename]
    modified = File.mtime(filename) > @index[filename][:mtime]
  else
    modified = true
  end

  # update index
  @index[filename] = {:mtime => File.mtime(filename)}
  File.open(index_filename, 'w') { |f| Marshal.dump(@index, f) }

  return modified
end

This keeps an index of SVG mtimes, and only rebuilds a slides if you modify the SVG.

Then to view the presentation, it’s a simple:

$ rake build ; rake perform

Now that you’re just dealing with a bunch of files, you can version control the whole presentation with something like bzr (which handles binary content really well). It’s worth setting up an ignore list so all the generated slides don’t get versioned:

slides/*
*.cache
cache/*