2

So, slightly different than similar questions on SO but any insight in how I would use an affline transformation to shrink or grow a text string so that the overall size is proportional to the region it's to be drawn on?

For example, I'd like the font to be 50% of the width of the region.

I've inherited some code like this but I don't really understand what its doing

Font font = new Font("Arial", Font.BOLD, 12);
FontRenderContext context = graphics.getFontRenderContext();
graphics.setFont(font);
Rectangle2D bounds = graphics.getFont().getStringBounds("Hello world", context);

LineMetrics line = font.getLineMetrics(text, context);
float xScale = (float) (region.width / bounds.getWidth());
float yScale = (region.height / (line.getAscent() + line.getDescent()));

double x = region.x;
double y = region.y + region.height - (yScale * line.getDescent());
AffineTransform transformation = AffineTransform.getTranslateInstance(x, y);

if (xScale > yScale)
    transformation.scale(yScale, yScale);
else
    transformation.scale(xScale, xScale);

graphics.setFont(font.deriveFont(transformation));
graphics.setRenderingHint(KEY_ANTIALIASING, VALUE_ANTIALIAS_ON);

int centerX = region.width / 2;
int centerY = region.height / 2;
graphics.drawString(text, centerX, centerY - (int) bounds.getHeight());

It seems to set the size to something reasonable but not center this in the region. If you can shed light on what its trying to do, that'd be great.

Any pointers (as long as they're not literally pointers to the java font tutorial) much appreciated.

Toby
  • 9,523
  • 8
  • 36
  • 59
  • How's that? I'd be happy if could calculate just the font size to use based on its container rather than transform the existing font but I'm not sure how/if a transform reduces the quality... – Toby Nov 14 '13 at 22:24

1 Answers1

3

Basically, this "seems" to try to center the text within a "given" area...

// Set the base, start font
Font font = new Font("Arial", Font.BOLD, 12);
// Get rendering context
FontRenderContext context = graphics.getFontRenderContext();
// Set the font used by the graphics context...
graphics.setFont(font);
// Get the rectangle "bounds" of the text
Rectangle2D bounds = graphics.getFont().getStringBounds("Hello world", context);
// Get the line metrics, which describes things like the fonts decent and ascent
LineMetrics line = font.getLineMetrics(text, context);
// Calculate the scaling requirements
float xScale = (float) (region.width / bounds.getWidth());
float yScale = (region.height / (line.getAscent() + line.getDescent()));

// Calculate the offset for the text
double x = region.x;
double y = region.y + region.height - (yScale * line.getDescent());
// Create a new transformation, translating the x, y position
// This appears to be trying to place the text at the bottom
// left position of the region, but the problem is, this then gets
// scaled, when means that this translation is no longer the same...   
AffineTransform transformation = AffineTransform.getTranslateInstance(x, y);

// Apply a scale...
if (xScale > yScale)
    transformation.scale(yScale, yScale);
else
    transformation.scale(xScale, xScale);

// Create a new instance of the font using the transformation
graphics.setFont(font.deriveFont(transformation));
// Make it look pretty
graphics.setRenderingHint(KEY_ANTIALIASING, VALUE_ANTIALIAS_ON);

// Render the font within the region...
int centerX = region.width / 2;
int centerY = region.height / 2;
graphics.drawString(text, centerX, centerY - (int) bounds.getHeight());

Now, having said all that, I've had a hard time getting it to work. Because you are simply creating a new Font based on the transformation, it would be simpler to throw away the translation, for example...

// Base font
Font font = new Font("Arial", Font.BOLD, 12);
// Get the fonts metrics
FontMetrics fm = g2d.getFontMetrics(font);

// Calculate the scaling requirements
float xScale = (float) (region.width / fm.stringWidth(text));
float yScale = (region.height / fm.getHeight());

// Determine which access to scale on...
float scale = 0f;
if (xScale > yScale) {
    scale = yScale;
} else {
    scale = xScale;
}

// Create a new font using the scaling facter
g2d.setFont(font.deriveFont(AffineTransform.getScaleInstance(scale, scale)));
// Make it pretty
g2d.setRenderingHint(KEY_ANTIALIASING, VALUE_ANTIALIAS_ON);

// Get the "scaled" metrics
fm = g2d.getFontMetrics();

// Position the text at the left, bottom position of the
// specified region...
int x = 0;
int y = (region.height - fm.getHeight()) + fm.getAscent();
g2d.drawString(text, x, y);

Not sure if that's exactly right (what you're looking for), but that's what the code you supplied "seems" to be trying to do...

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • +1 for `FontMetrics`; @Toby: also consider `TextLayout`, for [example](http://stackoverflow.com/a/8282330/230513). – trashgod Nov 15 '13 at 01:58
  • Thanks, I ended up using a combination of this and http://explodingpixels.wordpress.com/2009/01/29/drawing-text-about-its-visual-center/ – Toby Nov 15 '13 at 12:04