Marco
Marco

Reputation: 101

Split xml element content into fix number of lines

I would really appreciate a solution for this task that it's getting me crazy.

I have an xml element that has Free text inside, it could has 2 characters or a whole paragraph

<Description>text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text </Description>

I need a XSL template to produce this output:

11 lines with 80 charcters each.

For example for the above element, the result will be something like this:

enter image description here

I have been trying using a tokenize function, but no success at all. Thanks in advance.

Gerardo

Upvotes: 1

Views: 429

Answers (2)

Marco
Marco

Reputation: 101

Here's the final solution:

    ...
    <xsl:variable name="lineCount" select="11"/>
    <xsl:variable name="charPerLine" select="20"/>
    ...

<ElementX>
                        <xsl:call-template name="Texts">
                                    <xsl:with-param name="string" select="bi:fillText(../bi:SalesOrder/bi:Notes/bi:Description,$charPerLine*$lineCount)"/>
                                    <xsl:with-param name="Position">1</xsl:with-param>
                                    <xsl:with-param name="charLength" select="$charPerLine"/>
                                    <xsl:with-param name="lineCount" select="$lineCount"/>
                            </xsl:call-template>
                        </ElementX>

<!--Template to fill string. ..example 11 lines, 80 characters each.--> 
<xsl:template name="Texts">
    <xsl:param name="string"/>
    <xsl:param name="Position"/>
    <xsl:param  name="charLength" />
    <xsl:param name="lineCount"/>

    <xsl:variable name="line" select="substring($string,1,$charLength)"/>
    <xsl:variable name="rest" select="substring($string, $charLength+1)"/>
    <xsl:if test="$line">
        <xsl:value-of select="$line"/>
        <xsl:if test="$Position != $lineCount"> 
                <xsl:text>&#x0A;</xsl:text> 
        </xsl:if>
    </xsl:if>
    <xsl:if test="$rest and $Position &lt; $lineCount">
        <xsl:call-template name="Texts">
            <xsl:with-param name="string" select="$rest"/>
            <xsl:with-param name="Position" select="$Position+1"/>
            <xsl:with-param name="charLength"  select="$charLength"/>
            <xsl:with-param name="lineCount" select="$lineCount"/>
        </xsl:call-template>
    </xsl:if>
</xsl:template>

<xsl:function name="bi:fillText">
    <xsl:param name="value"/>
    <xsl:param name="lengthMax"/>
    <xsl:variable name="j" select="$lengthMax - string-length($value)"/>

    <xsl:value-of select="concat($value,string-join(
               (for $i in 1 to $j
                 return ' '
                ),
               ''
               ) )"/>
</xsl:function>

Upvotes: 0

Dimitre Novatchev
Dimitre Novatchev

Reputation: 243479

I. Here is an XSLT 1.0 solution (the XSLT 2.0 solution is much easier):

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:variable name="vLine" select=
"concat('**********',
        '**********',
        '**********',
        '**********',
        '**********',
        '**********',
        '**********',
        '**********'
        )
"/>

 <xsl:template match="/*">
     <xsl:copy>
       <xsl:variable name="vsplitResult">
           <xsl:call-template name="split">
             <xsl:with-param name="pText" select="translate(., '&#xA;', '')"/>
           </xsl:call-template>
       </xsl:variable>

       <xsl:variable name="vnumLines" select=
       "ceiling(string-length($vsplitResult) div 81)"/>

       <xsl:choose>
        <xsl:when test="$vnumLines > 11">
          <xsl:value-of select="substring($vsplitResult, 1, 81*11 -1)"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:variable name="vremLines" select="11 - $vnumLines"/>

          <xsl:value-of select="substring($vsplitResult, 1, 81*($vnumLines -1))"/>

          <xsl:call-template name="padRight">
           <xsl:with-param name="pText" select="substring($vsplitResult,81*($vnumLines -1)+1)"/>
          </xsl:call-template>

          <xsl:for-each select="(document('')//node())[not(position() > $vremLines)]">
            <xsl:value-of select="concat('&#xA;', $vLine)"/>
          </xsl:for-each>
        </xsl:otherwise>
       </xsl:choose>
     </xsl:copy>
 </xsl:template>

 <xsl:template name="split">
  <xsl:param name="pText" select="."/>
  <xsl:param name="pLineLength" select="80"/>

  <xsl:if test="$pText">
   <xsl:value-of select="substring($pText, 1, $pLineLength)"/>
   <xsl:if test="string-length($pText) > $pLineLength">
    <xsl:text>&#xA;</xsl:text>
   </xsl:if>

   <xsl:call-template name="split">
     <xsl:with-param name="pText" select="substring($pText, $pLineLength+1)"/>
     <xsl:with-param name="pLineLength" select="$pLineLength"/>
   </xsl:call-template>
  </xsl:if>
 </xsl:template>

 <xsl:template name="padRight">
  <xsl:param name="pText"/>
  <xsl:param name="pTotatLength" select="80"/>

  <xsl:value-of select=
  "concat($pText,
          substring($vLine, 1, $pTotatLength - string-length($pText))
          )"/>
 </xsl:template>
</xsl:stylesheet>

When this transformation is applied on the provided XML document:

<Description>
text text text text text text text text text text text text text
text text text text text text text text text text text text text text text
text text text text </Description>

the wanted, correct result is produced (I am using the * character in order to see what exactly is generated):

<Description>text text text text text text text text text text text text texttext text text t
ext text text text text text text text text text text texttext text text text **
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************</Description>

II. XSLT 2.0 solution:

<xsl:stylesheet version="2.0"   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xs="http://www.w3.org/2001/XMLSchema">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:param name="pLineLength" select="80" as="xs:integer"/>
 <xsl:param name="pTotalLines" select="11"  as="xs:integer"/>
 <xsl:param name="pPadChar" select="'*'" as="xs:string"/>

 <xsl:variable name="vLine" as="xs:string" select=
      "string-join(
                   (for $i in 1 to $pLineLength
                     return $pPadChar
                    ),
                   ''
                   )
     "/>
  <xsl:template match="/*">
   <xsl:variable name="vText" select="translate(., '&#xA;', '')"/>

   <xsl:copy>
     <xsl:value-of separator="&#xA;" select=
       "(for $numlines in string-length($vText) idiv $pLineLength +1,
             $line in 1 to $numlines
          return
             if($line ne $numlines)
               then substring($vText, 
                              1 + ($line -1)*$pLineLength, 
                              $pLineLength)
               else
                for $lastLine in substring($vText, 
                                           1 + ($line -1)*$pLineLength, 
                                           $pLineLength)
                 return
                   concat($lastLine, 
                          substring($vLine, 
                                    1, 
                                    $pLineLength - string-length($lastLine))
         ),

        (for $numlines in string-length($vText) idiv $pLineLength +1,
             $rem in 1 to $pTotalLines - $numlines
          return
             $vLine)
          )
       "/>
   </xsl:copy>
 </xsl:template>
</xsl:stylesheet>

When this transformation is applied on the same XML document (above), the same correct result is produced:

<Description>text text text text text text text text text text text text texttext text text t
ext text text text text text text text text text text texttext text text text **
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************</Description>

Upvotes: 1

Related Questions