Hi Kurt,
Thank you for the detailed explanation. I tried it but get the same error. I
also tried adding the map as a variable of the function, but still get the
error. It works in Oxygen but not in FrameMaker.
<xsl:function name="cp:find-change-text" as="xs:string">
<xsl:param name="text"/>
<xsl:variable name="find-change-map" as="map(xs:string, xs:string)"
select="map{'—' : '<emdash/>',
'–' : '<endash/>',
'“' : '<ldquote/>',
'”' : '<rdquote/>',
'’' : '<rsquote/>',
'§' : '<section/>' }"/>
<!--<xsl:value-of select="$text"/>-->
<xsl:sequence select="fold-left(map:keys($find-change-map), $text,
function($text, $key) { replace($text, $key,
$find-change-map($key)) })"/>
</xsl:function>
Rick
From: Kurt Cagle kurt.cagle@xxxxxxxxx
<xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx>
Sent: Friday, February 27, 2026 5:25 PM
To: xsl-list@xxxxxxxxxxxxxxxxxxxxxx
Subject: Re: Processor error
Try this:
The error occurs because the inline function inside fold-left closes over the
global variable $find-change-map, and some XSLT 3.0 processors (Saxon in
particular) have trouble with global variable references captured in closures
within named functions. The fix is to pass the map as a parameter to
cp:find-change-text instead of referencing it as a free variable:
<xsl:function name="cp:find-change-text" as="xs:string">
<xsl:param name="text" as="xs:string"/>
<xsl:param name="changes" as="map(xs:string, xs:string)"/>
<xsl:sequence select="fold-left(map:keys($changes), $text,
function($t, $key) { replace($t, $key, $changes($key)) })"/>
</xsl:function>
Then update the call sites:
<xsl:variable name="text" select="cp:find-change-text(normalize-space(title),
$find-change-map)"/>
The root cause: when an inline function (the lambda passed to fold-left) is
defined inside a named xsl:function, the processor compiles it as an anonymous
function object. If that lambda captures a global variable by reference,
certain processors flag it as an already-bound component during static
analysis b the global binding and the closure binding conflict. Passing the
map explicitly as a parameter keeps everything in the local call stack and
avoids the closure capture entirely.
A secondary issue worth noting: in your templates you have <xsl:text>[ /Title
({$text}) </xsl:text> b those curly braces only trigger AVT expansion inside
attribute values, not inside xsl:text. With expand-text="yes" on the
stylesheet element you'd use {$text} directly in text content, which your
stylesheet does have, but wrapped in xsl:text the expansion still won't fire.
You likely want either bare text content or <xsl:value-of select="$text"/>
there.
Kurt Cagle
Editor in Chief
The Cagle Report
<mailto:kurt.cagle@xxxxxxxxx> kurt.cagle@xxxxxxxxx
443-837-8725
On Fri, Feb 27, 2026 at 1:28b/PM rick@xxxxxxxxxxxxxx
<mailto:rick@xxxxxxxxxxxxxx> <xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx
<mailto:xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx> > wrote:
Hi All,
I am applying a stylesheet via Oxygen using Saxon-EE 9.9.1.7 and it processes
without errors. When I run the same stylesheet in Adobe FrameMaker using
SaxonEE9-8-0-7J, I get this error:
**** Component reference function anon:f_18205168#2 is already bound
When I remove this function, I donbt get the error:
<xsl:function name="cp:find-change-text" as="xs:string">
<xsl:param name="text"/>
<xsl:sequence select="fold-left(map:keys($find-change-map), $text,
function($text, $key) { replace($text, $key,
$find-change-map($key)) })"/>
</xsl:function>
Here is a partial input, followed by the stylesheet. I am outputting text.
<?xml version="1.0" standalone="no"?>
<map>
<chapter name="Chapter1TOC.fm" page="24">
<title>Chapter 1 Ethics and Professional Conduct</title>
<practice-notes>
<bookmark dest="G12.1038988">
<title>? 1.1 Introduction</title>
<bookmark dest="G12.1023242">
<title>? 1.1:1 The Lawyerbs Creed</title>
</bookmark>
<bookmark dest="G12.1023245">
<title>? 1.1:2 Notice of Grievance Process</title>
</bookmark>
</bookmark>
<bookmark dest="G12.1023248">
<title>? 1.2 Sources of Interpretation of Rules</title>
</bookmark>
</practice-notes>
<forms>
<bookmark name="Form1-1.fm" page="46">
<title>Form 1-1 Letter Declining Representation</title>
</bookmark>
<bookmark name="Form1-2.fm" page="48">
<title>Form 1-2 Letter Disclosing and Requesting Waiver of
Potential Conflict with Current Client</title>
</bookmark>
<bookmark name="Form1-17.fm" page="123">
<title>Form 1-17 Additional Clauses for Engagement
Letters</title>
<bookmark dest="G29.1726404">
<title>Clause 1-17-1 Corporate Transparency Act</title>
</bookmark>
<bookmark dest="G29.1726774">
<title>Clause 1-17-2</title>
</bookmark>
</bookmark>
</forms>
</chapter>
</map>
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:cp="http://www.frameexpert.com/functions"
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
exclude-result-prefixes="xs cp map"
version="3.0"
expand-text="yes">
<xsl:output method="text" indent="no"/>
<xsl:variable name="find-change-map" as="map(xs:string, xs:string)"
select="map{'—' : '<emdash/>',
'–' : '<endash/>',
'“' : '<ldquote/>',
'”' : '<rdquote/>',
'’' : '<rsquote/>',
'§' : '<section/>' }"/>
<xsl:template match="/map">
<xsl:text>[ /PageMode /UseOutlines /DOCVIEW pdfmark
</xsl:text>
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="indexes">
<xsl:variable name="child-count" select="count(index)"/>
<xsl:text>[ /Title (INDEXES) /Count -</xsl:text>
<xsl:value-of select="$child-count"/><xsl:text> </xsl:text>
<!-- Color is black; style is bold. -->
<xsl:text>/Color [0 0 1] /F 2 /Page 0 /OUT </xsl:text>
<xsl:text>pdfmark
</xsl:text>
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="chapter|index">
<xsl:variable name="child-count" select="count(bookmark)"/>
<!-- Call the function to replace special characters with elements.
-->
<xsl:variable name="text"
select="cp:find-change-text(normalize-space(title))"/>
<xsl:text>[ /Title ({$text}) </xsl:text>
<!-- Reserved bookmarks get grey color. -->
<xsl:if test="matches($text,'reserved$')">
<xsl:text>/Color [0.5 0.5 0.5] /F 0 </xsl:text>
</xsl:if>
<xsl:if test="$child-count > 0">
<xsl:text>/Count -</xsl:text><xsl:value-of
select="$child-count"/><xsl:text> </xsl:text>
</xsl:if>
<xsl:text>/Page </xsl:text><xsl:value-of select="if(@page) then @page
else 0"/><xsl:text> </xsl:text>
<xsl:text>/View [/XYZ -4 792 null] /OUT </xsl:text>
<xsl:text>pdfmark
</xsl:text>
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="bookmark">
<xsl:variable name="child-count" select="count(bookmark)"/>
<!-- Call the function to replace special characters with elements.
-->
<xsl:variable name="text"
select="cp:find-change-text(normalize-space(title))"/>
<xsl:text>[ /Title ({$text}) </xsl:text>
<!-- Reserved bookmarks get grey color. -->
<xsl:if test="matches($text,'reserved$')">
<xsl:text>/Color [0.5 0.5 0.5] /F 0 </xsl:text>
</xsl:if>
<xsl:if test="$child-count > 0">
<xsl:text>/Count -</xsl:text><xsl:value-of
select="$child-count"/><xsl:text> </xsl:text>
</xsl:if>
<xsl:if test="@page">
<xsl:text>/Page </xsl:text><xsl:value-of
select="@page"/><xsl:text> </xsl:text>
</xsl:if>
<xsl:if test="@dest">
<xsl:text>/Dest /{@dest} </xsl:text>
</xsl:if>
<xsl:text>/OUT pdfmark
</xsl:text>
<xsl:apply-templates/>
</xsl:template>
<xsl:mode on-no-match="shallow-skip"/>
<xsl:function name="cp:find-change-text" as="xs:string">
<xsl:param name="text"/>
<xsl:sequence select="fold-left(map:keys($find-change-map), $text,
function($text, $key) { replace($text, $key,
$find-change-map($key)) })"/>
</xsl:function>
</xsl:stylesheet>
Best regards,
Rick
Rick Quatro
Carmen Publishing Inc.
www.frameexpert.com <http://www.frameexpert.com>
rick@xxxxxxxxxxxxxxx <mailto:rick@xxxxxxxxxxxxxxx>
585-729-6746
XSL-List info and archive <http://www.mulberrytech.com/xsl/xsl-list>
EasyUnsubscribe <http://lists.mulberrytech.com/unsub/xsl-list/1217935> (by
email)
XSL-List info and archive <http://www.mulberrytech.com/xsl/xsl-list>
EasyUnsubscribe <http://lists.mulberrytech.com/unsub/xsl-list/612310> (by
email <> )
|