我正在研究自己的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 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),但变量限制......是正确的吗? (我想不)如果是的话,如何找到这个限制?如果不是,那有什么不对?
再次感谢。
修改
只需将字体大小设置为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中略小(宽度)。
答案 0 :(得分:5)
这个答案基本上是我评论的综述。
首次尝试使用字体“Time New Roman”(实际上 Times-Roman )这是PDF基本字体之一(不是计算并输出PDF格式的pdf)和Java AWT的“TimesRoman”,导致
基本上:您的应用程序使用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中略小(宽度)。
查看
FontMetrics.charWidth
方法注释:请注意,字符串的前进不一定是字符前进的总和。 AWT另外应用字距调整等导致轻微偏差。但是,在PDF中,使用单个Tj操作,这些进步确实会增加。
如果要在PDF中使用字距调整,则必须明确写出标准宽度的偏差。这里 TJ 运算符非常方便,允许使用字符串和偏移量的混合数组作为参数。
如果您想用例如某些字符替换某些字符连字,你也必须自己做
答案 1 :(得分:1)
对此有许多可能的解释,所有这些都有助于使用PDF中定义的标准14种字体可能是合法的,但通常不是一件明智的事情。它介绍了你遇到的那种含糊之处。 PDF通常旨在避免这种含糊不清;在这种意义上允许非嵌入和未正确指定的字体是一个坏主意。
如果仔细观察文字中的字符形状,我可能会冒昧地说你实际上在看不同的字体。相似但又不同。例如,看一下“i”,在一个案例中,“i”上的点多高一点。原因可能是Adobe Reader拥有自己的字体集并且不使用系统字体(例如Java可能会这样)。想想看 - Adobe Reader如何能够正常显示这些字体,无论它运行的系统如何?
实际上可能会更糟。如果我搜索我的Adobe Reader安装程序,我找不到Times字体(不是像你说的那样“Times New Roman”,那是一个不同的字体)。所以很可能Adobe Reader使用不同的字体来模仿Times(以及其他一些基本的14种字体)。我不是100%肯定这一点,但我不是说Acrobat和Reader过去常常使用MultiMaster字体来模拟非嵌入字体。
此外,您在PDF中呈现文本的方式不使用字符间字距调整,而Java很可能足以应用一些额外的字距或使用字符替换(例如使用一个字形来代表组合“ffl”而不是三个单独的字符)。 PDF能够使用字距调整和那些特殊的字形,但你必须做的工作是确保它们被使用...
如果您想要绝对确定您的PDF看起来与Java渲染完全相同,请找出Java中的字符位置。然后编写PDF文件,使每个角色都位于完全相同的位置......