Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I merge CSS definitions in files into inline style attributes, using Perl?

Many email clients don't like linked CSS stylesheets, or even the embedded <style> tag, but rather want the CSS to appear inline as style attributes on all your markup.

  • BAD: <link rel=stylesheet type="text/css" href="/style.css">
  • BAD: <style type="text/css">...</style>
  • WORKS: <h1 style="margin: 0">...</h1>

However this inline style attribute approach is a right pain to manage.

I've found tools for Ruby and PHP that will take a CSS file and some separate markup as input and return you the merged result - a single file of markup with all the CSS converted to style attributes.

I'm looking for a Perl solution to this problem, but I've not found one on CPAN or by searching Google. Any pointers? Alternatively, are there CPAN modules one could combine to achieve the same result?

  • Ruby http://premailer.dialect.ca/
  • PHP http://www.pelagodesign.com/sidecar/emogrifier/
  • Perl ?
like image 676
mintywalker Avatar asked Aug 13 '09 11:08

mintywalker


2 Answers

I do not know of a complete, pre-packaged solution.

CSS::DOM's compute_style is subject to pretty much the same caveats as emogrifier above. That module, in conjunction with HTML::TokeParser ought to be usable to cook up something.

Update: Here is a buggy mish-mash of things:

#!/usr/bin/perl

use strict;
use warnings;

use CSS::DOM;
use File::Slurp;
use HTML::DOM;
use HTML::TokeParser;

die "convert html_file css_file" unless @ARGV == 2;
my ($html_file, $css_file) = @ARGV;

my $html_parser = HTML::TokeParser->new($html_file)
    or die "Cannot open '$html_file': $!";

my $sheet = CSS::DOM::parse( scalar read_file $css_file );

while ( my $token = $html_parser->get_token ) {
    my $type = $token->[0];
    my $text = $type eq 'T' ? $token->[1] : $token->[-1];
    if ( $type eq 'S' ) {
        unless ( skip( $token->[1] ) ) {
            $text = insert_computed_style($sheet, $token);
        }
    }
    print $text;
}

sub insert_computed_style {
    my ($sheet, $token) = @_;
    my ($tag, $attr, $attrseq) = @$token[1 .. 3];
    my $doc = HTML::DOM->new;

    my $element = $doc->createElement($tag);

    for my $attr_name ( @$attrseq ) {
        $element->setAttribute($attr_name, $attr->{$attr_name});
    }

    my $style = CSS::DOM::compute_style(
        element => $element, user_sheet => $sheet
    );

    my @attrseq = (style => grep { lc $_ ne 'style' } @$attrseq );
    $attr->{style} = $style->cssText;

    my $text .= join(" ",
        "<$tag",
        map{ qq/$_='$attr->{$_}'/ } @attrseq );
    $text .= '>';

    return $text;
}

sub skip {
    my ($tag) = @_;
    $tag = lc $tag;
    return 1 if $tag =~ /^(?:h(?:ead|tml)|link|meta|script|title)$/;
}
like image 188
Sinan Ünür Avatar answered Sep 26 '22 02:09

Sinan Ünür


You can use CPAN Perl module CSS::Inliner https://metacpan.org/release/CSS-Inliner

like image 29
Victor Sanchez Avatar answered Sep 25 '22 02:09

Victor Sanchez