【问题标题】:Flying Saucer: set custom DPI for output PDF飞碟:为输出 PDF 设置自定义 DPI
【发布时间】:2013-12-10 12:51:25
【问题描述】:

我正在使用 Flying Saucer 进行 HTML 到 PDF 的转换。我需要以 Letter 大小生成 600dpi 的输出 PDF。我怎样才能做到这一点?

【问题讨论】:

    标签: pdf-generation dpi flying-saucer


    【解决方案1】:

    有四个不同的因素在起作用,它们都是相互关联的:

    目标页面指标

    您希望页面度量正确,这样当您要求 Flying Saucer 生成“字母”大小的页面时,生成的 PDF 将在 Acrobat 中显示为 8.5" x 11"。您可以通过在 CSS 中指定 page-size 属性直接在 FS 中配置页面大小,正如另一个答案中所指出的那样:@page { size: letter; }

    分辨率

    您希望最终输出适合在某些打印机上以特定 XXX dpi 打印。这一切都很好,但请记住,PDF(主要)是一种矢量格式。我没有检查规范,但据我所知,PDF 文件/页面没有“具有”分辨率,因为它们是基于矢量的。话虽如此,放置在页面内的东西都有一个有效的分辨率,所以我们需要你想要的 XXX dpi 数字来计算下面的数字。

    每点的点数

    在 FlyingSaucer(和 Java)世界中,一个点始终是 1/72 英寸的常数。时期。因此,我们可以通过采用所需的分辨率并除以点的大小来计算每点的点数。例如,如果您想要 300 dpi 输出

    • 每点点数 = 300 dpi / 72 ppi = 300 点/英寸 / 72 点/英寸 = 300/72 点/点 = 4.1666 点/点

    每像素点数

    这不是一个幻数,该值与每点的点数以及您尝试输入 FlyingSaucer 的图形图像的预期分辨率直接相关。更具体地说,给定一个尺寸为 X x Y 像素的图形图像,您需要确定要在 PDF 中呈现多大的图像。如果您使用的是为屏幕(网络)使用而准备的图像,您可能从标准的 96 像素/英寸开始(因此 96 x 96 像素的图像将在 PDF 输出中呈现为一英寸正方形)。

    所以我们可以很容易地计算每像素点数如下,再次假设我们想要 300 dpi 输出:

    • 每像素点数 = 每英寸点数(点)/每英寸像素数(像素)
    • 每像素点数 = 300 点/英寸 / 96 像素/英寸 = 3.125 点/像素

    如果您采用这种方法,您的图像尺寸会正确,但它们不会是您正在寻找的 300 dpi 打印质量。那是因为您的图像的分辨率不够高。稍后会详细介绍。

    一切就绪

    如果您只是按照其他答案之一的建议直接致电SharedContext#setDPI,您可能会得到错误的结果。这是因为在不改变分辨率(每英寸点数)的情况下改变每点点数是不合逻辑的。 ITextRenderer 构造函数对setDPI(72*dotsPerPoint) 进行固定调用,当它创建新页面时,它还使用构造函数设置的dotsPerPoint 值来计算正确的页面宽度(以磅为单位)。如果您通过调用setDPI 更改了它的分辨率,您最终会得到错误的页面大小。

    正确的方法是使用我们上面计算的值创建一个新的 ITextRenderer 对象。如果我们想要 300 dpi 的输出并且我们有 96 ppi 的图像来提供它,我们会调用:

    ITextRenderer renderer = new ITextRenderer(4.1666f, 3);
    

    请注意,dots-per-pixel 参数只接受整数,因此我们将 3.125 舍入为上面最接近的整数。然而......这确实是两个数字之间的比率似乎很重要,所以为了使最后一个参数成为整数,我们可以将两个数字乘以 8(产生整数的最小整数乘数),得到 33.3333 并且正好25. 这也是我对飞碟资料中神奇数字“20”的由来的猜测。

    检查你的输出

    此时,您的输出 PDF 应该看起来与您开始时几乎相同(假设您之前使用的是飞碟的默认 96 ppi 配置)。但现在我们知道了需要调整哪些参数才能让一切正常运行。

    要获得高质量的输出,您需要高质量的输入

    因此,我们已经验证了上述参数适用于我们的目的,但我们的图像仍然只有 96 ppi。如果我们想以高分辨率打印这些东西,您需要做的就是将图像换成 300 ppi 版本,更改构造函数参数,然后就完成了,对吧?

    也许吧。让我们计算一下数字:

    您的预期输出分辨率 (300 dpi) 没有改变,因此每点的点数仍为 4.1666。但是您的输入图像现在为 300 ppi,因此您的每像素点数 = 300 点/英寸/300 像素/英寸 = 1 点/像素。因此,您现在将像这样调用构造函数:

    ITextRenderer renderer = new ITextRenderer(4.1666f, 1);
    

    完成此操作后,您的新 300 像素 x 300 像素图像将在 PDF 上显示为 1" 正方形,这正是您想要的打印质量。

    但是等等!我所有的文字也变得非常小!

    Flying Saucer 使用每像素点数测量来转换许多东西,而不仅仅是图像。特别是,如果您在样式表中指定了使用像素的任何内容,那么每像素点的测量值也会对其大小产生影响。

    如果你有像font-size: 10px; 这样的样式表规则,那么增加提供给构造函数的每像素点数将使文本更小,这可能也不是你想要的。毕竟,您应该能够提高 PDF 中图像的分辨率,同时让文本保持相同的大小和位置。

    答案是将样式表中的所有内容都转换为使用点。 (或英寸。至少不是像素!)如果您开始使用默认的飞碟设置(意味着像素为 96 ppi),您只需将所有“px”测量值转换为点。由于 72 点 = 1 英寸,您可以将“px”更改为“pt”并将该值乘以 72/96。

    例如,上面的font-size: 10px; 将变为font-size: 7.5pt;。如果您希望与以前的内容真正保持一致,则 CSS 中提及“px”(以及任何内联样式)的所有内容也必须通过相同的转换更改为“pt”。

    一旦你做了这个改变,你的文本和其他布局将保持一致,如果你决定以后需要 600 dpi 输出,你可以调整你的图像并更改构造函数参数,但布局的其余部分会仍然保持不变。完成!

    【讨论】:

    • 很棒的答案:这应该是公认的。然而,目前尚不清楚为什么 ITextRenderer 需要两个不同的参数 dotsPerPoint 和 dotsPerPixel 而只是它们的比率似乎会影响绘图命令,以及为什么它们是不同类型(float 与 int),以及“20”从何而来(他们本可以使用 4和 3 - 顺便说一下,这是 96px(CSS 规范中的标准 PC 屏幕分辨率)与 72px(标准打印 API)的比率)
    • 这是我的推测,但我的猜测是:1)FS 需要一个单位来表示它自己的内部布局网格(“点”); 2) 矢量坐标可以很容易地映射到任意比例的点,那么为什么不是浮点数呢? 3)另一方面,位图需要重新采样以按除整数因子之外的任何值进行缩放; 4) 20 比例因子表明他们想使用twips 作为默认的内部单位; 5)拥有两个不同的比例因子可以让你只改变(比如说)你的位图分辨率而不影响其他任何东西。
    • 我想用你的猜测来验证我的猜测,我们也做了同样的猜测。只是为了讨论,你有没有发现任何“点”坐标系的证据?我什至联系了FS作者,他说猜测是正确的,但你为什么认为他们需要它?我的意思是,为什么要根据渲染设备将像素和点转换为“点”然后再转换回来?原作者说是因为“屏幕遗产中的整个坐标”,但这对我来说似乎只是不幸的 API - 他们更好地使用 orig 和目标 DPI 来处理图像,并将其他任何东西留在输出设备坐标中
    • 这仍然是推测,但在 ITtext 代码内部(例如,查看 PageBox),它在内部最终使用 java.awt.Dimension 和 Point 等对象来描述对象定位。关键因素是 Dimension 使用整数而不是浮点数,这意味着您的布局数字最终会被量化为某种东西。假设这些整数单位仍然是“点”(我没有深入研究源代码!),如果您每个点只有一个点,那么您将难以尝试呈现像 margin-top: 0.5pt; height: 0.5pt; 这样的 CSS 规则。
    • 舍入和 AWT 正是 Pete Brant(FS 的作者)给出的原因 - 他说 20 只是一个任意数字,但你猜测它可能来自 twips 很聪明
    【解决方案2】:

    您可以在 HTML 文档中使用 CSS page size 属性设置字母大小:

       @page {
          size: letter;
        }
    

    您可以使用以下 ITextRenderer 构造函数更改文档的 dpi:

    public ITextRenderer(float dotsPerPoint, int dotsPerPixel)
    

    我不明白这些值真正代表什么,但默认值是dotsPerPoint = 20f * 4f / 3fdotsPerPixel = 20,它会输出一个96dpi 的文档。

    要获得 600dpi,您可以使用 dotsPerPoint = 500f / 3fdotsPerPixel = 20

    查看ITextRenderer的代码,最终dpi由公式给出:dpi = dotsPerPoint * 72 / dotsPerPixel

    【讨论】:

    • 我能得到从 dotsPerPointdotsPerPixel 中找到 dpi 背后的计算吗?。
    • 我在响应中添加了公式。
    • 这似乎是假设的
    • 我会说经验性的。就我而言,我想要一个 300dpi 的文档,我尝试了不同的值,直到找到公式。
    • 96 dpi = 660 dppt * 72 / 20 dppx?这是第一种情况的方程式吗?
    【解决方案3】:

    使用飞碟时设置 DPI 的更简单答案:

    renderer.getSharedContext().setDPI(600);
    

    与 obourgain 的回答一样,与 @page { size:letter; } CSS 结合使用。

    Source

    【讨论】:

    • 但是,这会生成大小为 14.14 x 18.33 英寸的 PDF。
    • 请了解您需要的实际大小,然后了解相应的 css 描述符 - a4、a5、字母等 - 或手动设置大小,即@page { width:5in; height:4in }
    • 我设置了字母大小,应该是 8.5 x 11 英寸,但在这种情况下不是。
    猜你喜欢
    • 2012-04-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-26
    • 1970-01-01
    • 2013-05-05
    • 1970-01-01
    相关资源
    最近更新 更多