Amy Higgins
Amy Higgins

Reputation: 31

Replacing text using XSL-FO

I recently asked a similar question Convert text string to xml tagging using xsl pt.2 and received the solution, which has helped me put together some XSL-FO code.

Now, the XML I'm trying to convert is <p>The @c cmdname is a utility and @v varname is an option.</p>

to something like:

The cmdname is a utility and varname is an option.

I use the following XSL-FO to convert the word following '@c' (cmdname) to monospace font, and the word following '@v' (varname) to italics. But, after building the PDF with a sentence like the XML example, only the font for @c (cmdname) changes. Note that the <p> tag in the example can be any XML tag.

It's a long story, but I can't just use XML tags, like <i> to make the font italic.

My XSL-FO code looks like:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
   version="2.0">

<xsl:template match="text()[contains(., '@c')]">
   <xsl:analyze-string select="." regex="@c\s+([!-~]+)">
      <xsl:matching-substring>
         <fo:inline font-family="RobotoMono-Regular">
            <xsl:value-of select="regex-group(1)"/>
         </fo:inline>
      </xsl:matching-substring>
      <xsl:non-matching-substring>
         <xsl:value-of select="."/>
      </xsl:non-matching-substring>
   </xsl:analyze-string>
</xsl:template>

<xsl:template match="text()[contains(., '@v')]">
   <xsl:analyze-string select="." regex="@v\s+([!-~]+)">
      <xsl:matching-substring>
         <fo:inline font-family="Roboto" font-style="italic">
            <xsl:value-of select="regex-group(1)"/>
         </fo:inline>
      </xsl:matching-substring>
      <xsl:non-matching-substring>
         <xsl:value-of select="."/>
      </xsl:non-matching-substring>
   </xsl:analyze-string>
</xsl:template>

</xsl:stylesheet>

How can I modify this XSL-FO code to work with any '@*' tagging combination?

Upvotes: 0

Views: 160

Answers (2)

Daniel Haley
Daniel Haley

Reputation: 52888

You could do something like capture the letter after @ and use that to determine what attributes should be used in the fo:inline (perhaps using xsl:use-attribute-sets).

Example (updated to support ...work with any '@*' tagging combination?)...

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:fo="http://www.w3.org/1999/XSL/Format"
    version="2.0">
    <xsl:strip-space elements="*"/>
    <xsl:output indent="yes"/>
    
    <xsl:template match="/doc">
        <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
            <fo:layout-master-set>
                <fo:simple-page-master master-name="sample">
                    <fo:region-body/>
                </fo:simple-page-master>
            </fo:layout-master-set>
            <fo:page-sequence master-reference="sample">
                <fo:flow flow-name="xsl-region-body">
                    <xsl:apply-templates/>
                </fo:flow>
            </fo:page-sequence>
        </fo:root>
    </xsl:template>
    
    <xsl:template match="p">
        <fo:block>
            <xsl:apply-templates/>
        </fo:block>
    </xsl:template>
    
    <xsl:attribute-set name="at_c">
        <xsl:attribute name="font-family" select="'RobotoMono-Regular'"/>
    </xsl:attribute-set>
    <xsl:attribute-set name="at_v">
        <xsl:attribute name="font-family" select="'Roboto'"/>
        <xsl:attribute name="font-style" select="'italic'"/>
    </xsl:attribute-set>
    
    
    <xsl:template match="text()[matches(.,'@[^\s]\s+\w+')]">
        <xsl:analyze-string select="." regex="@([^\s])\s+(\w+)">
            <xsl:matching-substring>
                <xsl:choose>
                    <xsl:when test="regex-group(1)='c'">
                        <fo:inline xsl:use-attribute-sets="at_c">
                            <xsl:value-of select="regex-group(2)"/>
                        </fo:inline>
                    </xsl:when>
                    <xsl:when test="regex-group(1)='v'">
                        <fo:inline xsl:use-attribute-sets="at_v">
                            <xsl:value-of select="regex-group(2)"/>
                        </fo:inline>
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:message terminate="yes">Unhandled @ value: <xsl:value-of select="regex-group(1)"/></xsl:message>
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:matching-substring>
            <xsl:non-matching-substring>
                <xsl:value-of select="."/>
            </xsl:non-matching-substring>
        </xsl:analyze-string>
    </xsl:template>
    
</xsl:stylesheet>

Fiddle: http://xsltfiddle.liberty-development.net/eiZNCxj/2

Upvotes: 0

Tony Graham
Tony Graham

Reputation: 8068

Your two templates are the same priority, so for the text node that contains both @c and @v, you're processing the@c and getting the string value of the rest of the text node. (I'd be surprised if your XSLT processor isn't warning you about the two possible matching templates.)

You can connect the two either by having nested xsl:analyze-string in a template matching on text() or by having one template call the other:

<xsl:template match="text()[contains(., '@c')]" priority="5">
  <xsl:analyze-string select="." regex="@c\s+([!-~]+)">
    <xsl:matching-substring>
      <fo:inline font-family="RobotoMono-Regular">
        <xsl:value-of select="regex-group(1)"/>
      </fo:inline>
    </xsl:matching-substring>
    <xsl:non-matching-substring>
      <xsl:call-template name="varname" />
    </xsl:non-matching-substring>
  </xsl:analyze-string>
</xsl:template>

<xsl:template match="text()[contains(., '@v')]" name="varname">
  <xsl:param name="string" select="." />
  <xsl:analyze-string select="$string" regex="@v\s+([!-~]+)">
    <xsl:matching-substring>
      <fo:inline font-family="Roboto" font-style="italic">
        <xsl:value-of select="regex-group(1)"/>
      </fo:inline>
    </xsl:matching-substring>
    <xsl:non-matching-substring>
      <xsl:value-of select="."/>
    </xsl:non-matching-substring>
  </xsl:analyze-string>
</xsl:template>

Upvotes: 0

Related Questions