Java: Getting a font with a specific height in pixels

后端 未结 4 1187
名媛妹妹
名媛妹妹 2020-12-20 16:17

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 specif

4条回答
  •  春和景丽
    2020-12-20 16:51

    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.

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

提交回复
热议问题