You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

XML转PDF时含<title>元素的列表转换异常问题排查

XML转PDF异常排查:保留<title>元素时转换失败

我在将XML文档转换为PDF时遇到问题:移除XML示例中的<title>元素后转换可正常完成,但业务上必须保留该元素。附上疑似存在问题的XSLT代码片段,无法定位问题根源。

问题XML示例

<list type="bulleted">
    <title>Key:</title>
    <list-item>
        <para>Point 1</para>
    </list-item>
    <list-item>
        <para>Point 2</para>
    </list-item>
</list>

相关XSLT代码片段

list模板

<xsl:template match="list">
    <xsl:choose>
        <xsl:when test="parent::topic">
            <fo:block margin-bottom="2mm" margin-top="2mm" margin-left="0.3em" margin-right="2em" border-top="1pt solid gray" border-bottom="1pt solid gray" keep-together.within-page="always">
                <fo:block font-size="9pt" margin-bottom="2mm" margin-top="2mm">
                    <fo:inline font-weight="bold">Objective(s): </fo:inline>
                </fo:block>
                    <xsl:apply-templates/>
            </fo:block>
        </xsl:when>
        <xsl:otherwise>
            <xsl:choose>
                <xsl:when test='@type="bulleted" or @type="enumerated"'>
                    <fo:list-block margin-bottom="2mm">
                        <xsl:if test="count(ancestor::list) = 0">
                            <xsl:attribute name="margin-left">
                                <xsl:choose>
                                    <xsl:when test="$ib-doc-content='TOM'">5mm</xsl:when>               
                                <xsl:otherwise>10mm</xsl:otherwise>
                                </xsl:choose>
                            </xsl:attribute>
                        </xsl:if>
                        <xsl:if test="$ib-doc-content='TOM'">
                            <xsl:attribute name="provisional-distance-between-starts">6mm</xsl:attribute>
                        </xsl:if>   
                        <xsl:apply-templates/>
                    </fo:list-block>
                </xsl:when>
                <xsl:otherwise>
                    <fo:block margin-bottom="0.5em" margin-top="0.5em" margin-left="0.3em" margin-right="2em" >
                        <xsl:apply-templates/>
                    </fo:block>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

list-item模板

<xsl:template match="list-item">
    <xsl:choose>
        <xsl:when test='../@type="bulleted"'>
            <xsl:choose>
                <xsl:when test="count(preceding-sibling::list-item) = 0">
                    <fo:list-item margin-bottom="2mm" keep-with-previous="always">
                        <fo:list-item-label end-indent="label-end()">
                            <fo:block>
                                <xsl:choose>
                                    <xsl:when test="count(ancestor::list) = 3">
                                        <xsl:text>&#x25A0;</xsl:text>
                                    </xsl:when>
                                    <xsl:when test="count(ancestor::list) = 2">
                                        <xsl:text>&#x25CB;</xsl:text>
                                    </xsl:when>
                                    <xsl:otherwise>
                                        <xsl:text>&#x25CF;</xsl:text>
                                    </xsl:otherwise>
                                </xsl:choose>
                            </fo:block>
                        </fo:list-item-label>
                        <fo:list-item-body start-indent="body-start()">
                            <xsl:apply-templates/>
                        </fo:list-item-body>
                    </fo:list-item>
                </xsl:when>
                <xsl:when test="count(preceding-sibling::list-item) = 1">
                    <fo:list-item margin-bottom="2mm">          
                        <fo:list-item-label end-indent="label-end()">
                            <fo:block>
                                <xsl:choose>
                                    <xsl:when test="count(ancestor::list) = 3">
                                        <xsl:text>&#x25A0;</xsl:text>
                                    </xsl:when>
                                    <xsl:when test="count(ancestor::list) = 2">
                                        <xsl:text>&#x25CB;</xsl:text>
                                    </xsl:when>
                                    <xsl:otherwise>
                                        <xsl:text>&#x25CF;</xsl:text>
                                    </xsl:otherwise>
                                </xsl:choose>
                            </fo:block>
                        </fo:list-item-label>
                        <fo:list-item-body start-indent="body-start()">
                            <xsl:apply-templates/>
                        </fo:list-item-body>
                    </fo:list-item>
                </xsl:when>
                <xsl:otherwise>
                    <fo:list-item margin-bottom="2mm">
                        <fo:list-item-label end-indent="label-end()">
                            <fo:block>
                                <xsl:choose>
                                    <xsl:when test="count(ancestor::list) = 3">
                                        <xsl:text>&#x25A0;</xsl:text>
                                    </xsl:when>
                                    <xsl:when test="count(ancestor::list) = 2">
                                        <xsl:text>&#x25CB;</xsl:text>
                                    </xsl:when>
                                    <xsl:otherwise>
                                        <xsl:text>&#x25CF;</xsl:text>
                                    </xsl:otherwise>
                                </xsl:choose>
                            </fo:block>
                        </fo:list-item-label>
                        <fo:list-item-body start-indent="body-start()">
                            <xsl:apply-templates/>
                        </fo:list-item-body>
                    </fo:list-item>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:when>
        <xsl:when test='../@type="enumerated"'>
            <xsl:choose>
                <xsl:when test="count(preceding-sibling::list-item) = 0">
                    <fo:list-item margin-bottom="2mm" keep-with-previous="always">
                        <fo:list-item-label end-indent="label-end()">
                            <fo:block>
                                <xsl:number format="i." value="count(preceding-sibling::list-item) + 1"/>
                            </fo:block>
                        </fo:list-item-label>
                        <fo:list-item-body start-indent="body-start()">
                            <xsl:apply-templates/>
                        </fo:list-item-body>
                    </fo:list-item>
                </xsl:when>
                <xsl:otherwise>
                    <fo:list-item margin-bottom="2mm">
                        <fo:list-item-label end-indent="label-end()">
                            <fo:block>
                                <xsl:number format="i." value="count(preceding-sibling::list-item) + 1"/>
                            </fo:block>
                        </fo:list-item-label>
                        <fo:list-item-body start-indent="body-start()">
                            <xsl:apply-templates/>
                        </fo:list-item-body>
                    </fo:list-item>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:when>
        <xsl:otherwise>
            <fo:block padding-bottom="0.0em">
                <xsl:apply-templates/>
            </fo:block>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

问题根源与解决方法

问题根源

当前XSLT处理<list>时,通过<xsl:apply-templates/>直接处理所有子节点(包括<title>),但没有为<title>编写专门的模板,会触发默认模板直接输出文本。而FO(Formatting Objects)规范要求<fo:list-block>的直接子节点只能是<fo:list-item>,非法的子元素会导致PDF渲染引擎报错。

解决方法

  1. 新增<title>模板,将其转换为合法的FO块元素:
<xsl:template match="list/title">
    <fo:block font-weight="bold" margin-bottom="1mm">
        <xsl:apply-templates/>
    </fo:block>
</xsl:template>
  1. 调整<list>模板逻辑,将<title>放在<fo:list-block>外部输出,并仅对<list-item>应用模板:
<xsl:when test='@type="bulleted" or @type="enumerated"'>
    <!-- 先输出列表标题 -->
    <xsl:if test="title">
        <xsl:apply-templates select="title"/>
    </xsl:if>
    <fo:list-block margin-bottom="2mm">
        <xsl:if test="count(ancestor::list) = 0">
            <xsl:attribute name="margin-left">
                <xsl:choose>
                    <xsl:when test="$ib-doc-content='TOM'">5mm</xsl:when>               
                <xsl:otherwise>10mm</xsl:otherwise>
                </xsl:choose>
            </xsl:attribute>
        </xsl:if>
        <xsl:if test="$ib-doc-content='TOM'">
            <xsl:attribute name="provisional-distance-between-starts">6mm</xsl:attribute>
        </xsl:if>   
        <!-- 仅处理list-item,避免title进入list-block -->
        <xsl:apply-templates select="list-item"/>
    </fo:list-block>
</xsl:when>

修改后,<title>会被转换为独立的标题块放在列表上方,<fo:list-block>内仅包含合法的<fo:list-item>元素,符合FO规范,即可正常生成PDF。

内容的提问来源于stack exchange,提问作者Ruth Cundy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 05:40:55