Fonts JavaFXFontMetrics

Fonts JavaFXFontMetrics,fonts,javafx,Fonts,Javafx,我试图在水平和垂直方向将文本准确地放置在窗格的中心。使用fontmetrics和测试程序,我得到以下结果: 该测试提出了以下问题: 为什么上升值(黑色顶线)这么高?我以为它会穿过T字的顶端。品红色线是“lineheight”值,因此我假设这是它上面任何文本的基线 如果黑线包括行距,为什么“T”的顶部没有测量值 有没有一种方法可以得到一个精确的边界框,或者我必须用图形化的线条扫描文本图像才能找到边界?显然,左、右值还包含某种间距,因此扫描似乎是唯一的解决方案 经过一点实验,我想出了这个解决方案:

我试图在水平和垂直方向将文本准确地放置在窗格的中心。使用fontmetrics和测试程序,我得到以下结果:

该测试提出了以下问题:

  • 为什么上升值(黑色顶线)这么高?我以为它会穿过T字的顶端。品红色线是“lineheight”值,因此我假设这是它上面任何文本的基线
  • 如果黑线包括行距,为什么“T”的顶部没有测量值
  • 有没有一种方法可以得到一个精确的边界框,或者我必须用图形化的线条扫描文本图像才能找到边界?显然,左、右值还包含某种间距,因此扫描似乎是唯一的解决方案

  • 经过一点实验,我想出了这个解决方案:

    下面是生成它的代码:

    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));
    }
    
    并按如下方式调用边界框,例如:

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

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

    以下是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
    包类

    请看。谢谢,我已经看到了,但是getMaxAscent()与getAscent()的值相同,并且不是从基线到角色顶部的距离。它给出了我上图中的黑色顶线。该字体中没有延伸到黑线的字符。此问题被标记为
    javafx
    ,是否应该?据我所知,字体度量数据未在中公开,因此该问题似乎与JavaFX没有直接关系。您可能希望通过在问题中放置代码来共享您的测试程序。弗兰克:正确。“是从下行线底部到下一行顶部的建议距离。”如您所述,我已成功使用Toolkit.getToolkit().getFontLoader().getFontMetrics(..),但com.sun.javafx.tk.Toolkit以及com.sun.javafx.tk.FontMetrics是“内部”包。有人知道只使用公共javafx.*包获得同等信息的方法吗?另请参见Java9中的fm.computeStringWidth(String)已替换为getCharWidth(char),因此此示例不再有效。
    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()
        );
    }