PN (20040912)
CSS2HTML is an attempt to solve the problem of managing the styles of many websites. CSS provides a convenient and condensed description of style compared to what would have to be written into HTML tags. But the browser world is splitting into two camps. The mega browser for the home PC and the micro/lite browser for the overworked PC, the Palm and other small devices. It is now more important than ever to write the best HTML that stands by itself and produces the best page possible.
WJR - Would another way to approach this problem be to keep the HTML as structured as possible and use CSS media types [L1 ] to describe the presentation for different browsers?
So, how to embed the CSS back into the HTML for delivery to lite browsers ?
I propose
proc css2html {tag style} { . . . }
style is the name of a Tcl global object which holds the CSS. tag is the name of the tag which is to be extracted from the CSS.
CSS2HTML returns the opening HTML tag.
Here is an example of the BODY tag - first the CSS.
body { margin: 1em; line-height: 1.1em; font-family: "MS Sans Serif", sans-serif; font-size: 8pt; background-color: white; color: #222; } a:link { color: #777 } a:visited { color: #a86 } a:active { color: #8a6 } h2 { color: #77b } h3 { color: #66a } . . . .
The CSS is read into a Tcl array object so it looks like:
array set style { {1 body} {margin 1em line-height 1.1em font-family {"MS Sans Serif", sans-serif} font-size 8pt background-color white color #222} {2 a:link} {color #777} {3 a:visited} {color #a86} {4 a:active} {color #8a6} {5 h2} {color #77b} {6 h3} {color #66a} . . . . }
The structure is an array list of array lists. A line number is added to preserve the order of the CSS statement in the style list.
> "css2html body" style <body vlink='#0ff' bgcolor='white' style='font-family: "MS Sans Serif", sans-serif; line-height: 1.1em; margin: 1em; font-size: 8pt; ' text='#222' link='#00f' alink='#f00'>
Note that a style attribute is produced to hold the stuff that doesnt fit in HTML in this example. If a browser is going to read the style attribute it will be able to read the stylesheet itself. So the style attribute could be considered redundant. However a non-CSS browser could chose to read the style attribute if it implemented some features available in CSS. Lite browsers are going to expand their features using CSS as a base without taking on the whole of CSS and becoming big fat squishy things that bulge out the sides of my Palmy.
The body tag is easy because there is no need for id or class attributes but other tags are going to use these and more. I dont intend to implement the whole of CSS myself. I have other demands on my time. I will implement as much as I need to, to get my job done and no more.
However the general principle is interesting and aids the management of large websites and their ability to deliver high quality pages to lite application platforms.
As for the code - here is what does the above example:
#creates a new global array name which begins with the word in arg p followed by an integer proc globalarray p { for { set i 1} { [uplevel #0 info exists ${p}$i] } { incr i } {} global ${p}$i set ${p}${i}(0) {} unset ${p}${i}(0) return ${p}$i } # creates a tcl style global array object from a CSS file # returns the name of the tcl style global array (object) proc css2tcl file { set f [ open $file r ] set css [ read $f ] close $f # create the array object set style [globalarray style] global $style # first we get rid of comments - css comments are not allowed to go over a line regsub -all {/\*[^\n]*\*/} $css "" newcss # we want to grab every string of the form # "foo foo1 . . { attr1: val1 val2 ...; .... attrn: valn1 valn2 ...[;] }" # and turn it into # "style(stmt# foo foo1 ...) { attr1 {val1 val2 ...} .... attrn {valn1 valn2 ...} } for { set i 1 } {[regexp {^([^\{]*)\{([^\}]*)\}} $newcss match elements attrs]} { incr i } { # strip the match from newcss set newcss [ string range $newcss [string length $match] end ] # parse the attrs - "attr: val1 val2 ...; .... attrn: valn1 valn2 ...[;]" # becomes "attr {val1 val2 ...} .... attrn {valn1 valn2 ...}" # split and trim each element set newattrs {} foreach j [ split $attrs ":;" ] { lappend newattrs [ string trim $j ] } # remove the odd trailer if there was a terminating ; if { [lindex $newattrs end ] == {} } { set newattrs [ lrange $newattrs 0 end-1] } # dummy test of array list if { [ catch {array set a $newattrs} msg ] } { global errorInfo bgerror "Error in stylesheet $file The following attribute list does not pair up and has been ignored: $newattrs $errorInfo" } else { set "${style}($i [string trim $elements])" $newattrs } } # newcss should now be empty if { [string trim $newcss] != {} } { global errorInfo bgerror "Error in stylesheet $file The following was not parsed: [ string trim $newcss ] $errorInfo" } return $style } # this routine reads tcl array versions of a style sheet and returns an html BODY tag # style is the name of a tcl array containing the CSS style proc "css2html body" style { upvar $style css # set defaults array set body {bgcolor #000 text #fff link #00f vlink #0ff alink #f00} # get the body foreach s [ lsort -integer [array names css "* body*" ]] { array set cssbody $css($s) foreach {c h d} { background-color bgcolor color background-image background url color text color } { if { [info exists cssbody($c)] } { set body($h) [ "css2html $d" $cssbody($c) ] unset cssbody($c) } } # have not handled the CSS background attribute # put remaining body attributes into style set str "" foreach {i j} [array get cssbody] { append str "$i: $j; " } if { $str != {} } { set body(style) $str } unset cssbody } foreach {i j } { a:link link a:visited vlink a:active alink } { if { [ info exists css($i) ] } { array set cssbody $css($i) if { [ info exists cssbody(color) ] } { set body($j) [ "css2html color" $cssbody(color) ] } unset cssbody } } return [ "css2html make" body ] } # make the html opening tag for the tag type named tag proc "css2html make" tag { upvar $tag t set str "" foreach i [ array names t ] { append str " $i='$t($i)'" } return "<${tag}$str>" } # for now assume that CSS only uses HTML color specs proc "css2html color" color { return $color } proc "css2html url" url { regexp {url\((.*)\)} $url a b return $b }
All contributions gratefully received. PN
escargo - Any idea how to go the other way? It would be interesting to see how to factor existing HTML to remove duplicate formatting and put it into a style sheet. I've got that problem with a web site I'm maintaining now.