XHTML+MathML+SVG in Movable Type 4

January 13, 2008

Jacques Distler put a great deal of effort into modifying Movable Type 3 so that it could render MathML using the itex2MML plugin, serve XHTML+MathML+SVG content, allow MathML in comments, and validate those comments. This short guide describes how to do the same for Movable Type 4.01. It addresses the following three requirements:

There are other important aspects that I do not consider here, including validation of trackbacks and syndicated content, both equally as important as validating comments, and validation of the administrative interface so that authors can preview posts containing inline MathML or SVG content. When Jacques updated the MT3 interface to be XHTML compliant he ended up with a 1340-line diff file. Fortunately, my preliminary testing indicated that previewing works out-of-the-box on MT 4.01. I hope this means there isn’t much work to be done in this area.

You can see all of this in action at my MT4 MathML Sandbox. The notes I used to write this guide were a direct result of my setting up this site last October. Aside from some CSS and layout tweaks, everything I did is described here. Thus, you can reasonably expect to setup a functioning (though not necessarily bullet-proof) site in a relatively short amount of time.

Installation

This is related to allowing MathML in comments (addressed below). However, it is best to perform this step early. Later on, we will edit the database and paste a very long string in the sanitize_spec row. The MySQL type of this row needs to be text rather than string(255) in order to hold this long string.

An alternative option is to simply manually alter the mt_blog table in the database to change the type. The advantage of editing the source code directly is that later, if you install a plugin that updates your database, your changes will not be lost. Otherwise, Movable Type would read the type from lib/MT/Blog.pm and change it back to string(255), truncating your custom whitelist.

Generating MathML Content

This part is rather straightforward. First, install the itex2MML plugin. You will need to compile the itex2MML binary and edit the plugin to point to it. Then, posts authored using the MarkdownMML filter will result in valid XHTML+MathML content.

Serving MathML Content

To serve embedded MathML, we have to make sure that Movable Type is always emitting valid XHTML, served with the proper content type to each browser This involves three steps:

Set the DTD

Put the DTD string in a module called, say, “DTD”:

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN" "http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg-flat.dtd">

Then in the other templates which should include the DTD you can simply write <$MTInclude module="DTD"$>. The <?xml> tag has to be the very first thing in the file. Unfortunately there are other tags (e.g., MTSetVar) at the top of the templates that need to execute before the Header module is loaded. These tags leave whitespace behind so instead of simply including the DTD module in the Header module, the least redundant way, I placed a DTD include at the beginning of every template that needed it. This includes the following templates: Archive Index, Main Index, Entry, Entry Listing, Page, Comment Preview, Dynamic Error, and Search Results. Make sure to remove the old <!DOCTYPE> tag from the Header module.

For reference, there is a list of Movable Type 4.0 Default Templates.

Set the MIME Type

Here I’m following Jacques’s post, “Serve it up!”. To configure Apache to set the required content type for each browser, place the following in your .htaccess file:

RewriteEngine On
RewriteBase /
RewriteCond %{HTTP_USER_AGENT} MSIE.*
RewriteRule \.html$|\.xhtml$  - [T=text/html]
RewriteCond %{HTTP_USER_AGENT} Gecko|W3C.*Validator|MSIE.*MathPl
RewriteRule \.html$|\.xhtml$  - [T=application/xhtml+xml]
RewriteCond %{HTTP_USER_AGENT} Chimera|Camino|KHTML
RewriteRule \.html$|\.xhtml$  - [T=text/html]
RewriteCond %{HTTP_USER_AGENT} Camino.*MathML-Enabled
RewriteRule \.html$|\.xhtml$  - [T=application/xhtml+xml]

These rules tell Apache to set the content type to text/html for Internet Explorer, Chimera, Camino, and KHTML-based browsers (except for MSIE with the Math Player plugin and MathML-enabled Camino builds) and application/text+xhtml for everyone else. Here, I have allowed for either .html or .xhtml extensions. You can set the default (archive) file extension under Blog Settings | Publish.

We also need to make sure all CGI-generated pages are also served with the proper MIME type (for example /mt-comments.cgi?entry_id=1, which is still served as html/text). We can do this by passing an environment variable to Movable Type which gets set by Apache based on the user agent string. First, add the following to your .htaccess or Apache configuration:

RewriteCond  %{HTTP_USER_AGENT} MSIE.*
RewriteRule ^mt-comments|^mt-search  - [E=HTTP_CONTENT_TYPE:text/html]
RewriteCond  %{HTTP_USER_AGENT} Gecko|W3C.*Validator|MSIE.*MathPlayer
RewriteRule ^mt-comments|^mt-search  - [E=HTTP_CONTENT_TYPE:application/xhtml+xml]
RewriteCond  %{HTTP_USER_AGENT} MSIE.*|Chimera|Camino|KHTML
RewriteRule ^mt-comments|^mt-search  - [E=HTTP_CONTENT_TYPE:text/html]
RewriteCond  %{HTTP_USER_AGENT} Camino.*MathML-Enabled
RewriteRule ^mt-comments|mt-search  - [E=HTTP_CONTENT_TYPE:application/xhtml+xml]

Then, edit lib/MT/App.pm as follows:

--- lib/MT/App.pm       2007-10-18 20:55:05 +0000
+++ lib/MT/App.pm       2007-10-18 23:03:11 +0000
@@ -518,6 +518,9 @@
 sub send_http_header {
     my $app = shift;
     my($type) = @_;
+    if ($ENV{'HTTP_CONTENT_TYPE'}) {
+        $type = $ENV{'HTTP_CONTENT_TYPE'};
+    }
     $type ||= 'text/html';
     if (my $charset = $app->charset) {
         $type .= "; charset=$charset"

Validate Other Pages

Edit any remaining template items that don’t validate. Check all types of pages such as:

/
/archives/
/mt-comments.cgi?entry_id=1
/mt-search.cgi?search=test

In particular, you should escape the script that appears in the Comment Form:

<script type="text/javascript">
<!--//--><![CDATA[//><!--
<MTIf name="comment_preview_template">is_preview = true;</MTIf>
writeCommenterGreeting(commenter_name, <$MTEntryID$>, <$MTEntryBlogID$>, commenter_id, commenter_url);
//--><!]]>
</script>

NumericEntities

Finally, install the NumericEntities plugin to prevent sending named entities to browsers that can’t handle them.

MathML in Comments

Here I will outline how to enable MathML in comments and describe one solution for to automatically validating them. I will punt on the issue of Trackbacks.

Processing itex in Comments

Since this textarea is so small, I actually just put some temporary tags here and then edited the blog_sanitize_spec column of my mt_blog table. Make sure you get these right or you may experience MathML problems.

Enforcing Validation

Ideally we only want to accept comments that successfully validate after passing them through the MarkdownMML filter. This needs to be handled by a plugin. Jacques wrote the MTValidate which is a wrapper around the Perl module which powers the W3C Validator. It runs locally and thus has several dependencies.

I hacked up another plugin, called FragmentValidator, which can send an XHTML fragment off to the W3C Validator service for validation.1 Below, I show how to modify the default templates to force comment previews and to only allow submission of valid comments.

as well as the reporting:

    <MTIfFragmentInvalid>
    <p>Your is <strong>not</strong> valid <code>XHTML 1.1 + MathML 2.0 + SVG 1.1</code>.  Please revise and re-submit.</p>
    </MTIfFragmentInvalid>

    <MTIfFragmentValid>
    <p>Your is valid <code>XHTML 1.1 + MathML 2.0 + SVG 1.1</code>.
    You may submit it.</p>
    </MTIfFragmentValid>

Note that this approach does not prevent someone from previewing a valid comment, thus making the submit button visible, and then editing it and ultimately submitting an invalid comment. Although I haven’t implemented this myself, the MTHash plugin allows one to force comment previews.


  1. This plugin is just a quick hack and certainly leaves much to be desired. Since I don’t use Movable Type myself I don’t have plans to improve it. If you’d like to do so, feel free. Alternatively, for a local validation approach I would suggest starting with the MTValidate plugin.