As shown in the following picture, an AttributedString is drawn on a JPanel (500X500).
The FontMetrics.getStringBounds()
of that AttributedString gives a
The FontMetrics
only receives a CharacterIterator
, and does not take into account that it is actually an AttributedCharacterIterator
. You can use a TextMeasurer
to compute the actual bounds of your string. For comparison, add this after you called the drawString
method:
// Compensate for the 50,50 of the drawString position
g.translate(50, 50);
g.setColor(Color.RED);
Rectangle2D wrongBounds = fm.getStringBounds(
text.getIterator(), 0, text.getIterator().getEndIndex(), g);
g.draw(wrongBounds);
System.out.println("wrong: "+wrongBounds);
g.setColor(Color.BLUE);
AttributedCharacterIterator iterator = text.getIterator();
TextMeasurer tm = new TextMeasurer(iterator, g.getFontRenderContext());
Rectangle2D rightBounds = tm.getLayout(0, iterator.getEndIndex()).getBounds();
g.draw(rightBounds);
System.out.println("right: "+rightBounds);
(And BTW: Don't call g.dispose()
on the Graphics
that was handed to you in the paintComponent
method)
FontMetrics fontMetrics = graphics.getFontMetrics()
returns a FontMetrics
object based on the single font currently set on the graphics
object. You are not changing the font used by graphics
explicitly so it uses the default font designated for JPanel
by the current L&F.
FontMetrics
methods related to bounds calculation accept a "simple" CharacterIterator
(which does not provide font information) instead of AttributedCharacterIterator
(which does). Hence fontMetrics.getStringBounds()
simply calculates the text bounds based on the single font of the same size.
You need to use java.awt.font.TextLayout
to determine the proper bounds when using AttributedCharacterIterator
with different fonts and font sizes:
TextLayout textLayout = new TextLayout(
text.getIterator(),
g.getFontRenderContext()
);
Rectangle2D.Float textBounds = ( Rectangle2D.Float ) textLayout.getBounds();
g.drawString( text.getIterator(), 50, 50 );
// lets draw a bounding rect exactly around our text
// to be sure we calculated it properly
g.draw( new Rectangle2D.Float(
50 + textBounds.x, 50 + textBounds.y,
textBounds.width, textBounds.height
) );