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

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

“给我Verdana的尺寸从上升到下降30像素高。”

我如何向Java询问此问题?

仁,

我不认为有一种“直接”的方式来找到一个字体的高度; 只是间接的方式…通过循环大小,并测试每个的高度<=所需的高度。

如果你这样做一次,只需循环它们……如果你“在飞行中”这样做,那么进行二分搜索,它会更快。

干杯。 基思。

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

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

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

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

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

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

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

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

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

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

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 * 

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