Voici une autre approche dans XSLT 1.0, sans avoir à utiliser l'option node-set
extension :
<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>
Il s'agit essentiellement du modèle d'identité, la copie des nœuds enfants étant transférée à un modèle récursif qui se charge de respecter la longueur maximale de la chaîne, ainsi qu'un modèle distinct pour les nœuds de texte, qui les tronque à la longueur maximale.
Vous pouvez l'invoquer pour l'exemple d'entrée comme suit :
<xsl:call-template name="copy-nodes">
<xsl:with-param name="nodes" select="story/node()"/>
<xsl:with-param name="length" select="500"/>
</xsl:call-template>
Suivi : diviser l'histoire
Pour ce qui est de diviser l'histoire en deux parties après la première coupure ou fin de paragraphe après N caractères, je vais aller de l'avant et faire l'hypothèse simplificatrice que vous voulez envisager de diviser l'histoire seulement après que <p>
y <br>
qui apparaissent comme des enfants directs sous l'élément <story>
(et non pas imbriqués à une profondeur arbitraire). Cela facilite grandement la résolution du problème.
Voici une façon d'y parvenir : Pour obtenir le contenu de la première partie, vous pouvez utiliser un modèle qui traitera un ensemble de nœuds frères jusqu'à ce que la longueur maximale de la chaîne soit dépassée et qu'un message d'erreur br
o p
est rencontrée, puis s'arrêter.
<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>
Pour la deuxième partie, vous pouvez créer un autre modèle qui recherche la même condition que le modèle précédent, mais qui ne produit rien jusqu'à ce moment-là :
<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>
Voici comment vous pouvez utiliser ces modèles pour diviser une histoire en deux <div>
s.
<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>