I'm looking for a way to infer a Java AWT Font size from a width. For example, I know I want to write 'hello world' within 100 pixels. I know I'm using the font "Times", in style Font.PLAIN, and I want to get the font size that fits the best with my given width of 100 pixels.
I know I could calculate it in a loop (something like while(font.getSize() < panel.getWidth()), but to be honest I don't find it very elegant.
You can get the rendered width and height of a string using the FontMetrics class (be sure to enable fractional font metrics in the Graphics2D instance to avoid rounding errors):
Graphics2D g = ...;
g.setRenderingHint(
RenderingHints.KEY_FRACTIONALMETRICS,
RenderingHints.VALUE_FRACTIONALMETRICS_ON);
Font font = Font.decode("Times New Roman");
String text = "Foo";
Rectangle2D r2d = g.getFontMetrics(font).getStringBounds(text, g);
Now, when you have the width of the text using a font with the default (or actually any) size, you can scale the font, so that the text will fit within a specified width, e.g. 100px:
font = font.deriveFont((float)(font.getSize2D() * 100/r2d.getWidth()));
Similarly, you may have to limit the font size, so that you don't exceed the available panel height.
To improve the appearance of the rendered text, you should also consider enabling antialiasing for text rendering and/or kerning support in the font:
g.setRenderingHint(
RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
Map<TextAttribute, Object> atts = new HashMap<TextAttribute, Object>();
atts.put(TextAttribute.KERNING, TextAttribute.KERNING_ON);
font = font.deriveFont(atts);
Have a look at these two methods I am using. It is not elegant as you say, but it works.
private static int pickOptimalFontSize (Graphics2D g, String title, int width, int height) {
Rectangle2D rect = null;
int fontSize = 30; //initial value
do {
fontSize--;
Font font = Font("Arial", Font.PLAIN, fontSize);
rect = getStringBoundsRectangle2D(g, title, font);
} while (rect.getWidth() >= width || rect.getHeight() >= height);
return fontSize;
}
public static Rectangle2D getStringBoundsRectangle2D (Graphics g, String title, Font font) {
g.setFont(font);
FontMetrics fm = g.getFontMetrics();
Rectangle2D rect = fm.getStringBounds(title, g);
return rect;
}
Related
thanks for checking out my question.
I am working a menu for the game that I'm making, and I want to center the game's title on the main menu. I've looked at several questions here on Stack Overflow, but couldn't find an answer.
Problem
The problem that I'm having is that whenever I call my centerString() method, it puts it in the center of the y-axis, but not of the x-axis. What am I doing wrong here?
Code
Method code
private void centerString(String txt, int width, int height, Graphics g, Font font) {
FontMetrics metrics = g.getFontMetrics();
int x = (width - metrics.stringWidth(txt)) / 2;
int y = (metrics.getAscent() + (height - (metrics.getAscent() + metrics.getDescent())) / 2);
g.setFont(font);
g.drawString(txt, x, y);
}
Where it's called
final Font fnt = new Font("Arial", Font.BOLD, 36);
centerString("ThatMarioEngine", toInt(screenSize.getWidth()), toInt(screenSize.getHeight()), g, fnt);
I have fixed the issue by making using the windows size, instead of the screen size. The centering issue was fixed by putting g.setFont(font); before I get the FontMetrics.
I have a requirements to create a web service that will generate user's icon based on their initials. Similar to this Android project but on the server side using Java.
The size of that image should be dynamic. I already have the code that will create a rectangle with two letters in the middle but it is not scaling the text.
Here is my code so far:
public BufferedImage getAbbreviationImage(int height, int width, String abbreviation) throws IOException {
int centerX = width/2;
int centerY = height/2;
BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.SCALE_SMOOTH);
Graphics2D g = bufferedImage.createGraphics();
Font font = new Font("Helvetica", Font.BOLD, 90);
g.setFont(font);
g.setRenderingHint(
RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB);
g.setColor(Color.decode("#3f404c"));
g.fillRect(0, 0, width, height);
// get the bounds of the string to draw.
FontMetrics fontMetrics = g.getFontMetrics();
Rectangle stringBounds = fontMetrics.getStringBounds(abbreviation, g).getBounds();
FontRenderContext renderContext = g.getFontRenderContext();
GlyphVector glyphVector = font.createGlyphVector(renderContext, abbreviation);
Rectangle visualBounds = glyphVector.getVisualBounds().getBounds();
// calculate the lower left point at which to draw the string. note that this we
// give the graphics context the y corridinate at which we want the baseline to
// be placed. use the visual bounds height to center on in conjuction with the
// position returned in the visual bounds. the vertical position given back in the
// visualBounds is a negative offset from the basline of the text.
int textX = centerX - stringBounds.width/2;
int textY = centerY - visualBounds.height/2 - visualBounds.y;
g.setColor(Color.WHITE);
g.drawString(abbreviation, textX, textY);
g.dispose();
return bufferedImage;
}
Is there any Java library that can do something like this already so that I don't have to write my own code. If not, then what would be the best approach to scale text based on the image size?
Credits:
Some of my code was take from HERE
You need to set the size of the font which you attach to the Graphics2D object. From the oracle docs:
public abstract void drawString(String str,
int x,
int y)
Renders the text of the specified String, using the current text
attribute state in the Graphics2D context
You should set the size of the font you use appropriately to match the dimensions of the rectangle. Something like this:
int lFontSize = 90 * (originalRectangleWidth / newRectangleWidth);
Font font = new Font("Helvetica", Font.BOLD, lFontSize );
where:
90 is the reference font size (this is what you are setting in the example)
originalRectangleWidth would be the size of the rectangle you use when the font looks good with size 90
newRectangleWidth would be the new rectangle width
References:
Graphics2D (oracle ref)
Font (oracle ref)
This is a method that returns awt fonts character as a bufferedimage:
private BufferedImage getCharImage(char ch) {
BufferedImage sizeImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
Graphics2D sizeGraphics = (Graphics2D) sizeImage.getGraphics();
if (antiAlias) {
sizeGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
}
FontMetrics fontMetrics = sizeGraphics.getFontMetrics(font);
int charwidth = fontMetrics.charWidth(ch);
if (charwidth <= 0) {
charwidth = 1;
}
int charheight = fontMetrics.getHeight();
if (charheight <= 0) {
charheight = fontSize;
}
BufferedImage charImage = new BufferedImage(charwidth, charheight, BufferedImage.TYPE_INT_ARGB);
Graphics2D charGraphics = (Graphics2D) charImage.getGraphics();
if (antiAlias) {
charGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
}
charGraphics.setFont(font);
charGraphics.setColor(Color.WHITE);
charGraphics.drawString(String.valueOf(ch), 0, fontMetrics.getAscent());
return charImage;
}
The problem is that i get incorrect width, and the characters don't fit to crated buffered images. If i would create the buffered image like this new BufferedImage(charwidth + 10, charheight, BufferedImage.TYPE_INT_ARGB); the characters would fit to images (that means that i get wrong font width). Why am i facing this prolem and how ca i fix it? I'm using arialbi.ttf (bold, italic).
This is how the font renders
Edit:
This is how i define the font variable:
font = Font.createFont(Font.TRUETYPE_FONT, inputStream);
font = font.deriveFont(fontSize);
I think this is because the advance, or charWidth() doesn't have much to do with the actual width of the character as defined by the font.
Javadoc for FontMetrics.charWidth(char ch):
The advance is the distance from the leftmost point to the rightmost
point on the character's baseline
A character like "g" in Chancery Italic, below the baseline, extends left of the minimum x. Or "s" in the same font extends right of the maximum x, above the baseline. In a straighter font, like Arial Regular, the advance (baseline width) happens to be pretty close to the actual width of the character.
I'm not sure of a good solution besides adding arbitrary padding.
See also: http://docs.oracle.com/javase/tutorial/2d/text/measuringtext.html
I believe this is what is required to get the exact width of a string :
FontMetrics fontMetricesForLabel = graphics.getFontMetrics();
double width = fontMetricesForLabel.getStringBounds(label, graphics).getWidth();
I need text to fill a predefined area. I don't necessarily want the fonts to grow beyond a certain limit, but I would prefer that it at least shrinks if it doesn't fit in a certain area. (So to have an upperbound on the font size, and also a lowerbound, but take the upperbound as default and scale down when required.)
Is there any easy way to get something like that done easily?
I eventually ended up using the fitText operation on PdfSignatureAppearance, included in iText. That made it quite easy to get it done. It may not be perfect, but it's much better than not having anything at all.
public static final float calculateFontSize(String fontName, float maxFontsize, float width, float height, String text) {
Font font = FontFactory.getFont(fontName);
Rectangle rectangle = new Rectangle(width, height);
System.err.println("Calculating font size for " + width + " and " + height);
return PdfSignatureAppearance.fitText(font, text, rectangle, maxFontsize, PdfWriter.RUN_DIRECTION_LTR);
}
I would hand pick the font sizes you want to use to make sure they look good on the target output. No reason to stick to 3, use as many as you want. The rectangle passed into getFont() must have x an y both set to 0.
public class FontFactory {
private Font[] fonts;
public FontFactory() {
fonts = new Font[3];
// font[0] = largest font
// font[1] = middle font
// font[2] = smallest font
}
public Font getFont(String string, Graphics g, Rectangle rectangle) {
for (Font font : fonts) {
Rectangle2D bounds = g.getFontMetrics(font).getStringBounds(string, g);
if (rectangle.contains(bounds)) {
return font;
}
}
return fonts[fonts.length - 1];
}
}
How can I draw a String in Java (using Graphics2d) in monospace mode? I have a font that looks like LCD screen font, and I want to draw something like LCD label.
I am using Digital 7 Mono font.
Do you know where I can find another font that will be monospace and lcd (I wan to type only digitals)?
How can I draw a String in Java (using Graphics2d) in monospace mode?
The essential method needed to render text is drawString(), as outlined below. There is no "monospace mode", per se, but even proportionally spaced fonts typically use a constant width for digit glyphs .
private static final Font font = new Font("Monospaced", Font.PLAIN, 32);
private static final String s = "12:34";
...
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setFont(font);
int xx = this.getWidth();
int yy = this.getHeight();
int w2 = g.getFontMetrics().stringWidth(s) / 2;
int h2 = g.getFontMetrics().getDescent();
g.setColor(Color.green);
g.drawString(s, xx / 2 - w2, yy / 2 + h2);
}
There's a complete example here, and you can extend a suitable JComponent to control positioning using a layout manager, as seen here.