XML 提供了在结构化数据集中标识数据项与子组件的方法,但是它源起于文档的开发和生成。针对 XML 文档标记有很多健壮的开放标准,而 XML 文档解析及格式转换工具也十分丰富,而且可以免费使用,这就使得在 UNIX® 或 Linux® 系统上安装并配置一个完整的文档开发及格式化环境变得非常容易。
XML、标记与文档
XML 是一种通过在文本标记内包裹数据中不同的分层部分来标识该数据集内的结构化内容的机制,这些标记能够标示它们在该分层结构内所界定的那些文本的角色。这些文本标记,又称标记、标签、或更恰当的说是元素,符合预先定义的结构。该结构特定于不同的数据类型,通常称为 Schema,过去被称为 DTD。Schema 用 XML 编写,而 DTD 有自已的语法。
|
标记语言及 XML 本身的理念源起于文档生成系统,在这些系统中,文本文件包含了处理指令或用来标识文档的不同部分的结构标识符。这些文本文件可随后由软件处理,而这些软件知道如何解释这些指令以及如何生成针对特定的输出设备格式化了的输出。标记语言在文档处理方面的演变增加了使用标记的抽象性。
早期的标记语言使用格式化指令,它指定了较低级别的细节设置,例如字体及字体大小的变化。它们很快发展成了更为普遍的、可标识逻辑和结构组成(比如突出显示的术语、段落、列表、标题等)的标记。
较现代的标记语言,例如 Standard Generalized Markup Language(SGML)和 XML,则使像书、章、节等这类更为抽象的结构标记更易于为大众理解和接受。SGML 新加入了 DTD 的概念,这就使得 SGML 工具能够验证对特定的一些标记元素集和上下文的遵从性,这些元素在上下文中可以出现在某一类文档的结构内。
显而易见,XML 将标记从文档的领域中解放了出来,将它的使用范围扩展到了任何类型的结构化数据,并相应增加了对格式良好的 内容的要求,即每个元素都必须同时具备开始和结束标签。尽管 XML 规范的最初版本从 SGML 中继承了 DTD,但之后的版本则引入了 Schema 的概念以定义 XML 数据的结构。Schema 与 DTD 基本相同,只不过 Schema 本身也是用 XML 编写的,并且提供了一个更为灵活的模型来定义有效元素、元素的数据类型及 XML 文档中这些元素能够在其中出现的上下文。
将文档的内容(文档包含的确切的文字和数据)与文档的表示(文档以特定的输出格式或在特定的输出设备中显示的方式)分离开来是现代标记语言,更明确地说是 SGML 和 XML 背后的关键理念。
尽管 XML 简化了针对特定的应用或行业的 DTD 和 Schema 设计,但它在文档方面的真正优势却在于有大量现成的标准的 DTD 和 Schema 可用于文档标记。其中为人熟知的当属 DocBook、Text Encoding Initiative (TEI)、XHTML 以及 Darwin Information Typing Architecture (DITA) 所采用的那些 Schema 与 DTD。本文将重点介绍 DocBook,它是最著名也是最广泛用于文档标记的一种 Schema 。
XML 文档发布过程概述
由于其重心就是将内容与表示相分离,所以 XML 本质上是一个 “写一次,读多次” 的机制,这使得在多个文档中重用信息或针对特定的输出格式或群体定制信息都变得很容易。本系列中的另一篇文章将会深入探讨这些主题。
在本文的上下文环境中,术语发布 指的是将文档从一个包含 DocBook 标记的文本文件转换成一个或多个以特定格式(例如 HTML、Adobe® 的 Portable Document Format (PDF)、PostScript、Microsoft® 的 Rich Text Format (RTF) 等)表示的输出文件。从 XML 文档生成 HTML 输出只需一步就可完成,而且仅用一个应用程序就能生成 HTML 输出。生成 PDF、PostScript 或 RTF 输出的过程只需再多加一个步骤:
- 解析所包含的全部文档或 DocBook 文档内的外部引用,并用一个 XSL 样式表来生成这个文档的 Formatting Object(FO)格式的版本。
- 对于 PDF、PostScript 或 RTF 输出,将 DocBook 文档的 FO 版本转换为相应格式的输出文件即可。
很多开源工具都可用来进行这些转换,但本文中,我只重点介绍我发现的能提供最有用和最灵活解决方案的三种工具。 表 1 介绍了这些工具。
表 1. 本文所涵盖的的开源转换工具
工具 | 描述 |
---|---|
fop | 一个将 XML 输入转换为 PDF、PostScript、RTF 等输出格式的 Java™ 应用程序。要执行这个应用程序,系统必须安装 Java Virtual Machine (JVM)。 |
xmlto | 一个简单的 Bourne-Again shell 脚本和能从 XML 输入生成 HTML 输出的一组相关样式表。这个脚本还需要另外的一些开源实用工具,例如 GNU find 和 mktemp。 |
xsltproc | 一个编译了的应用程序,能够使用 XSL 样式表将 XML 输入转换为另一种 XML 输出格式。要实现这种转换,还需要安装 DocBook XSL 样式表。只要有了恰当的样式表,fop 就可以接受 DocBook XML 输入,倘若 DocBook 输入文件有问题,使用独立的一个 XML 到 FO 转换器,比如 xsltproc,可以简化调试过程。 |
下面的几个小节将会介绍如何下载并安装这些应用程序。随后的小节 给出了一个 shell 脚本,它将之前介绍的内容综合起来并提供了一些便捷的方式来指定输出格式、定制样式表等。
获取并安装所需软件
要获得 前一个小节中 提到的软件和样式表,请参见 参考资料。
注意:如果使用的是 Linux 系统,那么 xmlto 和 xsltproc 实用工具已经安装在系统上了,或至少可以在此 Linux 发布版的存储库或安装媒介中找到。此外,还需要安装一个 JVM 来运行 fop— GNU Compiler,因为 Java 并不提供 fop 所需的全部功能。
很多这样的包都会把软件安装在指定的位置以便系统的包管理软件能够监控并根据需要更新软件。但这一点并不适用于 XSL 样式表和 Apache Formatting Objects Processor(FOP),因为它们是作为包含有一个顶级目录和多个子目录的归档文件被下载的。
要统一文档解析和格式化环境,最好是将不具备硬连接(hard-wired)路径名的包安装在一个尚不存在的目录(例如 /home/doc)内。这样做能够提供一个独立的位置,可以在其中查找相关的文件、在需要的时候升级软件和收集所开发或收藏的其他工具和样式表。
要使 前面小节 中所讨论的包与后面小节内提供的统一脚本协同工作,必须以 UNIX 或 Linux 系统的根用户执行 清单 1 内的命令。
清单 1. 使 fop、xmlto 和 xsltproc 能够与统一脚本协同工作而执行的命令
mkdir /home/doc mkdir /home/doc/bin mkdir /home/doc/external mkdir /home/doc/external/SAVE mkdir /home/doc/XSL |
在下载了针对 FOP 和 DocBook XSL 样式表的压缩(.zip)文件后,可以将它们放在 /home/doc/external 目录,转到该目录,并使用 清单 2 内的命令解压缩其中的内容。
清单 2. 解压缩 FOP 和 DocBook XSL 样式表所需的命令
cd /home/doc/external unzip fop-0.95-bin.zip unzip docbook-xsl-1.74.3.zip chmod 755 fop-0.95/fop |
要验证所有必需的包是否已经全部安装到系统,可以执行 清单 3 内的命令。
清单 3. 验证所需包是否安装完毕所需的命令
xmlto --version xsltproc --version java --version |
如果 xmlto 和 xsltproc 应用程序以及 JVM 均已正确安装到系统并保存于执行路径,上述命令中的每一个都会返回有关该应用程序的版本信息。
编写一个用来发布文档的包装器脚本
将 DocBook 发布所需的所有软件都安装到系统上后,所需做的就是记住所有必需的选项并按正确的顺序加以执行。当然,也可以编写一个包装器脚本将所有这些综合起来并提供对发布过程的某些控制,类似 清单 4、清单 5 和 清单 6 所示。
此脚本基于所提供的参数生成 HTML、PDF 或 RTF 输出。在生成 PDF 或 RTF 输出时,它使用一个两步过程来生成 PDF 输出,先是执行 xsltproc 从 DocBook 输入生成 FO 文件,其中使用了 driverfile 变量内找到的 XSL 样式表来识别转换规则。之后,这个包装器脚本使用 fop 应用程序从该文件生成目标输出格式。其中的每一个步骤都会从文件 xsltproc.out 和 fop.out 内所使用的应用程序(xsltproc 和 fop)获取消息。此脚本使用这些中间文件来监视转换过程并在出现问题时获取潜在的调试信息。
清单 4 给出了此示例包装器脚本的开始部分,其中定义了一个使用消息,如果在没有参数或参数数量错误的情况下执行该脚本,就会显示这个消息。它还为此脚本设置了某些默认值,然后使用标准的 Bash shell 的 getopt 函数解析这些命令行参数(清单 7 给出了这个使用消息的输出)。
清单 4. 包装器脚本的初始部分
#! /bin/bash # # Simple script to generate HTML output from a DocBook file using # xmlto, or to generate PDF/RTF output by generating a fo file, # and then using Apache's FOP to format that. # # wvh@vonhagen.org # function usage { echo "Usage: $0 [OPTIONS] document-name" echo " -D : produce draft mode document" echo " -d : use specified xsl driver (requires name of XSL file) " echo " -h : generate HTML output (requires output directory name)" echo " -k : attempt PDF generation even if xsltproc errors were encountered" echo " -n : suppress TOC" echo " -r : produce RTF output rather than PDF" exit 1 } dir=`dirname $0` XSLTPROC="xsltproc" PATH=$dir/../external/fop-0.95:${PATH} if [ $# == 0 ] ; then usage exit fi driverfile="$dir/../external/docbook-xsl-1.74.3/fo/docbook.xsl" # driverfile="$dir/../XSL/generic-print-driver.xsl" draft="no" keepgoing="no" RTFoutput="no" HTMLoutput="no" while getopts ":d:h:Dknr" opt do case $opt in d ) driverfile=$OPTARG echo "Using driver file: $driverfile" if [ ! -f $driverfile ] ; then echo " Driver file not found" exit 1 fi ;; D ) draft="yes" echo "Producing Draft Mode Document" ;; h ) HTMLdir=$OPTARG HTMLoutput="yes" echo "Producing HTML output to $HTMLdir instead of PDF" if [ -d $HTMLdir ] ; then echo " Output directory $HTMLdir already exists" exit 1 fi ;; k ) keepgoing="yes" echo "Will not stop if errors are encountered" ;; n ) notoc="--stringparam generate.toc ''" echo "Suppressing TOC..." ;; r ) RTFoutput="yes" echo "Producing RTF output rather than PDF" ;; \? ) usage exit 1;; esac done shift $(($OPTIND - 1)) if [ ! -f $1 ] ; then echo " Input file $1 not found - exiting." exit fi |
清单 5 所示的这部分脚本的作用是:在试图生成 HTML 输出时,使用 xmlto 应用程序生成 HTML;在试图生成 PDF 或 RTF 输出时,使用 xsltproc 应用程序从输入 XML 文件创建 FO 输出。XML 到 FO 的转换步骤的输出被写入到一个名为 xsltproc.out 的临时文件,如果 xsltproc 应用程序在输入文件内遇到任何错误,临时文件能够简化调试的过程。若在此文件中检测到任何错误,脚本就会在生成 FO 文件后退出。之后您可以查看这个 xsltproc.out 文件,它应该能够有助于您发现和解决问题。与 清单 4 类似,也可以指定 -k 命令行选项来强制脚本继续,即便在 xsltproc 处理中检测到错误也是如此。
清单 5. 从输入 XML 文件生成 FO 输出
doc_base=`basename $1 .xml` echo "Processing base name: $doc_base" if [ x$HTMLoutput != "xno" ] ; then xmlto -o $HTMLdir html $1 exit fi echo " Generating FO file: $doc_base.fo" $XSLTPROC --output $doc_base".fo" \ --stringparam draft.mode $draft \ --stringparam fop1.extensions 1 \ --xinclude \ $notoc \ $driverfile \ $1 1> xsltproc.out 2> xsltproc.out probs=`grep -i error xsltproc.out | wc -l | sed -e 's; ;;g'` if [ x$probs != "x0" ] ; then echo "" echo "PROBLEMS:" echo "" cat xsltproc.out echo "" if [ x$keepgoing = "xno" ] ; then exit fi fi echo " Fixing FO file references to system images..." cat $doc_base".fo" | sed -e "s;\"url(\.\./external/;\"url($dir/\.\./external/;g" > $ mv $ $doc_base".fo" |
清单 6 给出了此脚本的最后部分,其中,使用了 fop 应用程序从一个 FO 文件生成 PDF 或 RTF 输出。与从 XML 到 FO 的转换过程类似,从 FO 到 PDF/RTF 转换步骤的输出也被写入到一个名为 fop.out 的临时文件,当 fop 应用程序在 FO 文件中遇到错误时,这个文件能够简化调试过程。如果 PDF/RTF 生成失败,您可以检查这个 fop.out 文件,它应该能够帮助您发现和解决问题。与 清单 4 类似,也可以指定 -k 命令行选项来强制脚本继续,即便在 xsltproc 处理过程中检测到错误也是如此。
清单 6. 从 FO 文件生成 PDF 或 RTF 输出
if [ x$RTFoutput = "xno" ] ; then echo " Generating PDF file: $doc_base"".pdf" fop -fo $doc_base".fo" -pdf $doc_base".pdf" 1> fop.out 2> fop.out else echo " Generating RTF file: $doc_base"".rtf" fop -fo $doc_base".fo" -rtf $doc_base".rtf" 1> fop.out 2> fop.out fi probs=`grep -i Exception fop.out | wc -l | sed -e 's; ;;g'` if [ x$probs != "x0" ] ; then echo "" echo "EXCEPTION OCCURRED DURING PROCESSING:" echo "" cat fop.out echo "" fi |
可以下载这个名为 xml-format.sh 的示例脚本(参见 下载 部分的 xml-format.zip)。要在系统上安装此脚本,下载这个 zip 文件并将 xml-format.sh 解压缩到 /home/doc/bin 目录。使用如下命令来确保此脚本是可执行的:
chmod 755 /home/doc/bin/xml-format.sh |
最后,确保 /home/doc/bin 目录位于执行路径中,方法是编辑 ~/.bashrc 文件并在该文件的末尾添加如下这行代码,也可以在系统 shell 提示符中作为命令执行这行代码:
export PATH=/home/doc/bin:${PATH} |
如果将此命令添加到 ~/.bashrc 文件,请在当前的登录会话中提供该文件的来源,以便所做的更改在当前 shell 中立即生效。为此,执行如下的命令:
source ~/.bashrc |
发布一个 DocBook 文档
在没有参数的情况下执行这个 xml-format.sh 脚本会显示有关脚本如何使用的总结信息,如 清单 7 所示。
清单 7. 在没有参数的情况下执行 xml-format.sh 脚本后的输出
Usage: xml-format.sh [OPTIONS] document-name -D : produce draft mode document -d : use specified xsl driver (requires name of XSL file) -h : generate HTML output (requires output directory name) -k : attempt PDF generation even if xsltproc errors were encountered -n : suppress TOC -r : produce RTF output rather than PDF |
要生成 DocBook 文档的 PDF 版本,只需以该文档的名称执行此脚本,如下面的例子所示:
xml-format.sh sample-document.xml |
要使用此脚本生成这个 DocBook 文档的 HTML 版本,必须使用 -h 选项并为 HTML 输出文件指定目标目录的名称。例如,要在 HTML-output 目录(相对于当前的工作目录)创建 DocBook 文档的 HTML 版本,可以执行如下命令:
xml-format.sh -h HTML-output sample-document.xml |
为 PDF 输出添加一个 XSL 定制层
默认情况下,本文提供的 xml-format.sh 脚本使用的是 DocBook XSL 包内提供的默认输出样式表。若要进行定制,需要创建您自己的样式表以便先加载默认的 DocBook XSL 样式表,然后提供想要覆盖的那些特定参数的定制值。定制层的一个例子可在 generic-print-driver.xsl 样式表内找到(参见 下载 部分获得 generic-print-driver.zip)。
要为 xml-format.sh 脚本使用不同的样式表,可使用 -d 选项来指定想要使用的样式表的完整或相对路径名,或者也可以修改 xml-format.sh 脚本内的 driverfile 变量的值。示例脚本中有一行代码允许您使用示例 generic-print-driver.xsl 脚本。只需注释掉 driverfile 变量之前的值并取消随后一行的注释,该行包含新的定义。
结束语
XML 的强大功能和灵活性、多组现有标准以及用于处理和转换 XML 文档的丰富工具让安装和配置您自己的一组工具来从 XML 文档生成各种格式的输出变得十分简单。虽然现在有很多商业工具可用来编辑和格式化 XML 文档,但是创建您自己的工具集实现此目的有助于您更好地理解此过程,而且成本更低。(责任编辑:A6)