XQuery/從 XSL-FO 檔案生成 PDF
您希望從 XML 文件生成具有精確頁面佈局的文件,例如生成 PDF。
通常,生成 PDF 文件所需的步驟為
- 檢索或計算基礎 XML 文件
- 將 XML 檔案轉換為 XSL-FO 標記,可能使用 XQuery 型別轉換或 XSL
- 使用免費的 Apache FOP 或商業 FOP 渲染引擎(例如
http://www.renderx.com/ RenderX] 或 Antennahouse 將 XSL-FO 轉換為 PDF
我們將使用內建的 eXist 函式將 XSL-FO 檔案轉換為 PDF。(如果此模組未安裝和配置,請參閱 安裝 XSL-FO 模組。)
該函式是 xslfo:render()。它具有以下結構
let $pdf-binary := xslfo:render($input-xml-fo-document, 'application/pdf', $parameters)
或者,如果您使用 XSL-FO 配置檔案
let $pdf-binary := xslfo:render($input-xml-fo-document, 'application/pdf', $parameters, $fo-config-file)
此檔案可以直接儲存到 XML 檔案系統。它將被儲存為不可搜尋的二進位制文件。
然後,您可以透過提供指向檔案的連結直接檢視它,也可以透過使用 response:stream-binary() 函式直接將其傳送到瀏覽器,如下所示
return response:stream-binary($pdf-binary, 'application/pdf', 'output.pdf')
以下程式將生成一個包含文字“Hello World”的 PDF 文件。
xquery version "1.0";
declare namespace fo="http://www.w3.org/1999/XSL/Format";
declare namespace xslfo="http://exist-db.org/xquery/xslfo";
let $fo :=
let $pdf := xslfo:render($fo, "application/pdf", ())
return response:stream-binary($pdf, "application/pdf", "output.pdf")
您將需要一個將 XSL-FO 轉換為 PDF 的模組。以下是一些示例:
- Apache FOP 處理器(免費開源)
- Antenna House FOP 處理器(商業) http://www.antennahouse.com/
- RenderX FTP 處理器(商業) http://www.renderx.com/
確保載入模組擴充套件。您可以透過轉到 $EXIST_HOME/conf.xml 檔案並取消以下行的註釋來執行此操作(大約在第 769 行)
<module class="org.exist.xquery.modules.xslfo.XSLFOModule"
uri="http://exist-db.org/xquery/xslfo">
<parameter name="processorAdapter" value="org.exist.xquery.modules.xslfo.ApacheFopProcessorAdapter"/>
</module
其中 processorAdapter 引數的可能值為
org.exist.xquery.modules.xslfo.ApacheFopProcessorAdapter for Apache's FOP
如果模組正確載入,那麼您應該在函式文件中看到它。
確保您已正確編輯 $EXIST_HOME/extensions/build.properties 以將 XSLFO 設定為 true
更改
# XSL FO transformations (Uses Apache FOP) include.module.xslfo = false
為
include.module.xslfo = true
更改這兩個檔案後,您需要在 $EXIST_HOME 中執行“build.sh”或“build.bat”程式,以便將新的 FOP 二進位制檔案放入 jar 檔案中。
確保構建檔案可以從 Apache 網站訪問正確的 fop.jar 檔案。
eXist 帶有一個示例 ant 任務,可以自動下載 FOP 分發 zip 檔案,提取我們需要的 jar 檔案樹,並刪除其餘檔案。以下是 eXist 1.4 $EXIST_HOME/modules/build.xml 中的 ant 目標
<target name="prepare-libs-xslfo" unless="libs.available.xslfo" if="include.module.xslfo.config">
<echo message="Load: ${include.module.xslfo}"/>
<echo message="------------------------------------------------------"/>
<echo message="Downloading libraries required by the xsl-fo module"/>
<echo message="------------------------------------------------------"/>
<!-- Apache FOP .95 -->
<get src="${include.module.xslfo.url}" dest="fop-0.95-bin.zip" verbose="true" usetimestamp="true" />
<unzip src="fop-0.95-bin.zip" dest="${top.dir}/${lib.user}">
<patternset>
<include name="fop-0.95/build/fop.jar"/>
<include name="fop-0.95/lib/batik-all-1.7.jar"/>
<include name="fop-0.95/lib/xmlgraphics-commons-1.3.1.jar"/>
</patternset>
<mapper type="flatten"/>
</unzip>
<delete file="fop-0.95-bin.zip"/>
</target>
請注意,現在提供 fop 1.0,因此您可以將此任務更改為以下內容
<target name="prepare-libs-xslfo" unless="libs.available.xslfo" if="include.module.xslfo.config">
<echo message="Load: ${include.module.xslfo}"/>
<echo message="------------------------------------------------------"/>
<echo message="Downloading libraries required by the xsl-fo module"/>
<echo message="------------------------------------------------------"/>
<!-- Download the Apache FOP Processor from the Apache Web Site-->
<get src="${include.module.xslfo.url}" dest="fop-1.0-bin.zip" verbose="true" usetimestamp="true" />
<unzip src="fop-1.0-bin.zip" dest="${top.dir}/${lib.user}">
<patternset>
<include name="fop-1.0/build/fop.jar"/>
<include name="fop-1.0/lib/batik-all-1.7.jar"/>
<include name="fop-1.0/lib/xmlgraphics-commons-1.3.1.jar"/>
</patternset>
<mapper type="flatten"/>
</unzip>
<delete file="fop-1.0-bin.zip"/>
</target>
以下是一個示例記錄
prepare-xslfo:
[echo] Load: true
[echo] ------------------------------------------------------
[echo] Downloading libraries required by the xsl-fo module
[echo] ------------------------------------------------------
[fetch] Getting: http://apache.cs.uu.nl/dist/xmlgraphics/fop/binaries/fop-1.0-bin.zip
[fetch] To: C:\DOCUME~1\DANMCC~1\LOCALS~1\Temp\FetchTask8407348433221748527tmp
[fetch] ....................................................
[fetch] ....................................................
[fetch] ....................................................
[fetch] ....................................................
[fetch] ....................................................
[fetch] ....................................................
[fetch] ....................................................
[fetch] ....................................................
[fetch] ....................................................
[fetch] ....................................................
[fetch] ....................................................
[fetch] ....................................................
[fetch] ....................................................
[fetch] ....................................................
[fetch] ....................
[fetch] Expanding: C:\DOCUME~1\DANMCC~1\LOCALS~1\Temp\FetchTask8407348433221748527tmp into C:\ws\exist-trunk\lib\us
在此過程結束時,您應該在 $EXIST_HOME/lib/extensions 資料夾中看到以下三個 jar 檔案
cd $EXIST_HOME/lib/extensions $ ls -l -rwxrwxrwx+ 1 Dan McCreary None 3318083 2010-12-10 09:23 batik-all-1.7.jar -rwxrwxrwx+ 1 Dan McCreary None 3079811 2010-12-10 09:23 fop.jar -rwxrwxrwx+ 1 Dan McCreary None 569113 2010-12-10 09:23 xmlgraphics-commons-1.4.jar
如果您沒有看到這些檔案,則可以手動從 XSL-FO 二進位制檔案的下載中複製它們。
現在,轉到 $EXIST_HOME 目錄並鍵入“build”。您不應該看到任何錯誤訊息。如果您看到錯誤,請轉到構建檔案並修復或刪除這些錯誤。
重新啟動後,您應該能夠看到 XSL-FO 將檔案轉換為 PDF 檔案。
RenderX 是一個商業 FOP 處理器,用於代替 Apache FOP 處理器。
在 eXist 1.4 上,您必須在 extensions/build.properties 中啟用 include.module.xslfo = true 並執行“build.sh”或“build.bat”。如果您執行 2.0 版本,則此步驟不是必需的。
編輯 conf.xml 並註釋掉對預設 Apache xslfo 模組的引用。將模組更改為使用 RenderX,如下所示
<module uri="http://exist-db.org/xquery/xslfo" class="org.exist.xquery.modules.xslfo.XSLFOModule">
<parameter name="processorAdapter" value="org.exist.xquery.modules.xslfo.RenderXXepProcessorAdapter"/>
</module>
將所有 .jar 從 XEP/lib 複製到 $EXIST_HOME/lib/user
重啟 eXist 資料庫。
將您的 XQuery 更改為包含 xep 配置作為 XML 元素並將其傳遞給渲染函式
let $pdf := xslfo:render(fo:main($id), "application/pdf", (), $config)
return
response:stream-binary($pdf, "media-type=application/pdf", $id || ".pdf")
在 $config 中,您需要確保許可證和字型的路徑指向磁碟上的正確位置。
引用影像時,您必須使用絕對路徑引用並確保伺服器具有讀取許可權,或者使用相對路徑引用。 相對路徑引用的根目錄可以在 xslfo 配置檔案中設定。
xquery version "1.0";
declare namespace fo="http://www.w3.org/1999/XSL/Format";
declare namespace xslfo="http://exist-db.org/xquery/xslfo";
let $fop-config :=
<fop version="1.0">
<!-- Base URL for resolving relative URLs -->
<base>https://:8080/exist/rest/db/nosql/pdf/images</base>
</fop>
let $fo := doc('/db/test/xslfo/fo-templates/sample-fo-file-with-external-references.fo')
let $pdf := xslfo:render($fo, "application/pdf", (), $fop-config)
return response:stream-binary($pdf, "application/pdf", "output.pdf")
您可能不想硬編碼主機名、埠和上下文。 為了使它在任何主機、埠和上下文中都能正常工作,您可以使用以下程式碼來構建您的 FOP 基礎。
let $get-server-name := request:get-server-name()
let $port := xs:string(request:get-server-port())
let $conditional-port :=
if ($port = '80') then () else concat(':', $port)
let $get-context-path := request:get-context-path()
let $fop-config :=
<fop version="1.0">
<!-- Base URL for resolving relative URLs -->
<base>http://{$get-server-name}{$conditional-port}{request:get-context-path()}/rest/db/nosql/resources/images</base>
</fop>
現在您可以使用以下 FOP 模板生成 PDF。
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:layout-master-set>
<fo:simple-page-master master-name="my-page">
<fo:region-body margin="0.5in"/>
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="my-page">
<fo:flow flow-name="xsl-region-body">
<fo:block>Test of external SVG reference
</fo:block>
<fo:block>
SVG Chart Test
<fo:external-graphic content-width="7.5in" scaling="uniform" src="url(my-test-image.png)"/>
content-width="7.5in"
scaling="uniform"
src="url(chart.svg)"
</fo:block>
</fo:flow>
</fo:page-sequence>
</fo:root>
建立 PDF 文件時,您可以直接在使用 SVG 格式的 PDF 檔案中包含“線框圖”。
從 SVG 到 PDF 的轉換過程中存在一些問題,但大多數線框圖都能很好地轉換。
要在 eXist 中使用 SVG 渲染,您還必須載入 Sun AWT 庫(如果您引用了 SVG 影像)。
http://xmlgraphics.apache.org/fop/0.95/graphics.html#batik
這意味著您必須在 JVM 啟動時告訴 Java 強制載入 awt 庫。
-Djava.awt.headless=true
在您的 $EXIST_HOME/startup.bat 或 $EXIST_HOME/startup.sh 中,您需要新增以下內容。
set JAVA_OPTS="-Xms128m -Xmx512m -Dfile.encoding=UTF-8 -Djava.endorsed.dirs=%JAVA_ENDORSED_DIRS% -Djava.awt.headless=true"
如果您使用“wrapper”工具啟動伺服器,則需要將以下行新增到 $EXIST_HOME/tools/wrapper/conf/wrapper.conf 中。
# make AWT load the fonts for SVG rendering inside of XSLFO wrapper.java.additional.6=-Djava.awt.headless=true
測試配置的簡便方法之一是使用對 SVG 檔案的內聯引用。 您可以透過使用 fo:instream-foreign-object 元素來實現。 以下是一個示例。
<fo:block>
Test of inline SVG reference.
<fo:block>
<fo:instream-foreign-object content-width="7.5in" scaling="uniform">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="200" width="200">
<circle cx="100" cy="100" r="40" stroke="black" stroke-width="2" fill="blue"/>
</svg>
</fo:instream-foreign-object>
</fo:block>
content-width="7.5in"
scaling="uniform"
</fo:block>
請注意,這假設您已在 FOP 配置檔案中配置了 <base> URL。
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:layout-master-set>
<fo:simple-page-master master-name="my-page">
<fo:region-body margin="0.5in"/>
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="my-page">
<fo:flow flow-name="xsl-region-body">
<fo:block>Test of external SVG reference</fo:block>
<fo:block>
SVG Chart Test
<fo:external-graphic content-width="7.5in" scaling="uniform" src="url(chart.svg)"/>
content-width="7.5in"
scaling="uniform"
src="url(chart.svg)"
</fo:block>
</fo:flow>
</fo:page-sequence>
</fo:root>
Latex 是一種非 XML 語言,用於排版包含數學公式的文件。 儘管語法非標,但 LaTeX 在許多數學和物理出版物中仍然很流行。 XSL-FO 包含一個擴充套件包,允許將 LaTeX 公式新增到 XSL-FO 文件中。 要使用該包,您必須將兩個 jar 檔案新增到 $EXIST_HOME/lib/extensions,重新啟動 eXist,然後將適當的語法新增到您的 XSL-FO 文件中。
從該網站:http://forge.scilab.org/index.php/p/jlatexmath/downloads/
將以下檔案複製到您的 $EXIST_HOME/lib/extensions 中。
- jlatexmath-1.0.3.jar
- jlatexmath-fop-1.0.3.jar
然後重啟您的 eXist 伺服器,以便載入 jar 檔案。
然後將以下程式碼新增到您的 FO 中。
<fo:block>To pass, you should see a symbols for 2/4 = 1/2</fo:block>
<fo:block>
<fo:instream-foreign-object>
<latex xmlns="http://forge.scilab.org/p/jlatexmath">\frac{2}{4}=\frac{1}{2}</latex>
</fo:instream-foreign-object>
</fo:block>
請注意,XSL-FO 軟體不會自動從配置檔案中提取字型。 要強制將字型載入到 RAM 中,您需要將以下 auto-detect 元素新增到您的 fop 配置檔案中。
<fop version="1.0">
<renderers>
<renderer mime="application/pdf">
<filterList>
<value>flate</value>
</filterList>
<fonts>
<auto-detect/>
</fonts>
</renderer>
</renderers>
</fop>

注意:此項尚未完成。
雖然 Latex 是表示公式的常用方法,但 Math Markup Language 也可以使用。
還有提示表明 http://jeuclid.sourceforge.net/ 可以使用。
這尚未經過測試。
有關如何將印刷質量的表格和圖表新增到文件中,請參見 XSL-FO 表格 和 XSL-FO 影像。
當您遵循 trunk 時,有時 conf.xml 會重置為預設值,您需要在 conf.xml 中重新啟用 xslfo 處理。 如果您錯過此操作,則列印的錯誤資訊類似於:“無法編譯 xquery: err:xpst0017 呼叫未宣告的函式: xslfo:render”。
Wolfgang 在 2014 年 1 月 6 日更新的步驟
- 將 license.xml 複製到 EXIST_HOME
- 將 x4u.jar、xep.jar 和 xt.jar 從 xep 複製到 EXIST_HOME/lib/user
- 編輯 conf.xml 以更改 XSL FO 驅動程式
<module uri="http://exist-db.org/xquery/xslfo" class="org.exist.xquery.modules.xslfo.XSLFOModule">
<parameter name="processorAdapter" value="org.exist.xquery.modules.xslfo.RenderXXepProcessorAdapter“/>
</module>
- 重啟 eXist 以載入 jar 檔案
- 將 xep.xml 從 xep 目錄上傳到 eXist 中的一個集合(例如 /db)
- 編輯 xep.xml 並更改 xep 字型的基目錄。 它應該指向 xep 安裝目錄
<fonts xml:base="/Users/wolf/Source/renderx/fonts/" default-family="Arial“>
- 如果您使用的是 Mac,則可能需要更改檔案中更靠後的 Arial 字型的目錄
<font-group xml:base="file:/Library/Fonts/" label="Windows TrueType" embed="true" subset="true“>
- 在您的 XQuery 中呼叫 xep,如下所示。
let $id := request:get-parameter("id", ())
let $config := util:expand(doc("/db/xep.xml")/*)
let $pdf := xslfo:render(fo:main($id), "application/pdf", (), $config)
return
response:stream-binary($pdf, "media-type=application/pdf", $id || ".pdf“)
util:expand 技巧是必需的,因為 xslfo:render 期望 $config 的記憶體中 DOM 元素(這可能需要修復)。
注意:xep 將錯誤訊息列印到標準輸出,因此您通常看不到它們。 我透過啟動器執行 eXist,所以我透過系統托盤選單打開了“工具視窗”,然後點選“顯示控制檯訊息”。
Kevin Brown (RenderX) 在 2017 年 5 月 17 日更新的步驟
與其將 RenderX 的安裝拆分開,您可以編輯 RenderX 的主配置檔案以解決您可能需要的其他所有檔案,包括許可證檔案。 因此,如果您按照以下步驟操作,安裝 RenderX 會很容易。
- 將 RenderX 安裝到您想要的任何目錄中,或者如果 RenderX 已經安裝,請記下該目錄。 例如:Windows 上的安裝可能位於“C:\Program Files\RenderX\XEP”中。
- 將“xep.jar”從 RenderX 的安裝目錄複製到 exist-db 的“/lib/user”目錄中。 請注意,從上面的安裝說明中可以看出,您不需要“x4u.jar”,只需要“xep.jar”。 如果您希望報告 XSL FO 的驗證結果,那麼您還需要“xt.jar”。 檔案“xep.jar”和“xt.jar”位於 RenderX 安裝目錄的“/lib”目錄中。 在上面的示例中,這將是“C:\Program Files\RenderX\XEP\lib”。
- 將“xep.xml”插入您的資料庫。 “xep.xml”是 RenderX 配置檔案,位於 RenderX 安裝目錄的根目錄中。
- 編輯資料庫中的“xep.xml”並更改“config”元素以新增一個“xml:base”屬性,該屬性指向磁碟上 RenderX 的安裝目錄。 此一步將允許找到所有其他檔案(如“license.xml”、“rolemap.xml”和其他內容,如連字元和字型),因為它們都是相對於配置的“xml:base”的。 鑑於上面的示例安裝,我將擁有以下根元素,它位於資料庫中的“xep.xml”中。
<config xmlns="http://www.renderx.com/XEP/config" xml:base="file:/C:/Program Files/RenderX/XEP/">
- 可選地,如果您沒有複製“xt.jar”或不希望進行驗證,請將以下內容新增到“xep.xml”中。
<option name="VALIDATE" value="false"/>
- 如上所述編輯“conf.xml”,重啟資料庫並進行格式化。
這種安裝實質上意味著 RenderX 的外部安裝和與 exist-db 的安裝本質上是相同的,共享相同的字型、連字元、許可證和其他檔案。 當然,如果您更新了磁碟上的“xep.xml”,則需要在資料庫中更新它。 或者,如果您更新了 RenderX,則需要將更新後的“xep.jar”複製到 exist-db 的安裝目錄中。
使用者 Dmitriy 在建立針對沒有原始碼的系統的安裝程式方面提供了幫助。Wolfgang 還針對 eXist 2.1 的 RenderX 說明提供了反饋。Josef Karthauser 幫助將 LaTeX 公式在 PDF 文件中正確渲染。
啟用 FOP 模組的步驟應該列在 eXist 管理網站的某個地方,並從本華夏公益教科書中刪除。
RenderX 指令可以大幅簡化。RenderX 配置檔案 ("xep.xml") 的根元素採用 "xml:base" 作為引數。如果 RenderX 安裝在某個目錄中,例如在系統中的 "C:\Program Files\RenderX\XEP" 中,那麼在匯入 "xep.xml" 後,您只需在匯入版本中編輯根元素,如下所示
<config xmlns="http://www.renderx.com/XEP/config" xml:base="file:/C:/Program Files/RenderX/XEP/">
"xep.xml" 中的所有其他引用將相對於此,因此您無需移動任何內容(例如將 license.xml 放置在 EXIST_HOME 或影像目錄或字型或任何內容中)。
還要注意,"X4U.jar" 不應需要,因為它僅用於 GUI,而 "xt.jar" 除非在 "xep.xml" 中將 validate 設定為 true,否則不需要。只需要 "xep.jar"。