Wednesday, December 23, 2009

HTML Generation - A Little Scribble Goes A Long Way

The Challenge

I have a client who needed a designed page turned into an HTML message suitable for e-mailing. HTML e-mail is especially painful because there's nothing resembling a modern standard for it. The basic rule of thumb is this: stick with ancient HTML 3.0 layout techniques, and ignore all the time-saving-clarity-providing CSS code you use daily. Sure, you can make occasional use of inline styles, where if they are ignored, no real damage is done - but external style sheets, tabeless layouts, and other goodies are out.

I suppose I could have just opened up a .html file in emacs and started typing away. But, it quickly becomes apparent that text - be it things like standard URLs, or style information, would be repeated. And repeating code is evil. I knew I needed something more sophisticated than a plain HTML document.

After mulling it over, I decided I would use scribble to generate the HTML document. And while it was tempting to create an entire language to express the document in (like say LAML), I decided that I would avoid this. My plan was to use as much HTML as possible, and just leverage Scribble where it would really help.

Getting Started

Generating HTML from a Scribble document is trivial. I put the following in message.scrbl:

#lang scribble/text
<html>
  <body>
    <h1>Hello World</h1>
  </body>
</html>

While this is technically a Scheme program, the first line puts Scribble in a friendly preprocessor mode.

To generate the HTML document, I whipped up a Makefile, which essentially did:

 mzscheme -t message.scrbl > message.html

At this point, I've got the warm and fuzzy feeling knowing I'm using a preprocessor, but I haven't actually done anything with it.

Adding Standard Variables

Throughout the HTML e-mail, a standard URL to link folks to is mentioned. Knowing that this URL can change, I figured it would be smart to it avariable. I did this by creating a file named vars.ss, which contained:

(define project-url "http://someurl.com/some/path")
(provide (all-defined-out))

In this case, I define a single variable. The second line says that any variable I define in this file should be exported. This is me being somewhat lazy, though, the purpose of this file is provide variables - so exporting all defined values seems reasonable. I can now use this variable in my document:

#lang scribble/text
@(require "vars.ss")
<html>
  <body>
    <h1>Hello World</h1>
    <p>
      Want to visit <a href='@|project-url|'>@|project-url|</a>
    </p>
  </body>
</html>

Notice that the vars.ss file is being included in here, and that project-url is being referenced. As you can see, @... is magic - the rules are defined here, and they aren't too painful to figure out.

Picking up images remotely

The HTML e-mail calls for images to be included. This is a relatively simple thing to do: one needs to put the image on a remote server, and then reference using an absolute path. Like the URL from the example above, it's quite possible that the location of these images could change. To make the system a bit more flexible, I added the following (trivial) function in utils.ss:

(define (asset name)
  (format "http://someurl/some/path/~a" name))
(provide/contract
 [asset (-> string? string?)])

I can now use this function in my document:

#lang scribble/text
@(require "vars.ss" "utils.ss")
<html>
  <body>
    <img src='@asset{header.gif}' alt='Header Graphic'/>
    <p>
      Want to visit <a href='@|project-url|'>@|project-url|</a>
    </p>
  </body>
</html>

Now I feel like I'm getting somewhere! The asset function is called in such a way that it naturally fits with the rest of the HTML doc.

Taming Styles

One of the features I miss most while working with straight HTML is the ability to do external stylesheets. It's just so nice to sprinkle a class='foo' throughout a document, and then later define what foo actually means.

Recognizing that any external style, can be turned into an inline one, I decided I would simulate this functionality. I did so by creating a quick library that would allow users to register and access styles. The code is a bit long for this article, so you can download here. Once this library has been included, I was able to define styles in styles.ss using the following notation::

(define-style loud 
  (color "#DD0000") 
  (font-size "18px") 
  (font-weight "bold"))

I can now use this style as follows:

#lang scribble/text
@(require "../lib/style.ss" "vars.ss" "utils.ss" "styles.ss")
<html>
  <body>
    <img src='@asset{header.gif}' alt='Header Graphic'/>
    <p>
      Want to visit <a href='@|project-url|'>@|project-url|</a>.
      We <span @class{loud}>rock</a>!
    </p>
  </body>
</html>

Again, notice how the @ notation kind of blends with the HTML. In fact, emacs' HTML mode isn't thrown off by the whacky @class{...} notation, and indents the document properly.

The Sky's The Limit

For my purposes, the above was really all I needed to make authoring the HTML document a much saner process. Though, as you can see, you're really only limited by your imagination.

3 comments:

  1. Awesome. The PLT guys would probably get a kick out of this.

    ReplyDelete
  2. *sigh* I'd post a semi-long comment about stuff that you could do with this, but blogger is being picky about stuff in comments, and having just text is not going to work. I'll email you instead...

    ReplyDelete
  3. Guys -

    Thanks so much for the feedback!

    -Ben

    ReplyDelete