Moody HTML for variable content

"But what if I don't want happy content?", you ask. "What use is a Happy HTML transform to me?"

Hey! I have some questions!

Oh, hello. I'm sure you do.

I heard you want to make me write a new transform.

Actually, that's not a question. Also, I'm pretty sure that's not what I said.

Yes you did! There's a whole blog on it!

Oh, of course. This one? It's actually about how "new transform" doesn't always mean much work, and how it can actually be a really good idea. I'd suggest re-reading it.

Oh … ok. Yeah, I see what you mean. But I also have problems with the follow-up!

Ahh, the tutorial! But … that's supposed to make you 😜smile😜!

That's the problem. YOU CAN'T MAKE ME SMILE!

Ahhhh... ok. So what you want is a way to make unhappy HTML.

Well … not exactly … you can't make me be unhappy either!

I think I understand now. You want variable HTML! Today you might publish something chipper, but tomorrow you might be grumps!

Exactly. How do I do that?

Finally, a question. For that you'll need another tutorial.


This tutorial is based on DITA-OT 2.4 and later. It will work, with some tweaks, on 2.2 and 2.3. But … you might want to stop here if you're still on 2.1, or [gasp!] 1.8.

Grumble grumble gripe gripe OK fine I did the other one let's get started.

OK! I'm going to assume you've already got the happy html plugin described in the previous tutorial. So, start by copying that entire plugin to a new directory in the dita-ot/plugins/ directory, such as dita-ot/plugins/org.metadita.moodyhtml

Does it matter what I call my plugin?

Nope. You can call it whatever you want - but make sure you update the ID in your plugin.xml file to match. I'd stick with that sample name for now just because it goes along with the rest of this tutorial.


I like to start with org.metadita because that's the domain you're looking at right now, followed by something related to the plugin. For example, org.metadita.moodyhtml works for a new "moodyhtml" transform. If I'm providing a new step for all transform types that converts every instance of <note type="other" othertype="giggles"> into an audio clip of my toddler, I might store that in a plugin called – but that's a tutorial for another day.

OK before that ridiculous tip, you said something about a plugin ID?

Right. First, we'll go into the plugin.xml file and make a few changes.

  1. Change the plugin @id to match the new directory name
  2. Change the declared transform type from "happyhtml" to "moodyhtml"
  3. It's not required, but let's also change the name of the imported Ant build code from happybuild.xml to moodybuild.xml

Remember to rename the file itself to moodybuild.xml as well. After that, your plugin.xml file should look like this:

<?xml version="1.0" encoding="UTF-8"?>
<plugin id="org.metadita.moodyhtml">
  <require plugin="org.dita.html5"/>
  <transtype name="moodyhtml" extends="html5" desc="MoodyHTML"/>
  <feature extension="" file="moodybuild.xml"/>
Got it. Change the ID, the transform type / description, and the build file name. What next?

There's a bit more housekeeping. At this moment, moodybuild.xml is still set up to support the happyhtml transform type. Open that file in your favorite text or XML editor, and change all instances of "happy" to "moody".

This will change your specified XSL file from happyStyle.xsl to moodyStyle.xsl – so go into the xsl directory and change that file name to match.


If you miss this step, and you try to install the plugin while there is still a copy of the "happyhtml" plugin, you'll get errors about duplicate project names. This is because you've got multiple copies of the same "happyhtml" build targets in your Ant code. Doing the search/replace fixes this, and leaves you with a working "moodyhtml" build that (at this point) will exactly replicate the earlier "happyhtml" build.

Your updated moodybuild.xml will look like this:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:dita="" name="dita2moodyhtml">

  <target name="dita2moodyhtml" depends="moodyhtml.init, dita2html5"/>

  <target name="moodyhtml.init">
    <property name="args.draft" value="yes"/>
    <property name="args.css" value="${}/css/myBrand.css"/>
    <property name="args.copycss" value="yes"/>
    <property name="args.xsl" value="${}/xsl/moodyStyle.xsl"/>

Well that was tedious. What next?

Now it gets fun.


Sorry. Now it gets interesting.

Ok. It's about time. How do we do that?

We're going to make a parameter that lets you specify your mood during a build.

I gotta say, my moods change pretty frequently.

This is an introductory tutorial, we're not worried about edge cases. The Happy HTML build is used to make content ☺happy☺; here we'll focus on a few of our most common moods.

  • chipper ☺
  • meh 😐
  • grumps ☹
  • stinky 💩
  • cat 😺
Seriously? You're going to go there? I can't believe a "professional" blog would use a mood called "cat"


Fine. What next?

So, DITA-OT offers this nifty feature now where you can 1) declare a parameter and 2) declare valid values for that parameter. It's actually part of that same plugin.xml file. Right where you declare the moodyhtml transform type, you can declare this new parameter and specify valid values. This means if you accidentally set your mood to "monkey", the build will (rightly) fail because that's not a valid mood. (Remember, we're not worried about edge cases!)

I'm going to call the parameter args.moodyhtml.mood. Yes, it's repetitive, but with a parameter specific to your transform type it's probably a good idea to name the parameter something based on the transform type; this ensures it won't conflict with any other plugins.

To declare the new parameter, update your plugin.xml file as follows:

<?xml version="1.0" encoding="UTF-8"?>
<plugin id="org.metadita.moodyhtml">
  <require plugin="org.dita.html5"/>
  <transtype name="moodyhtml" extends="html5" desc="MoodyHTML">
    <param name="args.moodyhtml.mood" desc="Specifies the mood of your moody content." type="enum">
  <feature extension="" file="moodybuild.xml"/>
I guess I understand that. Each value gets a <val>.

Yep. Now, it's time to install the plugin and try out the new parameter. It won't do anything, but you can at least see how it works. First, run the bin/dita -install command that should be familiar from the happyhtml tutorial. Next, try to run a build and specify the new parameter:

bin/dita -i docsrc/samples/tasks/changingtheoil.xml -f moodyhtml --args.moodyhtml.mood=meh
Note: If you're on DITA-OT 2.2 or 2.3, specify that last argument using -Dargs.moodyhtml.mood=meh

The build should work - and produce the same output you got with "happyhtml", because we're not actually using the parameter yet. Now, try changing the parameter value to something invalid – such as args.moodyhtml.mood=pineapple. With that value, you should get an immediate failure:

Error: Invalid value for property args.moodyhtml.mood: pineapple
Ok. But how do I make my HTML show the mood I specified?

You need to do a few things.

  1. Make sure that the args.moodyhtml.mood parameter is passed to the HTML process. This part takes us back to the plugin.xml file. We need to add a plugin feature that says "I have parameters that go with HTML5". The new plugin.xml looks like this:
    <?xml version="1.0" encoding="UTF-8"?>
    <plugin id="org.metadita.moodyhtml">
      <require plugin="org.dita.html5"/>
      <transtype name="moodyhtml" extends="html5" desc="MoodyHTML">
        <param name="args.moodyhtml.mood" desc="Specifies the mood of your moody content." type="enum">
          <val default="true">cat</val>
      <feature extension="dita.conductor.html5.param" file="params.xml"/>
      <feature extension="" file="moodybuild.xml"/>
  2. Create the params.xml file in your plugin directory; this lists parameters that will be passed to every HTML5 transform:
    <?xml version="1.0" encoding="UTF-8"?>
        <param name="args.moodyhtml.mood" expression="${args.moodyhtml.mood}" if="args.moodyhtml.mood"/>
  3. Update the XSL file in your plugin to do something with the new parameter. I've associated each possible value with the characters you (should) see above. This just tweaks the XSL from our earlier happy tutorial, though to simplify things it now only applies to titles.
    <?xml version="1.0" encoding="UTF-8" ?>
    <xsl:stylesheet xmlns:xsl=""
      <xsl:import href="plugin:org.dita.html5:xsl/dita2html5.xsl"/>
      <xsl:param name="args.moodyhtml.mood"/>
      <xsl:variable name="moodychar">
          <xsl:when test="$args.moodyhtml.mood='chipper'">&#x263a;</xsl:when>
          <xsl:when test="$args.moodyhtml.mood='meh'">&#x1f610;</xsl:when>
          <xsl:when test="$args.moodyhtml.mood='grumps'">&#x2639;</xsl:when>
          <xsl:when test="$args.moodyhtml.mood='stinky'">&#x1f4a9;</xsl:when>
          <xsl:when test="$args.moodyhtml.mood='cat'">&#x1f63a;</xsl:when>
          <xsl:otherwise><xsl:text> </xsl:text></xsl:otherwise>
      <xsl:template match="*[contains(@class,' topic/title ')]//text()">
        <xsl:value-of select="translate(.,' ',$moodychar)"/>
Ok I'm getting tired. What now?

Actually, you're done. Re-install the plugin, and build any topic. Be sure to set the parameter as above, and double check your output file. We'll go with grumps because I'm pretty sure you're getting tired of this. Here's a sample command:

bin/dita -i docsrc/samples/hierarchy.ditamap -f moodyhtml --args.moodyhtml.mood=grumps
Yeah, that worked. And yeah, I'm feeling kind of ☹

Would it help if I made this more applicable to your own life?

Of course.
Imagine some other parameters:
  • args.campaign could let you generate a different header based on today's marketing campaign
  • args.publish.platform could generate a different set of headers based on where you plan to host the content
  • args.reviewStage could highlight the current review status of every topic
  • args.floofy.level could be used to make your HTML more floofy (er … floofier?)
Floofy? I think you've gone too far here. Are you writing this late at night?

… Yes. I should probably stop now.

Wait, before you go … can I download this somewhere? You know … in case mine didn't work … I mean, I totally followed the instructions, I just, um, probably had a typo somewhere, and then I thought maybe …

Yeah, yeah. You can download a zip and make it your own. Have fun tweaking!