Как получить реальную высоту строки в Java?

StackOverflow https://stackoverflow.com/questions/368295

  •  21-08-2019
  •  | 
  •  

Вопрос

я использую FontMetrics.getHeight() чтобы получить высоту строки, но он дает мне неправильное значение, обрезая нижние элементы строковых символов.Есть ли лучшая функция, которую я могу использовать?

Это было полезно?

Решение

А 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.

Другие советы

Что заставляет вас думать, что он возвращает неправильное значение?Гораздо более вероятно, что ваши ожидания того, что он вернет, не соответствуют спецификации.Обратите внимание: совершенно нормально, если некоторые глифы в шрифте выходят за эти значения или занижают их.

getMaxDescent() и getMaxAscent() должен сообщить вам абсолютные максимальные значения этих полей для любого глифа в шрифте.

Если вы хотите узнать метрики для конкретной строки, вам обязательно нужно позвонить getLineMetrics().

FontMetrics.getAscent() и FontMetrics.getDescent() может сработать.

Недавно я написал приведенный ниже код, так как мне нужны были точные измерения высоты в пикселях для определенных диапазонов шрифта (например:все младшие символы или все цифры).

Если вам нужен более быстрый код (у меня есть циклы 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;
}

getHeight() невозможно отрезать нижние элементы строки, это можно сделать только путем рисования строки.Вы используете высоту, возвращенную из getHeight чтобы как-то нарисовать строку, и, вероятно, вы неправильно используете высоту.Например, если вы разместите начальную точку строки внизу блока с высотой getHeight(), то базовая линия вашего текста будет находиться на нижнем крае блока, и, скорее всего, нижние элементы будут обрезаны.

Геометрия текста — сложная тема, наполненная причудливыми историческими артефактами.Как предлагали другие, используйте getAscent и getDescent чтобы попытаться правильно расположить базовую линию внутри рамки.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top