Re: [xsl] Center string

Subject: Re: [xsl] Center string
From: Michael Ludwig <mlu@xxxxxxxxxxxxx>
Date: Thu, 11 Jun 2009 20:24:17 +0200
Michael Ludwig schrieb:

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

I think I found an improvement. Instead of reslicing sequences (possibly involving a copy operation internally, depending on the implementation), I increment an integer to move ahead in a tunneled sequence (that I think won't be copied). Comments concerning XSLT technique 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="t0" select="'abcde abcde abcde abcde abcde abcde
  abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde abcde'"/>
 <xsl:variable name="t1" select="'abcd abcd abcd abcd abcd abcd abcd abcd
  abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd
  abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd'"/>
 <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="queue"     tunnel="yes" as="xs:string*" />
  <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="offset"     as="xs:integer" select="1" />
  <xsl:param name="length"     as="xs:integer" select="1" />
  <xsl:param name="chars-left" as="xs:integer"
   select="$chars-max - string-length( $queue[1])" />

  <xsl:variable name="next" as="xs:string?"
   select="$queue[$offset + $length]"/>
  <xsl:variable name="next-len" as="xs:integer"
   select="string-length( $next)"/>
  <xsl:variable name="full" as="xs:boolean"
   select="not($next) or $sep-len + $next-len > $chars-left" />

  <xsl:if test="$full">
   <xsl:for-each select="1 to (($chars-left + 1) idiv 2) - 1">
    <xsl:text> </xsl:text>
   </xsl:for-each>
   <xsl:value-of select="string-join(
    subsequence( $queue, $offset, $length), $separator)"/>
   <xsl:text>&#10;</xsl:text>
  </xsl:if>
  <xsl:if test="$next">
   <xsl:call-template name="wrap">
    <xsl:with-param name="offset" select="
     if ($full) then $offset + $length
     else            $offset"/>
    <xsl:with-param name="length" select="
     if ($full) then 1 else $length + 1"/>
    <xsl:with-param name="chars-left" select="
     if ($full) then $chars-max  - $next-len
     else            $chars-left - $sep-len - $next-len"/>
   </xsl:call-template>
  </xsl:if>
 </xsl:template>

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

</xsl:stylesheet>

Current Thread