如何在Java中获取真实的字符串高度?

问题描述 投票:0回答:6

我使用

FontMetrics.getHeight()
来获取字符串的高度,但它给了我一个错误的值,切断了字符串字符的下降部分。有更好的功能我可以使用吗?

java fonts awt
6个回答
24
投票

下面的

getStringBounds()
方法基于当前
GlyphVector
字体的
Graphics2D
,对于一行文本字符串非常有效:

public class StringBoundsPanel extends JPanel
{
    public StringBoundsPanel()
    {
        setBackground(Color.white);
        setPreferredSize(new Dimension(400, 247));
    }

    @Override
    protected void paintComponent(Graphics g)
    {
        super.paintComponent(g);

        Graphics2D g2 = (Graphics2D) g;

        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                            RenderingHints.VALUE_ANTIALIAS_ON);

        // must be called before getStringBounds()
        g2.setFont(getDesiredFont());

        String str = "My Text";
        float x = 140, y = 128;

        Rectangle bounds = getStringBounds(g2, str, x, y);

        g2.setColor(Color.red);
        g2.drawString(str, x, y);

        g2.setColor(Color.blue);
        g2.draw(bounds);

        g2.dispose();
    }

    private Rectangle getStringBounds(Graphics2D g2, String str,
                                      float x, float y)
    {
        FontRenderContext frc = g2.getFontRenderContext();
        GlyphVector gv = g2.getFont().createGlyphVector(frc, str);
        return gv.getPixelBounds(null, x, y);
    }

    private Font getDesiredFont()
    {
        return new Font(Font.SANS_SERIF, Font.BOLD, 28);
    }

    private void startUI()
    {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(this);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static void main(String[] args) throws Exception
    {
        final StringBoundsPanel tb = new StringBoundsPanel();

        SwingUtilities.invokeAndWait(new Runnable()
        {
            public void run()
            {
                tb.startUI();
            }
        });
    }
}

请注意,为了清楚起见,我省略了导入。

结果:

Result screenshot.


14
投票

是什么让您认为它返回了错误的值?您对返回结果的期望与规范不符的可能性要大得多。请注意,如果字体中的某些字形超过或低于这些值,那就完全没问题。

getMaxDescent()
getMaxAscent()
应该告诉您字体中任何字形的这些字段的绝对最大值。

如果你想知道特定字符串的指标,那么你肯定想调用

getLineMetrics()


8
投票

FontMetrics.getAscent()FontMetrics.getDescent() 可能可以解决问题。


3
投票

getHeight()
无法切断字符串的下行部分,只有拉动字符串才能做到这一点。您正在使用从
getHeight
返回的高度以某种方式绘制字符串,并且很可能您误用了高度。例如,如果将字符串的起点定位在 getHeight() 高的框的底部,则文本的基线将位于框的底部边缘,并且下降部分很可能会被剪裁。

文本几何是一个复杂的话题,充满了奇异的历史文物。正如其他人所建议的,使用

getAscent
getDescent
尝试将基线正确定位在盒子内。


3
投票

我最近编写了下面的代码,因为我需要对字体的特定范围进行像素完美的高度测量(例如:所有较低的字符或所有数字)。

如果您需要更快的代码(我的代码有 for 循环),我建议在启动时运行一次以获取数组中的所有值(例如从 1 到 100),然后使用该数组。

该代码基本上将输入字符串中的所有字符绘制在 250x250 位图上重叠的同一位置(如果需要则增加或减少),它开始从顶部查找像素,然后从底部查找像素,然后返回找到的最大高度。即使它是为字符范围设计的,它也适用于普通字符串。这意味着在评估常规字符串时存在某种冗余,因为某些字符重复。因此,如果您的输入字符串超过字母表计数 (26),请使用“tRange”输入:“abcd...z”和其他可能使用的字符。速度更快了。

希望有帮助。

public int getFontPixelHeight(float inSize, Paint sourcePaint, String tRange)
{
    // It is assumed that the font is already set in the sourcePaint

    int bW = 250, bH = 250;                                     // bitmap's width and height
    int firstContact = -1, lastContact = -2;                    // Used when scanning the pixel rows. Initial values are set so that if no pixels found, the returned result is zero.
    int tX = (int)(bW - inSize)/2, tY = (int)(bH - inSize)/2;   // Used for a rough centering of the displayed characters

    int tSum = 0;

    // Preserve the original paint attributes
    float oldSize = sourcePaint.getTextSize();
    int oldColor = sourcePaint.getColor();
    // Set the size/color
    sourcePaint.setTextSize(inSize); sourcePaint.setColor(Color.WHITE);

    // Create the temporary bitmap/canvas
    Bitmap.Config bConf = Bitmap.Config.ARGB_8888;
    Bitmap hld = Bitmap.createBitmap(250, 250, bConf);
    Canvas canv = new Canvas(hld);

    for (int i = 0; i < bH; i++)
    {
        for (int j = 0; j < bW; j++)
        {
            hld.setPixel(j, i, 0); // Zero all pixel values. This might seem redundant, but I am not quite sure that creating a blank bitmap means the pixel color value is indeed zero, and I need them to be zero so the addition performed below is correct.
        }
    }

    // Display all characters overlapping at the same position
    for (int i = 0; i < tRange.length(); i++)
    {
        canv.drawText("" + tRange.charAt(i), tX, tY, sourcePaint);
    }

    for (int i = 0; i < bH; i++)
    {
        for (int j = 0; j < bW; j++)
        {
            tSum = tSum + hld.getPixel(j, i);
        }

        if (tSum > 0) // If we found at least a pixel, save row index and exit loop
        {
            firstContact = i;
            tSum = 0;   // Reset
            break;
        }   
    }

    for (int i = bH - 1; i > 0 ; i--)
    {
        for (int j = 0; j < bW; j++)
        {
            tSum = tSum + hld.getPixel(j, i);
        }

        if (tSum > 0) // If we found at least a pixel, save row index and exit loop
        {
            lastContact = i;
            break;
        }   
    }

    // Restore the initial attributes, just in case the paint was passed byRef somehow
    sourcePaint.setTextSize(oldSize);
    sourcePaint.setColor(oldColor);

    return lastContact - firstContact + 1;
}

0
投票
import javax.swing.*;
import java.awt.*;
public class CustomTextPanel
        extends JPanel
{
    private StringBuilder text;
    private int width, height, x, y;
    public CustomTextPanel(String text)
    {
        setBackground(Color.BLUE);
        this.text = new StringBuilder(text);
    }
    public void setText(String text)
    {
        this.text = new StringBuilder(text);
    }
    @Override
    public void paintComponent(Graphics g)
    {
        if (getParent() != null)
        {
            width = getParent().getWidth() - 40;
            height = getParent().getHeight() - 40;
            x = 20;
            y = 20;
            repaint();
        }

        var g2 = (Graphics2D) g;
        g2.setPaint(Color.RED);

        // here is rectangle logic
        var fontMetrics = g2.getFontMetrics();
        g2.drawString(String.valueOf(text), x, y);
        g2.setPaint(Color.BLUE);
        var outline = new Rectangle(x, y - fontMetrics.getMaxAscent(), width, fontMetrics.getMaxDescent() + fontMetrics.getMaxAscent());
        g2.draw(outline);
    }
    public static void main(String[] args)
    {
        JFrame frame = new JFrame();
        frame.setBounds(0, 0, 600, 400);
        frame.getContentPane().add(new CustomTextPanel("abgf"));
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }
}

输出:

© www.soinside.com 2019 - 2024. All rights reserved.