XSL FO 表中的粗边框线问题 - 使用 Apache FOP 创建 PDF

pun*_*ony 2 xslt pdf-generation xsl-fo xslt-1.0 apache-fop

我试图根据 Web 应用程序的要求隐藏 fo:table 行或列线,所以我曾经设置border-style="none"fo:table-columnfo:table-row根据需要,但是当我尝试它时,它要么在行中创建黑线(如果我隐藏列线)或在列中创建黑线(如果我隐藏行线)。

如果列行被隐藏:

列线问题图像

如果行行被隐藏:

行线问题图片

我的 XSL:

<?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet version="1.0"
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:fo="http://www.w3.org/1999/XSL/Format">
        <xsl:template match="pages">
            <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
                <fo:layout-master-set>
                    <fo:simple-page-master master-name="simple"
                        page-height="8.5in" page-width="11in" margin-top=".5in"
                        margin-bottom=".5in" margin-left=".5in" margin-right=".5in">
                        <fo:region-body margin-top="2cm" margin-bottom="2cm" />
                        <fo:region-before extent="2cm" overflow="hidden" />
                        <fo:region-after extent="1cm" overflow="hidden" />
                    </fo:simple-page-master>
                </fo:layout-master-set>
                <fo:page-sequence master-reference="simple"
                    initial-page-number="1">
                    <fo:static-content flow-name="xsl-region-before">
                        <fo:block font-size="13.0pt" font-family="serif"
                            padding-after="2.0pt" space-before="4.0pt" text-align="center"
                            border-bottom-style="solid" border-bottom-width="1.0pt">
                            <fo:block>IF COLUMN LINES HIDED</fo:block>
                        </fo:block>
                    </fo:static-content>
                    <fo:static-content flow-name="xsl-region-after">
                        <fo:block font-size="12.0pt" font-family="sans-serif"
                            padding-after="2.0pt" space-before="2.0pt" text-align="center"
                            border-top-style="solid" border-bottom-width="1.0pt">
                            <xsl:text>Page</xsl:text>
                            <fo:page-number />
                        </fo:block>
                    </fo:static-content>
                    <fo:flow flow-name="xsl-region-body">
                        <xsl:apply-templates select="page-body" />
                    </fo:flow>
                </fo:page-sequence>
            </fo:root>
        </xsl:template>
        <xsl:template match="page-body">
            <fo:block text-align="center" break-before="page">
                <fo:table table-layout="fixed" width="100%"
                    border-style="solid">
                    <fo:table-column border-style="solid"/>
                    <fo:table-column border-style="solid"/>
                    <fo:table-column border-style="solid"/>

<!--                    For hiding column lines -->
<!--                     <fo:table-column border-style="none"/> -->
<!--                     <fo:table-column border-style="none"/> -->
<!--                     <fo:table-column border-style="none"/> -->
                    <fo:table-header>
                        <xsl:apply-templates select="table-header" />
                    </fo:table-header>
                    <fo:table-body>
                        <xsl:apply-templates select="table-data" />
                    </fo:table-body>
                </fo:table>
            </fo:block>
        </xsl:template>
        <xsl:template match="table-header">
            <fo:table-row keep-together.within-page="always"
                border-style="solid">
                <fo:table-cell>
                    <fo:block font-size="10pt" font-family="sans-serif"
                        padding-top="3pt">
                        <xsl:value-of select="column-one"></xsl:value-of>
                    </fo:block>
                </fo:table-cell>
                <fo:table-cell>
                    <fo:block font-size="10pt" font-family="sans-serif"
                        padding-top="3pt">
                        <xsl:value-of select="column-two"></xsl:value-of>
                    </fo:block>
                </fo:table-cell>
                <fo:table-cell>
                    <fo:block font-size="10pt" font-family="sans-serif"
                        padding-top="3pt">
                        <xsl:value-of select="column-three"></xsl:value-of>
                    </fo:block>
                </fo:table-cell>
            </fo:table-row>
        </xsl:template>
        <xsl:template match="table-data">
            <fo:table-row keep-together.within-page="always"
                border-style="none">

<!--                 For showing row lines -->
<!--                 <fo:table-row keep-together.within-page="always" -->
<!--                 border-style="solid"> -->
                <fo:table-cell>
                    <fo:block font-size="10pt" font-family="sans-serif"
                        padding-top="3pt">
                        <xsl:value-of select="column-one"></xsl:value-of>
                    </fo:block>
                </fo:table-cell>
                <fo:table-cell>
                    <fo:block font-size="10pt" font-family="sans-serif"
                        padding-top="3pt">
                        <xsl:if test="number(column-two) = number(column-two)">
                        <xsl:value-of select="format-number(translate(column-two, ',','.'), '#,###.##')"></xsl:value-of>
                        </xsl:if>
                    </fo:block>
                </fo:table-cell>
                <fo:table-cell>
                    <fo:block font-size="10pt" font-family="sans-serif"
                        padding-top="3pt">
                        <xsl:value-of select="column-three"></xsl:value-of>
                    </fo:block>
                </fo:table-cell>
            </fo:table-row>
        </xsl:template>
    </xsl:stylesheet>
Run Code Online (Sandbox Code Playgroud)

我的 XML:

<?xml version="1.0" encoding="UTF-8"?>
<pages>
<page-body>
    <table-header>
        <column-one>Column One</column-one>
        <column-two>Column Two</column-two>
        <column-three>Column Three</column-three>
    </table-header>
    <table-data>
        <column-one>One</column-one>
        <column-two>5000</column-two>
        <column-three>Three</column-three>
    </table-data>
    <table-data>
        <column-one>One</column-one>
        <column-two>5000</column-two>
        <column-three>Three</column-three>
    </table-data>
    <table-data>
        <column-one>One</column-one>
        <column-two>0</column-two>
        <column-three>Three</column-three>
    </table-data>
    <table-data>
        <column-one>One</column-one>
        <column-two>0</column-two>
        <column-three>Four</column-three>
    </table-data>
    <table-data>
        <column-one>One</column-one>
        <column-two>2000</column-two>
        <column-three>Four</column-three>
    </table-data>
    <table-data>
        <column-one>One</column-one>
        <column-two>1234</column-two>
        <column-three>Five</column-three>
    </table-data>
    <table-data>
        <column-one>One</column-one>
        <column-two>5666</column-two>
        <column-three>Five</column-three>
    </table-data>
    <table-data>
        <column-one>One</column-one>
        <column-two>5666</column-two>
        <column-three>Five</column-three>
    </table-data>
     </page-body>
</pages>
Run Code Online (Sandbox Code Playgroud)

我正在使用这些 xml 和 xsl 文件使用 apache fop 生成 PDF,我的代码有问题吗?

小智 5

不幸的是,在涉及 FOP 渲染的 FO 开发中,这种事情不时出现。

有关您的答案,请参阅xmlgraphics.apache.org 的关于抗锯齿和 Acrobat 的常见问题解答的第 6.9 节

请注意,如果您在 Acrobat 中使用缩放系数,问题将根据设置消失/重新出现。

另请注意,如果您使用 RenderX(又名 XEP)尝试您的示例,则不会发生这种情况。他们显然已经发现(或从未有过)这个问题。

试图摆脱非商业 FO 产品的危险之一。(当然,并不是说某些东西必须是商业化的才能成为好的,但事实上商业 FO 处理器比 FOP 更好、更强大,尽管 FOP 正在改进,尽管速度很慢。)

如果您必须继续使用 FOP,请确保您使用的是最新版本(请参阅此图钉)。2.4 有一些重要的好处。

附录(SVG 的使用):

您可以通过动态构建的 SVG 标记来避免这种 FOP 错误。

以下是通过此方法获取 PDF 表必须生成的标记类型示例:

<fo:instream-foreign-object>
    <svg:svg width="1000%" height="1000%">
        <svg:g id='rowGroup' transform='translate(0, 0)' role="table">
            <svg:text x='30' y='30' font-size='18px' font-weight='bold' fill='crimson' text-anchor='middle' role="row">
                <svg:tspan role="columnheader" x='100'>Sales</svg:tspan>
                <svg:tspan role="columnheader" x='200'>Expenses</svg:tspan>
                <svg:tspan role="columnheader" x='300'>Net</svg:tspan>
            </svg:text>
            
            <svg:rect x='30' y='40' width='310' height='1' fill='black'/>
            
            <svg:text x='30' y='30' font-size='18px' text-anchor='middle' role="row">
                <svg:tspan id="q1" role="rowheader" x='30' dy='1.5em' font-weight='bold' fill='crimson' text-anchor='start'>Q1</svg:tspan>
                <svg:tspan role="cell" x='100'>$223</svg:tspan>
                <svg:tspan role="cell" x='200'>$195</svg:tspan>
                <svg:tspan role="cell" x='300'>$28</svg:tspan>
            </svg:text>
            
            <svg:rect x='30' y='60' width='310' height='1' fill='black'/>
            
            <svg:text x='30' y='50' font-size='18px' text-anchor='middle' role="row">
                <svg:tspan id="q2" role="rowheader" x='30' dy='1.5em' font-weight='bold' fill='crimson' text-anchor='start'>Q2</svg:tspan>
                <svg:tspan role="cell" x='100'>$295</svg:tspan>
                <svg:tspan role="cell" x='200'>$263</svg:tspan>
                <svg:tspan role="cell" x='300'>$32</svg:tspan>
            </svg:text>
            
            <svg:rect x='30' y='80' width='310' height='1' fill='black'/>
            
            <svg:text x='30' y='70' font-size='18px' text-anchor='middle' role="row">
                <svg:tspan id="q3" role="rowheader" x='30' dy='1.5em' font-weight='bold' fill='crimson' text-anchor='start'>Q3</svg:tspan>
                <svg:tspan role="cell" x='100'>$951</svg:tspan>
                <svg:tspan role="cell" x='200'>$232</svg:tspan>
                <svg:tspan role="cell" x='300'>$719</svg:tspan>
            </svg:text>
            
            <svg:rect x='30' y='100' width='310' height='1' fill='black'/>
            
            <svg:text x='30' y='90' font-size='18px' text-anchor='middle' role="row">
                <svg:tspan id="q4" role="rowheader" x='30' dy='1.5em' font-weight='bold' fill='crimson' text-anchor='start'>Q4</svg:tspan>
                <svg:tspan role="cell" x='100'>$823</svg:tspan>
                <svg:tspan role="cell" x='200'>$175</svg:tspan>
                <svg:tspan role="cell" x='300'>$648</svg:tspan>
            </svg:text>
            
            <svg:rect x='30' y='120' width='310' height='1' fill='black'/>
        </svg:g>
    </svg:svg>
</fo:instream-foreign-object>
Run Code Online (Sandbox Code Playgroud)

我让您为您的特定案例动态化 FO 创建,但这应该教您如何以这种方式构建表。

您可以通过将边框绘制为 SVG多边形来轻松实现您想要的效果,这正是 Kevin 要做的事情(尽管我相信他说的是 RenderX 引擎内部的代码。我的解决方案通过嵌入式 SVG 创建将其外部化。)当然,<svg:rect>标签是多边形的一种形式,这就是你用来渲染边界的东西。

<xsl:stylesheet>如果您要尝试此操作,请记住在您的标签上指定 SVG 命名空间。

xmlns:svg="http://www.w3.org/2000/svg"
Run Code Online (Sandbox Code Playgroud)

  • 这个技巧隐藏在 XSL FO 引擎中。为了解决这个问题,您可以使用多边形而不是线条来绘制边界。 (2认同)