Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/400.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 计算文本的正确宽度_Java_Pdf Generation_Pdfbox - Fatal编程技术网

Java 计算文本的正确宽度

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;

我需要阅读由AutoCAD导出为PDF的平面图,并使用PDFBox在其上放置一些带有文本的标记。 除了文字宽度的计算(写在标记旁边)之外,其他一切都很正常

我浏览了整个PDF规范,并详细阅读了其中涉及图形和文本的部分,但没有结果。据我所知,glyph坐标空间设置在用户坐标空间的1/1000处。因此,宽度需要放大1000倍,但它仍然是实际宽度的一小部分

这就是我为定位文本所做的:

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)
重载使用RGRG运算符隐式选择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动态选择字体大小为
    Math.min(pageWidth,pageHeight)/20
    时,该系数会发生变化。对于给定的两个样本文档,大约42个,但在其他文档中可能完全不同

定位文本 OP的代码从标识文本矩阵开始如下定位文本:

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
的情况更加困难。