JavaFX字体度量

8

我想要将文本在面板中水平和垂直方向上准确地居中。使用字体度量和测试程序,我得到以下结果:

输入图像描述

这个测试引发了以下问题:

  1. 为什么上升值(顶部黑线)如此高?我期望它跨越“T”的顶部。品红线是“lineheight”值,因此我认为这是任何位于其上方的文本的基线。
  2. 如果黑线包括行间距,为什么没有“T”的顶部的测量值?
  3. 是否有一种获取精确边界框的方法,还是我必须以图形方式扫描文本图像以找到边界?显然,左边和右边的值也包括某种间距,因此扫描似乎是唯一的解决方案。

请查看此链接 - trashgod
这个问题被标记为 javafx,是否正确?据我所知,字体测量数据在 JavaFX API 中没有暴露,因此这个问题似乎与JavaFX没有直接关联。 - jewelsea
1
您可以将测试程序的代码放在问题中以便分享。 - jewelsea
弗兰克: 正确。"行间距是从下行线的底部到下一行顶部的推荐距离。" - trashgod
通过Frank的自我回答,可以明确标签是正确的,这是一个JavaFX应用程序。为了获取Oracle JavaFX实现内部使用的字体度量信息,Frank使用了com.sun.javafx API,这些API不属于受支持的javafx. API之列。 - jewelsea
显示剩余2条评论
2个回答

12

这是Frank的reportSize函数的另一种实现方式:

public void reportSize(String s, Font myFont) {
    Text text = new Text(s);
    text.setFont(myFont);
    Bounds tb = text.getBoundsInLocal();
    Rectangle stencil = new Rectangle(
            tb.getMinX(), tb.getMinY(), tb.getWidth(), tb.getHeight()
    );

    Shape intersection = Shape.intersect(text, stencil);

    Bounds ib = intersection.getBoundsInLocal();
    System.out.println(
            "Text size: " + ib.getWidth() + ", " + ib.getHeight()
    );
}

此实现使用形状相交来确定呈现形状的边界框大小,不包含空白区域。该实现不依赖于Java 9+中用户应用程序代码可能无法直接访问的 com.sun 包类。


3

经过一些尝试,我想出了这个解决方案:

在此输入图片描述

这是生成它的代码:

public void getBoundingBox(String s, Font myFont) {                             

    final FontMetrics fm = Toolkit.getToolkit().getFontLoader().getFontMetrics(myFont); 

    final Canvas canvas = new Canvas(fm.computeStringWidth(s), fm.getAscent() + fm.getDescent());        
    final GraphicsContext gc = canvas.getGraphicsContext2D();

    gc.setFill(Color.RED);                  // Just an abitrary color
    gc.setTextBaseline(VPos.TOP);           // This saves having to scan the bottom
    gc.setFont(myFont);

    gc.fillText(s, -fm.getLeading(), 0);    // This saves having to scan the left

    // Get a snapshot of the canvas
    final WritableImage image = canvas.snapshot(null, null);
    final PixelReader pr = image.getPixelReader();

    final int h = (int) canvas.getHeight();
    final int w = (int) canvas.getWidth();

    int x;
    int y = 0;

    // Scan from the top down until we find a red pixel

    boolean found = false;
    while (y < h && !found) {
        x = 0;
        while (x < w && !found) {
            found = pr.getColor(x, y).equals(Color.RED);
            x++;
        }
        y++;
    }
    int yPos = y - 2;

    // Scan from right to left until we find a red pixel

    x = w;        
    found = false;
    while (x > 0 && !found) {
        y = 0;           
        while (y < h && !found) {
            found = pr.getColor(x, y).equals(Color.RED);
            y++;
        }
        x--;
    }
    int xPos = x + 3;

    // Here is a visible representation of the bounding box

    Rectangle mask = new Rectangle(0, yPos, xPos, h - yPos);

    mask.setFill(Color.rgb(0, 0, 255, 0.25));       
    root.getChildren().addAll(canvas, mask);   // root is a global AnchorPane

    System.out.println("The width of the bounding box is " + xPos);
    System.out.println("The height of the bounding box is " + (h - yPos));
}

FontMetrics需要两个导入:

 import com.sun.javafx.tk.FontMetrics;
 import com.sun.javafx.tk.Toolkit;

例如,可以像这样调用boundingbox:

并且如下:

 Font myFont = new Font("Arial", 100.0); 
 getBoundingBox("Testing", myFont);

它解决了我的问题,我希望这对其他人也有用。

2
我已经成功地使用了Toolkit.getToolkit().getFontLoader().getFontMetrics(..),就像你所描述的那样,但是com.sun.javafx.tk.Toolkit和com.sun.javafx.tk.FontMetrics都是“内部”包。有人知道如何只使用公共的javafx.*包获取等效信息吗?请参见https://bugs.openjdk.java.net/browse/JDK-8090775。 - Kay
3
在Java9中,fm.computeStringWidth(String)已被getCharWidth(char)所取代,因此这个示例不再起作用。 - Kay

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接