I am trying to get the precise bounding box of a character in Java. Is it normal that if I perform (for example in a Swing component):
public class MyFrame extends JFrame { public MyFrame() { super(); this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); } private void computeWidth() { String character = "A"; JLabel label = new JLabel(); Font font = label.getFont(); font = font.deriveFont(12); // method with FontMetrics FontMetrics fontMetrics = this.getFontMetrics(font); Rectangle2D recWithMetrics = fontMetrics.getStringBounds(character, this.getGraphics()); System.out.println("recWithMetrics : " + recWithMetrics); // method with GlyphVector outline FontRenderContext ctx = new FontRenderContext(new AffineTransform(), true, true); GlyphVector gv = font.createGlyphVector(ctx, character); Rectangle2D recWithOutline = gv.getOutline().getBounds2D(); System.out.println("recWithOutline : " + recWithOutline); } public static void main(String[] args) { MyFrame frame = new MyFrame(); frame.computeWidth(); frame.setVisible(true); } }
recWithMetrics
and recWithOutline
don’t have the same values at all, is it due to the fact that the method using the FontMetrics
includes the advance?
Advertisement
Answer
I’ve found it ‘simpler’ to get the Shape
of the text and work with that.
import java.awt.*; import java.awt.geom.*; import java.awt.font.*; import java.awt.image.*; import javax.swing.*; import javax.swing.border.EmptyBorder; public class TextBoundingBox { private JComponent ui = null; public static String string = "Red outline shows bounds of text"; public static Font font = new Font(Font.SANS_SERIF, Font.BOLD, 36); TextBoundingBox() { initUI(); } public void initUI() { if (ui != null) { return; } ui = new JPanel(new BorderLayout(4, 4)); ui.setBorder(new EmptyBorder(4, 4, 4, 4)); ui.add(new JLabel(new ImageIcon(getImage()))); } private static BufferedImage getImage() { Shape shape = getShapeOfText(font, string); Rectangle2D boundsRectangle = shape.getBounds2D(); double w = boundsRectangle.getWidth(); double h = boundsRectangle.getHeight(); int wBig = (int) (w * 1.1); int hBig = (int) (h * 2); BufferedImage bi = new BufferedImage( wBig, hBig, BufferedImage.TYPE_INT_RGB); Graphics2D g = bi.createGraphics(); g.setColor(Color.WHITE); g.fillRect(0, 0, wBig, hBig); g.setColor(Color.BLACK); g.fill(moveShapeToCenter(shape, wBig, hBig)); g.setColor(Color.RED); g.draw(moveShapeToCenter(boundsRectangle, wBig, hBig)); g.dispose(); return bi; } public static Shape moveShapeToCenter(Shape shape, int w, int h) { Rectangle2D b = shape.getBounds2D(); double xOff = -b.getX() + ((w - b.getWidth()) / 2d); double yOff = -b.getY() + ((h - b.getHeight()) / 2d); AffineTransform move = AffineTransform.getTranslateInstance(xOff, yOff); return move.createTransformedShape(shape); } public static Shape getShapeOfText(Font font, String msg) { BufferedImage bi = new BufferedImage( 1, 1, BufferedImage.TYPE_INT_RGB); Graphics2D g = bi.createGraphics(); FontRenderContext frc = g.getFontRenderContext(); GlyphVector gv = font.createGlyphVector(frc, msg); return gv.getOutline(); } public JComponent getUI() { return ui; } public static void main(String[] args) { Runnable r = () -> { TextBoundingBox o = new TextBoundingBox(); JFrame f = new JFrame(o.getClass().getSimpleName()); f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); f.setLocationByPlatform(true); f.setContentPane(o.getUI()); f.pack(); f.setMinimumSize(f.getSize()); f.setVisible(true); }; SwingUtilities.invokeLater(r); } }