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 文檔,但是創建您自己的工具集實現此目的有助於您更好地理解此過程,而且成本更低。