Re: [xsl] Center string

Subject: Re: [xsl] Center string
From: Michael Ludwig <mlu@xxxxxxxxxxxxx>
Date: Thu, 11 Jun 2009 18:14:03 +0200
Fridiric Schwebel schrieb:

Just curious if it has already been done : I need to center a string
with a max line length. The words must be uncut and balanced between
lines.

Here's a solution. Any comments concerning possible XSLT improvements very welcome.

Michael Ludwig

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="2.0"
 xmlns:xs="http://www.w3.org/2001/XMLSchema";
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform";>

<xsl:output method="text"/>

 <xsl:variable name="text" select="'Just curious if it has already been
  done : I need to center a string with a max line length. The words
  must be uncut and balanced between lines.'" />
 <xsl:variable name="text2" select="'Centering the lines is not a
  problem. The most difficult part is to balance words with line-length.
  Did somebody already do this with XSL ?'" />

 <xsl:template name="wrap">
  <xsl:param name="chars-max" tunnel="yes" as="xs:integer" select="30"/>
  <xsl:param name="separator" tunnel="yes" as="xs:string" select="' '"/>
  <xsl:param name="sep-len"   tunnel="yes" as="xs:integer"
   select="string-length($separator)" />
  <!-- invariants tunneled above, others below -->
  <xsl:param name="buffer"     as="xs:string*" />
  <xsl:param name="queue"      as="xs:string*" />
  <xsl:param name="chars-left" as="xs:integer" select="$chars-max" />
  <!--
<xsl:message select="string-join(( string($chars-left), $buffer), '_')"/>
  -->
  <xsl:variable name="next"    as="xs:string?"  select="$queue[1]"/>
  <xsl:variable name="add-len" as="xs:integer"
   select="string-length( $next) + $sep-len"/>
  <xsl:variable name="full" as="xs:boolean"
   select="not($next) or $add-len > $chars-left" />
  <xsl:if test="$full">
   <xsl:for-each select="1 to ($chars-left + 1) idiv 2">
    <xsl:text> </xsl:text>
   </xsl:for-each>
   <xsl:value-of select="string-join( $buffer, $separator)"/>
   <xsl:text>&#10;</xsl:text>
  </xsl:if>
  <xsl:if test="$next">
   <xsl:call-template name="wrap">
    <xsl:with-param name="buffer" select="
     if ($full) then           $next
     else            ($buffer, $next)"/>
    <xsl:with-param name="queue" select="$queue[ position() > 1 ]"/>
    <xsl:with-param name="chars-left" select="
     if ($full) then $chars-max  - $add-len
     else            $chars-left - $add-len"/>
   </xsl:call-template>
  </xsl:if>
 </xsl:template>

 <xsl:template match="/">
  <xsl:variable name="tokenizer" select="'\s+'" />
  <xsl:for-each select="( $text, $text2 )">
   <xsl:call-template name="wrap">
    <xsl:with-param name="queue" select="tokenize( . , $tokenizer)" />
    <!--
    <xsl:with-param name="separator" select="'__'" tunnel="yes"/>
    -->
   </xsl:call-template>
  </xsl:for-each>
 </xsl:template>

</xsl:stylesheet>

Current Thread