I've searched around the 'Net for something like this before, but without success, so decided to write my own. The basic idea is this: there are certain circumstances in which you need to write some styled HTML without access to the document's header. For example, when composing HTML-formatted e-mails, which may be displayed in a web-based e-mail client; when adding content to limited content-management system; or in an eBay auction description. In such a situation, you can't (validly) include a <link rel="stylesheet"> element, nor a block (if you do include such a construct it may just about work, but the document will not be valid), so instead you are reduced to using HTML style attributes all over the place.

However, it is slow work using style attributes as you can't take advantage of CSS selectors. Instead of writing the following CSS once:


p
{
	font-weight: normal;
	font-size: 100%;
	margin: 0.33em 0 0.67em;
}

You need to insert those styles into the style attribute of each paragraph. If you decide to, say, adjust the margin, you'll need to go through each paragraph changing it. Without CSS, styling is a drudgery.

That is where my "CSS Compiler" comes in. You write your HTML without style attributes, using an external CSS file. Then pass both through the compiler to produce an output HTML with all the CSS rules transferred into style attributes. Magic! As an added bonus, the output file is produced as well-formed (though not necessarily valid, unless the input is valid), properly indented XHTML. (If you want HTML instead of XHTML, as I normally do, just search and replace "/>" with ">".)

Usage

css-compile.pl --css=STYLESHEET INFILE OUTFILE

INFILE and OUTFILE may be given as "-" to refer to STDIN and STDOUT respectively.

CSS Support

Supports the following selectors:

  • Element selector (e.g. div {...})
  • Class selector (e.g. .warning {...})
  • ID selector (e.g. #header {...})
  • Descendent selector (e.g. div p {...})

The following selectors should work, but may be buggy:

  • Child selector (e.g. div>p {...})
  • Sibling selector (e.g. p+p {...})

Pseudo-class selectors such as :hover and :visited do not work of course and cannot work; they may cause the script to hang or crash! The :first-line pseudo-element similarly cannot be implemented; :first-letter is feasible I suppose, but not currently implemented. Attribute-based selectors are not yet supported.

Specificity and the CSS cascade are (mostly) respected. So a rule that already appears in a style attribute will not be overridden by one being compiled in from the CSS file; ID-selector rules will override class-selector rules which will override element-selector rules; deeply nested descendent selectors will override shallow ones; etc. All things being equal, rules later in the stylesheet override rules earlier in the file.

CSS Shorthand Properties

CSS properties are not interpreted in any way, so for example, the following rules:


.col { margin: 1em 2em 1em 2em; }
.col { margin-bottom: 3em; }

will not be "rationalised" to


style="margin: 1em 2em 3em 2em;"

To avoid conflict between CSS shorthand and longhand properties, it is recommended you either always use the shorthand form, or never use the shorthand form, and not to attempt mixed usage in the same document.

Requirements

  • Perl 5
  • CSS::Tiny
  • CSS::Tiny::Style
  • Getopt::ArgvFile
  • Getopt::Long
  • HTML::Element
  • HTML::TreeBuilder
  • HTTP::Cookies
  • Pod::Usage
  • URI::Escape

The script has only been tested on Linux, but I imagine it will work on any supported Perl platform, provided the above requirements are met.

Download