PDF への逐語出力用のハイフネーション スクリプトを作成する XSLT 1.0 に行き詰まっています。(これは PDF であるため、巧妙な CSS を使用して問題を解決することはできません。スクリプトの一部として stringlength を比較するために、text() ノードと子 text() ノードの両方にアクセスできるようにしたいと考えています。
例えば:
<programlisting>This text will be displayed without word-wrap
I can measure length of string between end line chars ' '
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) = ' '">
<xsl:value-of select="$text"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat($text,' ')"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="first_line">
<xsl:value-of select="substring-before(concat($fixed_text,' '),' ')"/>
</xsl:variable>
<xsl:variable name="other_lines">
<xsl:value-of select="substring-after($fixed_text,concat($first_line,' '))"/>
</xsl:variable>
<xsl:choose>
<xsl:when test="string-length(normalize-space($first_line)) < 1"><!-- blank line (just trim and copy)-->
<xsl:value-of select="concat($verbatim_padding,substring($first_line,1,100),' ')"/>
</xsl:when>
<xsl:when test="string-length($first_line) < 100"> <!-- short line (just copy)-->
<xsl:value-of select="concat($verbatim_padding,$first_line,' ')"/>
</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) > 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) < $max_chars) or (string-length($long_string) < $min_chars) or (string-length($long_string) < 1)">
<xsl:value-of select="concat($long_string,' ')"/>
</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 < $stop) or (string-length($string) < $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>