As already mentioned in a related question:
There are many (many) questions about computing the size (width or height) of a string that should be painted into a Swing component. And there are many proposed solutions.
However, the solution that is most commonly used and recommended (and that, from my experiences so far, at least computes the correct bounds for most cases) once more shows a rather odd behavior under certain conditions.
The following is an example that shows what I currently consider as a plain bug:
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.font.FontRenderContext;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.util.Locale;
public class StringBoundsBugTest
{
public static void main(String[] args)
{
Font font = new Font("Dialog", Font.PLAIN, 10);
BufferedImage bi = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = bi.createGraphics();
g.setRenderingHint(
RenderingHints.KEY_FRACTIONALMETRICS,
RenderingHints.VALUE_FRACTIONALMETRICS_ON);
for (int i=1; i<30; i++)
{
double scaling = 1.0 / i;
AffineTransform oldAt = g.getTransform();
g.scale(scaling, scaling);
FontRenderContext fontRenderContext = g.getFontRenderContext();
Rectangle2D bounds =
font.getStringBounds("Test", fontRenderContext);
g.setTransform(oldAt);
System.out.printf(Locale.ENGLISH,
"Scaling %8.5f, width %8.5f\n",
scaling, bounds.getWidth());
}
}
}
The program creates a Graphics2D
instance (where it does not matter whether it comes from a JComponent
or a BufferedImage
), then applies various scaling factors to this graphics object, and computes the bounds of a string using the FontRenderContext
of the graphics object.
From my understanding, the scaling factor of the graphics object should not affect the bounds (one might expect something different here, but that's what it seems to do anyhow).
Nevertheless, the output of the above program for me (with JDK 1.8.0_31) is
Scaling 1.00000, width 19.44824
Scaling 0.50000, width 19.44824
Scaling 0.33333, width 19.32669
Scaling 0.25000, width 19.44824
Scaling 0.20000, width 19.44824
Scaling 0.16667, width 19.32669
Scaling 0.14286, width 19.14436
Scaling 0.12500, width 19.44824
Scaling 0.11111, width 19.14436
Scaling 0.10000, width 19.44824
Scaling 0.09091, width 19.38747
Scaling 0.08333, width 18.96204
Scaling 0.07692, width 18.96204
Scaling 0.07143, width 18.71893
Scaling 0.06667, width 19.14436
Scaling 0.06250, width 19.44824
Scaling 0.05882, width 18.59738
Scaling 0.05556, width 18.59738
Scaling 0.05263, width 18.47583
Scaling 0.05000, width 19.44824
Scaling 0.04762, width 0.00000
Scaling 0.04545, width 0.00000
Scaling 0.04348, width 0.00000
Scaling 0.04167, width 0.00000
Scaling 0.04000, width 0.00000
Scaling 0.03846, width 0.00000
Scaling 0.03704, width 0.00000
Scaling 0.03571, width 0.00000
Scaling 0.03448, width 0.00000
One can see the computed size oddly wiggling about ~18-19. This indicates that the size should indeed be "fixed", regardless of the scaling that is applied to the graphics, and I would not mind the small errors that may come from rounding issues and the ridiculous complexity of font-related computations in general.
But what is not acceptable is that, for a certain scaling factor, the computed size plainly drops to zero. The scaling factor for which this happens depends on the font size, but even for larger fonts, it happens with smaller scaling factors, respectively.
Of course, there is an obvious, high level explanation: Somewhere, deep inside the font-related Swing classes like FontRenderContext
etc, some computation is performed, scaling some value with the scaling factor of the graphics and then ... casting it to int
. (The same was likely the issue in the question linked above).
And an obvious workaround could be to create a single, fixed FontRenderContext
and use this for font-related computations everywhere. But this defeats the purpose of the font-related computations usually being bound to a Graphics
: Doing the computations with a different FontRenderContext
than the painting may introduce deviations between the computed sizes and the actual, painted sizes.
Does anybody have a clean, reliable solution for computing the bounds of strings in Swing, regardless of the font size, and regardless of the scaling factors that are applied to the graphics?