#!/usr/bin/ruby
# Code to take a CSS file and an HTML file and render the styles inline.
# This is bad practice on the web, but can be very helpful when producing
# HTML email where external stylesheets can be problematic. Using this you
# can develop using external stylesheets and then easily move the styles
# inline.
#
# If the file is run from the command line it will take paths to a
# stylesheet and an HTML file and output the combined code
#
# Author:: James Stewart (mailto: james@jystewart.net)
# Copyright:: Copyright (c) 2007 James Stewart
# License:: Distributes under the same terms as Ruby
# This class takes a CSS file and provides a method to
# parse it into a hash. Usage is:
#
# parser = SimpleCSSParser.new('/path/to/myfile.css')
# hash_of_rules = parser.to_hash
#
# For more advanced CSS handling check out the cssparser gem
# http://code.dunae.ca/css_parser/
class SimpleCSSParser
# Receive and open the CSS file, storing its contents
def initialize(path_to_file)
@css = open(path_to_file).read
end
# Convert the CSS into a hash, where the keys are the selectors
# and the values are the rules
def to_hash
@to_hash ||= separate_rules.inject({}) do |collection, rule|
identifiers, rule = prepare_selectors_and_rule(rule)
identifiers.each do |identifier|
collection[identifier] ||= ''
collection[identifier] += rule
end
collection
end
end
private
def separate_rules
@css.split('}')
end
# Strip comments and extraneous white space from our CSS rules
def clean_up_rule(css_rule)
css_rule = css_rule.gsub(/\/\*.+?\*\//, '')
css_rule.gsub(/\n|\s{2,}/, '')
end
# Break apart our selector(s) and rule. We return an array
# of selectors to allow for situations where multiple selectors
# are specified (comma separated) for a single rule
def prepare_selectors_and_rule(rule)
parts = rule.split('{')
selectors = parts[0].split(',').map(&:strip)
return selectors, clean_up_rule(parts[1])
end
end
if __FILE__ == $0
if ARGV.length == 2
require 'rubygems'
require 'hpricot'
doc = Hpricot(open(ARGV[1]))
parser = SimpleCSSParser.new(ARGV[0])
parser.to_hash.each do |selector, rule|
(doc/selector).set('style', rule)
end
puts doc
else
puts "Usage ./css-inliner.rb /path/to/stylesheet.css /path/to/webpage.html"
end
end