RE: [xsl] substring manipulation to wrap text in an SVG <rect>

Subject: RE: [xsl] substring manipulation to wrap text in an SVG <rect>
From: "Michael Kay" <mhkay@xxxxxxxxxxxx>
Date: Sun, 16 Sep 2001 23:20:21 +0100
Firstly, the following template returns the length of the first line of text
to output. There are other ways to write it but I've chosen to be simple and
verbose.

<xsl:template name="line-length">
  <xsl:param name="text"/>
  <xsl:choose>
  <xsl:when test="string-length($text) &lt; 18">
    <xsl:value-of select="string-length($text)"/>
  </xsl:when>
  <xsl:when test="substring($text, 18, 1) = ' '">18</xsl:when>
  <xsl:when test="substring($text, 17, 1) = ' '">17</xsl:when>
  <xsl:when test="substring($text, 16, 1) = ' '">16</xsl:when>
  ...
  <xsl:otherwise>1</xsl:otherwise>
  </xsl:choose>
</xsl:template>

Now you need a recursive template to process your chunk of text. The
template outputs a <text> element for the first line, and calls itself to
output <text> elements for the remaining lines:

<xsl:template name="output-text">
  <xsl:param name="text"/>
  <xsl:variable name="first-len">
    <xsl:call-template name="line-length">
      <xsl:with-param name="$text"/>
    </xsl:call-template>
  </xsl:variable>
  <text><xsl:value-of select="substring($text, 1, $first-len)"/></text>
  <xsl:variable name="rest" select="substring($text, $first-len+1)"/>
  <xsl:if test="$rest">
    <xsl:call-template name="output-text">
      <xsl:with-param name="text" select="$rest"/>
    </xsl:call-template>
  </xsl:if>
</xsl:template>

I haven't included the code to output the attributes of the <text> element,
this may need extra parameters to the template.

Some comments on your code:

Match patterns never need to start with "//". It's best not to include a
leading "//" because it means that the rules for assigning a default
priority don't work as intended.

Using disable-output-escaping="yes" when your output is intended to be
well-formed XML is a clear indication that you haven't yet mastered XSLT! It
is never necessary in such circumstances, and is always undesirable, because
it means your stylesheet can only produce serialized XML, not a tree that
can be input to another process.

Using <xsl:element> and <xsl:attribute> with fixed element and attribute
names is much harder to write and harder to read than using literal result
elements and attribute value templates.

Using <xsl:variable> with content consisting of a single <xsl:value-of>
instruction can in nearly all cases be replaced by an <xsl:variable> with a
select attribute, which again is easier to read and easier to write.

ancestor::*[self::HOTSPOT] is a rather longwinded way of writing
ancestor::HOTSPOT.

Mike Kay

>
> I am trying to wrap text inside of an svg <rect> element thru svg. Im
> getting close, just need to get over this last hurdle.
> Right now, I take the width of the rectangle, multiply it by
> 18, and round
> this number, to give me the number of characters
> that will fit across the rectangle in 1 line.
> I then use substring() to take the calculated number of
> characters and draw
> them, then take the next set of characters and so on.
>
> This part works, though the xsl is not pretty.
>
> Now, I need it to be a little more intelligent. I want to stop it from
> cutting off words in the middle of them..for example, instead of
>
> This is some te
> xt. See how it c
> uts off.
>
> This is some
> text. Now it
> doesnt cut off
>
>
> I havent thought of a way to do this yet with only having
> substring()
> substring-before()
> substring-after()
> contains()
> starts-with()
>
> at my disposal. If there were some sort of indexOf('some character or
> string') kind of function, that would return the position within the
> string of the argument given to it..well that would make this easier.
>
> So....any ideas? Ive included sample xsl and xml below
>
> thanks,
>
> Paul
>
> xsl:
>
>
> <xsl:template match="//HOTSPOT">
> <xsl:variable name="xval">
> <xsl:value-of select="number(substring-before(@X, 'i'))"/>
> </xsl:variable>
> <xsl:variable name="yval">
> <xsl:value-of select="number(substring-before(@Y, 'i'))"/>
> </xsl:variable>
> <xsl:variable name="wval">
> <xsl:value-of select="number(substring-before(@W, 'i')) div 15"/>
> </xsl:variable>
> <xsl:variable name="hval">
> <xsl:value-of select="number(substring-before(@H, 'i')) div 9"/>
> </xsl:variable>
> <xsl:element name="g">
>
> <!--draw the rectangle-->
> <xsl:element name="rect">
> <xsl:attribute name="id"><xsl:value-of
> select="@ID"/></xsl:attribute>
> <xsl:attribute name="x"><xsl:value-of select="@X"/></xsl:attribute>
> <xsl:attribute name="y"><xsl:value-of select="@Y"/></xsl:attribute>
> <xsl:attribute name="width"><xsl:value-of
> select="@W"/></xsl:attribute>
> <xsl:attribute name="height"><xsl:value-of
> select="@H"/></xsl:attribute>
> <xsl:attribute name="style"><xsl:text>fill:white; fill-opacity:0;
> stroke:red; stroke-width:1; stroke-linejoin:bevel;
> stroke-dasharray:5,2</xsl:text></xsl:attribute>
> </xsl:element>
> <--set up the containing text element-->
> <xsl:element name="text">
> <xsl:attribute name="x"><xsl:value-of select="$xval + $wval"
> /><xsl:text>in</xsl:text></xsl:attribute>
> <xsl:attribute name="y"><xsl:value-of select="$yval + $hval"
> /><xsl:text>in</xsl:text></xsl:attribute>
> <xsl:attribute
> name="style"><xsl:text>font-family:'Arial';font-size:10;fill-r
> ule:nonzero;cl
> ip-rule:nonzero;</xsl:text></xsl:attribute>
> <xsl:apply-templates/>
> </xsl:element>
> </xsl:element>
> <xsl:text disable-output-escaping="yes">
> &lt;/a&gt;
> </xsl:text>
> </xsl:template>
>
> <!--this number goes on the first line-->
> <xsl:template match="//FIGURE[descendant::GRAPHIC[substring(@FILE,
> string-length(@FILE)-2)= 'svg']]//CALLOUT-NUMBER">
> <xsl:element name="tspan">
> <xsl:attribute name="style">
> <xsl:text>font-size:12;font-weight:bold</xsl:text>
> </xsl:attribute>
> <xsl:value-of select="."/>
> </xsl:element>
>
> <!-- now this text needs to wrap inside the rectangle-->
> <xsl:template match="//P">
> <xsl:variable name="charsperline">
> <xsl:value-of
> select="round(number(substring-before(ancestor::*[self::HOTSPO
> T][1]/@W,
> 'i')) * 18.732)"/>
> </xsl:variable>
> <xsl:variable name="xval">
> <xsl:value-of
> select="number(substring-before(ancestor::*[self::HOTSPOT][1]/
> @X, 'i'))"/>
> </xsl:variable>
> <xsl:variable name="wval">
> <xsl:value-of
> select="number(substring-before(ancestor::*[self::HOTSPOT][1]/
> @W, 'i')) div
> 15"/>
> </xsl:variable>
>
> <xsl:element name="tspan">
> <xsl:attribute name="x"><xsl:value-of select="$xval + $wval"
> /><xsl:text>in</xsl:text></xsl:attribute>
> <xsl:attribute name="dy"><xsl:text>1em</xsl:text></xsl:attribute>
> <!--Here get the first line of characters-->
> <xsl:value-of select="substring(., 0, $charsperline)"/>
> </xsl:element>
> <xsl:element name="tspan">
> <xsl:attribute name="x"><xsl:value-of select="$xval + $wval"
> /><xsl:text>in</xsl:text></xsl:attribute>
> <xsl:attribute name="dy"><xsl:text>1em</xsl:text></xsl:attribute>
> <!--now get the next line of characters-->
> <xsl:value-of select="substring(., $charsperline, $charsperline)"/>
> </xsl:element>
> <xsl:element name="tspan">
> <xsl:attribute name="x"><xsl:value-of select="$xval + $wval"
> /><xsl:text>in</xsl:text></xsl:attribute>
> <xsl:attribute name="dy"><xsl:text>1em</xsl:text></xsl:attribute>
> <!--now get the last line of characters-->
> <xsl:value-of select="substring(., $charsperline*2, $charsperline)"/>
> </xsl:element>
> </xsl:template>
>
>
> sample xml input...........
>
>  <FIGURE POSITION="SPAN" DISPLAY-TYPE="POPUP"
> DISPLAY-HOTSPOTS="DISPLAY"
> ID="svc.bt08_pe.0000110477">
>                         <GRAPHIC FILE="whatever.svg" WIDTH="9.295in"
> HEIGHT="6.521in" />
>                         <HOTSPOTS SIZEX="9.253in" SIZEY="6.500in">
>                            <LINE X1="1.421" Y1="1.192"
> X2="2.807" Y2="1.017"
> WIDTH="0.007"/>
>                            <HOTSPOT X="1.258in" Y="1.162in"
> W="0.951in"
> H="0.462in" ID="svc.bt08_pe.0000110478">
>                               <CALLOUT-NUMBER>2</CALLOUT-NUMBER>
>                               <P>Text Text Text Text Text
> Text Text</P>
>                            </HOTSPOT>
>                            <HOTSPOT X="7.553in" Y="1.603in"
> W="1.165in"
> H="0.599in" ID="svc.bt08_pe.0000110481">
>                               <CALLOUT-NUMBER>1</CALLOUT-NUMBER>
>                               <P>This is a bunch of text, a
> whole lot. Ther
> text just keeps coming.</P>
>                            </HOTSPOT>
>                            <LINE X1="7.480" Y1="1.636"
> X2="5.953" Y2="1.565"
> WIDTH="0.007"/>
>                            <HOTSPOT X="0.109in" Y="2.290in"
> W="1.197in"
> H="0.510in">
>                               <CALLOUT-NUMBER>3</CALLOUT-NUMBER>
>                               <P>Words words words words words words
> words</P>
>                            </HOTSPOT>
>                            <LINE X1="0.244" Y1="2.324"
> X2="1.648" Y2="2.152"
> WIDTH="0.007"/>
>                         </HOTSPOTS>
>                         <CAPTION>Lots more text is here. Check it
> out.</CAPTION>
>                      </FIGURE>
>
>
>
> __________________________________________________________
>
>  <<...OLE_Obj...>>
> Paul Wilcox
> Software Engineer
> Global Knowledge and Language Services
> XEROX
> Phone #: 8*222-4727/(716) 422-4727
> Email: Paul.Wilcox@xxxxxxxxxxxxx
>
>
>  XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list
>


 XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list


Current Thread