Skip to content

Instantly share code, notes, and snippets.

@mrinterweb
Created April 25, 2014 21:21
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save mrinterweb/11303706 to your computer and use it in GitHub Desktop.
Save mrinterweb/11303706 to your computer and use it in GitHub Desktop.
Command line SVG optimizer and base64 converter for CSS
#! /usr/bin/env ruby
# nokogiri is required below unless --no-modify option is specified
require 'optparse'
require 'tempfile'
@options = {}
OptionParser.new do |opts|
opts.banner = "Usage: svgoptimize [options] <path-to-svg-file>"
opts.on('-v', '--verbose', 'verbose logging') do |v|
@options[:verbose] = true
end
opts.on('-q', '--quiet', 'only result output is displayed') do |v|
@options[:quiet] = true
end
opts.on('--no-modify', 'disables svg xml element removal') do |v|
@options[:skip_xml_optimization] = true
end
opts.on('-c', '--copy', 'copies the output to the system clipboard') do |v|
@options[:copy_to_clipboard] = true
end
opts.on('--css', 'wraps the output in a background css rule') do |v|
@options[:css_wrap] = true
end
end.parse!
path = ARGV[0]
unless path
puts "you must specify a path to the svg file you'd like to be optimized."
exit 1
end
unless File.exist?(path)
puts "path to file does not exist."
exit 1
end
def log(str, mode=:normal)
if @options[:quiet]
nil
elsif mode == :normal
puts str
elsif @options[:verbose] && mode == :verbose
puts str
end
end
def kb(total_bytes)
sprintf "%.2f KB", total_bytes / 1024.0
end
def size_to_kb(file)
kb file.size
end
input_file = File.open(path)
tmp_file = Tempfile.new("svg-optimize-#{File.basename(path)}")
tmp_file.write(input_file.read)
tmp_file.rewind
log "using temp file: #{tmp_file.path}", :verbose
log "Input file: \n#{input_file.read}", :verbose
log "Input SVG file size: #{size_to_kb(input_file)}"
unless @options[:skip_xml_optimization]
require 'nokogiri'
xml = Nokogiri::XML(tmp_file.read.gsub(%r~.*<svg ~mi, '<svg '))
%w[description title].each do |node_name|
xml.css(node_name).remove
end
tmp_file.truncate(0)
tmp_file.rewind
# xml.remove_namespaces!
extraneous_attributes = xml.root.attributes.reject { |k| %w[version viewBox].include?(k) }.keys
extraneous_attributes.each { |attr| xml.root.delete(attr) }
xml_str = xml.to_xml(save_with: Nokogiri::XML::Node::SaveOptions::NO_DECLARATION).gsub(/>\s+</, "><")
tmp_file.write(xml_str)
tmp_file.rewind
log `cat #{tmp_file.path}`, :verbose
log "file size: #{`ls -lh #{tmp_file.path}`}", :verbose
end
base64tmp = Tempfile.new('svg-base64')
`cat #{tmp_file.path} | openssl base64 | tr -d '\n' > #{base64tmp.path}`
result = `cat #{base64tmp.path}`
log "Optimized size: #{kb result.length}"
log ""
if @options[:css_wrap]
result = "background: url(data:image/svg+xml;base64,#{result}) top right no-repeat"
f = File.open(base64tmp.path, 'w')
f.write(result)
f.close
end
puts result
if @options[:copy_to_clipboard]
clipboard_cmd = case RUBY_PLATFORM
when /darwin/
'pbcopy'
else
'xclip'
end
`cat #{base64tmp.path} | #{clipboard_cmd}`
puts "\n output copied to clipboard"
end
@dmnkhhn
Copy link

dmnkhhn commented May 5, 2014

Thanks for that. :-)
I've changed mine to include the trailing semicolon and I've changed "top right" to "0 0".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment