如何在Eclipse SWT中获取字符实际占用的精确高度?
问题描述
我开发了一个Eclipse应用,需要获取文本实际占用区域的精确高度。但GC.textExtent方法无法满足需求:对于Arial这类非等宽字体,它返回的高度值对不同尺寸的字符(如'a'和带下沉的'j')是相同的,返回的矩形无法完全包含'j'、'g'这类带有下沉部分的字符。
我编写了测试代码复现该问题,在静态代码块中计算'a'和'j'的textExtent高度,结果二者均为21,触发了预期外的异常。代码如下:
import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.widgets.Display; class SomeClass { ... static { Image imgTemp = new Image(Display.getDefault(), 123, 123); GC gcTemp = new GC(imgTemp); Font fontTemp = new Font(Display.getDefault(), "Arial", 13, SWT.NORMAL); int shortStringSize = gcTemp.textExtent("a").y; int tallStringSize = gcTemp.textExtent("j").y; imgTemp.dispose(); fontTemp.dispose(); gcTemp.dispose(); if (shortStringSize == tallStringSize) { throw new RuntimeException("I expected letter 'a' have smaller" + " text extent than letter 'j', because it is visually" + " shorter.\n" + "'j' size = " + tallStringSize + "\n" + "'a' size = " + shortStringSize); } } ... }
错误输出如下:
<big stacktrace> Caused by: java.lang.RuntimeException: I expected letter 'a' have smaller text extent than letter 'j', because it is visually shorter. 'j' size = 21 'a' size = 21 at <my file> <big stacktrace>
请问是否有办法获取依赖字符实际高度的字符串尺寸?
解决方案
核心原因
GC.textExtent()返回的高度是字体的标准行高(上升高度ascent + 下沉高度descent),这是字体设计时定义的统一高度,确保所有字符都能容纳在该范围内,因此无论单个字符的视觉形态如何,返回的高度值都相同。
两种可行方法
1. 使用GC.textBounds()获取字符精确边界
textBounds()会返回包围目标字符/字符串的最小矩形,矩形的高度即为该字符实际占用的垂直空间。注意必须先给GC设置目标字体,否则会使用默认字体导致结果错误。示例代码:
Image imgTemp = new Image(Display.getDefault(), 123, 123); GC gcTemp = new GC(imgTemp); Font fontTemp = new Font(Display.getDefault(), "Arial", 13, SWT.NORMAL); gcTemp.setFont(fontTemp); // 必须设置GC的目标字体 // 获取'a'的实际边界矩形 Rectangle aBounds = gcTemp.textBounds(0, 0, "a"); int aActualHeight = aBounds.height; // 获取'j'的实际边界矩形 Rectangle jBounds = gcTemp.textBounds(0, 0, "j"); int jActualHeight = jBounds.height; // 清理资源 imgTemp.dispose(); fontTemp.dispose(); gcTemp.dispose();
2. 结合FontMetrics计算字符垂直范围
FontMetrics提供了字体的基线、最大上升/下沉高度等信息,配合textBounds可以更细致地获取字符的垂直位置信息:
Image imgTemp = new Image(Display.getDefault(), 123, 123); GC gcTemp = new GC(imgTemp); Font fontTemp = new Font(Display.getDefault(), "Arial", 13, SWT.NORMAL); gcTemp.setFont(fontTemp); FontMetrics metrics = gcTemp.getFontMetrics(); int baselineAscent = metrics.getAscent(); // 基线到字体顶部的距离 // 获取'j'的边界 Rectangle jBounds = gcTemp.textBounds(0, 0, "j"); // 字符顶部相对于基线的位置:baselineAscent + jBounds.y(jBounds.y通常为负值,代表向上偏移) // 字符底部相对于基线的位置:jBounds.y + jBounds.height // 字符实际高度即为jBounds.height // 清理资源 imgTemp.dispose(); fontTemp.dispose(); gcTemp.dispose();
内容的提问来源于stack exchange,提问作者Andrey Kachow
相关产品推荐
相关产品推荐

