WordML import: fix huge recursion depth in replace-spaces

Bug document: fdo67478-1.doc

The replace-spaces template consumed one pair of spaces per recursion
level. WordML documents with very long space runs (e.g. 61000
consecutive spaces) exceeded libxslt's 3000 depth limit, causing an
import error.

Consume the entire consecutive space run in a single call by using
translate() to find the first non-space character, counting all leading
spaces at once, and emitting a single <text:s text:c="N"/> element.
Recursion depth is now bounded by the number of separate space runs,
not the total number of spaces.

Change-Id: Ifb11a61a2e89274684e1d6d38920c8792598fa43
Reviewed-on: https://gerrit.collaboraoffice.com/c/core/+/62
Reviewed-by: Michael Stahl <michael.stahl@collabora.com>
Tested-by: Jenkins CPCI <releng@collaboraoffice.com>
(cherry picked from commit 7ba970bce9e1200e5712afe76ec3a85056be9263)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/204666
Tested-by: Jenkins
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
This commit is contained in:
Ujjawal Kumar
2026-05-02 17:46:23 +02:00
committed by Xisco Fauli
parent 65cc02da04
commit cb2bc2ba0e
6 changed files with 134 additions and 15 deletions
@@ -1001,21 +1001,37 @@
</xsl:template>
<xsl:template name="replace-spaces">
<xsl:param name="curr-string"/>
<xsl:if test="contains($curr-string,' ')">
<xsl:value-of select="substring-before($curr-string,' ')"/>
<text:s text:c="2"/>
<xsl:variable name="next-string" select="substring-after($curr-string,' ')"/>
<xsl:choose>
<xsl:when test="contains($next-string, ' ')">
<xsl:call-template name="replace-spaces">
<xsl:with-param name="curr-string" select="$next-string"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$next-string"/>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
<xsl:choose>
<xsl:when test="not(contains($curr-string, ' '))">
<xsl:value-of select="$curr-string"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="substring-before($curr-string, ' ')"/>
<xsl:variable name="rest" select="substring-after($curr-string, ' ')"/>
<xsl:variable name="rest-no-spaces" select="translate($rest, ' ', '')"/>
<xsl:variable name="first-nonspace-char" select="substring($rest-no-spaces, 1, 1)"/>
<xsl:choose>
<xsl:when test="$first-nonspace-char = ''">
<text:s>
<xsl:attribute name="text:c">
<xsl:value-of select="2 + string-length($rest)"/>
</xsl:attribute>
</text:s>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="leading" select="substring-before($rest, $first-nonspace-char)"/>
<text:s>
<xsl:attribute name="text:c">
<xsl:value-of select="2 + string-length($leading)"/>
</xsl:attribute>
</text:s>
<xsl:call-template name="replace-spaces">
<xsl:with-param name="curr-string" select="substring($rest, string-length($leading) + 1)"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="w:tab">
<xsl:element name="text:tab"/>