Вот еще один подход в XSLT 1.0, без использования расширения node-set
:
<xsl:template match="@*|node()" mode="limit-length">
<xsl:param name="length"/>
<xsl:copy>
<xsl:apply-templates select="@*" mode="limit-length"/>
<xsl:call-template name="copy-nodes">
<xsl:with-param name="nodes" select="node()"/>
<xsl:with-param name="length" select="$length"/>
</xsl:call-template>
</xsl:copy>
</xsl:template>
<xsl:template match="text()" mode="limit-length">
<xsl:param name="length"/>
<xsl:value-of select="substring(., 1, $length)"/>
</xsl:template>
<xsl:template name="copy-nodes">
<xsl:param name="nodes"/>
<xsl:param name="length"/>
<xsl:if test="$length > 0 and $nodes">
<xsl:variable name="head" select="$nodes[1]"/>
<xsl:apply-templates select="$head" mode="limit-length">
<xsl:with-param name="length" select="$length"/>
</xsl:apply-templates>
<xsl:variable name="remaining" select="$length - string-length($head)"/>
<xsl:if test="$remaining > 0 and count($nodes) > 1">
<xsl:call-template name="copy-nodes">
<xsl:with-param name="nodes" select="$nodes[position() > 1]"/>
<xsl:with-param name="length" select="$remaining"/>
</xsl:call-template>
</xsl:if>
</xsl:if>
</xsl:template>
По сути, это шаблон идентификации, с копированием дочерних узлов, выгруженных в рекурсивный шаблон, который принимаетзаботиться о сохранении максимальной длины строки, а также отдельного шаблона для текстовых узлов, обрезая их до максимальной длины.
Вы можете вызвать это для примера ввода следующим образом:
<xsl:call-template name="copy-nodes">
<xsl:with-param name="nodes" select="story/node()"/>
<xsl:with-param name="length" select="500"/>
</xsl:call-template>
Продолжение: Разделение истории
В качестве дополнительного вопроса о разделении истории на две части после первого разрыва или конца абзаца после N символов я продолжу и сделаю упрощающее предположение, что вы хотитерассмотрите возможность расщепления только после элементов <p>
и <br>
, которые отображаются как прямые дочерние элементы под элементом <story>
(и не вложены на произвольную глубину).Это делает всю проблему намного проще.
Вот один из способов сделать это: чтобы получить содержимое первой части, вы можете использовать шаблон, который будет обрабатывать набор узлов одного уровня, пока максимальная длина строки не будетпревышен, и br
или p
обнаружен, а затем остановитесь.
<xsl:template match="node()" mode="before-break">
<xsl:param name="length"/>
<xsl:if test="$length > 0 or not(self::br or self::p)">
<xsl:copy-of select="."/>
<xsl:apply-templates select="following-sibling::node()[1]"
mode="before-break">
<xsl:with-param name="length" select="$length - string-length(.)"/>
</xsl:apply-templates>
</xsl:if>
</xsl:template>
И для второй части вы можете создать другой шаблон, который ищет то же условие, что и предыдущий шаблон, но ничего не выводит до этого момента:
<xsl:template match="node()" mode="after-break">
<xsl:param name="length"/>
<xsl:choose>
<xsl:when test="$length > 0 or not(self::br or self::p)">
<xsl:apply-templates select="following-sibling::node()[1]"
mode="after-break">
<xsl:with-param name="length" select="$length - string-length(.)"/>
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<xsl:if test="not(self::br)"> <!-- suppress the <br/> -->
<xsl:copy-of select="."/>
</xsl:if>
<xsl:copy-of select="following-sibling::node()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
И воткак вы можете использовать эти шаблоны для разделения истории на две <div>
с.
<xsl:template match="story">
<xsl:copy>
<xsl:copy-of select="@*"/>
<div>
<xsl:apply-templates select="node()[1]" mode="before-break">
<xsl:with-param name="length" select="500"/>
</xsl:apply-templates>
</div>
<div>
<xsl:apply-templates select="node()[1]" mode="after-break">
<xsl:with-param name="length" select="500"/>
</xsl:apply-templates>
</div>
</xsl:copy>
</xsl:template>