我正在用 Java 编写自己的 pdf 生成库,但在字体/文本渲染方面遇到了一些麻烦。Java 中显示的文本(字体、字间距、字符间距...)与 PDF 中显示的文本不同。
在下面的示例中,我使用的是 PDF 基本字体之一的字体“Time New Roman”(因此我不必计算所有字体指标并将其输出到 pdf 中)。
所以具体来说,在我生成的 PDF 中,我有这个:
BT
/F5 16 Tf
849 921 Td
(Normal Return Distribution) Tj
ET
字体 F5 由对象 29 0 R 定义,即(仅 basefont,因此未指定文本度量):
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 中,文本大于矩形。
这是一个屏幕截图(我通过截取显示我的 PDF 的 Adobe Acrobat Reader 的屏幕截图并截取显示缓冲图像的程序的屏幕截图来构建它;然后将 pdf 屏幕截图的部分复制/粘贴到我的矩形下方程序屏幕截图到 MSPaint。要具有相同的矩形大小,我必须在 Adobe 中以原始大小的 65.5% 显示 pdf):
所以我们可以看到java en adobe中用来显示文字的字体是一样的。但文字在 Adobe 中似乎有点大。事实上,如果我叠加两个单词(一个来自 java 和一个来自 adobe 的顶部),似乎单词间距是相同的,字母间距也是一样,但有些字母有 1 个像素宽度差异。
为什么?我能做些什么来解决这个问题?我尝试使用字符间距(Tc 运算符)、字间距(Tw 运算符)、水平缩放(Tz 运算符)来播放(pdf 格式);我认为它可以“工作”;但是为什么两个程序中的缩放/间距/...不一样?这些(默认)参数不是字体文件的一部分(这是一个真正的类型)?以及如何正确检索它们(无需手动将参数放入我的 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 中的矩形重叠。因此,对于 Tahoma 字体,我必须将我的 EM 限制(进入我的蛮力循环)设置为 780;Verdana 字体为 850;... 显示相似的文本(不完全相同,但可能是由于抗锯齿算法?)(见下面的屏幕截图)。所以它不是一个恒定的“限制”(理论上必须等于 1000),而是一个可变的限制……对吗?(我认为不是)如果是,如何找到这个限制?如果不是,有什么问题?
再次感谢。
编辑
只需将字体大小设置为 1000 并且无需暴力破解即可找到 EM/Line 高度大小,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 中的文本要小一些(宽度)。