当前位置: 首页 > 知识库问答 >
问题:

计算文本的正确宽度

沙星波
2023-03-14

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

我浏览了整个PDF规范,详细阅读了其中涉及图形和文本的部分,但没有任何效果。据我所知,字形坐标空间设置在用户坐标空间的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:https://github.com/ascheucher/pdf-stamp-prototype

谢谢你的帮助!

共有1个答案

杜苏燕
2023-03-14

不幸的是,问题和评论仅仅包括(通过运行示例项目)两个源文档的实际结果和描述

注释文本应在顶部和底部标记上居中对齐,在右侧标记上与左侧对齐,在左侧标记上与右侧对齐。这种对齐方式对我不起作用,就像字体一样。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不能包括的文档。

OP的代码使用以下构造函数向当前页面添加内容流:

PDPageContentStream contentStream = new PDPageContentStream(doc, page, true, true);

此构造函数追加(第一true)和压缩(第二true),但不幸的是,它在先前存在的内容留下的图形状态下继续。

对于手头的观察结果来说,图形状态的重要性的详细信息:

  • 变换矩阵-它可能已被更改为缩放(或旋转,歪斜,移动...)添加的任何新内容
  • 字符行间距-它可能已被更改为将添加的任何新字符彼此更近或更远
  • 字行间距-它可能已被更改为将添加的任何新词彼此更近或更远
  • 水平缩放-它可能已被更改为缩放任何添加的新字符
  • 文本上升-它可能已被更改为替换垂直添加的任何新字符

因此,应选择一个构造函数,该构造函数也会重置图形状态:

PDPageContentStream contentStream = new PDPageContentStream(doc, page, true, true, true);

第三个true告诉PDFBox重置图形状态,即用保存状态/恢复状态运算符对包围前一个内容。

这与给定的示例文档有关,至少转换矩阵已更改。

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;

执行长很惊讶

*0.043f近似于一个文档,但无法用于下一个文档。

有两个因素构成了这个“神奇”数字:

>

  • 正如OP所说,字形坐标空间设置在用户坐标空间的1/1000中,该数字在字形空间中,因此因子为0.001。

    由于OP忽略了,他希望使用他选择的字体大小来确定字符串的宽度。但font对象不知道当前字体大小,并返回字体大小为1的宽度。当OP动态选择字体大小为Math时。最小值(页面宽度、页面高度)/20,该系数各不相同。对于给定的两个样本文件,大约42个,但在其他文件中可能完全不同。

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

    contentStream.moveTextPositionByAmount(
        marker.endX + marker.getXTextOffset(textWidth, fontPadding),
        marker.endY + marker.getYTextOffset(fontSize, fontPadding));
    

    使用方法getXTextOffsetgetYTextOffset

    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;
    }
    

    getXTextOffset的情况下,我怀疑添加fontPadd对于位置。TOP位置。BOTTOM是有意义的,尤其是在OP的愿望下

    The annotating text should be center aligned on the top and bottom marker
    

    要使文本居中,不应使其偏离中心。

    getYTextOffset的情况比较困难。OP的代码建立在两个误解之上:它假设

    • moveTextPositionByAmount选择的文本位置在左下角,并且
    • 字体大小就是字符高度

    实际上,文本位置定位在基线上,下一个绘制的字形的字形原点将定位在那里,例如。

    因此,必须校正y定位,以考虑下降(以整个轮廓高度为中心)或仅使用上升(以高于基准轮廓高度为中心)。

    字体大小并不表示实际的字符高度,但其排列方式是,紧密间隔的文本行的标称高度为字体大小1的1个单位。“紧密间隔”意味着字体大小中包含少量额外的行间空间。

    从本质上讲,垂直居中必须决定居中位置、整个高度或高于基线高度、仅第一个字母、整个标签或所有字体图示符。PDFBox并不能为所有情况提供必要的信息,但提供了PDFont等方法。getFontBoundingBox()应该会有所帮助。

  •  类似资料:
    • 我本以为GridPane会根据该列中所有元素的最大首选宽度计算每个列的默认宽度。然而,在我的代码中,它似乎计算的宽度太小,导致列中的一个标签被剪切。 下面是我的“cashflowform.fxml”: 这就是我设置舞台的方式(注意我没有设置任何大小,所以我让它自己计算大小): 结果: 正如你所看到的,第一列太小了,无法完全显示我的“安默空”标签。为什么会这样,解决这个问题的最好方法是什么? PS:

    • 我正在尝试匹配从服务器下载的文件的md5sum。只有当总和匹配时,处理才会继续。 上面的代码并没有每次为某些文件正确提供md5sum。 当我去控制台检查md5sum时 下载文件的vimdiff未提供任何差异。。下载后的文件是正确的。 我无法在上述代码中看到问题。 我正在尝试更改缓冲区大小。但没有运气,所以我猜这不是因为缓冲区大小等。 问候Dheeraj Joshi

    • 问题内容: 我想在UILabel旁边显示图像,但是UILabel具有可变的文本长度,所以我不知道将图像放置在哪里。我该怎么做? 问题答案: -[NSString sizeWithFont:forWidth:lineBreakMode:]有什么用? 这个问题可能有您的答案,对我有用。 2014年,我根据下面的诺伯特的超方便评论编辑了这个新版本!这样就可以了。干杯

    • 问题内容: 我想使用JavaScript计算字符串的宽度。是否可以不必使用等宽字体? 如果不是内置的,我唯一的想法就是为每个字符创建一个宽度表,但这是非常不合理的,特别是支持Unicode和不同类型的大小(以及与此相关的所有浏览器)。 问题答案: 创建具有以下样式的DIV。在JavaScript中,设置要测量的字体大小和属性,将字符串放入DIV中,然后读取DIV的当前宽度和高度。它将拉伸以适合内容

    • null 我相信这个答案是正确的,但我无法证明。有人能证明它为什么起作用或提供一个反例吗?

    • 晚上好!我是一名律师,我经常要计算被判刑的人多久才能获得福利,比如假释。 它的工作原理如下: 首先,我需要得到一些主要变量,比如那个人开始服刑的那一天(他被捕的那一天)。这将是第一项福利的基准日期。假设有人在2014年11月12日被捕。 我必须做的第二件事是知道每项罪行的判决是什么(有时这个人被判犯有不止一项罪行,对于每项罪行,都有不同的计算方法。假设这个人被判犯有两项罪行: 对于第一项罪行(这是