Main | More Subtemplates ... »

A subscripting welcome and sub templates

Welcome to the official XML Publisher BLOG, we hope to fill these pages not only with XMLP news and views but also provide how to's and walkthroughs. We're going to hook up the content here with some of the questions we see on the XML Publisher forum on OTN so we can provide more detail on answers to questions or comments.
That said, their was a question recently on how to represent chemical formulae in the XMLP output. Those of you with a little chemistry knowledge will know that the '2' in H2O should be 'subscripted' to be shown correctly in text i.e. H2O.

So the question piqued my interest, just how would you do it, we have to make some assumptions about the formulae to keep things simple, I assumed that all numbers within a formula such as 'C2H5O' need to be subscripted.

Now this is not a generic enough requirement, at least not at the moment for XMLP to support natively so we need to turn to the power of XSL and our ability to embed it in the layout template to achieve the number subscripting. As you know you are able to use any XSL/XSLFO code in your templates and using these languages you can create 'templates' in your layouts or for those of us that use other programming languages, think of them as functions. You can pass parameters and get resultsets back. Its a very powerful feature. If you have templates (functions) that might be needed across multiple layouts you can externalize them in a sub template.

So the first task is to build a template that can accept a formula and pass back the formatted value ... being somewhat lazy and thinking that someone else must have done this already I cast around the web looking for a possible fit ... and came up trumps on one of the big XSL mailing lists. Kevin Lanham had put together a template that accepted the formula and then recursively looked through the string for numbers and set the subscript properties for them:


<xsl:template name="chemical_formatter">
<!-- accepts a parameter e.g. H2O -->
<xsl:param name="formula"/>
<!-- Takes the first character of the string and tests it to see if its a number -->
<!-- between 0-9-->
<xsl:variable name="each_char" select="substring($formula,1,1)"/>
<xsl:choose>
<xsl:when test="$each_char='1' or $each_char='2' or $each_char='3' or
$each_char='4' or $each_char='5' or $each_char='6' or $each_char='7' or
$each_char='8' or $each_char='9' or $each_char='0'">
<!-- if it is numeric it sets the FO subscripting properties -->
<fo:inline baseline-shift="sub" font-size="75%">
<xsl:value-of select="$each_char"/>
</fo:inline>
</xsl:when>
<xsl:otherwise>
<!-- otherwise the charater is left as is -->
<fo:inline baseline-shift="normal">
<xsl:value-of select="$each_char"/>
</fo:inline>
</xsl:otherwise>
</xsl:choose>
<!--  test if there are other chars in the string, if so the recall the template -->
<xsl:if test="substring-after($formula,$each_char)!=''">
<xsl:call-template name="chemical_formater">
<xsl:with-param name="formula">
<xsl:value-of
select="substring-after($formula,$each_char)"/>
</xsl:with-param>
</xsl:call-template>
</xsl:if>
</xsl:template>

Fantastic, this does everything we need based on our assumptions about chemical formulae. Now we just need to use it in a layout template, thats going to be pretty straight forward.
Lets assume we have the following data:
<ROWSET>
 <ROW>
  <FORMULA>CO2</FORMULA>
 </ROW>
 <ROW>
  <FORMULA>H2O</FORMULA>
 </ROW>
</ROWSET>

All we need is to create a loop over the data and in the VALUE field use:
<xsl:call-template name="chemical_formatter">
<xsl:with-param name="formula" select="VALUE"/> </xsl:call-template>
This calls the formatting template with the FORMULA value e.g. H2O.  Once rendered, the formulae will be shown as expected ie H2O.

Of course this sub template could be very easily adapted to provide superscripting for say document notes1 or any other requirements around text formatting.


So we know how to call the template but where do we put it. XMLP allows you to use XSL/FO in the RTF template as long as its in a formfield. Putting the code above into a series of formfields is going to be a bit tedious. Plus we may need to use this function across multiple templates, so its easier to put this function into a sub-template. This can be a straightforward XSL file and here we can store multiple functions if we wish. All we need do is declare that we want to use the sub template in our main template:
   <?import: file:///C:WorkChemical.xsl?>
As you can see while we are testing we can use a file URI to reference the sub template, we could equally use a full URL. This can be used in the Word Template Builder or the Template Viewer. Once deployed we can then use either a full URL or load the sub template to the Template Manager in EBS and use the xdo: URL format.
Sample RTF, sub XSL template and data are available here.


1. Heres that document note I was talking about :o)

Comments (1)

Tim Dexter:

Hi


You can do this pretty easily assuming you know you want to always sub or super the last character. The following function will get a handle on the last character of a string.


substring("HELLO",string-length("HELLO"),string-length("HELLO")-1)


You can plug this into the template in the article quite easily and you're done.


Tim

Post a comment