XSLT实现表格打印分页控制 避免指定行后出现分页符
DITA表格行打印分页控制失效修复
问题场景
使用XSLT 2.0处理DITA文档打印场景时,需要实现相邻表格行同页显示,禁止行之间出现分页符,具体要求为:内容为“P Type”的跨列表格行后方不能出现分页。
已完成的开发逻辑:
- 编写XSLT模板匹配目标row节点,为其追加
outputclass="keep-with-next"属性 - 编写打印媒体CSS规则,为携带
keep-with-next标识的元素设置page-break-after: avoid属性
实际渲染异常:目标行后仍然出现分页,规则未生效。
原有问题代码
XSLT脚本
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="2.0" exclude-result-prefixes="xs" xmlns:xs=http://www.w3.org/2001/XMLSchema xmlns:xsl=http://www.w3.org/1999/XSL/Transform> <xsl:template priority="101" match="row[.//entry[@nameend='c2']/ph]"> <xsl:call-template name="row-keep-with-next"/> </xsl:template> <xsl:template name="row-keep-with-next"> <xsl:variable name="row" as="node()"> <topic class="- topic/tbody "> <xsl:copy > <xsl:sequence select="@* except @outputclass"/> <xsl:attribute name="outputclass" select="string-join(('keep-with-next', tokenize(@outputclass, '\s+')), ' ')"/> <xsl:sequence select="node()"/> </xsl:copy> </topic> </xsl:variable> <xsl:apply-templates select="$row"/> </xsl:template> </xsl:stylesheet>
CSS样式
@media print { *[outputclass~="keep-with-next"] { page-break-after: avoid; } *[outputclass~="keep-with-previous"] { page-break-before: avoid; } *[outputclass~="top-of-page"] { page-break-before: always; } }
测试DITA样例
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <!DOCTYPE dita PUBLIC "-//OASIS//DTD DITA Composite//EN" "dtd/ditabase.dtd"> <dita> <reference xmlns:ditaarch=http://dita.oasis-open.org/architecture/2005/ id="parameters_top"> <title>Parts</title> <refbody outputclass="db.sect2"> <section> <table id="paratable" pgwide="1" outputclass="PTable"> <title outputclass="db.title">Basic Config</title> <tgroup cols="2" outputclass="ParametersTable"> <colspec colnum="1" colname="c1" colwidth="2*"/> <colspec colnum="2" colname="c2" colwidth="5*"/> <thead outputclass="db.thead"> <row outputclass="db.row"> <entry outputclass="db.entry">Label</entry> <entry outputclass="db.entry">Description</entry> </row> </thead> <tbody outputclass="db.tbody"> <row outputclass="db.row"> <entry outputclass="db.entry" namest="c1" nameend="c2" align="center"><ph outputclass="db.phrase"> P Type </ph></entry> </row> <row outputclass="db.row"> <entry outputclass="db.entry"><ph outputclass="db.phrase">Phy</ph></entry> <entry outputclass="db.entry"><p outputclass="db.para"><ph outputclass="db.phrase">Select the type of PHY.</ph></p></entry> </row> <row outputclass="db.row"> <entry outputclass="db.entry"><ph outputclass="db.phrase">Speed</ph></entry> <entry outputclass="db.entry"><p outputclass="db.para">Link speed </p></entry> </row> </tbody> </tgroup> </table> </section> </refbody> </reference> </dita>
失效原因
- XSLT逻辑错误
- 自定义模板中给复制的row节点额外包裹了一层多余的
<topic>标签,破坏了DITA表格tgroup > tbody > row > entry的固定层级结构,渲染引擎处理结构异常的表格时会直接忽略节点上的自定义属性 - 匹配规则过于宽泛,仅判断行内存在跨c2列的ph节点,未校验ph文本内容是否为“P Type”,容易误匹配其他无关行
- 命名空间声明未加引号,存在XML语法错误
- 自定义模板中给复制的row节点额外包裹了一层多余的
- CSS兼容问题
- 绝大多数DITA渲染流程会将DITA元素的
outputclass属性映射为HTML元素的class属性,不会保留原生outputclass属性,原有属性选择器无法匹配到渲染后的DOM节点 - 浏览器/打印引擎对表格行(tr)的分页属性支持存在兼容问题,仅给tr设置
page-break-after不会生效,需要同时给行内单元格设置对应规则,且要兼容W3C标准的break-after属性
- 绝大多数DITA渲染流程会将DITA元素的
- 场景适配问题
- 如果使用DITA OT默认XSL-FO流程生成PDF,CSS规则完全不生效,需要直接给FO节点设置分页属性
修复方案
修正XSLT脚本
移除多余的topic包裹逻辑,修正匹配规则精确命中目标行,修复语法错误:
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="2.0" exclude-result-prefixes="xs" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <!-- 精确匹配跨c1-c2列、ph文本为P Type的行,优先级高于默认行处理模板 --> <xsl:template priority="101" match="row[normalize-space(.//entry[@namest='c1' and @nameend='c2']/ph) = 'P Type']"> <xsl:copy> <!-- 复制原有除outputclass外的所有属性 --> <xsl:sequence select="@* except @outputclass"/> <!-- 追加keep-with-next标识,保留原有outputclass值 --> <xsl:attribute name="outputclass" select="string-join(('keep-with-next', tokenize(@outputclass, '\s+')), ' ')"/> <!-- 复制行内所有子节点,不破坏原有结构 --> <xsl:sequence select="node()"/> </xsl:copy> </xsl:template> </xsl:stylesheet>
修正CSS打印规则
适配DITA渲染后的DOM结构,兼容多引擎分页规则:
@media print { /* 同时兼容原生outputclass属性和映射后的class属性 */ *[outputclass~="keep-with-next"], tr.keep-with-next { page-break-after: avoid !important; break-after: avoid-page !important; } /* 给行内最后一个单元格追加规则,适配Webkit、Gecko等不同内核的分页逻辑 */ *[outputclass~="keep-with-next"] > entry:last-child, tr.keep-with-next > td:last-child, tr.keep-with-next > th:last-child { page-break-after: avoid !important; break-after: avoid-page !important; } *[outputclass~="keep-with-previous"], tr.keep-with-previous { page-break-before: avoid !important; break-before: avoid-page !important; } *[outputclass~="top-of-page"], tr.top-of-page { page-break-before: always !important; break-before: page !important; } }
补充:如果使用XSL-FO生成PDF,需要额外追加模板给对应
fo:table-row设置keep-with-next.within-page="always"属性,CSS规则在FO流程中不生效。
内容的提问来源于stack exchange,提问作者ane435
相关产品推荐
相关产品推荐

