Java:获取具有特定高度(以像素为单位)的字体

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5829703/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 12:59:57  来源:igfitidea点击:

Java: Getting a font with a specific height in pixels

javafontsgraphics2dfontmetrics

提问by Tony the Pony

It's easy to determine the rendered height of a font using FontMetrics, but what about the other way around? How can I obtain a font that will fit into a specific height in pixels?

使用 确定字体的渲染高度很容易FontMetrics,但反过来呢?如何获得适合特定高度(以像素为单位)的字体?

"Give me Verdanain a size that is 30 pixels high from ascender to descender."

“给我Verdana一个从上升到下降高度为 30 像素的大小。”

How do I ask Java for this?

我如何向Java询问这个?

采纳答案by corlettk

I don't think there's a "direct" way to find a font by height; only an indirect way... by looping through the sizes, and testing the height of each is <= required height.

我认为没有一种“直接”的方式可以按高度查找字体;只有一种间接的方式......通过循环尺寸,并测试每个的高度是 <= 所需的高度。

If you're doing this once, just loop through them... if you've doing it "on the fly" then do a binary search, it'll be quicker.

如果您只执行一次,只需循环遍历它们……如果您“即时”执行此操作,则进行二分查找,速度会更快。

回答by Markus A.

I know this is a very old question, but someone might still find it:

我知道这是一个非常古老的问题,但仍然有人可能会发现它:

The font height in Java (and many other places) is given in "typographic points", which are defined as roughly 1/72nd of an inch.

Java(和许多其他地方)中的字体高度以“排版点”给出,定义为大约 1/72 英寸。

To calculate the points needed for a certain pixel height, you should be able to use the following:

要计算特定像素高度所需的点,您应该能够使用以下内容:

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

I haven't tested this extensively yet, but it seems to work for the monitors that I've used. I'll report back if I ever find a case where it doesn't work.

我还没有对此进行广泛的测试,但它似乎适用于我使用过的显示器。如果我发现它不起作用的情况,我会回来报告。

For the standard system fonts I've used this with, this sets the height of a capital letter (i.e. the ascent) to the provided pixel size. If you need to set the ascent+descent to the pixel size, you can correct the value using the FontMetrics:

对于我使用过的标准系统字体,这将大写字母的高度(即上升)设置为提供的像素大小。如果需要将 ascent+descent 设置为像素大小,可以使用FontMetrics更正该值:

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

Of course, the actual pixel-height of some specific letters will depend on the letter and the font used, so if you want to make sure that your "H" is some exact number of pixels tall, you might still want to use the trial-and-error methods mentioned in the other answers. Just keep in mind that if you use these methods to get the size for each specific text you want to display (as @Bob suggested), you might end up with a random font-size-mess on your screen where a text like "ace" will have much bigger letters than "Tag". To avoid this, I would pick one specific letter or letter sequence ("T" or "Tg" or something) and fix that one to your pixel height once and then use the font size you get from that everywhere.

当然,某些特定字母的实际像素高度将取决于所使用的字母和字体,因此如果您想确保您的“H”是某个确切的像素高数,您可能仍想使用试用版其他答案中提到的错误方法。请记住,如果您使用这些方法来获取要显示的每个特定文本的大小(如@Bob 建议的那样),您最终可能会在屏幕上出现随机字体大小混乱,其中“ace”之类的文本" 将有比 "Tag" 大得多的字母。为了避免这种情况,我会选择一个特定的字母或字母序列(“T”或“Tg”或其他东西)并将其固定到您的像素高度一次,然后在任何地方使用您从中获得的字体大小。

回答by WhiteFang34

I'm not aware of a way to get a font by its actual height in pixels. It depends on the context it's used in so there's probably no shorter way than to sample for the best match. It should be pretty quick to look for sizes up or down from the designed height. Here's an example method that does that:

我不知道有一种方法可以通过字体的实际高度(以像素为单位)来获取字体。这取决于它所使用的上下文,因此可能没有比采样以获得最佳匹配更短的方法了。从设计高度向上或向下寻找尺寸应该非常快。这是一个执行此操作的示例方法:

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

回答by Bob

WhiteFang34's code is useful in combination with the following method that returns the actual height of a specific string. It might be a bit slow for real-time rendering, especially for large fonts/strings and I'm sure it can be further optimised, but for now it meets my own needs and is fast enough to run in a back-end process.

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 [[email protected]]
 */
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;
}