跳轉到內容

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() 函式

[編輯 | 編輯原始碼]

該函式是 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')

生成 PDF 的示例 XQuery

[編輯 | 編輯原始碼]

以下程式將生成一個包含文字“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")

執行

關於安裝 Apache FOP 處理器的說明

[編輯 | 編輯原始碼]

啟用 XSL-FO 模組

[編輯 | 編輯原始碼]

您將需要一個將 XSL-FO 轉換為 PDF 的模組。以下是一些示例:

  1. Apache FOP 處理器(免費開源)
  2. Antenna House FOP 處理器(商業) http://www.antennahouse.com/
  3. 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 檔案。

自動下載 Apache XSL-FO 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 XSL-FO 處理器的說明

[編輯 | 編輯原始碼]

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>

複製 RenderX jar 檔案

[編輯 | 編輯原始碼]

將所有 .jar 從 XEP/lib 複製到 $EXIST_HOME/lib/user

重啟 eXist-db

[編輯 | 編輯原始碼]

重啟 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 檔案中包含“線框圖”。

從 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

[編輯 | 編輯原始碼]

測試配置的簡便方法之一是使用對 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>

外部 SVG 引用示例

[編輯 | 編輯原始碼]

請注意,這假設您已在 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>

新增對公式的支援

[編輯 | 編輯原始碼]

在 XSL-FO 中格式化 LaTeX 公式

[編輯 | 編輯原始碼]

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>
單元測試輸出

Math ML 公式支援

[編輯 | 編輯原始碼]

注意:此項尚未完成。

雖然 Latex 是表示公式的常用方法,但 Math Markup Language 也可以使用。

還有提示表明 http://jeuclid.sourceforge.net/ 可以使用。

這尚未經過測試。

有關如何將印刷質量的表格和圖表新增到文件中,請參見 XSL-FO 表格XSL-FO 影像

當您遵循 trunk 時,有時 conf.xml 會重置為預設值,您需要在 conf.xml 中重新啟用 xslfo 處理。 如果您錯過此操作,則列印的錯誤資訊類似於:“無法編譯 xquery: err:xpst0017 呼叫未宣告的函式: xslfo:render”。

RenderX 說明

[編輯 | 編輯原始碼]

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"。

華夏公益教科書