Java: получение шрифта с определенной высотой в пикселях - PullRequest
6 голосов
/ 29 апреля 2011

Легко определить визуализированную высоту шрифта с помощью FontMetrics, но как быть с другой? Как я могу получить шрифт, который будет соответствовать определенной высоте в пикселях?

«Дайте мне Verdana в размере 30 пикселей в высоту от восходящего к спускающемуся».

Как мне спросить об этом Java?

Ответы [ 4 ]

6 голосов
/ 25 октября 2014

Я знаю, что это очень старый вопрос, но кто-то может найти его:

Высота шрифта в Java (и во многих других местах) указывается в «типографских точках», которые определяются примерно как 1/72 дюйма.

Чтобы вычислить точки, необходимые для определенной высоты пикселя, вы должны использовать следующее:

double fontSize= pixelSize * Toolkit.getDefaultToolkit().getScreenResolution() / 72.0;

Я еще не тестировал это, но похоже, что он работает для мониторов, которые я использовал. Я сообщу, если найду случай, когда это не сработает.

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

FontMetrics m= g.getFontMetrics(font); // g is your current Graphics object
double totalSize= fontSize * (m.getAscent() + m.getDescent()) / m.getAscent();

Конечно, фактическая высота в пикселях некоторых конкретных букв будет зависеть от буквы и используемого шрифта, поэтому, если вы хотите убедиться, что ваша буква "H" имеет какое-то точное количество пикселей в высоту, вы все равно можете захотеть используйте методы проб и ошибок, упомянутые в других ответах. Просто имейте в виду, что если вы используете эти методы, чтобы получить размер для каждого конкретного текста, который вы хотите отобразить (как предложено @Bob), у вас может получиться случайный беспорядок font-size на вашем экране, где текст типа «туз» "будет иметь гораздо большие буквы, чем" Tag ". Чтобы избежать этого, я бы выбрал одну конкретную букву или последовательность букв («T» или «Tg» или что-то в этом роде) и установил ее один раз на высоту своего пикселя, а затем использовал бы размер шрифта, который вы получаете от этого везде.

6 голосов
/ 29 апреля 2011

Джен,

Я не думаю, что есть "прямой" способ найти шрифт по высоте; только косвенный путь ... путем циклического просмотра размеров и проверки высоты каждого из них <= требуемая высота. </p>

Если вы делаете это один раз, просто просматривайте их ... если вы делаете это "на лету", тогда выполняйте бинарный поиск, это будет быстрее.

Приветствия. Кит.

5 голосов
/ 29 апреля 2011

Я не знаю, как получить шрифт по его фактической высоте в пикселях.Это зависит от контекста, в котором он используется, поэтому, вероятно, нет более короткого пути, чем выборка для лучшего соответствия.Должно быть довольно быстро искать размеры вверх или вниз от проектной высоты.Вот пример метода, который делает это:

public Font getFont(String name, int style, int height) {
    int size = height;
    Boolean up = null;
    while (true) {
        Font font = new Font(name, style, size);
        int testHeight = getFontMetrics(font).getHeight();
        if (testHeight < height && up != Boolean.FALSE) {
            size++;
            up = Boolean.TRUE;
        } else if (testHeight > height && up != Boolean.TRUE) {
            size--;
            up = Boolean.FALSE;
        } else {
            return font;
        }
    }
}
1 голос
/ 12 октября 2011

Код WhiteFang34 полезен в сочетании со следующим методом, который возвращает фактическую высоту конкретной строки.Он может быть немного медленным для рендеринга в реальном времени, особенно для больших шрифтов / строк, и я уверен, что он может быть дополнительно оптимизирован, но пока он отвечает моим собственным потребностям и достаточно быстр для запуска в бэкэнд-процессе.

/*
 * getFontRenderedHeight
 * *************************************************************************
 * Summary: Font metrics do not give an accurate measurement of the rendered
 * font height for certain strings because the space between the ascender
 * limit and baseline is not always fully used and descenders may not be
 * present. for example the strings '0' 'a' 'f' and 'j' are all different
 * heights from top to bottom but the metrics returned are always the same.
 * If you want to place text that exactly fills a specific height, you need
 * to work out what the exact height is for the specific string. This method
 * achieves that by rendering the text and then scanning the top and bottom
 * rows until the real height of the string is found.
 */
/**
 * Calculate the actual height of rendered text for a specific string more
 * accurately than metrics when ascenders and descenders may not be present
 * <p>
 * Note: this method is probably not very efficient for repeated measurement
 * of large strings and large font sizes but it works quite effectively for
 * short strings. Consider measuring a subset of your string value. Also
 * beware of measuring symbols such as '-' and '.' the results may be
 * unexpected!
 * 
 * @param string
 *            The text to measure. You might be able to speed this process
 *            up by only measuring a single character or subset of your
 *            string i.e if you know your string ONLY contains numbers and
 *            all the numbers in the font are the same height, just pass in
 *            a single digit rather than the whole numeric string.
 * @param font
 *            The font being used. Obviously the size of the font affects
 *            the result
 * @param targetGraphicsContext
 *            The graphics context the text will actually be rendered in.
 *            This is passed in so the rendering options for anti-aliasing
 *            can be matched.
 * @return Integer - the exact actual height of the text.
 * @author Robert Heritage [mrheritage@gmail.com]
 */
public Integer getFontRenderedHeight(String string, Font font, Graphics2D targetGraphicsContext) {
    BufferedImage image;
    Graphics2D g;
    Color textColour = Color.white;

    // In the first instance; use a temporary BufferedImage object to render
    // the text and get the font metrics.
    image = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
    g = image.createGraphics();
    FontMetrics metrics = g.getFontMetrics(font);
    Rectangle2D rect = metrics.getStringBounds(string, g);

    // now set up the buffered Image with a canvas size slightly larger than
    // the font metrics - this guarantees that there is at least one row of
    // black pixels at the top and the bottom
    image = new BufferedImage((int) rect.getWidth() + 1, (int) metrics.getHeight() + 2, BufferedImage.TYPE_INT_RGB);
    g = image.createGraphics();

    // take the rendering hints from the target graphics context to ensure
    // the results are accurate.
    g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, targetGraphicsContext.getRenderingHint(RenderingHints.KEY_ANTIALIASING));
    g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, targetGraphicsContext.getRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING));

    g.setColor(textColour);
    g.setFont(font);
    g.drawString(string, 0, image.getHeight());

    // scan the bottom row - descenders will be cropped initially, so the
    // text will need to be moved up (down in the co-ordinates system) to
    // fit it in the canvas if it contains any. This may need to be done a
    // few times until there is a row of black pixels at the bottom.
    boolean foundBottom, foundTop = false;
    int offset = 0;
    do {
        g.setColor(Color.BLACK);
        g.fillRect(0, 0, image.getWidth(), image.getHeight());
        g.setColor(textColour);
        g.drawString(string, 0, image.getHeight() - offset);

        foundBottom = true;
        for (int x = 0; x < image.getWidth(); x++) {
            if (image.getRGB(x, image.getHeight() - 1) != Color.BLACK.getRGB()) {
                foundBottom = false;
            }
        }
        offset++;
    } while (!foundBottom);

    System.out.println(image.getHeight());

    // Scan the top of the image downwards one line at a time until it
    // contains a non-black pixel. This loop uses the break statement to
    // stop the while loop as soon as a non-black pixel is found, this
    // avoids the need to scan the rest of the line
    int y = 0;
    do {
        for (int x = 0; x < image.getWidth(); x++) {
            if (image.getRGB(x, y) != Color.BLACK.getRGB()) {
                foundTop = true;
                break;
            }
        }
        y++;
    } while (!foundTop);

    return image.getHeight() - y;
}
...