You want to format multilined text within an XML document into a fixed-width-aligned format, insuring that lines wrap at word boundaries.
Here is a solution that handles both wrapping and alignment by
reusing the text:justify
template constructed in
Recipe 5.3. For added flexibility, you can allow
the alignment width to be specified separately from wrapping width,
but default to it when unspecified:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" id="text.wrap" xmlns:str="http://www.ora.com/XSLTCookbook/namespaces/strings" xmlns:text="http://www.ora.com/XSLTCookbook/namespaces/text" exclude-result-prefixes="text"> <xsl:include href="../strings/str.find-last.xslt"/> <xsl:include href="text.justify.xslt"/> <xsl:template match="node( ) | @*" mode="text:wrap" name="text:wrap"> <xsl:param name="input" select="normalize-space( )"/> <xsl:param name="width" select="70"/> <xsl:param name="align-width" select="$width"/> <xsl:param name="align" select=" 'left' "/> <xsl:if test="$input"> <xsl:variable name="line"> <xsl:choose> <xsl:when test="string-length($input) > $width"> <xsl:call-template name="str:substring-before-last"> <xsl:with-param name="input" select="substring($input,1,$width)"/> <xsl:with-param name="substr" select=" ' ' "/> </xsl:call-template> </xsl:when> <xsl:otherwise> <xsl:value-of select="$input"/> </xsl:otherwise> </xsl:choose> </xsl:variable> <xsl:if test="$line"> <xsl:call-template name="text:justify"> <xsl:with-param name="value" select="$line"/> <xsl:with-param name="width" select="$align-width"/> <xsl:with-param name="align" select="$align"/> </xsl:call-template> <xsl:text>
</xsl:text> </xsl:if> <xsl:call-template name="text:wrap"> <xsl:with-param name="input" select="substring($input, string-length($line) + 2)"/> <xsl:with-param name="width" select="$width"/> <xsl:with-param name="align-width" select="$align-width"/> <xsl:with-param name="align" select="$align"/> </xsl:call-template> </xsl:if> </xsl:template>
The solution reuses the str:substring-before-last
template created in Recipe 1.4. The basic idea is
to extract a line containing up to $width
characters, extracting less if the line would not end in a space. The
rest of the input is then processed recursively. The tricky part is
to make sure that if a word with $width
characters
is encountered, you allow it to be split.
The following example shows how you can use this recipe to wrap and center some sample. It uses different alignment and wrapping widths to demonstrate the effect of these parameters:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:text="http://www.ora.com/XSLTCookbook/namespaces/text"> <xsl:include href="text.wrap.xslt"/> <xsl:strip-space elements="*"/> <xsl:output method="text"/> <xsl:template match="p"> <xsl:apply-templates select="." mode="text:wrap"> <xsl:with-param name="width" select="40"/> <xsl:with-param name="align" select=" 'center' "/> <xsl:with-param name="align-width" select="60"/> </xsl:apply-templates> <xsl:text>
</xsl:text> </xsl:template> </xsl:stylesheet>
Input:
<doc> <p>In the age of the internet, formats such HTML, XHTML and PDF clearly dominate the application of XSL and XSLT. However, plain old text will never become obsolete because it is the lowest common denominator in both human and machine-readable formats. XML is often converted to text to be imported into another application that does not know how to read XML or does not interpret it the way you would prefer. Text output is also used when the result will be sent to a terminal or post processed in a Unix pipeline.</p> <p>Many recipes in this section place stress on XSLT techniques that create very generic XML to text converters. Here generic means that the transformation can easily be customized to work on many different XML inputs or produce a variety of outputs or both. The techniques employed in these recipes have application beyond specifics of a given recipe and often beyond the domain of text processing. In particular, you may want to look at recipes 5.2 through 5.5 even if they do not address a present need. </p> </doc>
Output:
In the age of the internet, formats such HTML, XHTML and PDF clearly dominate the application of XSL and XSLT. However, plain old text will never become obsolete because it is the lowest common denominator in both human and machine-readable formats. XML is often converted to text to be imported into another application that does not know how to read XML or does not interpret it the way you would prefer. Text output is also used when the result will be sent to a terminal or post processed in a Unix pipeline. Many recipes in this section place stress on XSLT techniques that create very generic XML to text converters. Here generic means that the transformation can easily be customized to work on many different XML inputs or produce a variety of outputs or both. The techniques employed in these recipes have application beyond specifics of a given recipe and often beyond the domain of text processing. In particular, you may want to look at recipes 5.2 through 5.5 even if they do not address a present need.
In many text-conversion scenarios, the final output device cannot handle text of arbitrary line length. Most devices (such as terminals) wrap the text that overflows its horizontal display area. This wrapping results in a sloppy-looking output. This example allows you to deal with fixed-width formatting more intelligently.