HTML::Transmorgify - HTML transformation compiler


 use HTML::Transmorgify;

 my $magic = HTML::Transmorgify->new(xml_quoting => 1);


 my $output = $magic->process($input_text, { %options }, %variables);
 my %output_by_type = $magic->process($input_text, { %options }, %variables);


HTML::Transmorgify is an HTML compiler framework. It transforms HTML into a arrays of static text and CODE callbacks. The arrays can be turned into customized HTML very quickly. The compilation process is quick and the runtime process is even quicker. It is designed for sites that will dynamically generate all their content.

By itself, HTML::Transmorgify doesn't do anything useful: it is just a framework. Most users will want to use one or more of the addon modules for HTML::Transmorgify:


Provides a template language processor with macros, control flow, and basic functions.


Adds cryptographic checksums to forms so that users cannot add additional elements, change or remove hidden values, or set dropdowns or radio buttons to values that were not provided.


Overrides the default values in the form definition so that the end-user's prior input is remembered when re-displaying the form.


Adds cookie crumbs to URLs to and forms so to protect against cross-site scripting attacks that direct a logged-in user back to a page that will do something naughty.


Changes multiple <img> tags into <div> tags with background attributes that point into shared composite image. This decreases page load time because many fewer items need to be fetched.

It also adds height and width attributes to images that don't have them yet.

It notices roll-over images and adds javascript to display them.

As of January 2011, this addon is not yet complete.

Additional modules can be added to the framework.

USING HTML::Transmorgify

By itself, HTML::Transmorgify doesn't do anything useful. You need to blend in one of the addon modules or write your own.

You invoke HTML::Transmorgify with:

 my $output = $magic->process($input_text, { %options }, %variables);


 my %output_by_type = $magic->process($input_text, { %options }, %variables);

In array context, the results from processing templates are returned as a hash, mapping the type of result to the result string. Two result types are always present: text and script. Other result types may be added by add-on modules.

Currently, all of the options in { %options } are made available to addon modules via the $invocation_options variable. In additon the following items are singled out and have defined meanings:


A reference to a query parameters hash.


The filename from which the $input_text came from.


A line-number offset from the beginning of the input_file so that error messages can refer to the line number of the error.

The %variables are used by HTML::Transmorgify::Metatags as pre-defined macro values. They can be strings, hashes, arrays, or objects. If they're objects, they should inherit from HTML::Transmorgify::ObjectGlue.


HTML::Transmorgify compiles HTML. It compiles HTML into an array of callbacks and literals. It then executes the compiled array. Compilation output from an input string is cached for reuse. Files are compiled separately.

When you write an addon module, you register to be called back for particular HTML tags. When you are called back, you need to generate strings to be appended to the the final document or callbacks to be called at runtime.

The runtime program is an array: @$HTML::Transmorgify::rbuf. There is a little function for appending to that array: rbuf().

Addon modules generally start the same:

 package HTML::Transmorgify::MODULE_NAME;

 use HTML::Transmorgify qw(continue_compile capture_compile run queue_intercept rbuf postbuf);

 our @ISA = qw(HTML::Transmorgify Exporter);

 sub add_tags
        my ($self, $tobj) = @_;
        $self->intercept_exclusive($tobj, __PACKAGE__, A_NUMBER, %EXCLUSIVE_TAGS)
        $self->intercept_shared($tobj, __PACKAGE__, A_NUMBER, %SHARED_TAGS);

Where "A_NUMBER" is a priority number for choosing which callbacks get called first (lower is earlier); %EXCLUSIVE_TAGS maps HTML tags to callbacks that compile that HTML tag; and %SHARED_TAGS maps HTML tags to callbacks that participate in compiling that HTML tag.

Generally you want an exclusive tag when the tag isn't actually part of HTML like when you are creating a template language like what is done in HTML::Transmorgify::Metatags.

For situations where you are making an adjustment to regular HTML tags, you usually want a shared tag. For example, HTML::Transmorgify::FormChecksum and HTML::Transmorgify::FormDefault both modify forms and attach callbacks to the <input> tags.

Shared tags must be handled carefully so that the multiple modules that are acting on a tag do not get in each other's way. Not everything can be handled by with a shared tag For example, the <foreach> tag defined in HTML::Transmorgify::Metatags is an exclusive tag.

All callbacks are invoked with the following arguments:


A HTML::Transmorgify::Attributes object representing the a tag and its attributes.


Closed if this is an end-tag or self-contained tag like <hr />.

Shared Tags

The return value from a shared tag callback is ignored unless the return value is a CODE ref. The callbacks are called at compile time. Any CODE refs returned by the callback will be invoked at runtime BEFORE the tag is interpolated into the results.

Shared tags are always interpolated into the results.

The usual form of a shared tag is something like:

 $SHARED_TAGS{img} = \&img_tag;
 sub img_tag {
    my ($attr, $closed) = @_;
    return 1 unless $attr->raw('alt');
    rbuf(sub {
        my $text = $attr->get('src');
        $text =~ s{.*/}{};
        $text =~ s/([a-z])([A-Z])/$1 $2/g;
        $text =~ s/_/ /g;
        $attr->set(alt => $text);
    return 1;

This example transformation will try to turn the image file name into an <alt> tag.

It uses rbuf() to add a callback that will run just before the <img> tag is added to the output stream.

It calls


to make sure that the <img> tag is evaluated at runtime rather than added as a literal string at compile time.

It returns a value of 1 to indicate that the <img> tag should be included in the output stream.

Exclusive Tags

The return value from an exclusive tag indicates whether the tag should be included in the resulting text or not. A true value will cause the tag to be inclucd; a false value will not.

APIs for Callback Writers


The tiny rbuf function pushes its arguments on the HTML::Transmorgify::rbuf array. This array represents the output from compiling the HTML. It can conntain the following elements:


Plain scalars will be interpolated into the final output unchanged.

CODE refs

Code references will be called. For them to add to the final output, they need to append to the strings in @$HTML::Transmorgify::result. The first of these, $$HTML::Transmorgify::result[0] is used for regular results. If additional types of output are needed, the array indexes should be allocated by calling allocate_result_type.


Pushes its arguments onto @HTML::Transmorgify::post_intercept_push. This is used during shared tag callback processing. The contents of @HTML::Transmorgify::post_intercept_push is pushed onto HTML::Transmorgify::rbuf after the rest of the shared tag callback processing is done.


Alocates another "key" for the results array. The predefined keys are: text and script.

Keys can be looked up in %HTML::Transmorgify::result_index.


The boolean function returns false if it's argument is: false, no, off, 0, or undefined. Otherwise it returns true.

run($buf, $results)

The run function "executes" the compiled HTML in @$buf. Literal strings will be added to $$results[0], code references will be invoked.

If $results is not specified, it will default to $HTML::Transmorgify::results.


Exclusive tag callbacks only. Advance the input parsing position pos($$HTML::Transmorgify::textref) past a newline.

$rbuf = compile($cacheline, $textref)

Compiles $$textref and returns the results. Results are are cached so compiling the same string ($$textref) in the same context will result in a cached result. Do not modify the returned array (@$rbuf) as since it can be handed out again from another call to compile.

The $cacheline argument specifies the caching context for the input string. For compiling things in the normal context where all currently active transformations can apply, use the context $HTML::Transmorgify::modules.

($rbuf, $deferred) = capture_compile($tag, $starting_attr, $opts, %tags)

Exclusive tag callbacks only. Continues to compile the current $HTML::Transmorgify::textref (as passed to compile()) until a closing </$tag> is reached. For better error messages, the HTML::Transmorgify::Attributes for the opening tag ($starting_attr) is passed in.

The callbacks for multiple tags may be overridden with the %tags argument. Since these may be shared callbacks, only the callbacks for the module doing the overridding should be modified. That should be specified as: $$opts{tag_package}.

The call to capture_returns after the input pointer has moved past </$tag>. No </$tag>. is pushed into the compile output buffer HTML::Transmorgify::rbuf and the callbacks requested for </$tag> have not been called. To invoke them, simply run:


This will also push a </$tag> into the output stream.

continue_compile($tag, $starting_attr, $opts, %tags)

Exclusive tag callbacks only. continue_compile() is very much like capture_compile() except that the output from the compilation process is appended to the existing HTML::Transmorgify::rbuf array and there is no return value from the function.

The purpose of continue_compile() is to allow the normal compilation process to continue for a while. The function returns after </$tag> has been rpocessed. Tag callbacks may be temporarily overridden for the duration of the call.

If $tag is undef, the compilation will continue until the end of $$textref is reached or until a HTML::Transmorgify::CloseTag callback is invoked.

local($HTML::Transmorgify::dispatch{"/$tag"}) = HTML::Transmorgify::ClosedTag->new($HTML::Transmorgify::dispatch{"/$tag"});

This will override the callbacks for </$tag> so that continue_compile or capture_compile will stop when </$tag> is reached. This is normally done automatically but this invocation is needed if stopping for more than one tag.


Shared tag callbacks only. Shared tag callbacks cannot use capture_compile() and continue_compile(): they can use queue_capture() instead. The function that handles calling all the shared tag callbacks will do a capture_compile(), looking for the close tag to match the current tag if any of the shared tag callbacks uses queue_capture().

After the $coderefs are invoked (they receive the $rbuf from capture_compile() as their only argument) the temporary $rbuf compile buffer will be appended to the main compile buffer. After that, the deferred callbacks from the close tag will be invoked.

queue_intercept($tag_pkg, %tags)

Shared tag callbacks only. Shared tag callbacks cannot use capture_compile() and continue_compile(): they can use queue_capture() instead. The function that handles calling all the shared tag callbacks will do a continue_compile(), looking for the close tag to match the current tag if any of the shared tag callbacks uses queue_intercept(). If queue_capture is also used by a tag callback, then capture_compile() will be used instead.

For the duration of the continue_compile() (or capture_compile()) tag callbacks will be overridden by %tags. $tag_pkg should be set to the name of the invoking package (__PACKAGE__).


Calvin & Hobbs used to transmogrify not transmorgify things. This module will be renamed to fix my spelling mistake soon. Other API changes may still be made. Please contact the author if you want to be included in dicussions of this module's future.


Other modules that do similar things: HTML::Seamstreess


David Muir Sharnoff <muir at idiom dot org>

Development of this module is hosted on github:

Some parts of this module are Copyright (C) Google, Inc.


Your choice of LGPL or Artistic License.