Skip to content
Advertisement

How to get the precise bounding box of a character in Java

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.

enter image description here

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);
    }
}
User contributions licensed under: CC BY-SA
5 People found this is helpful
Advertisement