title: Calculating string length of text node when parent has other child nodes too
body: <p>I am stuck with XSLT 1.0 making a hyphenation script for verbatim output to PDF. (note it is PDF so I can't use clever CSS to hellp me out.
  I would like to be able to access both text() nodes and child text() nodes to compare stringlength as part of the script.</p>
  
  <p>For example:</p>
  
  <pre><code>&lt;programlisting&gt;This text will be displayed without word-wrap
  I can measure length of string between end line chars '&amp;#10;'
  and break long lines. That works great until
  
  &lt;emphasis&gt;this gets added&lt;/emphasis&gt; then my counting loop doesn't
  count the text in the emphasis tags.
  &lt;/programlisting&gt;
  </code></pre>
  
  <p>I am looping through the text() using a template called screen. It detects the end of line character and decides if the line is too long. Long lines are broken and the remainder recursed through the template until the remainder is less than the max line length... then onto the next line of text...
  ... that works great, but now i have some children in the  and I can't figure out how I can access the text() and any child node's text() at the same time to calculate string length.</p>
  
  <p>Sorry for the long example code...</p>
  
  <p>The template to match  looks like this...</p>
  
  <pre><code>&lt;xsl:template match="programlisting/text()"&gt; 
     &lt;xsl:variable name="max_width"&gt;100&lt;/xsl:variable&gt;
     &lt;xsl:variable name="min_width"&gt;80&lt;/xsl:variable&gt;
            &lt;xsl:call-template name="screen"&gt;
              &lt;xsl:with-param name="text" select="."/&gt;
              &lt;xsl:with-param name="max_width" select="$max_width"/&gt;
              &lt;xsl:with-param name="min_width" select="$min_width"/&gt;
            &lt;/xsl:call-template&gt;
  &lt;/xsl:template&gt;
  </code></pre>
  
  <p>The template to decide if string before linebreak is too long:</p>
  
  <pre><code>&lt;xsl:template name="screen"&gt; 
      &lt;xsl:param name="text" select="."/&gt;
      &lt;xsl:param name="max_width"/&gt;
      &lt;xsl:param name="min_width"/&gt;
      &lt;xsl:variable name="fixed_text"&gt; &lt;!-- add end linebreak if missing --&gt;
          &lt;xsl:choose&gt;
              &lt;xsl:when test="substring($text,string-length($text),1) = '&amp;#10;'"&gt;
                  &lt;xsl:value-of select="$text"/&gt;  
              &lt;/xsl:when&gt;
              &lt;xsl:otherwise&gt;
                  &lt;xsl:value-of select="concat($text,'&amp;#10;')"/&gt;  
              &lt;/xsl:otherwise&gt;
          &lt;/xsl:choose&gt; 
       &lt;/xsl:variable&gt;
       &lt;xsl:variable name="first_line"&gt;
                  &lt;xsl:value-of select="substring-before(concat($fixed_text,'&amp;#10;'),'&amp;#10;')"/&gt;
      &lt;/xsl:variable&gt;
      &lt;xsl:variable name="other_lines"&gt;
                  &lt;xsl:value-of select="substring-after($fixed_text,concat($first_line,'&amp;#10;'))"/&gt;
      &lt;/xsl:variable&gt;
      &lt;xsl:choose&gt;
          &lt;xsl:when test="string-length(normalize-space($first_line)) &amp;lt; 1"&gt;&lt;!-- blank line (just trim and copy)--&gt;
              &lt;xsl:value-of select="concat($verbatim_padding,substring($first_line,1,100),'&amp;#10;')"/&gt;
          &lt;/xsl:when&gt;
          &lt;xsl:when test="string-length($first_line) &amp;lt; 100"&gt; &lt;!-- short line (just copy)--&gt;
              &lt;xsl:value-of select="concat($verbatim_padding,$first_line,'&amp;#10;')"/&gt;
          &lt;/xsl:when&gt;
          &lt;xsl:otherwise&gt;
              &lt;!--  Line is too long!!  --&gt;
              &lt;xsl:variable name="wrapped_lines"&gt;
                 &lt;xsl:call-template name="break_line"&gt; 
                      &lt;xsl:with-param name="long_string" select="$first_line"/&gt;
                      &lt;xsl:with-param name="max_chars" select="100"/&gt;
                      &lt;xsl:with-param name="min_chars" select="80"/&gt;
                      &lt;xsl:with-param name="break_chars" select="' ,/;'"/&gt;
                      &lt;xsl:with-param name="linebreak_string" select="'~'"/&gt;
                  &lt;/xsl:call-template&gt; 
              &lt;/xsl:variable&gt;
              &lt;xsl:value-of select="concat($verbatim_padding,$wrapped_lines)"/&gt;
          &lt;/xsl:otherwise&gt;
      &lt;/xsl:choose&gt;
      &lt;xsl:if test="string-length($other_lines) &amp;gt; 0"&gt;
            &lt;xsl:call-template name="screen"&gt;
              &lt;xsl:with-param name="text" select="$other_lines"/&gt;
              &lt;xsl:with-param name="max_width" select="$max_width"/&gt;
              &lt;xsl:with-param name="min_width" select="$min_width"/&gt;
            &lt;/xsl:call-template&gt;
      &lt;/xsl:if&gt;
  &lt;/xsl:template&gt;
  </code></pre>
  
  <p>The template to break up long lines:</p>
  
  <pre><code>&lt;xsl:template name="break_line"&gt; 
      &lt;xsl:param name="long_string"/&gt;
      &lt;xsl:param name="max_chars"/&gt; &lt;!--max chars allowed on a line --&gt;
      &lt;xsl:param name="min_chars"/&gt; &lt;!--max char position foa soft linebreak (else we hard break at the max chars!) --&gt;
      &lt;xsl:param name="break_chars"/&gt;   &lt;!-- chars used for soft breaking --&gt;
      &lt;xsl:param name="linebreak_string"/&gt;  &lt;!-- add this to end of a broken line --&gt;
      &lt;xsl:choose&gt;
        &lt;xsl:when test="(string-length($long_string) &amp;lt; $max_chars) or (string-length($long_string) &amp;lt; $min_chars) or (string-length($long_string) &amp;lt; 1)"&gt;
            &lt;xsl:value-of select="concat($long_string,'&amp;#10;')"/&gt;
         &lt;/xsl:when&gt;
        &lt;xsl:otherwise&gt;
              &lt;xsl:variable name="trim_x_by"&gt;
                      &lt;xsl:call-template name="CheckLastChar"&gt;
                          &lt;xsl:with-param name="string" select="$long_string"/&gt;
                          &lt;xsl:with-param name="start" select="$max_chars"/&gt;
                          &lt;xsl:with-param name="stop" select="$min_chars"/&gt;
                          &lt;xsl:with-param name="break_chars" select="$break_chars"/&gt;
                          &lt;xsl:with-param name="max_chars" select="$max_chars"/&gt;
                       &lt;/xsl:call-template&gt;
              &lt;/xsl:variable&gt;
              &lt;xsl:variable name="first_x_chars"&gt;&lt;xsl:value-of select="substring($long_string,1,$trim_x_by)"/&gt;&lt;/xsl:variable&gt;
              &lt;xsl:variable name="remaining_chars"&gt;&lt;xsl:value-of select="substring-after($long_string,$first_x_chars)"/&gt;&lt;/xsl:variable&gt;
  
              &lt;xsl:value-of select="concat($first_x_chars,' ',$linebreak_string)"/&gt;
  
              &lt;xsl:call-template name="break_line"&gt; 
                  &lt;xsl:with-param name="long_string" select="$remaining_chars"/&gt;
                  &lt;xsl:with-param name="max_chars" select="$max_chars"/&gt;
                  &lt;xsl:with-param name="min_chars" select="$min_chars"/&gt;
                  &lt;xsl:with-param name="break_chars" select="$break_chars"/&gt;
                  &lt;xsl:with-param name="linebreak_string" select="$linebreak_string"/&gt;
              &lt;/xsl:call-template&gt; 
        &lt;/xsl:otherwise&gt;
      &lt;/xsl:choose&gt;
  &lt;/xsl:template&gt;
  </code></pre>
  
  <p>And finally the recursive checking of string chars to see if its a safe place to line-break (soft break)</p>
  
  <pre><code>   &lt;xsl:template name="CheckLastChar"&gt;
          &lt;xsl:param name="string"/&gt;
          &lt;xsl:param name="stop" select="1"/&gt;
          &lt;xsl:param name="start" select="string-length($string)"/&gt;
          &lt;xsl:param name="count" select="$start"/&gt;
          &lt;xsl:param name="break_chars"/&gt;
          &lt;xsl:param name="max_chars"/&gt;
          &lt;xsl:choose&gt;
            &lt;xsl:when test="($count &amp;lt; $stop) or (string-length($string) &amp;lt; $stop)"&gt; 
                  &lt;!-- gone so far into the line that a linebreak would look weird! So return the max-length and that will
                  force a 'hard' linebreak exactly at the max-length point regardless of the character --&gt;
                  &lt;xsl:value-of select="$max_chars"/&gt; 
            &lt;/xsl:when&gt;
            &lt;xsl:otherwise&gt;
                  &lt;xsl:variable name="last_char"&gt;&lt;xsl:value-of select="substring($string,$count,1)"/&gt;&lt;/xsl:variable&gt;
                  &lt;xsl:choose&gt;
                    &lt;xsl:when test="contains($break_chars,$last_char)"&gt;
                          &lt;xsl:value-of select="$count"/&gt;
                    &lt;/xsl:when&gt;
                   &lt;xsl:otherwise&gt;
                          &lt;!-- keep looking --&gt;
                          &lt;xsl:call-template name="CheckLastChar"&gt;
                              &lt;xsl:with-param name="string" select="$string"/&gt;
                              &lt;xsl:with-param name="stop" select="$stop"/&gt;
                              &lt;xsl:with-param name="start" select="$start"/&gt;
                              &lt;xsl:with-param name="count" select="$count - 1"/&gt;
                              &lt;xsl:with-param name="break_chars" select="$break_chars"/&gt;
                              &lt;xsl:with-param name="max_chars" select="$max_chars"/&gt;
                          &lt;/xsl:call-template&gt;
                    &lt;/xsl:otherwise&gt;
                  &lt;/xsl:choose&gt;
            &lt;/xsl:otherwise&gt;
          &lt;/xsl:choose&gt;
      &lt;/xsl:template&gt;
  </code></pre>