Java 计算文本的正确宽度
我需要阅读由AutoCAD导出为PDF的平面图,并使用PDFBox在其上放置一些带有文本的标记。 除了文字宽度的计算(写在标记旁边)之外,其他一切都很正常 我浏览了整个PDF规范,并详细阅读了其中涉及图形和文本的部分,但没有结果。据我所知,glyph坐标空间设置在用户坐标空间的1/1000处。因此,宽度需要放大1000倍,但它仍然是实际宽度的一小部分 这就是我为定位文本所做的:Java 计算文本的正确宽度,java,pdf-generation,pdfbox,Java,Pdf Generation,Pdfbox,我需要阅读由AutoCAD导出为PDF的平面图,并使用PDFBox在其上放置一些带有文本的标记。 除了文字宽度的计算(写在标记旁边)之外,其他一切都很正常 我浏览了整个PDF规范,并详细阅读了其中涉及图形和文本的部分,但没有结果。据我所知,glyph坐标空间设置在用户坐标空间的1/1000处。因此,宽度需要放大1000倍,但它仍然是实际宽度的一小部分 这就是我为定位文本所做的: float textWidth = font.getStringWidth(marker.id) * 0.043f;
float textWidth = font.getStringWidth(marker.id) * 0.043f;
contentStream.beginText();
contentStream.setTextScaling(1, 1, 0, 0);
contentStream.moveTextPositionByAmount(
marker.endX + marker.getXTextOffset(textWidth, fontPadding),
marker.endY + marker.getYTextOffset(fontSize, fontPadding));
contentStream.drawString(marker.id);
contentStream.endText();
*0.043f近似于一个文档,但无法用于下一个文档。
我是否需要重置除文本矩阵之外的任何其他变换矩阵
编辑:github上有一个完整的idea示例项目,包括测试和示例PDF:
谢谢你的帮助 不幸的是,问题和注释仅包括(通过运行示例项目)两个源文档的实际结果和描述 注释文字应在顶部和底部标记上居中对齐,在右侧标记上与左侧对齐,在左侧标记上与右侧对齐。这种对齐方式对我不起作用,因为font.getSTringWidth(..)只返回看起来的一小部分。两种PDF中的差异似乎有所不同 但没有具体的样本差异需要修复 然而,代码中有几个问题可能导致这样的观察(以及其他的观察!)。首先要解决这些问题;这可能已经解决了OP观察到的问题 拿哪个箱子 OP的代码从媒体盒中导出多个值:
PDRectangle pageSize = page.findMediaBox();
float pageWidth = pageSize.getWidth();
float pageHeight = pageSize.getHeight();
float lineWidth = Math.max(pageWidth, pageHeight) / 1000;
float markerRadius = lineWidth * 10;
float fontSize = Math.min(pageWidth, pageHeight) / 20;
float fontPadding = Math.max(pageWidth, pageHeight) / 100;
根据页面大小,选择这些选项似乎在视觉上令人愉悦。但媒体框通常不是最终显示或打印的页面大小,裁剪框是。因此,它应该是
PDRectangle pageSize = page.findCropBox();
(实际上,修剪框,即修剪后完成页面的预期尺寸,可能更合适;修剪框默认为裁剪框。有关详细信息,请阅读。)
这与给定的示例文档无关,因为它们不包含明确的裁剪框定义,因此裁剪框默认为媒体框。但是,它可能与其他文件相关,例如OP不能包括的文件
要使用哪个PDPageContentStream构造函数
OP的代码使用以下构造函数将内容流添加到当前页面:
PDPageContentStream contentStream = new PDPageContentStream(doc, page, true, true);
此构造函数追加(第一个true
)并压缩(第二个true
),但不幸的是,它继续处于预先存在的内容留下的图形状态
当前观测的重要图形状态详情:
- 变换矩阵-它可能已更改为缩放(或旋转、倾斜、移动…)任何新添加的内容
- 字符间距-可能已更改为使添加的任何新字符彼此更近或更远
- 单词间距-它可能已被更改为使添加的任何新词彼此更靠近或更远
- 水平缩放-它可能已更改为缩放添加的任何新字符
- 文字上升-可能已更改为替换垂直添加的任何新字符
PDPageContentStream contentStream = new PDPageContentStream(doc, page, true, true, true);
第三个true
告诉PDFBox重置图形状态,即使用保存状态/恢复状态运算符对包围前一个内容
这与给定的示例文档相关,至少转换矩阵已更改
设置并使用CalRGB颜色空间
OP的代码将笔划和非笔划颜色空间设置为校准颜色空间:
contentStream.setStrokingColorSpace(new PDCalRGB());
contentStream.setNonStrokingColorSpace(new PDCalRGB());
不幸的是,new PDCalRGB()
没有创建有效的CalRGB颜色空间对象,其所需的白点值丢失。因此,在选择校准颜色空间之前,请正确初始化它
此后,OP的代码使用
contentStream.setStrokingColor(marker.color.r, marker.color.g, marker.color.b);
contentStream.setNonStrokingColor(marker.color.r, marker.color.g, marker.color.b);
不幸的是,这些(int,int,int)
重载使用RG和RG运算符隐式选择DeviceRG颜色空间。要不覆盖当前颜色空间,请使用带有标准化(0..1)值的(float[])
重载
虽然这与观察到的问题无关,但会导致PDF查看器发出错误消息
计算绘制字符串的宽度
OP的代码使用
float textWidth = font.getStringWidth(marker.id) * 0.043f;
而OP对此感到惊讶
*0.043f近似于一个文档,但无法用于下一个文档
有两个因素构成了这个“神奇”数字:
- 正如OP所指出的,字形坐标空间设置在用户坐标空间的1/1000中,该数字在字形空间中,因此系数为0.001
- 由于OP已忽略,因此他希望使用所选字体大小获得字符串的宽度。但是font对象不知道当前字体大小,返回字体大小为1的宽度。当OP动态选择字体大小为
时,该系数会发生变化。对于给定的两个样本文档,大约42个,但在其他文档中可能完全不同Math.min(pageWidth,pageHeight)/20
contentStream.moveTextPositionByAmount(
marker.endX + marker.getXTextOffset(textWidth, fontPadding),
marker.endY + marker.getYTextOffset(fontSize, fontPadding));
使用方法getxtoffset
和getYTextOffset
:
public float getXTextOffset(float textWidth, float fontPadding) {
if (getLocation() == Location.TOP)
return (textWidth / 2 + fontPadding) * -1;
else if (getLocation() == Location.BOTTOM)
return (textWidth / 2 + fontPadding) * -1;
else if (getLocation() == Location.RIGHT)
return 0 + fontPadding;
else
return (textWidth + fontPadding) * -1;
}
public float getYTextOffset(float fontSize, float fontPadding) {
if (getLocation() == Location.TOP)
return 0 + fontPadding;
else if (getLocation() == Location.BOTTOM)
return (fontSize + fontPadding) * -1f;
else
return fontSize / 2 * -1;
}
在getxtoffset
的情况下,我怀疑为Location.TOP
和Location.BOTTOM添加fontPadding
是否有意义,特别是考虑到OP的需求
The annotating text should be center aligned on the top and bottom marker
要使文本居中,不应使其偏离中心
getYTextOffset
的情况更加困难。