java / pdf文本呈现

时间:2013-08-01 15:21:51

标签: java pdf text fonts

我正在研究自己的pdf生成lib的Java,我遇到了一些字体/文本渲染问题。 Java中显示的文本(字体,字间距,字符间距等)与PDF中显示的文本不同。

在下面的示例中,我使用的是字体“Time New Roman”,它是PDF基本字体之一(因此我无法计算并将所有字体指标输出到pdf中)。

具体来说,在我生成的PDF中,我有这个:

BT
/F5 16 Tf
849 921 Td
(Normal Return Distribution) Tj
ET

字体F5由对象29 0 R定义,它是(仅为基本字符,因此未指定文本度量标准):

29 0 obj <</Type /Font /Subtype /Type1 /BaseFont /Times-Roman>>
endobj

在Java中,我正在使用:

g2d.setFont(new Font("TimesRoman", Font.PLAIN, 16));
g2d.drawString("Normal Return Distribution", 849, 921);

我已经将文本绘制成与文本边界匹配的矩形,并且在Java中它都可以(我在java中计算字符串边界),但在adobe acrobat reader中,文本比矩形大。

这是一个截图(我通过截取Adobe Acrobat Reader的屏幕截图显示我的PDF,并截取我的程序显示缓冲图像的截图;然后复制/粘贴pdf屏幕截图下面的部分我的程序的矩形截图到MSPaint。为了具有相同的矩形大小,我必须以原始大小的65.5%在Adobe中显示pdf:

Java vs PDF text output

因此我们可以看到java en adobe中用于显示文本的字体是相同的。但是Adobe的文字看起来有点大。事实上,如果我叠加两个单词(一个来自java在adobe之上的一个),似乎单词间距是相同的,字母间距也是,但有些字母有1个像素宽度差异。

为什么呢? 我该怎么做才能解决这个问题?我尝试使用字符间距(Tc运算符),字间距(Tw运算符),水平缩放(Tz运算符)来播放(pdf格式);我认为它可以“奏效”;但为什么两个程序中的缩放/间距/ ...不一样?这些(默认)参数不是Font文件的一部分(这是真正的类型)?以及如何正确检索它们(不手动将参数放入我的java代码中)?

由于

修改

因此,正如您已经解释的那样,我正在调查不使用pdf基本字体以确保Java和Adobe Reader使用相同的字体(ttf文件)。但我有一个问题(同样的问题?)。

在PDF输出中,我正在生成如下字体:

31 0 obj <<
/Type /Font
/FirstChar 0
/LastChar 255
/Widths[1298 ... 646]
/Name /F7
/Encoding /WinAnsiEncoding
/Subtype /TrueType /BaseFont /Tahoma /FontDescriptor 32 0 R
>>
endobj

32 0 obj <<
/Type /FontDescriptor
/Ascent 1299
/CapHeight 1298
/Descent -269
/Flags 32
/FontBBox [0 -269 2012 1299]
/FontName /Tahoma
/ItalicAngle 0
/StemV 126
/XHeight 1298
>>
endobj

如果我正确理解了规范,所有数字(宽度,上升,下降,......)都是相对于字形单位(基于1em?),其中1em = 1000(而1em是M字符的宽度)

因此要从java生成所有这些参数,我首先尝试找到正确的java字体大小,使M字符的宽度等于1000(因为Java不允许访问Font类或其他中的这些参数类;并且即使这些信息都在ttf文件中,PDF也需要它。)。

float size = 1f;
while (true) {
    font = font.deriveFont(size);
    fm = g2d.getFontMetrics(font);
    int em = fm.charWidth('M');
    if (em >= 1000)
        break ;
    size += 1;
}

然后我可以生成所有必需的参数。例如,对于Widths数组(每个字符的宽度):

String pdfWidths = "";
for (int i = 0; i <= 255; ++i) {
    int width = fm.charWidth(i);
    pdfWidths += width + " ";
}

但是这样做,我仍然在Adobe Viewer中将文本与矩形重叠。 所以我必须将我的EM限制(进入我的暴力循环)设置为Tahoma字体的780;对于Verdana字体为850; ...显示类似的文本(不完全相同,但也许是由于抗锯齿算法?)(见下面的截图)。所以它不是一个恒定的“限制”(必须在理论上等于1000),但变量限制......是正确的吗? (我想不)如果是的话,如何找到这个限制?如果不是,那有什么不对?

Java vs PDF text output - EM

再次感谢。

修改

只需将字体大小设置为1000并且没有强制执行以找到EM /行高度大小,pdf中的结果实际上是java。

font = font.deriveFont(1000f);
fm = g2d.getFontMetrics(font);
//Retrieve Widths attribute
_pdfWidths = "";
for (int i = _firstChar; i <= _lastChar; ++i) {
    int width = fm.charWidth(i);
    _pdfWidths += width + " ";
}

但是仍然有一些差别,也许是由于文字绘图算法(字距可能与java和adobe reader不同?)。如下图所示,我们可以看到Verdana的文本在pdf中比在java中略小(宽度)。

Java vs PDF text output - 1000

2 个答案:

答案 0 :(得分:5)

这个答案基本上是我评论的综述。

首次尝试使用字体“Time New Roman”(实际上 Times-Roman 这是PDF基本字体之一(不是计算并输出PDF格式的pdf)和Java AWT的“TimesRoman”,导致

Attempt with standard-14 Times-Roman

  

基本上:您的应用程序使用Java AWT认为TimesRoman明确的16pt,以自己的方式应用字体指标;您的PDF查看器在16个用户空间单位中使用它认为Times-Roman的内容,应用PDF规范中指定的字体指标。所有你可以期待的是一些相似之处(否则其中一个上下文会做出一个非常糟糕的选择)但不是完全不同。

大卫实际上在第1项(不同的字体)和第3项(不同的字距调整和替换应用)中更详细地解释了他的答案。

此外,

  

BTW:从PDF 1.5开始,不推荐对标准14字体给予特殊处理。 (ISO 32000-1中的第9.6.2.1节)。因此,通过不在PDF中明确包含字体度量,您可以执行已弃用很多年的事情。

下一次尝试不使用pdf基本字体以确保Java和Adobe Reader 使用相同的字体(ttf文件),需要计算要嵌入PDF的字符宽度。在这种情况下,假设所有数字(宽度,上升,下降,......)都相对于字形单位(基于1em?),其中1em = 1000(并且1em是M字符的宽度) 。因此,尝试找到正确的java字体大小,使M字符的宽度等于1000 ,然后生成所有必需的参数那个字体。

  

不,不是基于em,而是:字体定义一个标准尺寸的字形。安排此标准,使紧密间隔的文本行的标称高度为1个单位。因此,1000个字形空间单位是该标称行的高度。

这导致了问题究竟是什么“标称线”。幸运的是,反过来更容易接近:根据定义,大小为1的字体是“标称线“的高度为1.因此,

  

不应该使用1000 * fm.charWidth(i)填充 Widths 数组,其中fm是1号字体的指标吗?或者,由于AWT适用于int宽度,fm.charWidth(i)其中fm是1000大小的字体指标?

考虑到这一点,只需将字体大小设置为1000并且没有强制发现EM / Line高度大小,pdf中的结果实际上是java。但是仍然存在一些差异,也许是由于文本绘图算法(字距调整可能与java和adobe reader不同?)。如下图所示,我们可以看到Verdana的文本在pdf中比在java中略小(宽度)。

Attempt with embedded fonts and correct character widths

  

查看FontMetrics.charWidth方法注释:请注意,字符串的前进不一定是字符前进的总和。 AWT另外应用字距调整等导致轻微偏差。但是,在PDF中,使用单个Tj操作,这些进步确实会增加。

如果要在PDF中使用字距调整,则必须明确写出标准宽度的偏差。这里 TJ 运算符非常方便,允许使用字符串和偏移量的混合数组作为参数。

如果您想用例如某些字符替换某些字符连字,你也必须自己做

答案 1 :(得分:1)

对此有许多可能的解释,所有这些都有助于使用PDF中定义的标准14种字体可能是合法的,但通常不是一件明智的事情。它介绍了你遇到的那种含糊之处。 PDF通常旨在避免这种含糊不清;在这种意义上允许非嵌入和未正确指定的字体是一个坏主意。

  • 如果仔细观察文字中的字符形状,我可能会冒昧地说你实际上在看不同的字体。相似但又不同。例如,看一下“i”,在一个案例中,“i”上的点多高一点。原因可能是Adobe Reader拥有自己的字体集并且不使用系统字体(例如Java可能会这样)。想想看 - Adob​​e Reader如何能够正常显示这些字体,无论它运行的系统如何?

  • 实际上可能会更糟。如果我搜索我的Adobe Reader安装程序,我找不到Times字体(不是像你说的那样“Times New Roman”,那是一个不同的字体)。所以很可能Adobe Reader使用不同的字体来模仿Times(以及其他一些基本的14种字体)。我不是100%肯定这一点,但我不是说Acrobat和Reader过去常常使用MultiMaster字体来模拟非嵌入字体。

  • 此外,您在PDF中呈现文本的方式不使用字符间字距调整,而Java很可能足以应用一些额外的字距或使用字符替换(例如使用一个字形来代表组合“ffl”而不是三个单独的字符)。 PDF能够使用字距调整和那些特殊的字形,但你必须做的工作是确保它们被使用...

如果您想要绝对确定您的PDF看起来与Java渲染完全相同,请找出Java中的字符位置。然后编写PDF文件,使每个角色都位于完全相同的位置......