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>■</xsl:text> </xsl:when> <xsl:when test="count(ancestor::list) = 2"> <xsl:text>○</xsl:text> </xsl:when> <xsl:otherwise> <xsl:text>●</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>■</xsl:text> </xsl:when> <xsl:when test="count(ancestor::list) = 2"> <xsl:text>○</xsl:text> </xsl:when> <xsl:otherwise> <xsl:text>●</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>■</xsl:text> </xsl:when> <xsl:when test="count(ancestor::list) = 2"> <xsl:text>○</xsl:text> </xsl:when> <xsl:otherwise> <xsl:text>●</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渲染引擎报错。
解决方法
- 新增
<title>模板,将其转换为合法的FO块元素:
<xsl:template match="list/title"> <fo:block font-weight="bold" margin-bottom="1mm"> <xsl:apply-templates/> </fo:block> </xsl:template>
- 调整
<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
相关产品推荐
相关产品推荐

