A Better PDF Plugin: Using the topic2fo.xsl File instead of custom.xsl
In my book DITA For Print, I explain how to create a custom PDF plugin. The method I outline there is to first add stylesheets with the templates and attribute sets you want to override to the custom plugin; then import each of those stylesheets via multiple xsl:import statements in one or more custom.xsl files. These custom.xsl file(s) are integrated using the plugin’s catalog file.
In his DITA North America presentation “Managing Complexity in DITA-OT PDF Plugins,” delivered in April 2018, Bob Thomas of Tagsmiths, LLC explained an alternative method of creating a custom PDF plugin that offers a great deal more flexibility. With Bob’s blessing (and edits!), I’ll first explain this method and then its advantages.
The Scenario
Let’s set the scene. Your employer, BigCorp, has tasked you with developing two custom PDF plugins. The first (let’s call it “standard”) produces a PDF with full U.S. letter-sized pages (8.5 inches by 11 inches). The second (let’s call it “booklet”) produces a PDF with half U.S. letter-sized pages (5.5 inches by 8.5 inches). Aside from the different page sizes, a few other things are different as well. The standard PDF has 1 inch margins all around; the booklet PDF has .5 inch margins all around. The main body type size is 11pt in the standard PDF and 10pt in the booklet PDF.
Titles are smaller in the booklet PDF than in the standard PDF. However, the two formats share a great deal as well. Both use the same boilerplate images (cover page image, header logo, note icon, etc.) and both share the same language-specific variables and strings files. In addition, you’re injecting a custom build parameter, bigc.dist Level, that you want to be accessible to both plugins.
Based on this information, you determine that the most efficient thing to do is to create three plugins: com.bigcorp.standard.pdf, com.bigcorp.booklet.pdf, and com.bigcorp.strings. The com.bigcorp.standard.pdf plugin contains the following:
- boilerplate images
- language-specific variables files
- all the templates and attribute sets needed to process and format your DITA content
- font specifications bigc.dist
- level build parameter definition
The com.bigcorp.booklet.pdf plugin contains only the templates and attribute sets that diff er from those in com.bigcorp.standard.pdf. In all other respects, it “points to” or “shares” the language-specific variables and strings files, templates, attribute sets, font specifications, and build parameter definition in com.bigcorp.standard.pdf. Finally, the com.bigcorp.strings plugin contains the language-specific strings that you need to customize. Both com.bigcorp.standard.pdf and com.bigcorp.booklet.pdf share these strings. The strings plugin is also integrated with any HTML outputs that are available in the same DITA-OT. More on that later.
The Plugin Creation Process
Note: These instructions are for DITA OT 3.1. For earlier versions of the DITA OT, the steps might be slightly different, especially with respect to the language variables files.
This article assumes you are familiar with PDF plugin creation and does not provide detailed step-by-step instructions for creating a PDF plugin.
Step One: Create the Plugin Folders and Integration Files
First, create the com.bigcorp.standard.pdf, com.bigcorp.booklet.pdf, and com.bigcorp.strings folders as usual in the plugins folder of the DITA Open Toolkit. In each, create a plugin.xml file. In com.bigcorp.standard.pdf and com.bigcorp.booklet.pdf, create an integrator.xml file as well.
The following are examples of the syntax.
Note: The line numbers are obviously not part of the actual code.
plugin.xml (com.bigcorp.standard.pdf):
1 <plugin id=”com.bigcorp.standard.pdf”>
2 <feature extension=”package.version” value=”1.0.0″ />
3 <require plugin=”org.dita.pdf2″ />
4 <require plugin=”org.dita.pdf2.fop” />
5 <require plugin=”com.bigcorp.strings” />
6 <feature extension=”dita.conductor.target.relative” fi le=”integrator.xml”/>
7 <feature extension=”dita.conductor.pdf2.param” file=”parameters.xml”/>
8 <transtype name=”bigcstandardpdf” desc=”BigCorp standard PDF output”/>
9 </plugin>
Lines 3 and 4 specify that this plugin extends both the default org.dita.pdf2 plugin and the PDF formatter-specific org.dita.pdf2.fop plugin. (There are separate formatter-specific plugins for FOP, Render X and Antenna House.)
integrator.xml (com.bigcorp.standard.pdf):
1 <project name=”com.bigcorp.standard.pdf”>
2 <target name=”dita2bigcstandardpdf.init”>
3 <property location=”${dita.plugin.com.bigcorp.standard.pdf.dir}/ cfg” name=”customization.dir” />
4 <property location=”${dita.plugin.com.com.bigcorp.standard.pdf.dir}/cfg/fo/xsl/ bigc-stn.topic2fo.xsl” name=”args.xsl.pdf”/>
5 </target>
6 <target depends=”dita2bigcstandardpdf.init, dita2pdf2″ name=”dita2bigcstandardpdf” />
7 </project>
Line 4 specifies a file which determines which stylesheets to import to override the templates and attribute sets in org.dita.pdf2 (using the args.xsl.pdf property). This line is the main point of this article, so we’ll return to it in more detail.
plugin.xml (com.bigcorp.booklet.pdf):
1 <plugin id=”com.bigcorp.booklet.pdf”>
2 <feature extension=”package.version” value=”1.0.0″ />
3 <require plugin=”org.dita.pdf2″ />
4 <require plugin=”org.dita.pdf2.fop” />
5 <require plugin=”com.bigcorp.standard.pdf” />
6 <require plugin=”com.bigcorp.strings” />
7 <feature extension=”dita.conductor.target.relative” fi le=”integrator.xml”/>
8 <transtype name=”bigcbookletpdf” desc=”BigCorp booklet PDF output”/>
9 </plugin>
This file is deceptively similar to the plugin.xml file for the com.bigcorp.standard.pdf plugin, but there are some important differences. Line 5 specifies that this plugin requires the com.bigcorp. standard.pdf plugin in addition to the org.dita.pdf2 plugin and the formatter-specific plugin. This tells you that this plugin extends not only org.dita.pdf2 and org.dita.pdf2.fop, but also the com.bigcorp.standard.pdf plugin.
Notice that in this file, there is no line <feature extension=”dita.conductor.pdf2.param”file=”parameters.xml”/>
You don’t need this line because the DITA OT already integrated parameters.xsl into the org.dita.pdf2 plugin when it integrated the com.bigcorp.standard.pdf plugin.
integrator.xml (com.bigcorp.booklet.pdf):
This file is also very similar to the integrator.xml file for the com.bigcorp.standard.pdf plugin. Line 3 specifies the location of the customization directory for the plugin (using the customization.dir property).
This line illustrates one critical difference between this file and the integrator.xml file for the com. bigcorp.standard.pdf plugin: this line does not point to com.bigcorp.booklet.pdf (the current plugin) but instead to com.bigcorp.standard.pdf—because this plugin should use the boilerplate images, language variables and strings files, font specifications, and custom build parameters from com.bigcorp.standard.pdf. This means that you do not need to maintain redundant copies of these resources in each PDF plugin.
Again, line 4 specifies a file which determines which stylesheets to import to override the templates and attribute sets in org.dita.pdf2 (using the args.xsl.pdf property).
plugin.xml (com.bigcorp.strings):
This file is simpler than the plugin.xml files for the com.bigcorp.standard.pdf and com.bigcorp.booklet.pdf plugins. This plugin is not used to produce output, so you don’t need a transtype. This plugin also does not extend any other plugins. It merely provides a collection of customized strings files that override or extend the default DITA-OT string files found under dita-ot/xsl/common. Once you have installed this plugin, its overrides and extensions are in effect for all your output plugins, including PDF and HTML.
Line 3 specifies that this plugin uses the DITA-OT extension point dita.xsl.strings, which is meant to incorporate language-specific generated text files. If that seems a bit circular, don’t worry about it. The extension point just tells the DITA-OT, “Hey…here are my language strings!”
You’re now done creating the basic plugin structures.
Step Two: Create the Build Parameters File
Create a text file named parameters.xsl and add it to the com.bigcorp.standard.pdf folder. This is the file referred to in Line 6 of the plugin.xml file in com.bigcorp.standard.pdf.
The content of this file should be similar to the following:
<plugin id=”com.bigcorp.standard.pdf”> <param name=”bigc.distLevel” expression=”${bigc.distLevel}” if=”bigc.distLevel”/> </plugin>
Step Three: Create the Plugin Subfolders
In the com.bigcorp.standard.pdf folder, create this subfolder structure:
com.bigcorp.standard.pdf
|_cfg
|_common
|_artwork
|_vars
|_fo
|_attrs
|_xsl
In the com.bigcorp.booklet.pdf folder, create this subfolder structure:
You don’t need the common folder in com.bigcorp.booklet.pdf, remember, because you’ve specified that com.bigcorp.booklet.pdf should use the files in the common folder in com.bigcorp.standard.pdf (via the customization.dir property in the integrator.xml file of com.bigcorp.booklet.pdf).
In the com.bigcorp.strings folder, create this subfolder structure:
com.bigcorp.strings
|_xsl
|_common
Into the com.bigcorp.standard.com\cfg\common\artwork folder, copy all boilerplate images used by the standard and booklet PDF formats.
Step Four: Copy the Language Variable Files
Look in the [dita-ot]\plugins\org.dita.pdf2\cfg\common\vars folder. From that folder, copy the variables files for any languages you use into the com.bigcorp.standard.com\cfg\common\vars folder. (If you’ve worked with earlier versions of the DITA OT, notice that many of the variables that used to be in the individual language files are now in commonvariables.xml, to reduce redundancy. These include the variables for header and footer content, list numbering and bulleting, admonition icon paths, and step numbering, among a few others. If you need to modify these variables, first copy them from commonvariables.xml into the individual language variables files in your plugin.)
You might also need to modify some of the language strings found in the files in the [dita-ot]\xsl\common folder. That’s a slightly different process which we’ll cover later in this article.
Step Five: Copy and Edit the Topic2FO.XSL File
Here is where the magic happens! Copy the file topic2fo.xsl from [dita-ot]\plugins\org.dita.pdf2\xsl\fo into the cfg\fo\xsl folder of com.bigcorp.standard.pdf.
Rename the file as bigc-stn.topic2fo.xsl.
Note: It’s not strictly necessary to rename the file, but I consider it a best practice to do so, to reinforce that the file is specific to your plugin. Whatever you decide to do, be sure that the file name you use agrees with the file name in line 4 of the integrator.xsl file in com.bigcorp.standard.pdf.
This file tells your plugin
- where to find the base, or default templates and attribute sets in org.dita.pdf2 (the plugin that your plugin extends)
- where to find your custom override files
Without this information, your plugin can’t do anything.
Open bigc-stn.topic2fo.xsl. Notice there is an xsl:import line pointing to every stylesheet in the org. dita.pdf2 plugin. However, since you have copied this file into your plugin, the relative paths are no longer correct. The first thing you need to do is correct them. You need to correct the path for every line in this file, not just the lines that import the files you plan to override.
The first three lines, importing dita-utilities.xsl, dita-textonly.xsl, and related-links.xsl are correct as-is. You don’t need to change them.
From there on, you need to pay attention to the actual location of the file you are importing. For example, the file attr-set-reflection.xsl is located in [dita-ot]\plugins\org.dita.pdf2\xsl\common, so you need to change the line from
<xsl:import href=”../common/attr-set-reflection.xsl”/>
to
<xsl:import href=”plugin:org.dita.pdf2:xsl/common/attr-set-refl
ection.xsl”/>
The same is true of the line that imports vars.xsl.
Note: The “plugin:” syntax is a shorthand for the full path to the plugin and was introduced in DITA OT 1.6.3 to make it easier to reference files in other plugins within an instance of the DITA OT.
Moving on from there, the remaining lines import files that are either in org.dita.pdf2\cfg\fo\attrs or org.dita.pdf2\xsl\fo. For the most part, files that include -attr in their names are found in org.dita.pdf2\cfg\fo\attrs and the rest are found in org.dita.pdf2\xsl\fo.
There are two exceptions:
- basic-settings.xsl is found in org.dita.pdf2\cfg\fo\attrs
- layout-masters.xsl is found in org.dita.pdf2\cfg\fo
Two Renegade Files
There are two other files to look out for as well. You might have noticed (as of somewhere around DITA OT 2.5.4) that there are four files that are not imported via topic2fo.xsl: topic-attr.xsl, topic.xsl, concept-attr.xsl, and concept.xsl. Rather than being imported in the standard way like all the other files, they are imported via commons-attr.xsl and commons.xsl. At the beginning of both of those files are xsl:import statements that import the files. For example, topic2fo.xsl imports commons-attr.xsl and in turn commons-attr.xsl imports concept-attr.xsl and topic-attr.xsl. Likewise for commons.xsl.
This exception (and it appears to be an oversight rather than a planned exception) only becomes an issue if you override commons.xsl or commons-attr.xsl in your own plugin— that is, you copy these files to your plugin. If you copy those files to your plugin, you should remove the xsl:import statements that import topic-attr.xsl, topic.xsl, concept-attr.xsl, and concept.xsl because the relative paths are no longer correct after you add commons.xsl or commons-attr.xsl to your plugin.
If you later need to add topic-attr.xsl, topic.xsl, concept-attr.xsl, or concept.xsl to your plugin, you can add appropriate xsl:import statements for them to your copy of topic2fo.xsl. Ideally, add them right after the import statements for commons.xsl and commons-attr.xsl.
Important! Ensure this line appears after the last xsl:import statement your copy of topic2fo.xsl:
<xsl:output method=”xml” encoding=”utf-8″ indent=”no”/>
If it is missing, all sorts of mysterious white-space problems occur.
Step Six: Test the Base Plugin
At this point, it’s a good idea to integrate the new plugins and run a test build with them. Obviously, the output is identical to the generic org.dita.pdf2 output because you haven’t added any template overrides yet.
Integrate and test the base plugin first (in this example, com.bigcorp.standard.pdf) and make sure you get not only the default output but as clean an OT log as possible.
Step Seven: Integrate the Formatter-Specific Files
Next, add the PDF formatter override files into bigc-stn.topic2fo.xsl. Your plugin overrides should come before the formatter plugin overrides unless you intend to override attribute sets or templates found in the formatter plugin. Here’s an example:
If you want to override templates in tables-attr_fop.xsl, you could either:
- override those templates in bigc-stn.tables-attr.xsl and reverse the order of bigc-stn.tables-attr.xsl and tables-attr_fop.xsl shown here;
- or include your own copy of tables-attr_fop.xsl (bigc-stn.tables-attr_fop.xsl) and import it after tables-attr_fop.xsl
Then test again to make sure you get the default output and a clean OT log. If so, you are now ready to setup, integrate and test the extension plugin.
Step Eight: Set Up and Test the Extension Plugin
When the base plugin is working, copy the bigc-stn.topic2fo.xsl fi le to com.bigcorp.booklet.pdf\ cfg\fo\xsl and rename it bigc-bkl.topic2fo.xsl. (or whatever you named the file referenced in line 4 of the integrator.xsl file in com.bigcorp.booklet.pdf.)
Integrate and test the extension plugin and again, make sure you get not only the default output but as clean an OT log as possible.
When both are working correctly, first pat yourself on the back! At this point, both of your custom plugins reference the entire set of attribute set and templates from the org.dita.pdf2 plugin, which is where you want to start.
You are now ready to start overriding templates and attribute sets.
Step Nine: Override Attribute Sets and Templates in Your Custom Plugins
You always want to include only the attribute sets and templates you’re overriding in your plugins. There are several ways to accomplish that. I prefer to copy entire stylesheets and note with a comment which templates and attribute sets I’ve changed. When I’m done, I delete all the templates and attributes sets I did not change, leaving only my overrides. You might prefer to create empty stylesheets in your plugin and copy individual templates and attribute sets to them. It’s entirely up to you…use whatever method makes sense to you and ensures that you end up with only your overrides in your plugins. Having only your overrides makes it much easier to update your plugin going forward.
Regardless of your approach, you end up with new files that your plugin needs to be aware of, in addition to the core files. You use bigc-stn.topic2fo.xsl and bigc-bkl.topic2fo.xsl to inform your plugins of your custom files.
For example, say that you’ve added custom formatting for topic titles to your standard PDF plugin. The attributes sets for topic title are found in topic-attr.xsl.
Note: This is one of the “renegade” files mentioned earlier.
Via whatever method you prefer, you now have your own file, bigc-stn.topic-attr.xsl. To inform your standard PDF plugin of this file, add an import line for it to the bigc-stn.topic2fo.xsl file.
… <xsl:import href=”plugin:org.dita.pdf2:cfg/fo/attrs/commons-attr.xsl”/>
<xsl:import href=”plugin:com.bigcorp.standard.pdf/cfg/fo/attrs/bigc-stn.topic-attr.xsl”/> …
Always add the xsl:import line for your custom files after the line for the corresponding core file. This order specifies that the DITA-OT should first import the core file with all its attribute sets or templates and then apply your overrides from your custom file. In this case, because the org.dita.pdf2 topic-attr.xsl is imported from within commons-attr.xsl, bigc-s.topic-attr.xsl needs to be imported after commons-attr.xsl. If you reverse the order of the xsl:import lines, the DITA-OT first imports your customized attribute sets and templates and then override them with the core ones so that your changes are overwritten and lost.
In other words, say that topic-attr.xsl in org.dita.pdf2 specifies that level-1 titles (the topic.title attribute set) should be 18pt bold. Your bigc-stn.topic-attr.xsl file specifies they should be 24pt normal. By importing bigc-stn.topic-attr.xsl after topic-attr.xsl, you’re telling your plugin: “make level-1 titles 18pt bold—no, forget that, make them 24pt normal.” If you imported the files in the reverse order, you’d be saying: “make level-1 titles 24pt normal—no, forget that, make them 18pt bold.”
Now let’s say that all your overrides in bigc-stn.topic-attr.xsl also apply to the booklet PDF except that level-1 topics should be 14pt instead of the 24pt you specified for the standard PDF or 18pt as specified in the core attribute set. Clearly, you need to include bigc-stn.topic-attr.xsl in your booklet PDF plugin but you also need to include bigc-bkl.topic-attr.xsl. (In bigc-bkl.topic-attr.xsl, you have only the topic.title attribute set with the 18pt specification.)
Again, the order is critical. Into the file bigc-bkl.topic2fo.xsl, you want to import bigc-bkl.topic-attr.xsl after bigc-stn.topic-attr.xsl: …
<xsl:import href=”plugin:org.dita.pdf2:cfg/fo/attrs/commons-attr.xsl”/>
<xsl:import href=”plugin:com.bigcorp.standard.pdf/cfg/fo/attrs/bigc-stn.topic-attr.xsl”/>
<xsl:import href=”plugin:com.bigcorp.booklet.pdf/cfg/fo/attrs/bigc-bkl.topic-attr.xsl”/> …
This order has the effect of telling the DITA OT: “make level-1 titles 18pt bold—no, forget that, make them 24pt normal—no, forget that, make them 14pt normal.” If you import the files in any other order, you do not end up with the formatting you want.
Step Ten: Copy the Language-Specific String Files
Many of the variables that used to be in language-specific files in the org.dita.pdf2\cfg\common\vars folder have been moved to language-specific files in the dita-ot\xsl\common folder. Th is move is meant to facilitate the sharing of language-specific strings between PDF and HTML-based outputs, rather than having to define many of the same strings twice for different output formats. The consolidation process is not complete, so for the time being, you likely need to edit both the vars files and the strings files, depending on what you need to customize. (The vars files mainly contain variables that are specific to PDF outputs.)
To customize strings files, it’s not enough to simply include them in your custom PDF plugin, although in a perfect world, it should be. Instead, you need a separate strings plugin, which you created earlier in this process. Now you copy the files you need to customize into this plugin.
To keep things simple, let’s say you plan to publish only in English and Spanish. To keep things even simpler, let’s say you do not plan to localize English or Spanish per country or region. You want just one strings file for English and one for Spanish. Looking in the dita-ot\xsl\common folder, you see quite a few files there. For English, you see strings-en-ca.xml (for Canadian English), strings-en-gb.xml (for British English), and strings-en-us.xml (for U.S. English). You also see strings-es-es.xml for Spanish.
Which English strings file you choose depends on your situation, but let’s choose strings-en-us.xml for this example. Copy strings-en-us.xml and strings-es-es.xml to the common folder of the com.bigcorp.strings plugin.
Wonk alert!
You might notice that [DITA OT]/xsl/commons/strings.xsl associates en-ca and en-gb with strings-en-us.xsl rather than their respective country-specific strings files. This means that unless you point en-ca or en-gb to their local-specific strings files in your plugin’s strings.xml file all overrides and extensions you make in your plugin strings-en-us.xml show up for all three locales. The same sort of thing happens with French and its various locales.
Next create a file named strings.xml, also in the common folder of the com.bigcorp.strings plugin. This is the file referred to in line 3 of the plugin.xml file in the the com.bigcorp.strings plugin. Here is the starting content for that file:
Notice that in the langlist, there are two <lang> elements for each language, English and Spanish. The first two lines provide two possible variants on the xml:lang value that you might find in your body of English content. These two lines essentially say to the plugin, “When you find a topic or map where the value of xml:lang is “en” or “EN,” get the strings from strings-en-us.xml.
The third and fourth lines provide two possible variants on the xml:lang value that you might find in your body of Spanish content. These two lines essentially say to the plugin, “When you find a topic or map where the value of xml:lang is “es” or “ES,” get the strings from strings-es-es.xml.
Now, if you know much about language codes, you know the ISO standard has changed a bit over time and that different XML editors and Content Management Systems sometimes play a bit loose with the standard. So, unfortunately, you might have other variations on the English or Spanish language code in your content.
For English, you might see “en-us,” “en-US,” “en_us,” “en_US” or others. In a perfect world, you could refactor all of these variations to a single standard, but we live in an imperfect world and so the next best thing is to make sure that all these variations are accounted for in your strings.xml file. To do that, add a <lang> element for each variation in your content and map that particular variation to the appropriate language strings file.
Why Is This Approach Better?
Now that you understand the process, you might be wondering why this is a better approach than using custom.xsl files, as I described in DITA For Print. Well, one advantage that should already be pretty clear is the greater ease of creating multiple related plugins that share one set of boilerplate
images, language-specific variables and strings files, and a large percentage of the same attribute sets and templates.
Another advantage that is a little less obvious is that this method enables you to be more specific about the order in which templates and attribute sets are processed…and the order is very important.
The org.dita.pdf2 plugin integrates stylesheets in the following order:
1. The default templates and attribute sets from org.dita.pdf2
2. Overrides from renderer-specific plugins such as org.dita.pdf2.fop
3. Custom attribute sets in your plugin
4. Custom templates in your plugin
For example, consider the attribute set note__text__column. This attribute set is defined in the topic-attr.xsl file in the org.dita.pdf2 plugin:
If you are using the FOP PDF renderer, then this attribute set is redefined in the commons-attr.xsl file in the org.dita.pdf2.fop plugin:
Note: Many of the attribute sets that used to be in commons-attr.xsl in the org.dita.pdf2 plugin were moved to a new topic-attr.xsl file after DITA-OT 2.3.1. Th e corresponding changes have not yet been made in the renderer-specific plugins, so the file names do not match up in some cases.
In your custom PDF, you might not want the column that contains the note to extend to the outside page margin, so you’ve added the note__text__column attribute to your plugin and changed the column-width value:
Following the processing order, the attribute set in org.dita.pdf2 is combined with the one in org.dita.pdf2.fop so that the result is:
<xsl:attribute-set name=”note__text__column”> <xsl:attribute name=”column-width”>100% – 32pt</xsl:attribute> </xsl:attribute-set>
<xsl:attribute-set name=”note__text__column”> <xsl:attribute name=”column-width”>70% – 32pt</xsl:attribute> </xsl:attribute-set>
<xsl:attribute-set name=”note__text__column”> <xsl:attribute name=”column-number”>2</xsl:attribute> <xsl:attribute name=”column-width”>100% – 32pt</xsl:attribute> </xsl:attribute-set>
That combined attribute set is then combined with your custom one, so that the final result used by FOP for formatting is:
The important thing to remember is that attribute sets combine. On the other hand, templates overwrite. For example, say that in a Quick Start Guide, within a <step> element, you only want to output <cmd> elements—not <info>, <stepxmp>, <stepresult>, etc. One way to accomplish this is to simply copy the template that processes each of these elements into your plugin, and make it an “empty” template. Let’s look at the template for <info> as an example. This is the original template from the task-elements.xsl file in org.dita.pdf2:
<xsl:template match=”*[contains(@class, ‘ task/info ‘)]”>
<fo:block xsl:use-attribute-sets=”info”>
<xsl:call-template name=”commonattributes”/>
<xsl:apply-templates/> </fo:block>
</xsl:template>
To completely suppress processing for <info> elements altogether, copy this template into the task-elements.xsl file in your plugin and edit it to:
Your copy of the template completely replaces the original copy so that when the processor finds an <info> element, it does…nothing.
Now that you understand the rules of precedence a bit better, you might still be wondering why the topic2fo.xsl-based approach is better in this respect. Well, the simplest reason is that every template in custom.xsl and its imports takes precedence over all the templates in org.dita.pdf2 and the formatter-specific plugin that you choose. This means that it is very easy to inadvertently override base templates that you don’t mean to override. Th e next section explains the implications of doing that.
On the other hand, in topic2fo.xsl, the default stylesheets are imported in a specific order, which is very clear from the file. You simply follow that same order when importing your custom stylesheets and it’s immediately clear in what order your attribute sets and templates are applied.
Tip: Avoid Late Overrides of Base Elements
Let’s say that you’re following the old model, importing your custom stylesheets via custom.xsl. You want to suppress the output of <keyword>, so you either add the template to custom.xsl (in the old model) or you add it to custom.xsl and import custom.xsl into your copy of topic2fo.xsl, at the end of the file (in the new model):
Remember that your custom templates overwrite the defaults from both org.dita.pdf2 and the renderer-specific plugins. Also remember that many elements are specialized from <keyword>.
This template matches on any element whose class includes “topic/keyword.” Among the elements matched by this template are the following:
- cmdname (sw-domain.xsl)
- msgnum (sw-domain.xsl)
- varname (sw-domain.xsl)
- wintitle (ui-domain.xsl)
- xmlatt (xml-domain.xsl)
- xmlelement (xml-domain.xsl)
- xmlnsname (xml-domain.xsl)
- xmlpi (xml-domain.xsl)
Each of these elements also has its own specific template (found in the stylesheet noted). Let’s say you want to apply some specific processing for <wintitle>, so you copy the <wintitle>-specific template from the org.dita.pdf2 copy of ui-domain.xsl into the copy of ui-domain.xsl in your plugin. In your custom.xsl file, you import your copy of ui-domain.xsl, and you add the empty template for <keyword> shown above, like so:
Here’s what is going to happen when you process a <wintitle> element:
1. The default <wintitle>-specific template in the org.dita.pdf2 copy of ui-domain.xsl is overwritten by the <wintitle>-specific template in plugin, thus applying your changes.
2. The empty template matching <keyword> is applied. It matches <wintitle> because the class of <wintitle> includes “topic/keyword”.
3. <wintitle> is not output at all because the last set of instructions that apply to <wintitle> say, “Don’t output any element that this template matches.” In general, late overrides of the base elements (elements which are used for specializing other elements) is a bad idea for just this reason. The odds of over-applying your overrides is very high and it’s often difficult to troubleshoot the problem.
In Closing
I hope this article has shown you a better process for PDF plugin creation…especially for creating multiple interdependent plugins, but even for creating a single independent plugin. If you follow this method, please consider sharing your experience—and any tips or pitfalls that you encounter along the way—in the Yahoo DITA Users Group. Good luck!
About the Author
Leigh White
Leigh White is a DITA Specialist, where she works with product integrations, product design, and marketing communications. Leigh has spoken on DITA, content management systems content conversion, and DITA-OT plugin development at a number of industry conferences, including DITA North America, DITA Europe, Intelligent Content, Lavacon, Writers UA, DITA Netherlands, and Congility. She is the author of DITA For Print: A DITA Open Toolkit Workbook and a contributor to The Language of Content Strategy and The Language of Technical Communication. Leigh is also a member of the OASIS DITA Technical Committee.