级别: 中级 Doug Tidwell (dtidwell@us.ibm.com), developerWorks 计算机传道士, IBM developerWorks
2003 年 6 月 01 日 将 HTML 文档转换成 PDF 时需要帮助吗?这篇参考指南将通过示例向您演示如何使用 XSLT 模板将 45 个常用 HTML 元素转换成格式化对象(Formatting Objects,FO)(出自 XSL-FO 词汇表),从而能方便地使用 XSLT 转换成 PDF。本文中的示例假定您正在使用基于 Java 的 XSLT 处理器 Xalan 和 Apache XML 项目的 FOP 工具,但本文介绍的大多数方法同样适用于其它工具。
我们设计 HTML 页面都是为了使它们在屏幕上看起来很好,但打印这些 Web 页面却往往是事后才想到的事。要创建 Web 页面的可打印版本,最好的方法是使用 XSLT 和 XSL-FO 来生成 PDF 文件。您可以利用开放源码 XSLT 处理器、XSL 格式化对象(XSL Formatting Objects,XSL-FO)词汇表和格式化对象引擎来做这项工作。如果您已经知道如何使用 XSL-FO 和 XSLT,那么本指南可为您提供有价值的参考资料:它逐一介绍最常用的 HTML 标记并定义了如何将每个标记转换成格式化对象。(如果您需要关于使用 XSL-FO 的背景知识,可以尝试学习有关这一主题的
developerWorks教程,在
参考资料中可以很容易找到这些教程。)
本指南包含若干示例,这些示例说明了如何编写将 HTML 元素转换成相应格式化对象的 XSLT 样式表,格式化对象是用 XSL-FO 来处理文档的基本构件。
关于本指南中 XSLT 模板的简要说明;几乎所有的模板都包含以下文本:
<xsl:apply-templates select="*|text()"/>
|
该元素告诉 XSLT 处理器获取当前元素的所有文本和子元素并转换它们。这一递归技术确保所有 HTML 元素都能得以处理,不管它们是如何相互嵌套的。有关 XSLT 和 XSL-FO 技术的更多信息,请参阅本指南结尾处的
参考资料。
示例的上下文
关于本参考资料的上下文,您可以打开 HTML 文档 everything.html
(下载 x-xslfo2app-samples.zip 查看 everything.html),它包含本指南中讨论的所有元素。您还可以参阅 XSLT 样式表
xhtml-to-xslfo.xsl,它包含本指南中引用的所有模板,以及本教程姊妹篇中介绍的大多数高级技术。要对 HTML 文件使用该样式表,请使用以下命令:
> java org.apache.xalan.xslt.Process -in everything.html
-xsl xhtml-to-xslfo.xsl -out everything.fo
|
顺便说一下,必须在一行上输入该命令,这里是为了适应
developerWorks 格式才将它分成两行。该命令告诉 Xalan 样式表引擎(在教程示例中使用)去读取文件
everything.html ,然后使用
xhtml-to-xslfo.xsl 样式表中的规则来转换它。转换的结果写入文件
everything.fo 。如果您想查看由该样式表创建的格式化对象,可以查看文件
everything.fo。
有了 XSL-FO 文件之后,就可以使用以下命令来创建 PDF:
> java org.apache.fop.apps.Fop everything.fo everything.pdf
|
以下是这个 PDF 文件的抓屏:
如果愿意,可以查看文件
everything.pdf。
不见了的 HTML 元素
出于种种原因(大多数是合理原因),本指南没有介绍某些 HTML 元素。不介绍它们的主要原因是它们在 PDF 文件中没有意义,这是格式化对象转换最常见的结果。略去的 HTML 元素中有些已经被 W3C 弃用,这也是不介绍它们的一个正当理由。有些元素在 PDF 上下文中可能对于您是有意义的。请告诉我您的想法;如果您有很好的理由将其它 HTML 元素加入到本指南,我将对其加以考虑(您可以使用本指南结尾处的反馈表单来提出建议)。
转换 HTML 元素指南
本指南向您演示如何将大多数 HTML 元素转换成 XSL 格式化对象。如果您正在在线查看本文,可以单击以下的任何链接,直接查看对某个特定元素的讨论。对于每个 HTML 元素,您将看到对该元素的简要描述、对应的格式化对象和将 HTML 转换成 XSL-FO 的 XSLT 模板。同其要处理的 HTML 元素一样,有些格式化对象和模板很简单,有些则非常棘手。
我必须针对手边的案例做出一些特定选择,这在采用示例代码时不可避免。这里的所有示例都假定您最终将使用格式化对象作为实现至 PDF 转换的中间产物。我所选择的几个值是随意的,但我所做的大多数选择都参照为 PDF 文件(它是整个转换操作的最终结果)定义的布局。这个布局和我已经为
developerWorks完成的两篇教程中所用的相同。当然,为了使示例适应您自己的需要,可以替换成能生成您想要外观的值;您不必遵照我们 PDF 文件的外观。
请记住,字母顺序尽管非常适合于引用,但不适合直接阅读。例如,尽管用于构建表格的大多数 HTML 标记都一起排在
T 下,但
title 元素却搅乱了它们的字母顺序。
<a name="..."> 命名锚点
本指南讨论转换三种不同锚点元素:本项讨论的命名锚点以及在这篇指南中按字母排列接下来的两项要讨论的
命名锚点引用和
锚点引用。第三项包括
一个 XSLT 模板样本,它演示了这三种锚点元素的转换。
命名锚点类似于
<a name="xyz" /> 。它通常被转换为带
id 的
<fo:block> 元素。通常情况下,会生成下面这个结果:
这看起来很简单,但可能出现问题,这取决于文档的组织。例如,在教程的这个示例中,样式要求在标题文本
前插入水平线和分页符来显示 HTML
<h1> 元素。在该位置的分页符对于如下所示的命名锚点会引起问题:
<a name="xslt"/>
<!-- A page break will be inserted here -->
<h1>Using XSLT style sheets</h1>
|
如果
<h1> 从新的一页开始,则创建命名锚点的链接将会把用户带到上一页的末尾,这不是我们所期望的。要处理这种情况,可以让处理器查看 HTML 文档中命名锚点
之后的元素。如果之后的元素是
<h1> ,则忽略该命名锚点;
<h1> 元素的 XSLT 模板会处理这一情况下的命名锚点。以下是处理命名锚点的 XSLT 逻辑(即使标题前有分页符也可以处理):
<xsl:template match="a">
<xsl:choose>
<xsl:when test="@name">
<xsl:if test="not(name(following-sibling::*[1]) = 'h1')">
<fo:block line-height="0pt" space-after="0pt"
font-size="0pt" id="{@name}"/>
</xsl:if>
</xsl:when>
|
指定
following-sibling 轴可确保样式表处理器检查紧随命名锚点之后的元素。如果该元素之后首个元素的名称
不是
h1 ,则处理器创建带
id 的
<fo:block> 。另注:
<fo:block> 元素将
line-height 、
font-size 和
space-after 属性设为零;您不会希望把任何垂直方向上的空间浪费在显示不可见的锚点上。
<a href="#..."> 命名锚点引用
要转换引用同一文档中另一目标的锚点标记,可将其转换成
<fo:basic-link> 元素。对于对同一文档的引用,可使用
internal-destination 属性。例如,假定有如下所示的锚点元素:
For more information, see <a href="#chapter1">Chapter 1</a>.
|
需要将该锚点元素转换成以下 XSL-FO 标记:
For more information, see
<fo:basic-link color="blue" internal-destination="chapter1">
Chapter 1
</fo:basic-link>.
|
如果 HTML 锚点元素有
href 属性,则查看该属性是否以井号(
# )开头。如果是的话,则可将
href 属性用作
<fo:basic-link> 的
internal-destination 。为使用该值,则必须除去井号:使用 XSLT
substring() 函数。处理内部链接要做的最后一件事是将
<fo:page-number-citation> 添加到要引用的部分。
进行这一转换的 XSLT 模板可能类似于这样:
<xsl:template match="a">
<xsl:choose>
<xsl:when test="@name">
... The previous entry covered named anchors ...
</xsl:when>
<xsl:when test="@href">
<fo:basic-link color="blue">
<xsl:choose>
<xsl:when test="starts-with(@href, '#')">
<xsl:attribute name="internal-destination">
<xsl:value-of select="substring(@href, 2)"/>
</xsl:attribute>
</xsl:when>
<xsl:otherwise>
... Handle external links here ...
</xsl:otherwise>
</xsl:choose>
<xsl:apply-templates select="*|text()"/>
</fo:basic-link>
<xsl:if test="starts-with(@href, '#')">
<xsl:text> on page </xsl:text>
<fo:page-number-citation ref-id="{substring(@href, 2)}"/>
</xsl:if>
</xsl:when>
</xsl:choose>
</xsl:template>
|
<fo:page-number-citation> 元素意味着所表示的链接类似于这样:
For more information, see Chapter 1 on page 73.
|
<a href="#..."> 锚点引用
本指南中讨论的最后一种链接是对 URI 的引用。要在本教程的示例 PDF 文件中表示这些链接,可使用
<fo:basic-link> 的
external-destination 属性。例如,假定有如下所示的锚点元素:
<a href="http://www.ibm.com/developerworks/">
IBM's developerWorks Web site
</a>
|
您可以将该元素转换成以下标记:
<fo:basic-link color="blue"
external-destination="http://www.ibm.com/developerworks/">
IBM's developerWorks Web site
</fo:basic-link>
|
以下是用于这三种锚点元素的完整的 XSLT 模板:
<xsl:template match="a">
<xsl:choose>
<xsl:when test="@name">
<xsl:if test="not(name(following-sibling::*[1]) = 'h1')">
<fo:block line-height="0" space-after="0pt"
font-size="0pt" id="{@name}"/>
</xsl:if>
</xsl:when>
<xsl:when test="@href">
<fo:basic-link color="blue">
<xsl:choose>
<xsl:when test="starts-with(@href, '#')">
<xsl:attribute name="internal-destination">
<xsl:value-of select="substring(@href, 2)"/>
</xsl:attribute>
</xsl:when>
<xsl:otherwise>
<xsl:attribute name="external-destination">
<xsl:value-of select="@href"/>
</xsl:attribute>
</xsl:otherwise>
</xsl:choose>
<xsl:apply-templates select="*|text()"/>
</fo:basic-link>
<xsl:if test="starts-with(@href, '#')">
<xsl:text> on page </xsl:text>
<fo:page-number-citation ref-id="{substring(@href, 2)}"/>
</xsl:if>
</xsl:when>
</xsl:choose>
</xsl:template>
|
<address> 地址
我们很少用到这个 HTML 元素,它可以定义地址,尽管在
<address> 元素中没有标识出通常地址中的各组成部分(电话号码、电子邮件地址、街道地址和城市等)。通常按如下方式使用
<address> 元素:
<address>
Mrs. Mary Backstayge
<br />
283 First Avenue
<br />
Skunk Haven, MA 02718
</address>
|
注:该示例在
<address> 中用
<br> 元素标明换行。以下是相对应的 XSL-FO 标记:
<fo:block>Mrs. Mary Backstayge<fo:block> </fo:block>
283 First Avenue<fo:block> </fo:block>
Skunk Haven, MA 02718</fo:block>
|
<address> 的 XSLT 模板十分简单;只是将
<address> 元素转换成
<fo:block> 元素,然后处理其中的文本和任何其它元素。以下是模板:
<xsl:template match="address">
<fo:block>
<xsl:apply-templates select="*|text()"/>
</fo:block>
</xsl:template>
|
<b> 粗体字文本
转换粗体字元素十分简单;只需将它转换为带有属性为
font-weight="bold" 的
<fo:inline> 元素。以下是示例:
<p>Jackdaws <b>love</b> my big sphinx of quartz.</p>
|
使用
<fo:block> 和
<fo:inline> 这些基本的 XSL-FO 元素来显示该内容:
<fo:block>
Jackdaws <fo:inline font-weight="bold">love</fo:inline>
my big sphinx of quartz.
</fo:block>
|
请记住下面的 XSL-FO 基本知识:
<fo:block> 元素总会导致换行,而
<fo:inline> 元素则不会。由于这个原因,所以使用
<fo:inline> 来处理
<b> 元素的内容。下面这个简单的 XSLT 模板可以完成这项工作:
<xsl:template match="b">
<fo:inline
font-weight="bold">
<xsl:apply-templates select="*|text()"/>
</fo:inline>
</xsl:template>
|
注:
<xsl:apply-templates> 元素的
select 属性选择
<b> 元素的文本以及其可能包含的任何子元素。例如,如果上面的标记是
<p>Jackdaws <b><i>love</i></b> ... ,选择
<b> 的任何子元素可以确保粗体字和斜体字元素都能得到处理。
<big> 较大的文本
极少使用的
<big> HTML 元素使得被其括起的文本比旁边的文本稍大一些。以下是示例:
<p>Jackdaws <big>love</big> my big sphinx of quartz. </p>
|
这个转换比较简单,因为在 XSL-FO 教程示例中用作 FO 处理器的 FOP 工具现在支持以百分比作为
font-size 属性的值。(情况并非总是如此。)表示
<big> 元素较合理的方式是采用 120% 的
font-size :
<xsl:template match="big">
<fo:inline
font-size="120%">
<xsl:apply-templates select="*|text()"/>
</fo:inline>
</xsl:template>
|
使用相对字体大小意味着多个相互嵌套的
<big> 元素使得通过这种转换所表示的文本逐渐变大,就象 HTML 中嵌套的
<big> 元素所产生的效果一样。例如,上面这段示例中,所生成的格式化对象类似于这样:
<fo:block>Jackdaws
<fo:inline font-size="120%">love</fo:inline> my big
sphinx of quartz. </fo:block>
|
当然,您可以修改这个示例模板以按照您的方式处理
<big> 元素;您甚至可以使字体更大,更改颜色,诸如此类。
<blockquote> 块引用
为了处理块引用,本示例将它表示为左右两边各有 1.5 厘米缩进且保持一倍行距的段。要做到这一点,可使用
<fo:block> 元素的
start-indent 和
end-indent 属性。以下是
<blockquote> 元素:
<blockquote>
When in the Course of human events, it becomes necessary for one people
to dissolve the political bands which have connected them with another,
and to assume among the powers of the earth, the separate and equal
station to which the Laws of Nature and of Nature's God entitle them,
a decent respect to the opinions of mankind requires that they should
declare the causes which impel them to the separation.
</blockquote>
|
要将这段摘录格式化为两边缩进的段,可使用以下 XSL-FO 标记:
<fo:block start-indent="1.5cm" end-indent="1.5cm">
When in the Course of human events, it becomes necessary for one people
to dissolve the political bands which have connected them with another,
...
</fo:block>
|
使用以下模板来转换
<blockquote> 元素:
<xsl:template match="blockquote">
<fo:block
start-indent="1.5cm" end-indent="1.5cm">
<xsl:apply-templates select="*|text()"/>
</fo:block>
</xsl:template>
|
当然,您可以通过更改缩进属性值来修改该引用的布局。
<body> 文档主体
在 XSL-FO 中,与
<body> 元素相对应的是
<fo:flow flow-name="xsl-region-body"> 元素。为了保持 HTML 文档和 XSLT 样式表之间的对称,这里的示例对
<body> 元素进行处理以生成对应的 XSL-FO 元素。对于该示例文档,
<fo:flow flow-name="xsl-region-body"> 元素包含以下六项内容:
- 文档标题(
<head> 中的 HTML
title 元素)
- 一条振奋人心的消息:
developerWorks loves you!
- developerWorks URL
- 目录
- 文档的全部内容
- 用于标识文档上一页的
id
显然,将包括这些项的大多数,从而使文档具有预期的布局。可以更改 XSLT 模板来创建不同的布局(例如,您可能想要一个标题页),或者您有多个样式表可以用来以多种格式表示同一信息。以下是一个完整的模板:
<xsl:template match="body">
<fo:flow flow-name="xsl-region-body">
<!-- Item 1 -->
<xsl:apply-templates select="/html/head/title"/>
<!-- Item 2 -->
<fo:block space-after="12pt" line-height="17pt"
font-size="14pt" text-align="center">
developerWorks loves you!
</fo:block>
<!-- Item 3 -->
<fo:block space-after="24pt" line-height="17pt"
font-size="14pt" text-align="center" font-weight="bold"
font-family="monospace">
ibm.com/developerWorks
</fo:block>
<!-- Item 4 -->
<xsl:call-template name="toc"/>
<!-- Item 5 -->
<xsl:apply-templates select="*|text()"/>
<!-- Item 6 -->
<fo:block id="TheVeryLastPage" font-size="0pt"
line-height="0pt" space-after="0pt"/>
</fo:flow>
</xsl:template>
|
<br> 换行符
您已经知道
<fo:block> 元素会导致换行;要处理
<br> 元素,只需在一个
<fo:block> 元素中嵌入另一个
<fo:block> 元素即可。例如,考虑以下标记:
<p>My mistress' eyes are nothing like the sun,
<br/>Coral is far more red than her lips red.
<br/>If snow be white, why then her breasts be dun,
<br/>If hairs be wires, black wires grow on her head.
...
</p>
|
将该标记转换为 XSL-FO 元素后,结果如下所示:
<fo:block>
My mistress' eyes are nothing like the sun,
<fo:block> </fo:block>
Coral is far more red than her lips red.
<fo:block> </fo:block>
If snow be white, why then her breasts be dun,
<fo:block> </fo:block>
If hairs be wires, black wires grow on her head.
</fo:block>
|
以下是转换
<br> 元素的 XSLT 模板:
<xsl:template match="br">
<fo:block> </fo:block>
</xsl:template>
|
<caption> 表的标题文本
<caption> 元素用于创建表的标题。在 XSL-FO 中,该元素是用
<fo:table-caption> 元素表示的。
尽管 FOP 目前还不支持
<fo:table-caption> 和
<fo:table-and-caption> 元素,本指南还是在这里讨论它们。顺利的话,很快就会弥补这一缺陷。
对于这个转换,有一个略微复杂的情况:在 HTML 中,
<caption> 元素几乎可以在任何位置出现;在 XSL-FO 中,它必须位于
<fo:table-and-caption> 元素中。XSL-FO 结构如下所示:
<table-and-caption>
<table-caption>
<table>
</table-and-caption>
|
以下是转换
<caption> 元素的简单 XSLT 模板:
<xsl:template match="caption">
<fo:table-caption>
<xsl:apply-templates select="*|text()"/>
</fo:table-caption>
</xsl:template>
|
使用 HTML
<caption> 元素的唯一的问题是:它可能没有出现在正在处理的 HTML
<table> 中,或者它可能不是作为
<table> 中的第一个元素出现。以下是设法解决这些问题的
<table> 元素模板:
<xsl:template match="table">
<fo:table-and-caption>
<xsl:choice>
<xsl:when test=".//caption">
<xsl:apply-templates select=".//caption[1]">
</xsl:when>
<xsl:when test="preceding-sibling::caption">
<xsl:apply-templates select="preceding-sibling::caption"/>
</xsl:when>
</xsl:choice>
<fo:table>
<xsl:apply-templates select="*[not(name()='caption')]"/>
</fo:table>
</fo:table-and-caption>
</xsl:template>
|
该模板首先检查
<table> 元素中是否存在任何
<caption> 元素。如果有的话,它会选择第一个
<caption> 元素并处理它。(这使处理器忽略任何其它
<caption> 。)如果表中没有
<caption> ,则处理器会检查表之前的首个元素。如果它是
<caption> ,则可假定它是表标题。在这里,最后的问题是:当处理实际
<table> 本身时,必须对所有
<caption> 元素
之外的每个元素使用
<xsl:apply-templates> 。
<center> 居中文本
要处理居中文本,可使用
<fo:block> 元素的
text-align="center" 属性。假如有以下标记:
<center>
Table of Contents
</center>
|
您可以将它转换成以下 XSL-FO 元素:
<fo:block text-align="center">
Table of Contents
</fo:block>
|
以下是按您所期望的执行转换的 XSLT 模板:
<xsl:template match="center">
<fo:block text-align="center">
<xsl:apply-templates select="*|text()"/>
</fo:block>
</xsl:template>
|
<cite> 引用
通常以斜体字文本显示
<cite> 元素。为了稍稍增加一点难度,可以这样编写 XSLT 模板:将
<i> 元素中包含的
<cite> 元素作为正常文本表示,以便将其与周围的斜体字文本区分开。以下是一些样本标记:
<p>
When she was little, my daughter loved it when I read
<cite>Goodnight Moon</cite> to her.
<i>But <cite>Harold and the Purple Crayon</cite>
was her favorite.</i>
</p>
|
要处理该标记,可使用 XSL-FO
<fo:inline> 元素:
<fo:block>
When she was little, my daughter loved it when I read
<fo:inline font-style="italic">Goodnight Moon</fo:inline> to her.
<fo:inline font-style="italic">But
<fo:inline font-style="normal">Harold and the Purple Crayon</fo:inline>
was her favorite.
</fo:inline>
</fo:block>
|
为了处理斜体字段落中的任何
<cite> 元素,应在转换它之前检查其
parent :
<xsl:template match="cite">
<xsl:choose>
<xsl:when test="parent::i">
<fo:inline font-style="normal">
<xsl:apply-templates select="*|text()"/>
</fo:inline>
</xsl:when>
<xsl:otherwise>
<fo:inline font-style="italic">
<xsl:apply-templates select="*|text()"/>
</fo:inline>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
|
如果
<cite> 元素的父元素是
<i> 元素,则将
font-style 更改为
normal ;否则,将其更改为
italic 。当
<cite> 和
<i> 元素相互嵌套时,这一技术可以正确地处理它们的组合。
<code> 代码样本
显示
<code> 元素需要使用等宽字体。如您所料,可以使用
<fo:inline> 元素。下面是一些样本标记:
<p>If you're a Java programmer, an easy way to break a string apart
is with the <code>java.util.StringTokenizer</code> class.</p>
|
要正确地显示这段文本,可使用带
font-family="monospace" 属性的
<fo:inline> 元素:
<fo:block>If you're a Java programmer, an easy way to
break a string apart is with the
<fo:inline font-family="monospace">java.util.StringTokenizer</fo:inline>
class.
</fo:block>
|
以下是转换
<code> 元素的 XSLT 模板:
<xsl:template match="code">
<fo:inline
font-family="monospace">
<xsl:apply-templates select="*|text()"/>
</fo:inline>
</xsl:template>
|
<dl>、<dt> 和 <dd> 定义列表
尽管定义列表不及有序列表(
<ol> )和无序列表(
<ul> )那么常用,但它们在定义术语或选项列表时非常有用。以下是一个定义列表,它定义了某个参数的一些选项:
<p>There are four valid values for the
<code>text-align</code> parameter:</p>
<dl>
<dt>start</dt>
<dd>
The text is aligned at the start of the paragraph, normally
the left side.
</dd>
<dt>middle</dt>
<dd>The text is aligned at the middle of the paragraph.</dd>
<dt>end</dt>
<dd>
The text is aligned at the end of the paragraph, normally
the right side.
</dd>
<dt>justify</dt>
<dd>The text is aligned at both the start and end of
the paragraph.</dd>
</dl>
|
定义列表的典型格式是:在一行上以粗体显示术语(
<dt> 元素),在接下来的一行上以缩进格式在术语下显示定义。以下是
<dl> 、
<dt> 和
<dd> 的模板:
<xsl:template match="dl">
<xsl:apply-templates select="*"/>
</xsl:template>
<xsl:template match="dt">
<fo:block
font-weight="bold" space-after="2pt"
keep-with-next="always">
<xsl:apply-templates select="*|text()"/>
</fo:block>
</xsl:template>
<xsl:template match="dd">
<fo:block
start-indent="1cm">
<xsl:attribute name="space-after">
<xsl:choose>
<xsl:when test="name(following::*[1]) = 'dd'">
<xsl:text>3pt</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>12pt</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:apply-templates select="*|text()"/>
</fo:block>
</xsl:template>
|
在
<dd> 元素的模板中,处理器检查下一个元素是否也是
<dd> 元素。如果是的话,则当前
<dt> 元素对同一术语一定有多个定义,因此处理器在当前定义之后留出距离为 3 点的垂直空间。否则的话,处理器会留出距离为 12 点的垂直空间。另请注意:即使 FOP 并不总是能够正确地处理它,该模板仍使用了
<fo:block> 元素的
keep-with-next 属性。
<em> 强调文本
大多数浏览器以斜体字表示强调文本,因此只需将
<em> 元素转换为带 <font-style="italic"> 属性的
<fo:inline> 元素即可。我们从以下标记着手:
<p>You <em>must<> disconnect the power supply before
you open the product housing. </p>
|
在 FO 中,您可以如下处理它:
<fo:block>
You <fo:inline font-style="italic">must</fo:inline> disconnect
the power supply before you open the product housing.
</fo:block>
|
以下是 XSLT 模板:
<xsl:template match="em">
<fo:inline
font-style="italic">
<xsl:apply-templates select="*|text()"/>
</fo:inline>
</xsl:template>
|
<font color="..."> 更改文本颜色
我将使用
<font> 元素的三个属性来演示如何向 XSL-FO 元素进行转换:
color 、
face 和
size 。对于
color 属性,可以使用十六进制 RGB 值(如
x33cc99 )或由 XSL-FO 规范定义的 16 种颜色名称之一:
aqua
|
black
|
blue
|
fuchsia
|
gray
|
green
|
lime
|
maroon
|
navy
|
olive
|
purple
|
red
|
silver
|
teal
|
white
|
yellow
|
如果存在某种颜色,XSLT 模板便假定 FOP 可以处理该颜色值。缺省颜色是
black :
<xsl:template match="font">
<xsl:variable name="color">
<xsl:choose>
<xsl:when test="@color">
<xsl:value-of select="@color"/>
</xsl:when>
<xsl:otherwise>
<xsl:text>black</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
...
<fo:inline font-size="{$size}" font-family="{$face}"
color="{$color}">
<xsl:apply-templates select="*|text()"/>
</fo:inline>
</xsl:template>
|
注意:该 XSLT 模板创建一个变量以保存
color 属性的值。该模板创建
color 、
face 和
size 的变量,然后用这些值构建
<fo:inline> 元素。
<font face="..."> 更改文本字体
<font> 元素的
face 属性映射至 XSL-FO
font-family 属性,但有一个问题:FOP 工具只支持数量有限的字体。
font-family 的有效值为:
-
serif
-
sans-serif
-
monospace
-
Courier 、
Courier-Bold 、
Courier-BoldOblique 或
Courier-Oblique
-
Helvetica 、
Helvetica-Bold 、
Helvetica-BoldOblique 或
Helvetica-Oblique
-
Symbol
-
Times-Roman 、
Times-Bold 、
Times-BoldItalic 或
Times-Italic
如同处理颜色一样,XSLT 模板创建一个字体变量。它将缺省字体设置为
sans-serif :
<xsl:template match="font">
<xsl:variable name="face">
<xsl:choose>
<xsl:when test="@face">
<xsl:value-of select="@face"/>
</xsl:when>
<xsl:otherwise>
<xsl:text>sans-serif</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
...
<fo:inline font-size="{$size}" font-family="{$face}"
color="{$color}">
<xsl:apply-templates select="*|text()"/>
</fo:inline>
</xsl:template>
|
另外,FOP 工具提供了一些方法将 Adobe Type 1 字体和 Truetype 字体转换为 XML font-metric 文件。FOP 用这些文件并以与上面所列字体不同的字体来显示文本。请参阅 FOP 文档以了解详细信息。
<font size="..."> 更改文本大小
<font> 元素的
size 属性映射至 XSL-FO
font-size 属性。下面是用于处理 HTML
size 属性的逻辑:
- 如果
size 属性包含字符串
pt (例如
size="24pt" ),则按原样使用值。
- 如果
size 属性以一个加号或减号开头(如
size="+2" 或
size="-1" ),则使用字体的相对大小;例如,将
+1 映射为
110% 的相对字体大小。(我随便选了这些值,您可以自由地更改它们。)
- 如果
size 属性是 1 到 7 之间的数字,则将字体设置为任意大小。(同样,如果愿意,可以更改这些属性值。)
- 作为最后手段,将字体设置为
12pt 。
以下是
<font> 元素完整的 XSLT 模板。在结尾处,它使用在前面初始化的变量来创建
<fo:inline> 元素:
<xsl:template match="font">
<xsl:variable name="color">
<xsl:choose>
<xsl:when test="@color">
<xsl:value-of select="@color"/>
</xsl:when>
<xsl:otherwise>
<xsl:text>black</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="face">
<xsl:choose>
<xsl:when test="@face">
<xsl:value-of select="@face"/>
</xsl:when>
<xsl:otherwise>
<xsl:text>sans-serif</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="size">
<xsl:choose>
<xsl:when test="@size">
<xsl:choose>
<xsl:when test="contains(@size, 'pt')">
<xsl:text>@size</xsl:text>
</xsl:when>
<xsl:when test="@size = '+1'">
<xsl:text>110%</xsl:text>
</xsl:when>
<xsl:when test="@size = '+2'">
<xsl:text>120%</xsl:text>
</xsl:when>
<xsl:when test="@size = '+3'">
<xsl:text>130%</xsl:text>
</xsl:when>
<xsl:when test="@size = '+4'">
<xsl:text>140%</xsl:text>
</xsl:when>
<xsl:when test="@size = '+5'">
<xsl:text>150%</xsl:text>
</xsl:when>
<xsl:when test="@size = '+6'">
<xsl:text>175%</xsl:text>
</xsl:when>
<xsl:when test="@size = '+7'">
<xsl:text>200%</xsl:text>
</xsl:when>
<xsl:when test="@size = '-1'">
<xsl:text>90%</xsl:text>
</xsl:when>
<xsl:when test="@size = '-2'">
<xsl:text>80%</xsl:text>
</xsl:when>
<xsl:when test="@size = '-3'">
<xsl:text>70%</xsl:text>
</xsl:when>
<xsl:when test="@size = '-4'">
<xsl:text>60%</xsl:text>
</xsl:when>
<xsl:when test="@size = '-5'">
<xsl:text>50%</xsl:text>
</xsl:when>
<xsl:when test="@size = '-6'">
<xsl:text>40%</xsl:text>
</xsl:when>
<xsl:when test="@size = '-7'">
<xsl:text>30%</xsl:text>
</xsl:when>
<xsl:when test="@size = '1'">
<xsl:text>8pt</xsl:text>
</xsl:when>
<xsl:when test="@size = '2'">
<xsl:text>10pt</xsl:text>
</xsl:when>
<xsl:when test="@size = '3'">
<xsl:text>12pt</xsl:text>
</xsl:when>
<xsl:when test="@size = '4'">
<xsl:text>14pt</xsl:text>
</xsl:when>
<xsl:when test="@size = '5'">
<xsl:text>18pt</xsl:text>
</xsl:when>
<xsl:when test="@size = '6'">
<xsl:text>24pt</xsl:text>
</xsl:when>
<xsl:when test="@size = '7'">
<xsl:text>36pt</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>12pt</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:text>12pt</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<fo:inline font-size="{$size}" font-family="{$face}"
color="{$color}">
<xsl:apply-templates select="*|text()"/>
</fo:inline>
</xsl:template>
|

 |

|
<h1> 至 <h6> 标题
转换标题标记相对较简单;将每种标题放入
<fo:block> 元素,然后根据标题级别更改字体、字体大小和其它属性。为了使顶级标题真正突出,该示例布局在
<h1> 文本之前放置了一个分页符和一条水平线。以下是所用的格式选项:
|
HTML 标记
|
字体大小
|
行高
|
标题后的空间
|
其它
|
<h1>
| 28pt | 32pt | 22pt | 在文本之前添加分页符和水平线 |
<h2>
| 24pt | 28pt | 18pt | 无 |
<h3>
| 21pt | 24pt | 14pt | 无 |
<h4>
| 18pt | 21pt | 12pt | 无 |
<h5>
| 16pt | 19pt | 12pt | 文本 加下划线 |
<h6>
| 14pt | 17pt | 12pt | 文本 加下划线 并且是 斜体字 |
以下是一些标题元素:
<h1>Sample text from Henry Fielding's <cite>Tom Jones</cite></h1>
<h2><b>Book I.</b> Containing as Much of the Birth of the Foundling
as Is Necessary or Proper to Acquaint the Reader with in the
Beginning of This History</h2>
<h3><b>Chapter VII.</b> Containing Such Grave Matter, That the Reader
Cannot Laugh Once Through the Whole Chapter, Unless Peradventure He
Should Laugh at the Author</h3>
|
这些元素被转换为以下格式化对象:
<fo:block break-before="page">
<fo:leader leader-pattern="rule"/>
</fo:block>
<fo:block font-family="serif" space-after="22pt" keep-with-next="always"
line-height="32pt" font-size="28pt" id="tomjones">
Sample text from Henry Fielding's
<fo:inline font-style="italic">Tom Jones</fo:inline>
</fo:block>
<fo:block font-family="serif" space-after="18pt" keep-with-next="always"
line-height="28pt" font-size="24pt" id="N10017">
<fo:inline font-weight="bold">Book I.</fo:inline>
Containing as Much of the Birth of the Foundling
as Is Necessary or Proper to Acquaint the Reader with in the
Beginning of This History
</fo:block>
<fo:block font-family="serif" space-after="14pt" keep-with-next="always"
line-height="24pt" font-size="21pt" id="N1001C">
<fo:inline font-weight="bold">Chapter VII.</fo:inline>
Containing Such Grave Matter, That the Reader
Cannot Laugh Once Through the Whole Chapter, Unless Peradventure He
Should Laugh at the Author
</fo:block>
|
在
<h1> 元素的文本前插入的分页符使得共同使用命名锚点和
<h1> 元素变得复杂。出于这个原因,
<h1> 元素的 XSLT 模板检查前一个元素,看它是否为命名锚点。以下是
<h1> 和
<h6> 元素的模板:
<xsl:template match="h1">
<fo:block break-before="page">
<fo:leader leader-pattern="rule"/>
</fo:block>
<fo:block font-size="28pt" line-height="32pt"
keep-with-next="always"
space-after="22pt" font-family="serif">
<xsl:attribute name="id">
<xsl:choose>
<xsl:when test="@id">
<xsl:value-of select="@id"/>
</xsl:when>
<xsl:when test="name(preceding-sibling::*[1]) = 'a' and
preceding-sibling::*[1][@name]">
<xsl:value-of select="preceding-sibling::*[1]/@name"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="generate-id()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:apply-templates select="*|text()"/>
</fo:block>
</xsl:template>
<xsl:template match="h6">
<fo:block font-size="14pt" line-height="17pt"
keep-with-next="always" space-after="12pt"
font-family="serif"
font-style="italic"
text-decoration="underline">
<xsl:attribute name="id">
<xsl:choose>
<xsl:when test="@id">
<xsl:value-of select="@id"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="generate-id()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:apply-templates select="*|text()"/>
</fo:block>
</xsl:template>
|
最后提一点:因为标题可用于书签和目录,而且它还是有用的链接点,所以最好确保每个标题都有一个
id 。如果给定的标题元素已经有
id 属性,那么就使用它;如果没有,则使用 XSLT
generate-id() 函数创建一个:
<xsl:attribute name="id">
<xsl:choose>
<xsl:when test="@id">
<xsl:value-of select="@id"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="generate-id()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
|
<hr> 水平线
<fo:leader> 是一个特殊的 XSL-FO 元素,它被设计成用来处理水平线。以下是一些 HTML 标记:
<p>Here's a short paragraph.</p>
<hr/>
<p>Here's another paragraph, following a horizontal rule.</p>
|
表示该内容的 XSL-FO 标记类似于这样:
<fo:block>
Here's a short paragraph.
</fo:block>
<fo:block>
<fo:leader leader-pattern="rule"/>
</fo:block>
<fo:block>
Here's another paragraph, following a horizontal rule.
</fo:block>
|
处理
<hr> 元素的 XSLT 模板非常简单:
<xsl:template match="hr">
<fo:block>
<fo:leader leader-pattern="rule"/>
</fo:block>
</xsl:template>
|
最后提一点:
leader-pattern 属性还支持
dots 和
space 值,前者通常用于目录,后者创建一个空白区域。
<i> 斜体字文本
在 XSL-FO 中很容易表示 HTML
<i> 元素:只需将该元素的文本放入带
font-style="italic" 属性的
<fo:inline> 中即可。下面是一个示例,它与前面演示的
<b> 元素的示例非常相似:
<p>Jackdaws <i>love</i> my big sphinx of quartz.</p>
|
使用基本的 XSL-FO 元素
<fo:block> 和
<fo:inline> 来处理以上内容:
<fo:block>
Jackdaws <fo:inline font-style="italic">love</fo:inline>
my big sphinx of quartz.
</fo:block>
|
<fo:block> 元素总是会引起换行,而
<fo:inline> 元素则不会。由于这个原因,所以使用
<fo:inline> 来处理
<i> 元素的内容。下面这个简单的 XSLT 模板可完成这项工作:
<xsl:template match="i">
<fo:inline
font-style="italic">
<xsl:apply-templates select="*|text()"/>
</fo:inline>
</xsl:template>
|
<img> 嵌入的图像
<img> 直接映射至 XSL-FO
<fo:external-graphic> 元素。将
<img> 转换为
<fo:external-graphic> 之所以复杂是由于 FOP 工具不支持象
width="200" 这样的 HTML 属性;
width 和
height 属性必须包括长度单位(例如
width="200px" )。出于这个原因,可这样设置 XSLT 模板:使处理器在生成
<fo:external-graphic> 元素时检查 HTML 值。以下是模板:
<xsl:template match="img">
<fo:block space-after="12pt">
<fo:external-graphic src="{@src}">
<xsl:if test="@width">
<xsl:attribute name="width">
<xsl:choose>
<xsl:when test="contains(@width, 'px')">
<xsl:value-of select="@width"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat(@width, 'px')"/>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
</xsl:if>
<xsl:if test="@height">
<xsl:attribute name="height">
<xsl:choose>
<xsl:when test="contains(@height, 'px')">
<xsl:value-of select="@height"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat(@height, 'px')"/>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
</xsl:if>
</fo:external-graphic>
</fo:block>
</xsl:template>
|
注意:模板按原样使用
src 属性,而处理器根据需要将长度单位添加到
width 和
height 属性。
<kbd> 键盘输入
通常以稍大的等宽字体来表示键盘输入(在 HTML 中,用
<kbd> 元素表示键盘输入)。下面是 HTML 样本:
<p>
An easy way to delete a directory and all its contents (including
all subdirectories) is with the <kbd>rd /s</kbd> command.
</p>
|
可以用如下的 XSL-FO 元素来表示该样本:
<fo:block>
An easy way to delete a directory and all its contents (including
all subdirectories) is with the
<fo:inline font-family="monospace" font-size="110%">rd /s</fo:inline>
command.
</fo:block>
|
进行这一转换的 XSLT 模板比较简单:
<xsl:template match="kbd">
<fo:inline
font-family="monospace" font-size="110%">
<xsl:apply-templates select="*|text()"/>
</fo:inline>
</xsl:template>
|
<li> 列表项
处理列表项
几乎和处理段落一样,只是有几处细微的差别。因为这些差别取决于列表项的父元素(
<li> 元素在
<ol> 元素中还是在
<ul> 元素中?),所以请在
<ol> 元素
和
<ul> 元素
项下查看有关列表项的详细信息。
<nobr> 无换行的文本
HTML
<nobr> 元素指出:无论
<nobr> 元素中包含多少内容,浏览器都不应换行。例如,考虑以下 HTML 标记:
<p>
On the Windows 2000 command line, you can use the ampersand (&) to
combine several commands into one statement.
<nobr>
pushd d:\\projects\\xslfo & del *.fo & rebuild.bat & popd
</nobr>
is an example of this technique.
</p>
|
要用格式化对象表示这一内容,可使用带
wrap-option="no-wrap" 属性的
<fo:inline> 元素。
<fo:block>
On the Windows 2000 command line, you can use the ampersand (&)
to combine several commands into one statement.
<fo:inline wrap-option="no-wrap">pushd d:\\projects\\xslfo &
del *.fo & rebuild.bat & popd </fo:inline> is an
example of this technique.
</fo:block>
|
进行这一转换的 XSLT 模板比较简单:
<xsl:template match="nobr">
<fo:inline
wrap-option="no-wrap">
<xsl:apply-templates select="*|text()"/>
</fo:inline>
</xsl:template>
|
<ol> 有序列表
XSL-FO 词汇所具有的特征是:有专门用于列表(包括有序列表)的元素。以下是样本 HTML 列表:
<p>A few of my favorite albums</p>
<ol>
<li>A Love Supreme</li>
<li>Remain in Light</li>
<li>London Calling</li>
<li>The Indestructible Beat of Soweto</li>
<li>The Joshua Tree</li>
</ol>
|
要用 XSL-FO 表示这些列表项,必须使用这几个元素:
<fo:list-block> 、
<fo:list-item> 、
<fo:list-item-label> 和
<fo:list-item-body> 。下面介绍如何使用它们:
-
<fo:list-block> 元素包含整个列表。
- 每个
<fo:list-item> 元素包含单个列表项;该元素包含
<fo:list-item-label> 和
<list-item-body> 元素。
-
<fo:list-item-label> 是内容旁边的标号。例如,对于有序列表,
<fo:list-item-label> 元素可能包含文本
10. 。
-
<fo:list-item-body> 包含列表项的实际内容。
下面是 HTML 标记转换为格式化对象后的情形:
<fo:block>A few of my favorite albums</fo:block>
<fo:list-block provisional-distance-between-starts="0.75cm"
provisional-label-separation="0.5cm" space-after="12pt"
start-indent="1cm">
<fo:list-item>
<fo:list-item-label end-indent="label-end()">
<fo:block>1. </fo:block>
</fo:list-item-label>
<fo:list-item-body start-indent="body-start()">
<fo:block>
A Love Supreme
</fo:block>
</fo:list-item-body>
</fo:list-item>
...
<!-- other list items appear here -->
...
</fo:list-block>
|
以下显示了进行转换的 XSLT 模板。这些模板必须处理以下几种情况:
- 如果该列表出现在另一列表中,则不在该列表后插入任何空白。否则,在该列表后插入距离为 12 点的垂直空间。
- 如果该列表不在任何其它列表中,则将其缩进
1 cm 。否则,它缩进的距离为
1 cm 加上其它每个列表缩进的
1.25 cm 。例如,如果该列表是第三级列表,则将该列表缩进
3.5 cm 。
- 如果
<ol> 元素有
start 属性,则以这个数字开始对列表项编号。如果没有该属性,那么只需用 XSLT
position() 函数对列表项编号。
- 如果
<ol> 元素有
type 属性,则用这个值来决定应该如何格式化每个列表项的数字(罗马数字、阿拉伯数字或按字母次序的字符)。该模板用
<xsl:number> 元素的
format 属性做到这一点。
以下就是转换所有
<ol> 和
<li> 项的 XSLT 模板(使用带项目符号的列表中的规则):
<xsl:template match="ol">
<fo:list-block provisional-distance-between-starts="1cm"
provisional-label-separation="0.5cm">
<xsl:attribute name="space-after">
<xsl:choose>
<xsl:when test="ancestor::ul or ancestor::ol">
<xsl:text>0pt</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>12pt</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:attribute name="start-indent">
<xsl:variable name="ancestors">
<xsl:choose>
<xsl:when test="count(ancestor::ol) or count(ancestor::ul)">
<xsl:value-of select="1 +
(count(ancestor::ol) +
count(ancestor::ul)) *
1.25"/>
</xsl:when>
<xsl:otherwise>
<xsl:text>1</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:value-of select="concat($ancestors, 'cm')"/>
</xsl:attribute>
<xsl:apply-templates select="*"/>
</fo:list-block>
</xsl:template>
<xsl:template match="ol/li">
<fo:list-item>
<fo:list-item-label end-indent="label-end()">
<fo:block>
<xsl:variable name="value-attr">
<xsl:choose>
<xsl:when test="../@start">
<xsl:number value="position() + ../@start - 1"/>
</xsl:when>
<xsl:otherwise>
<xsl:number value="position()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:choose>
<xsl:when test="../@type='i'">
<xsl:number value="$value-attr" format="i. "/>
</xsl:when>
<xsl:when test="../@type='I'">
<xsl:number value="$value-attr" format="I. "/>
</xsl:when>
<xsl:when test="../@type='a'">
<xsl:number value="$value-attr" format="a. "/>
</xsl:when>
<xsl:when test="../@type='A'">
<xsl:number value="$value-attr" format="A. "/>
</xsl:when>
<xsl:otherwise>
<xsl:number value="$value-attr" format="1. "/>
</xsl:otherwise>
</xsl:choose>
</fo:block>
</fo:list-item-label>
<fo:list-item-body start-indent="body-start()">
<fo:block>
<xsl:apply-templates select="*|text()"/>
</fo:block>
</fo:list-item-body>
</fo:list-item>
</xsl:template>
|
这两个模板将
<ol> 和
<li> 元素转换成相应的 XSL-FO 元素。
<p> 段落
将 HTML 段落元素转换成
<fo:block> 元素比较简单。例如,考虑下面这个段落:
<p>When in the Course of human events, it becomes necessary
for one people to dissolve the political bonds which have connected
them with another, and to assume among the powers of the earth,
the separate and equal station to which the Laws of Nature and
of Nature's God entitle them, a decent respect to the opinions
of mankind requires that they should declare the causes which
impel them to the separation.</p>
|
该段落将转换成以下 XSL-FO 标记:
<fo:block font-size="12pt" line-height="15pt
space-after="12pt">When in the Course of human events, it becomes
necessary for one people to dissolve the political bonds which
...
</fo:block>
|
下面是进行该转换的简单的 XSLT 模板:
<xsl:template match="p">
<fo:block font-size="12pt" line-height="15pt"
space-after="12pt">
<xsl:apply-templates select="*|text()"/>
</fo:block>
</xsl:template>
|
我们用
font-size 、
line-height 和
space-after 属性的缺省值实现该模板。显然,如果愿意的话,您可以更改这些值。
<pre> 预先格式化的文本
<pre> 元素有几处复杂之处。需要保留它包含的所有空白域,并且需要关闭 XSL-FO 引擎执行的自动换行。依照约定,还要用等宽字体显示
<pre> 元素的内容。下面这段摘录是示例
<pre> 元素:
<pre>
public static void main(String args[])
{
System.out.println("Hello, world!");
}
</pre>
|
要正确处理这个
<pre> 元素示例,必须将其转换成以下 XSL-FO 标记:
<fo:block font-family="monospace"
white-space-collapse="false"
wrap-option="no-wrap">
public static void main(String args[])
{
System.out.println("Hello, world!");
}
</fo:block>
|
进行这一转换的 XSLT 模板比较简单:
<xsl:template match="pre">
<fo:block
font-family="monospace"
white-space-collapse="false"
wrap-option="no-wrap">
<xsl:apply-templates select="*|text()"/>
</fo:block>
</xsl:template>
|
<samp> 样本文本
通常用稍大的等宽字体表示
<samp> 元素。尽管很少使用
<samp> ,但将它转换成格式化对象很容易。以下是一个样本
<samp> 元素:
<p>The <samp>DOCTYPE</samp> keyword lets you
refer to a DTD from your XML source document.</p>
|
将其转换成格式化对象的 XSLT 模板简短明了:
<xsl:template match="samp">
<fo:inline
font-family="monospace"
font-size="110%">
<xsl:apply-templates select="*|text()"/>
</fo:inline>
</xsl:template>
|
<small> 字体较小的文本
转换
<small> 元素也很容易。如您所料,它是用较小的字体表示的。因为
<big> 元素的模板用比正常字体大 20% 的
font-size 表示该元素,所以有理由将
<small> 定义为比正常字体小 20%。以下是简单的 XSLT 模板:
<xsl:template match="small">
<fo:inline
font-size="80%">
<xsl:apply-templates select="*|text()"/>
</fo:inline>
</xsl:template>
|
假如有
<p>The Lakers' chances for a fourth straight title are <small>slim</small>.</p> 这样一个段落,则模板生成这样的标记:
<fo:block font-size="12pt" line-height="15pt" space-after="12pt">
The Lakers' chances for a fourth straight title are
<fo:inline font-size="80%">slim</fo:inline>.
</fo:block>
|
和
<big> 元素一样,在内部相互嵌套多个
<small> 元素会创建逐渐变小的文本。
<strike> 加删除线的文本
实现 HTML
<strike> 元素比较简单;创建带
text-decoration="line-through" 属性的
<fo:inline> 元素。加删除线的文本可用于突出显示文档中的删除部分。以下是示例:
<p>The underline property
<strike>is not currently supported by FOP.</strike>
is now supported by FOP.</p>
|
在显示时,该段落清楚地表明文本是如何变化的。
<strike> 的 XSLT 模板比较简短:
<xsl:template match="strike">
<fo:inline
text-decoration="line-through">
<xsl:apply-templates select="*|text()"/>
</fo:inline>
</xsl:template>
|
注:
text-decoration 属性的关键字可以是负值也可以是正值。如果出于某种原因而希望在启用
line-through 的一长段文本中关闭其中一小段具有
line-through 的文本,可以创建一个带
text-decoration="no-line-through" 属性的
<fo:inline> 元素。也可以指定多个值;属性
text-decoration="line-through underline" 同时对文本加删除线和下划线。
<strong> 突出显示的文本
通常以粗体字显示
<strong> 元素。以下是进行该转换的简单模板:
<xsl:template match="strong">
<fo:inline
font-weight="bold">
<xsl:apply-templates select="*|text()"/>
</fo:inline>
</xsl:template>
|
<sub> 下标文本
要处理下标文本,可使用 XSL-FO
vertical-align 属性更改文本的基线。通常还需要缩小字体。以下是一个 HTML 样本:
<p>When I'm thirsty, nothing beats a cold
glass of H<sub>2</sub>O.</p>
|
以下是我们的 XSLT 模板:
<xsl:template match="sub">
<fo:inline
vertical-align="sub"
font-size="75%">
<xsl:apply-templates select="*|text()"/>
</fo:inline>
</xsl:template>
|
<sup> 上标文本
正如下标文本一样,可使用 XSL-FO
vertical-align 属性来处理上标文本。以下是一个 HTML 样本:
<p>Einstein's famous e=mc<sup>2</sup>
is an equation that changed the world. </p>
|
以下是我们的 XSLT 模板:
<xsl:template match="sup">
<fo:inline
vertical-align="super"
font-size="75%">
<xsl:apply-templates select="*|text()"/>
</fo:inline>
</xsl:template>
|
<table> 表标记
要处理 HTML
<table> 元素,最大的难题是确定 XSL-FO 表应该有多少列,以及这些列的宽度。FOP 要求为表中的每一列提供一个
<fo:table-column> 元素。一旦处理完这些列,就只需调用表中所有元素的 XSLT 模板。这一 XSLT 模板处理类似于这样的
cols :
<table cols="200 100pt" border="1">
|
需要将
cols 属性转换成以下标记:
<fo:table-column column-width="200pt"/>
<fo:table-column column-width="100pt"/>
|
要实现这一转换,需要使用常用的被称为
尾递归(tail recursion)的 XSLT 技术。创建这样一个指定的模板:它使用属性值的首个单词,将其转换成
<fo:table-column> 元素,然后调用自身以处理属性值的余下部分。最终,处理整个属性。下面来看这个 XSLT 模板是如何处理
<table> 元素的:
<xsl:template match="table">
<fo:table
table-layout="fixed">
<xsl:choose>
<xsl:when test="@cols">
<xsl:call-template name="build-columns">
<xsl:with-param name="cols"
select="concat(@cols, ' ')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<fo:table-column column-width="200pt"/>
</xsl:otherwise>
</xsl:choose>
<fo:table-body>
<xsl:apply-templates select="*"/>
</fo:table-body>
</fo:table>
</xsl:template>
|
如果有
cols 属性,则处理器调用
build-columns 模板;否则它创建一个
<fo:table-column> 元素。另请注意:
<fo:table> 元素有属性
table-layout="fixed" ;目前,如果没有该属性,FOP 会发出一条警告消息。
让我们研究一下
build-columns 模板:
<xsl:template
name="build-columns">
<xsl:param name="cols"/>
<xsl:if test="string-length(normalize-space($cols))">
<xsl:variable name="next-col">
<xsl:value-of select="substring-before($cols, ' ')"/>
</xsl:variable>
<xsl:variable name="remaining-cols">
<xsl:value-of select="substring-after($cols, ' ')"/>
</xsl:variable>
<xsl:choose>
<xsl:when test="contains($next-col, 'pt')">
<fo:table-column column-width="{$next-col}"/>
</xsl:when>
<xsl:when test="number($next-col) > 0">
<fo:table-column column-width="{concat($next-col, 'pt')}"/>
</xsl:when>
<xsl:otherwise>
<fo:table-column column-width="50pt"/>
</xsl:otherwise>
</xsl:choose>
<xsl:call-template name="build-columns">
<xsl:with-param name="cols" select="concat($remaining-cols, ' ')"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
|
首先,模板在除去所有首尾处的空白(这是 XSLT
normalize-space() 函数所做的工作)之后检查
$cols 参数的长度。接下来,它将
$cols 值分为两部分:第一个空格
之前的子串,和
之后的所有内容。(注:当处理器从
<table> 元素的模板调用该模板时,它将一个空格添加到值的末尾,因此始终至少有一个空格。)
既然
$cols 参数被分为两部分,则首先处理第一部分:
- 如果值包含字符串
pt ,则假定它是一个类似
200pt 的值,并且生成带该值的
<fo:table-column> 元素。
- 如果值是数字,则将字符串
pt 添加到它的末尾,然后生成
<fo:table-column> 元素。使用 XSLT
number() 函数将该值转换成数字。如果值不是数字(譬如,假设它是
number(xyz) ),则函数返回字符串
NaN (表示不是数字)。
- 如果以上两条规则都不适用,则仅创建
column-width 为
50pt 的新
<fo:table-column> 元素。
在处理器处理完
$cols 参数的第一部分之后,这个指定的模板用
$cols 参数的最后部分调用自己,并在值的末尾添加一个空格以确保存在空格。
<td> 表单元
HTML
<td> 元素直接映射至 XSL-FO
<fo:table-cell> 元素。缺省情况下,模板要求表单元的顶部、底部以及左右两边各留有距离为 3 点的空间。有三种 HTML 属性需要处理:
colspan 、
rowspan 和
align 。您还要以有限的方式处理
border 属性。
colspan 和
rowspan 属性直接映射成 XSL-FO
number-columns-spanned 和
number-rows-spanned 属性,因此处理它们并不困难。
HTML
align 属性映射成 XSL-FO
text-align 属性,但有两组不同的值。HTML
align 的
left 、
center 、
right 和
justify 值映射成 XSL-FO
text-align 的
start 、
center 、
end 和
justify 。这里最后的复杂之处在于可能在 HTML
<td> 、
<tr> 、
<thead> 或
<table> 元素上出现对齐值,因此必须检查所有的祖先元素直到找到该值为止。(注:在
<fo:block> 元素上设置
text-align 属性,而不是在
<fo:table-cell> 上。)
最后,如果
<td> 、
<tr> 、
<thead> 或
<table> 元素有
border="1" 属性,则用
border-style="solid" border-color="black" border-width="1pt" 这三个 XSL-FO 属性在表单元的四周绘制边框。
以下是完整的 XSLT 模板,其中的大多数由确定使用哪种 XSL-FO 属性的
<xsl:choose> 元素所组成:
<xsl:template match="td">
<fo:table-cell
padding-start="3pt" padding-end="3pt"
padding-before="3pt" padding-after="3pt">
<xsl:if test="@colspan">
<xsl:attribute name="number-columns-spanned">
<xsl:value-of select="@colspan"/>
</xsl:attribute>
</xsl:if>
<xsl:if test="@rowspan">
<xsl:attribute name="number-rows-spanned">
<xsl:value-of select="@rowspan"/>
</xsl:attribute>
</xsl:if>
<xsl:if test="@border='1' or
ancestor::tr[@border='1'] or
ancestor::thead[@border='1'] or
ancestor::table[@border='1']">
<xsl:attribute name="border-style">
<xsl:text>solid</xsl:text>
</xsl:attribute>
<xsl:attribute name="border-color">
<xsl:text>black</xsl:text>
</xsl:attribute>
<xsl:attribute name="border-width">
<xsl:text>1pt</xsl:text>
</xsl:attribute>
</xsl:if>
<xsl:variable name="align">
<xsl:choose>
<xsl:when test="@align">
<xsl:choose>
<xsl:when test="@align='center'">
<xsl:text>center</xsl:text>
</xsl:when>
<xsl:when test="@align='right'">
<xsl:text>end</xsl:text>
</xsl:when>
<xsl:when test="@align='justify'">
<xsl:text>justify</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>start</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:when test="ancestor::tr[@align]">
<xsl:choose>
<xsl:when test="ancestor::tr/@align='center'">
<xsl:text>center</xsl:text>
</xsl:when>
<xsl:when test="ancestor::tr/@align='right'">
<xsl:text>end</xsl:text>
</xsl:when>
<xsl:when test="ancestor::tr/@align='justify'">
<xsl:text>justify</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>start</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:when test="ancestor::thead">
<xsl:text>center</xsl:text>
</xsl:when>
<xsl:when test="ancestor::table[@align]">
<xsl:choose>
<xsl:when test="ancestor::table/@align='center'">
<xsl:text>center</xsl:text>
</xsl:when>
<xsl:when test="ancestor::table/@align='right'">
<xsl:text>end</xsl:text>
</xsl:when>
<xsl:when test="ancestor::table/@align='justify'">
<xsl:text>justify</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>start</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:text>start</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<fo:block text-align="{$align}">
<xsl:apply-templates select="*|text()"/>
</fo:block>
</fo:table-cell>
</xsl:template>
|

 |

|
<tfoot> 表页脚
很少用到
<tfoot> 元素,它可以创建表页脚。它包含若干表行(
<tr> )元素,每个表行元素包含若干表单元。要处理它,只需调用用于
<tfoot> 元素所包含的
<tr> 元素的 XSLT 模板:
<xsl:template match="tfoot">
<xsl:apply-templates select="tr"/>
</xsl:template>
|
<th> 表头单元
HTML
<th> 元素包含一个表头单元。要处理它,可创建一个四周边界宽度为 3 点的
<fo:table-cell> (如前面在
<td> 元素的缺省值中指定的那样)。
<fo:table-cell> 元素包含一个文本为粗体且居中的
<fo:block> 元素。该模板查找此元素有
border="1" 属性的祖先元素;如果其中任何一个有这样的边界属性,则该模板相应地设置 XSL-FO 边界属性。以下是完整的 XSLT 模板:
<xsl:template match="th">
<fo:table-cell
padding-start="3pt" padding-end="3pt"
padding-before="3pt" padding-after="3pt">
<xsl:if test="@border='1' or
ancestor::tr[@border='1'] or
ancestor::table[@border='1']">
<xsl:attribute name="border-style">
<xsl:text>solid</xsl:text>
</xsl:attribute>
<xsl:attribute name="border-color">
<xsl:text>black</xsl:text>
</xsl:attribute>
<xsl:attribute name="border-width">
<xsl:text>1pt</xsl:text>
</xsl:attribute>
</xsl:if>
<fo:block
font-weight="bold" text-align="center">
<xsl:apply-templates select="*|text()"/>
</fo:block>
</fo:table-cell>
</xsl:template>
|
<thead> 表头
处理极少使用的
<thead> 元素和处理
<tfoot> 元素相似。以下是一个简单的 XSLT 模板:
<xsl:template match="thead">
<xsl:apply-templates select="tr"/>
</xsl:template>
|
<title> 文档标题
我们假定所有这些指令都是为了将 FO 文档最终转换成 PDF 文件,后者有需要遵循的布局。为完成教程示例中所要求的 PDF 布局,需要将文档的标题(
<html> 元素中
<head> 元素中的
<title> 元素)设置为大字体、粗体并且位于页面顶部中央。以下是 XSLT 模板:
<xsl:template match="title">
<fo:block space-after="18pt" line-height="27pt"
font-size="24pt" font-weight="bold" text-align="center">
<xsl:apply-templates select="*|text()"/>
</fo:block>
</xsl:template>
|
<tr> 表行
HTML
<tr> 元素直接映射成 XSL-FO
<fo:table-row> 元素。因为处理表的绝大部分工作都在
<td> 元素的模板中,所以现在必须要做的就是创建
<fo:table-row> 并调用 HTML
<tr> 元素中所包含的每个元素的 XSLT 模板。以下是一个简单的模板:
<xsl:template match="tr">
<fo:table-row>
<xsl:apply-templates select="*|text()"/>
</fo:table-row>
</xsl:template>
|
<tt> 电报体文本
电报体文本是以等宽字体显示的。以下是 XSLT 模板:
<xsl:template match="tt">
<fo:inline
font-family="monospace">
<xsl:apply-templates select="*|text()"/>
</fo:inline>
</xsl:template>
|
<u> 加下划线的文本
要显示加下划线的文本,可使用 XSL-FO
text-decoration 属性。以下是简短的 HTML 样本:
<p>When typewriters ruled the earth,
<u>underlining</u> was the most
common way to highlight text.</p>
|
当将其转换成 XSL-FO 时,要使用带
text-decoration="underline" 属性的
<fo:inline> 元素:
<xsl:template match="u">
<fo:inline
text-decoration="underline">
<xsl:apply-templates select="*|text()"/>
</fo:inline>
</xsl:template>
|
注:
text-decoration 属性的关键字可以是负值也可以是正值。如果出于某种原因而希望在启用
underline 的一长段文本中关闭其中一小段具有
underline 属性的文本,则可以创建一个带
text-decoration="no-underline" 属性的
<fo:inline> 元素。也可以指定多个值;属性
text-decoration="underline line-through" 同时对文本加删除线和下划线。
<ul> 无序列表
无序列表比有序列表和定义列表简单,因为
<fo:list-item-label> 是所有项的项目符号。以下是用作样本的 HTML 列表:
<p>A few of my favorite albums</p>
<ul>
<li>A Love Supreme</li>
<li>Remain in Light</li>
<li>London Calling</li>
<li>The Indestructible Beat of Soweto</li>
<li>The Joshua Tree</li>
</ul>
|
要用到的 XSL-FO 列表元素有:
<fo:list-block> 、
<fo:list-item> 、
<fo:list-item-label> 和
<fo:list-item-body> 。以下是如何将 HTML 列表转换为 XSL-FO:
<fo:block>A few of my favorite albums</fo:block>
<fo:list-block provisional-distance-between-starts="0.2cm"
provisional-label-separation="0.5cm"
space-after="12pt" start-indent="1cm">
<fo:list-item>
<fo:list-item-label end-indent="label-end()">
<fo:block>•</fo:block>
</fo:list-item-label>
<fo:list-item-body start-indent="body-start()">
A Love Supreme
</fo:list-item-body>
</fo:list-item>
...
<fo:list-item>
<fo:list-item-label end-indent="label-end()">
<fo:block>•</fo:block>
</fo:list-item-label>
<fo:list-item-body start-indent="body-start()">
The Joshua Tree
</fo:list-item-body>
</fo:list-item>
</fo:list-block>
|
该模板使用 Unicode 实体
• 作为项目符号。
将
<ul> 和
<li> 项转换成格式化对象的 XSLT 模板在处理
<ul> 元素时遵循以下规则:
- 如果该列表出现在另一列表中,则不在其后插入任何空白。
- 如果该列表不在任何其它列表中,则将其缩进
1 cm 。否则,它缩进的距离为
1 cm 加上其它每个列表缩进的
1.25 cm 。例如,如果该列表是第三级列表,则将该列表缩进
3.5 cm 。
以下是 XSLT 模板:
<xsl:template match="ul">
<fo:list-block provisional-distance-between-starts="1cm"
provisional-label-separation="0.5cm">
<xsl:attribute name="space-after">
<xsl:choose>
<xsl:when test="ancestor::ul or ancestor::ol">
<xsl:text>0pt</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>12pt</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:attribute name="start-indent">
<xsl:variable name="ancestors">
<xsl:choose>
<xsl:when test="count(ancestor::ol) or count(ancestor::ul)">
<xsl:value-of select="1 +
(count(ancestor::ol) +
count(ancestor::ul)) *
1.25"/>
</xsl:when>
<xsl:otherwise>
<xsl:text>1</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:value-of select="concat($ancestors, 'cm')"/>
</xsl:attribute>
<xsl:apply-templates select="*"/>
</fo:list-block>
</xsl:template>
<xsl:template match="ul/li">
<fo:list-item>
<fo:list-item-label end-indent="label-end()">
<fo:block>•</fo:block>
</fo:list-item-label>
<fo:list-item-body start-indent="body-start()">
<fo:block>
<xsl:apply-templates select="*|text()"/>
</fo:block>
</fo:list-item-body>
</fo:list-item>
</xsl:template>
|

 |

|
<var> 变量名
通常以斜体等宽字体来显示变量名。使用 XSL-FO
font-family 和
font-style 属性。以下是一个简短的样本:
<p>To run the FOP program, you must make sure
your <var>classpath</var> variable
is set correctly.</p>
|
以下是进行该转换的 XSLT 模板:
<xsl:template match="var">
<fo:inline
font-style="italic"
font-family="monospace">
<xsl:apply-templates select="*|text()"/>
</fo:inline>
</xsl:template>
|
下载 | 描述 | 名字 | 大小 | 下载方法 |
|---|
| Example files (html, xsl, fo, pdf) | x-xslfo2app-samples.zip | 44 KB | HTTP |
|---|
参考资料
关于作者  | 
|  | Doug Tidwell 是
developerWorks的计算机传道士,帮助人们使用新技术来解决问题。他已经为全世界数万名开发人员讲解过 Web 服务和 XML,并确实令其中的许多人受到启发。他还是 O'Reilly
XSLT的作者,以及 O'Reilly
Programming Web Services with SOAP的合著者,这两本书都是送给您亲朋好友的绝佳礼物。可以通过
dtidwell@us.ibm.com与他联系。
|
对本文的评价
|