如何在Java中获得真正的字符串高度?

时间:2023-02-04 17:24:24

I'm using FontMetrics.getHeight() to get the height of the string, but it gives me a wrong value, cutting off the descenders of string characters. Is there a better function I can use?

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

5 个解决方案

#1


19  

The getStringBounds() method below is based on the GlyphVector for the current Graphics2D font, which works very well for one line string of text:

下面的getStringBounds()方法基于当前Graphics2D字体的GlyphVector,它适用于一行文本字符串:

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

Note that I've omitted the imports for clarity.

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

The result:

如何在Java中获得真正的字符串高度?

#2


12  

What makes you think it returns the wrong value? It's far more probable that your expectation of what it returns does not match the specification. Note that it's perfectly fine if some glyphs in the Font go over or under those values.

是什么让你认为它返回错误的价值?你对它返回的东西的期望与规格不符的可能性要大得多。请注意,如果Font中的某些字形超过或低于这些值,则完全没问题。

getMaxDescent() and getMaxAscent() should tell you the absolute maximum values of those fields for any glyph in the Font.

getMaxDescent()和getMaxAscent()应该告诉你Font中任何字形的那些字段的绝对最大值。

If you want to know the metrics for a specific String, then you definitely want to call getLineMetrics().

如果您想知道特定String的指标,那么您肯定想要调用getLineMetrics()。

#3


7  

FontMetrics.getAscent() and FontMetrics.getDescent() might do the trick.

FontMetrics.getAscent()和FontMetrics.getDescent()可能会成功。

#4


3  

I recently wrote the code below as I needed pixel perfect height measurements for specific ranges of the font (for example: all lower characters, or all numbers).

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

If you need faster code (mine has for loops) I would recommend running it once at the start-up to get all values (for example from 1 to 100) in an array and then use the array instead.

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

The code basically draws all characters from the input string at the same place overlapped on a 250x250 bitmap (increase or reduce if needed), it starts looking for pixels from top, then from bottom, then it returns the maximum height found. It works with normal strings even if it was designed for character ranges. This means there is a sort of redundancy when evaluating regular strings as some of the characters repeat. So if your imput string exceeds the alphabet count (26), use as 'tRange' imput: "abcd...z" and other characters that may be used. It is faster.

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

Hope that helps.

希望有所帮助。

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

#5


2  

getHeight() can't cut off the descenders of a string, only drawing the string can do that. You are using the height returned from getHeight to draw the string somehow, and likely you are misusing the height. For example, if you position the start point of the string at the bottom of a box that is getHeight() high, then the baseline of your text will sit on the bottom edge of the box, and very likely the descenders will be clipped.

getHeight()不能切断字符串的下延,只绘制字符串就可以做到。您正在使用getHeight返回的高度以某种方式绘制字符串,并且可能是您滥用高度。例如,如果将字符串的起点定位在getHeight()高的框的底部,则文本的基线将位于框的底部边缘,并且很可能会剪切下划线。

Text geometry is a complex topic, infused with bizarre historical artifacts. As others have suggested, use getAscent and getDescent to try to position the baseline properly within your box.

文本几何是一个复杂的主题,注入了奇异的历史文物。正如其他人所建议的那样,使用getAscent和getDescent尝试在您的框中正确定位基线。

#1


19  

The getStringBounds() method below is based on the GlyphVector for the current Graphics2D font, which works very well for one line string of text:

下面的getStringBounds()方法基于当前Graphics2D字体的GlyphVector,它适用于一行文本字符串:

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

Note that I've omitted the imports for clarity.

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

The result:

如何在Java中获得真正的字符串高度?

#2


12  

What makes you think it returns the wrong value? It's far more probable that your expectation of what it returns does not match the specification. Note that it's perfectly fine if some glyphs in the Font go over or under those values.

是什么让你认为它返回错误的价值?你对它返回的东西的期望与规格不符的可能性要大得多。请注意,如果Font中的某些字形超过或低于这些值,则完全没问题。

getMaxDescent() and getMaxAscent() should tell you the absolute maximum values of those fields for any glyph in the Font.

getMaxDescent()和getMaxAscent()应该告诉你Font中任何字形的那些字段的绝对最大值。

If you want to know the metrics for a specific String, then you definitely want to call getLineMetrics().

如果您想知道特定String的指标,那么您肯定想要调用getLineMetrics()。

#3


7  

FontMetrics.getAscent() and FontMetrics.getDescent() might do the trick.

FontMetrics.getAscent()和FontMetrics.getDescent()可能会成功。

#4


3  

I recently wrote the code below as I needed pixel perfect height measurements for specific ranges of the font (for example: all lower characters, or all numbers).

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

If you need faster code (mine has for loops) I would recommend running it once at the start-up to get all values (for example from 1 to 100) in an array and then use the array instead.

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

The code basically draws all characters from the input string at the same place overlapped on a 250x250 bitmap (increase or reduce if needed), it starts looking for pixels from top, then from bottom, then it returns the maximum height found. It works with normal strings even if it was designed for character ranges. This means there is a sort of redundancy when evaluating regular strings as some of the characters repeat. So if your imput string exceeds the alphabet count (26), use as 'tRange' imput: "abcd...z" and other characters that may be used. It is faster.

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

Hope that helps.

希望有所帮助。

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

#5


2  

getHeight() can't cut off the descenders of a string, only drawing the string can do that. You are using the height returned from getHeight to draw the string somehow, and likely you are misusing the height. For example, if you position the start point of the string at the bottom of a box that is getHeight() high, then the baseline of your text will sit on the bottom edge of the box, and very likely the descenders will be clipped.

getHeight()不能切断字符串的下延,只绘制字符串就可以做到。您正在使用getHeight返回的高度以某种方式绘制字符串,并且可能是您滥用高度。例如,如果将字符串的起点定位在getHeight()高的框的底部,则文本的基线将位于框的底部边缘,并且很可能会剪切下划线。

Text geometry is a complex topic, infused with bizarre historical artifacts. As others have suggested, use getAscent and getDescent to try to position the baseline properly within your box.

文本几何是一个复杂的主题,注入了奇异的历史文物。正如其他人所建议的那样,使用getAscent和getDescent尝试在您的框中正确定位基线。