Java - Centering Text (on a Canvas) - java

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.

Related

Java 15 - Adding Customisable Text to my canvas produces scaling/position issue

I am jumping back into an old bunch of code and my Java is very rough. Please be kind.
Problem: I have an application that draws on the canvas. The placement of the screen objects works well. Even Text attached to other objects. However when I place a Text object on the canvas the scale of the canvas halves. I have fiddled off and on for months and can't seem to find the resolution. Any advice would be helpful.
Below is the code to draw the text on screen it is in a class Visualise2D with the other drawing method. All other objects use the same scale etc. This only occurred since I upgraded to Java 15, last java I used was java 8 and it worked fine.
//TEXT
public void paintText(Graphics2D t2D, Color color,Text t, Font font, double bearing, Rectangle2D bounds, double scale, boolean selected, boolean isRotationTool, double enhance) {
//Draws text where ever the user clicks
FontMetrics fm = t2D.getFontMetrics();
t2D.setFont(default_FONT);
AffineTransform at = new AffineTransform();
int x = (int) ((t.getX() - bounds.getX())*(scale));
int y = (int) ((bounds.getHeight() + bounds.getY() - t.getY()) *(scale));
at.setToRotation(Math.toRadians(bearing+270), x,y);
FontRenderContext frc = t2D.getFontRenderContext();
TextLayout layout = new TextLayout(t.getText(), t2D.getFont(), frc);
t2D.setTransform(at);
if (!(selected)) {
t2D.setColor(color);
}
else
{
//pixel size of the circle
float size = 20;//(float) (fm.stringWidth(t.getText())*0.5);
t2D.setColor(p_selectedObjectsColour);
t2D.setStroke(LINE_100);
//Highlight and origin indicator when selected - START
t2D.setColor(p_selectedObjectsColour);
t2D.draw(new Ellipse2D.Double((((t.getX() - bounds.getX())*scale) - size), (((bounds.getHeight() + bounds.getY() - t.getY())*scale) - size), (size*2), (size*2)));
if(isRotationTool){
t2D.drawString(" : \uu27f3 "+dec1P.format(bearing)+"\u00b0",(float) (x + (fm.stringWidth(t.getText())*1.05)),y);
}
t2D.setColor(p_selectedObjectsColour);
t2D.draw(new Rectangle2D.Double(
(t.getX() - bounds.getX())* scale,
((bounds.getHeight() + bounds.getY() - t.getY())*scale)-fm.getStringBounds(t.toString(), t2D).getHeight(),
t.getBounds().getWidth(),
t.getBounds().getHeight()
));
t2D.drawLine((int) (((t.getX() - bounds.getX())) * scale),
(int)(((bounds.getHeight() + bounds.getY())-(t.getY()))*scale),
(int)(((t.getX())- bounds.getX())*scale)+fm.stringWidth(t.getText()),
(int)(((bounds.getHeight() + bounds.getY())-(t.getY()))*scale));
}
t2D.setColor(color);
//t2D.drawString(t.getText(), x, y);
layout.draw(t2D, x, y);
at.setToRotation(0, x, y);
t2D.setTransform(at);
//This error is to remind you that the Affine transform is not working and the text is in the collection still after it is moved.
}
Below are two images that describe the issue.
Image 1 is the Normal View at Normal Scale
Image 2 is the Alter after Text addition Scale.
If the text is deleted the Scale returns to the first image.
Normal Scale:
Added Text Scale Changes:

FontMetrics returns wrong height

I want to get the exact height of my string in pixels on my panel. So I wrote a program that draws the string, and then draws a rectangle around it.
Using FontMetrics I used the getStringBounds method to get me the enclosing rectangle.
However it looks wrong :
I was expecting the rectangle to perfectly enclose my text, but there is space at the top (And a tiny bit of space on the left and right). Why is it giving me this result?
Here is my code :
public class Test extends JPanel {
#Override
protected void paintComponent(Graphics g) {
Font font = new Font("Arial", Font.PLAIN, 60);
g.setFont(font);
FontMetrics fm = this.getFontMetrics(font);
String str = "100dhgt";
Rectangle2D rect = fm.getStringBounds(str, g);
int x = 5;
int y = 100;
g.drawRect(x, y - (int)rect.getHeight(), (int)rect.getWidth(), (int)rect.getHeight());
g.drawString(str, x, y);
}
public static void main(String[] args) {
JFrame f = new JFrame();
Test test = new Test();
f.add(test);
f.setVisible(true);
f.setSize(400, 400);
}
}
Regarding your rectangle, you have to consider the font's descend (how far is it below the line)
g.drawString(str, x, y - fm.getDescent());
Also note that the font height usually considers some kind of line spacing. In this case fm.getDescent() + fm.getAscent() = 68 whereas fm.getHeight() = 70
The space at the top can be explained by your not taking account of the descent (which takes me back to one of my favorite methods from java 1.0: getMaxDecent)
Otherwise, the box looks pretty good. The only other advice I can offer is that fm.getStringBounds works better with some fonts than it does with others

Scaling image in java

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)

Text in center of Circle

There is a circle (planet), drawn through fillOval(). I need to write the text (number) on the circle, so that it must be always aligned exactly in the center. There is a center of the circle and its radius.
I understand that I need to calculate the width and length of the text (tried by getStringBounds), but does not work correctly.
protected void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
for (Planet planet : planets) {
g2d.setColor(planet.getColor());
g2d.fillOval((int) planet.getX() - planet.getRadius(), (int) planet.getY() - planet.getRadius(),
planet.getRadius() * 2, planet.getRadius() * 2);
g.setFont(new Font("Serif", Font.PLAIN, 20));
g.setColor(Color.black);
String s = String.valueOf(250);
FontMetrics fm = g.getFontMetrics();
double textWidth = fm.getStringBounds(s, g).getWidth();
g.setColor(Color.white);
g.drawString(s, (int) ((int) planet.getX() - textWidth/2), ((int) planet.getY() + fm.getMaxAscent() / 2));}}
You're using planet.getX()-planet.getRadius() and planet.getY() - planet.getRadius() to calculate your centered point of the circle.
Then you have to add the Text you want to paint at the centered position, which is calculated
planet.getX() - planet.getRadius() - textWidth/2 //or centered point - textWidth/2
which is your x-coordinate and
planet.getY() - planet.getRadius() + fm.getMaxAscent() / 2 //or centered point + fm.getMaxAscent()/2
which will be the y-coordinate.
But you are using planet.getX() - textWidth/2
This origin point has to be the center of the circle, not planet.getX()
In the past I've had to use both the ascent/descent values to calculate actual font height and it looks like you're only using ascent. Something to consider: not all strings will have the same max ascent/descent for a given font, so while the string "Dojo" may appear to be properly vertically aligned, the strings "cujo" and "coco" might not.
Another suggestion: instead of calculating the half-width of your text and using that to offset your drawing starting position, why not just center justify the text?

Java monospace draw string

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.

Categories

Resources