1

我坚持使用 XSLT 1.0 为 PDF 逐字输出制作连字符脚本。(注意它是 PDF,所以我不能使用聪明的 CSS 来帮助我。我希望能够同时访问 text() 节点和子 text() 节点来比较 stringlength 作为脚本的一部分。

例如:

<programlisting>This text will be displayed without word-wrap
I can measure length of string between end line chars '&#10;'
and break long lines. That works great until

<emphasis>this gets added</emphasis> then my counting loop doesn't
count the text in the emphasis tags.
</programlisting>

我正在使用名为 screen 的模板循环遍历 text()。它检测行尾字符并确定该行是否太长。长线被打断,剩余部分通过模板递归,直到剩余部分小于最大行长度......然后到下一行文本......效果很好,但现在我有一些孩子在和我不知道如何同时访问 text() 和任何子节点的 text() 来计算字符串长度。

抱歉,示例代码很长...

要匹配的模板如下所示...

<xsl:template match="programlisting/text()"> 
   <xsl:variable name="max_width">100</xsl:variable>
   <xsl:variable name="min_width">80</xsl:variable>
          <xsl:call-template name="screen">
            <xsl:with-param name="text" select="."/>
            <xsl:with-param name="max_width" select="$max_width"/>
            <xsl:with-param name="min_width" select="$min_width"/>
          </xsl:call-template>
</xsl:template>

判断换行前字符串是否过长的模板:

<xsl:template name="screen"> 
    <xsl:param name="text" select="."/>
    <xsl:param name="max_width"/>
    <xsl:param name="min_width"/>
    <xsl:variable name="fixed_text"> <!-- add end linebreak if missing -->
        <xsl:choose>
            <xsl:when test="substring($text,string-length($text),1) = '&#10;'">
                <xsl:value-of select="$text"/>  
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="concat($text,'&#10;')"/>  
            </xsl:otherwise>
        </xsl:choose> 
     </xsl:variable>
     <xsl:variable name="first_line">
                <xsl:value-of select="substring-before(concat($fixed_text,'&#10;'),'&#10;')"/>
    </xsl:variable>
    <xsl:variable name="other_lines">
                <xsl:value-of select="substring-after($fixed_text,concat($first_line,'&#10;'))"/>
    </xsl:variable>
    <xsl:choose>
        <xsl:when test="string-length(normalize-space($first_line)) &lt; 1"><!-- blank line (just trim and copy)-->
            <xsl:value-of select="concat($verbatim_padding,substring($first_line,1,100),'&#10;')"/>
        </xsl:when>
        <xsl:when test="string-length($first_line) &lt; 100"> <!-- short line (just copy)-->
            <xsl:value-of select="concat($verbatim_padding,$first_line,'&#10;')"/>
        </xsl:when>
        <xsl:otherwise>
            <!--  Line is too long!!  -->
            <xsl:variable name="wrapped_lines">
               <xsl:call-template name="break_line"> 
                    <xsl:with-param name="long_string" select="$first_line"/>
                    <xsl:with-param name="max_chars" select="100"/>
                    <xsl:with-param name="min_chars" select="80"/>
                    <xsl:with-param name="break_chars" select="' ,/;'"/>
                    <xsl:with-param name="linebreak_string" select="'~'"/>
                </xsl:call-template> 
            </xsl:variable>
            <xsl:value-of select="concat($verbatim_padding,$wrapped_lines)"/>
        </xsl:otherwise>
    </xsl:choose>
    <xsl:if test="string-length($other_lines) &gt; 0">
          <xsl:call-template name="screen">
            <xsl:with-param name="text" select="$other_lines"/>
            <xsl:with-param name="max_width" select="$max_width"/>
            <xsl:with-param name="min_width" select="$min_width"/>
          </xsl:call-template>
    </xsl:if>
</xsl:template>

分解长行的模板:

<xsl:template name="break_line"> 
    <xsl:param name="long_string"/>
    <xsl:param name="max_chars"/> <!--max chars allowed on a line -->
    <xsl:param name="min_chars"/> <!--max char position foa soft linebreak (else we hard break at the max chars!) -->
    <xsl:param name="break_chars"/>   <!-- chars used for soft breaking -->
    <xsl:param name="linebreak_string"/>  <!-- add this to end of a broken line -->
    <xsl:choose>
      <xsl:when test="(string-length($long_string) &lt; $max_chars) or (string-length($long_string) &lt; $min_chars) or (string-length($long_string) &lt; 1)">
          <xsl:value-of select="concat($long_string,'&#10;')"/>
       </xsl:when>
      <xsl:otherwise>
            <xsl:variable name="trim_x_by">
                    <xsl:call-template name="CheckLastChar">
                        <xsl:with-param name="string" select="$long_string"/>
                        <xsl:with-param name="start" select="$max_chars"/>
                        <xsl:with-param name="stop" select="$min_chars"/>
                        <xsl:with-param name="break_chars" select="$break_chars"/>
                        <xsl:with-param name="max_chars" select="$max_chars"/>
                     </xsl:call-template>
            </xsl:variable>
            <xsl:variable name="first_x_chars"><xsl:value-of select="substring($long_string,1,$trim_x_by)"/></xsl:variable>
            <xsl:variable name="remaining_chars"><xsl:value-of select="substring-after($long_string,$first_x_chars)"/></xsl:variable>

            <xsl:value-of select="concat($first_x_chars,' ',$linebreak_string)"/>

            <xsl:call-template name="break_line"> 
                <xsl:with-param name="long_string" select="$remaining_chars"/>
                <xsl:with-param name="max_chars" select="$max_chars"/>
                <xsl:with-param name="min_chars" select="$min_chars"/>
                <xsl:with-param name="break_chars" select="$break_chars"/>
                <xsl:with-param name="linebreak_string" select="$linebreak_string"/>
            </xsl:call-template> 
      </xsl:otherwise>
    </xsl:choose>
</xsl:template>

最后是字符串字符的递归检查,看看它是否是一个安全的换行位置(软中断)

   <xsl:template name="CheckLastChar">
        <xsl:param name="string"/>
        <xsl:param name="stop" select="1"/>
        <xsl:param name="start" select="string-length($string)"/>
        <xsl:param name="count" select="$start"/>
        <xsl:param name="break_chars"/>
        <xsl:param name="max_chars"/>
        <xsl:choose>
          <xsl:when test="($count &lt; $stop) or (string-length($string) &lt; $stop)"> 
                <!-- 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 -->
                <xsl:value-of select="$max_chars"/> 
          </xsl:when>
          <xsl:otherwise>
                <xsl:variable name="last_char"><xsl:value-of select="substring($string,$count,1)"/></xsl:variable>
                <xsl:choose>
                  <xsl:when test="contains($break_chars,$last_char)">
                        <xsl:value-of select="$count"/>
                  </xsl:when>
                 <xsl:otherwise>
                        <!-- keep looking -->
                        <xsl:call-template name="CheckLastChar">
                            <xsl:with-param name="string" select="$string"/>
                            <xsl:with-param name="stop" select="$stop"/>
                            <xsl:with-param name="start" select="$start"/>
                            <xsl:with-param name="count" select="$count - 1"/>
                            <xsl:with-param name="break_chars" select="$break_chars"/>
                            <xsl:with-param name="max_chars" select="$max_chars"/>
                        </xsl:call-template>
                  </xsl:otherwise>
                </xsl:choose>
          </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
4

1 回答 1

1

要获取元素中的所有文本(包括其后代的文本),只需使用string(Element)

<xsl:template match="programlisting"> 
   <xsl:variable name="max_width">100</xsl:variable>
   <xsl:variable name="min_width">80</xsl:variable>
          <xsl:call-template name="screen">
            <xsl:with-param name="text" select="string(.)"/>
            <xsl:with-param name="max_width" select="$max_width"/>
            <xsl:with-param name="min_width" select="$min_width"/>
          </xsl:call-template>
</xsl:template>

text()注意从match属性中删除。

XPath 规范

元素节点的字符串值是该元素节点的所有文本节点后代的字符串值按文档顺序串联而成。

string() 函数将对象转换为字符串,如下所示: 通过返回节点集中文档顺序中第一个节点的字符串值,将节点集转换为字符串。如果节点集为空,则返回一个空字符串。

于 2013-01-24T19:12:05.437 回答