飞碟:设置输出PDF的自定义DPI

4

我正在使用Flying Saucer进行HTML转PDF。我需要生成一个600dpi的输出PDF,大小为Letter。我该如何实现这一点?

3个回答

15

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

目标页面度量

您希望页面度量正确,这样当您要求 Flying Saucer 生成“信纸”大小的页面时,所得到的 PDF 将在 Acrobat 中显示为 8.5" x 11"。您可以通过在 CSS 中指定 page-size 属性直接配置 FS 页面大小,如 obourgain 在另一个答案中所提到的:@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 中呈现多大。 如果您正在使用为屏幕(Web)使用准备的图像,则可能从标准的 96 像素/英寸开始(因此,96 x 96 像素的图像将呈现为 PDF 输出上的一英寸正方形)。

因此,我们可以轻松计算出每像素点数,假设我们想要 300 dpi 输出:

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

如果您采用这种方法,那么您的图像大小将是正确的,但它们不会具有您所寻找的 300 dpi 打印质量。这是因为您的图像分辨率不够高。稍后我们将详细介绍这一点。

设置好所有内容

如果您只是像另一个答案建议的那样直接调用 SharedContext#setDPI,则可能会得到错误的结果。这是因为更改每点点数而不更改分辨率(每英寸点数)毫无逻辑意义。ITextRenderer 构造函数以固定值调用 setDPI(72*dotsPerPoint) ,并在创建新页面时,还使用构造函数设置的 dotsPerPoint 值计算正确的页面宽度(以点为单位)。如果您在其鼻子下更改了分

ITextRenderer renderer = new ITextRenderer(4.1666f, 3);
请注意,每像素点数参数只接受整数,因此我们将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像素的图像将以1英寸的正方形出现在PDF上,这正是你想要的打印质量。

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

Flying Saucer使用每像素点数(dots-per-pixel)来转换许多东西,不仅仅是图片。特别是,如果你已经在样式表中指定了任何使用像素的东西,那么每像素点数对它们的大小也会有影响。

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

答案是将样式表中的所有内容都转换为使用磅(points)。(或英寸。至少不要使用像素!)如果你从默认的Flying Saucer设置开始(意味着像素为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 vs int),以及“20”来自哪里(他们本可以使用4和3 - 顺便说一下,这是96px(CSS规范中的标准PC屏幕分辨率)到72px(标准打印API)的比率)。 - Raffaele
这只是我的猜测,但我的猜测是:1)FS需要一个单位来表示其自己的内部布局网格(“点”);2)矢量坐标可以轻松映射到任意比例尺上的点,那么为什么不使用浮点数呢?3)另一方面,位图需要重新采样才能按除整数因子以外的任何比例缩放;4)20个缩放系数表明他们想要将twips作为默认的内部单位;5)具有两个不同的比例因子可让您更改(例如)仅位图分辨率而不影响其他任何内容。 - Scott Dudley
我想验证一下我的猜测是否与你的相同。仅供讨论,你有发现“点”坐标系统的任何证据吗?我甚至联系了FS的作者,他说猜测是正确的,但你为什么认为他们需要它呢?我的意思是,为什么要将像素和点转换为“点”,然后根据渲染设备再转换回来呢?原始作者说是因为“屏幕遗产中的整个坐标”,但对我来说,这似乎只是不幸的API - 他们最好使用原始和目标DPI来处理图像,并将其他所有内容保留在输出设备坐标中。 - Raffaele
1
这仍然是猜测,但在ITtext代码内部(例如,查看PageBox),它最终使用像java.awt.Dimension和Point这样的对象来描述对象定位。关键因素是Dimension使用整数而不是浮点数,这意味着您的布局数字最终会被量化为某些内容。假设这些整数单位仍然是“点”(我没有深入研究源代码!),如果每个点只有一个点,您将难以尝试呈现CSS规则,例如margin-top: 0.5pt; height: 0.5pt; - Scott Dudley
四舍五入和AWT正是Pete Brant(FS作者)给出的原因-他说20只是一个任意的数字,但你猜测它可能来自twips是很聪明的。 - Raffaele

3
使用Flying Saucer设置DPI的简单方法如下:
renderer.getSharedContext().setDPI(600);

与obourgain的答案类似,需要与@page { size:letter; } CSS一起使用。
来源:链接

但是,这将生成一个大小为14.14 x 18.33英寸的PDF文件。 - N K
我们需要先了解实际需要的尺寸,然后对应相应的CSS描述符 - A4、A5、信纸等 - 或手动设置尺寸,例如 @page { width:5in; height:4in } - Charles Goodwin
我已经设置了信纸尺寸,应该是8.5 x 11英寸,但在这种情况下不是。 - N K

3
您可以通过在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


我能否获得从dotsPerPoint和dotsPerPixel计算dpi的计算过程? - N K
我已将公式添加到响应中。 - obourgain
这似乎是假设性的。 - N K
我会说是经验性的。在我的情况下,我想要一个300dpi的文档,我尝试了不同的值,直到我找到了公式。 - obourgain
96 dpi = 660 dppt * 72 / 20 dppx?这是第一个情况的方程吗? - N K
dppt的默认值为20*4/3 = 26.67。96dpi为26.67 * 72 / 20dppx。 - obourgain

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接