Skip to content

Instantly share code, notes, and snippets.

@d0k
Created October 19, 2008 20:37
Show Gist options
  • Save d0k/17930 to your computer and use it in GitHub Desktop.
Save d0k/17930 to your computer and use it in GitHub Desktop.
ruby script to convert images inside keynote 4 bundles to PNG
#!/usr/bin/env ruby -w
# Converts images inside keynote bundles to PNG
# We need RMagick with tiff support installed
# You may get libtiff from macports
# and RMagick from rubygems
# Licensed under GPLv3
require 'rubygems'
require 'optparse'
require 'zlib'
require 'rexml/document'
require 'RMagick'
# the extensions that will be compressed to PNG
EXTENSIONS = ['.tiff', '.pict', '.bmp']
quiet = false
clean = false
# deletes a directory and all files in it (non-recursive)
def deletedir(dir, quiet = false)
if File.exists?(dir)
Dir.foreach(dir) do |f|
fn = dir + File::SEPARATOR + f
unless File.directory?(fn)
puts "Deleting #{fn}" unless quiet
File.unlink(fn)
end
end
puts "Deleting #{dir}" unless quiet
Dir.unlink(dir)
end
end
OptionParser.new do |o|
o.banner = "Usage: #{$0} [options] filename"
o.on('-c', '--[no-]clean', 'clean up temporary files (e.g. thumbnails)') do |v|
clean = v
end
o.on('-q', '--[no-]quiet', 'No output') do |v|
quiet = v
end
o.on_tail("-h", "--help", "Shows this message") do
puts o
exit
end
end.parse!
if ARGV.length != 1
puts 'Missing argument (try --help)'
exit(1)
end
indir = ARGV.shift + File::SEPARATOR
index = indir + 'index.apxl.gz'
if clean
deletedir(indir + 'thumbs', quiet) # thumbnails will be regenerated by keynote
deletedir(indir + 'QuickLook', quiet) # quicklook thumbnail
end
# read index.apxl.gz
doc = nil
Zlib::GzipReader.open(index) { |f| doc = REXML::Document.new(f) }
# XPath is really slow in REXML :(
doc.elements.each('//key:page//sf:data') do |element|
name = element.attributes['sf:path']
newname = element.attributes['sf:path'] + '.png'
if EXTENSIONS.include?(File.extname(name))
if File.exists?(newname)
puts "Won't compress #{name} because #{newname} already exists" unless quiet
else
# compress image
image = Magick::ImageList.new(indir + name)
image.write(indir + newname)
# write new filename
if element.attributes['sf:displayname'] == name # not sure if this is necessary
element.attributes['sf:displayname'] = newname
end
element.attributes['sf:path'] = newname
puts "Compressed #{name}" unless quiet
File.unlink(indir + name) # delete old file
end
else
puts "Won't compress #{name}" unless quiet
end
end
# write changed xml back
Zlib::GzipWriter.open(index) { |f| doc.write(f) }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment