跳转到内容

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://127.0.0.1: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"。

华夏公益教科书