I'm writing code against the Java Personal Basis Profile in J2ME. I need to measure the width of an AttributedString in pixels.
In Java SE, I'd get an AttributedCharacterIterator from my AttributedString and pass it to FontMetrics#getStringBounds, but in J2ME PBP, FontMetrics doesn't have a getStringBounds method, or any other method that accepts a CharacterIterator.
What do I do?
I struggled really hard with this. I needed to resize a panel to the width of an AttributedString. My solution is:
double GetWidthOfAttributedString(Graphics2D graphics2D, AttributedString attributedString) {
AttributedCharacterIterator characterIterator = attributedString.getIterator();
FontRenderContext fontRenderContext = graphics2D.getFontRenderContext();
LineBreakMeasurer lbm = new LineBreakMeasurer(characterIterator, fontRenderContext);
TextLayout textLayout = lbm.nextLayout(Integer.MAX_VALUE);
return textLayout.getBounds().getWidth();
}
It uses the LineBreakMeasurer to find a TextLayout for the string, and then simply checks the with of the TextLayout. (The wrapping width is set to Integer.MAX_VALUE, so texts wider than that will be cut off).
You can find the width of the text in pixels.
String text = "Hello world";
int widthOfText = fontObject.charsWidth(text.toCharArray(), 0, text.length());
Now, you will have the width of text in pixels in the variable widthOfText;
Related
I have an assignment right now about OpenStreetMaps, where one of the exercises is to display the road names at their respective roads on the map.
My problem right now, is that the coordinates we're using are so small, that even the smallest int font size is hundred times larger than what it's supposed to be.
I have tried the method deriveFont(), but it doesn't seem to have any effect.
g.setPaint(Color.black);
for (DrawnString d : model.getRoads()){
Point2D.Double p = d.getPosition();
Font font = new Font("TimesRoman", Font.PLAIN, 1);
font.deriveFont(0.0001f); //doesn't work!
g.setFont(font);
g.drawString(d.getText(), (float) p.x, (float) p.y);
}
My question is, if there's a way to decrease the font size to a small size like 0.0001f?
The deriveFont() method returns an object of type font that is a replica of the calling font with changed parameters.
So change the line to: font = font.deriveFont(0.001f); and everything works just as expected (with very tiny font)
Okay it's me who's stupid, I just missed a "font =" in front of derivedFont().
font = font.deriveFont(0.0001f);
It works now.
I have a custom string. I can have dynamic length, font-size and font-family. Once I know how much number of pixels this text is going to occupy I would be able to truncate it if it does not fit inside its parent.
Here, I cannot draw string or box while I am evaluating the pixels but I have all the information like font size and font family. I am using java.
if you need the lenght of your String based on the label, you can work with:
lYourLabel.getFontMetrics(lYourLabel.getFont()).stringWidth("Hello World");
I am using that method to cut the displayed String and expand it with "..." inside a resizable TableColumn.
Here is a couple methods I use for this sort of thing:
public static int getTextWidth(Font font, String text) {
FontMetrics metrics = new FontMetrics(font) {};
Rectangle2D bounds = metrics.getStringBounds(text, null);
return (int) bounds.getWidth();
}
public static int getTextHeight(Font font, String text) {
FontMetrics metrics = new FontMetrics(font) {};
Rectangle2D bounds = metrics.getStringBounds(text, null);
return (int) bounds.getHeight();
}
How would you effectively do a word wrap on a short label String below with TextLayout in java?
My label are only two or three words long
Some examples:
1. Inflatable Greenhouse D10 ;
2. Command and Control Center A5;
3. Jason Boris;
I'd like to wrap the words in such a way to shape as like a square as possible, rather than one long rectangle.
So my question is: What does it take to wrap the building names to the 2nd line, instead of one long line? See pic below:
Is there a way to set the maximum number of characters to be contained in a line of text and wrap the remaining characters to the second line and so on (it would need to account for whitespace)?
For example, I'd like to wrap the name "Residential Quarter D12" into three lines.
Residential
Quarter
D12
and wrap "Command and Control D16" into four lines.
Command
and
Control
D16
Wouldn't it be nice if TextLayout can understand html codes like a regular JLabel!? Then it'll make things easy:
String label = "<html>" + "Inflatable" + "<br>" + "Greenhouse" + "<br>" + "D10" + "</html>";
Note: it doesn't have to be one word per line. But I'd like to have them "centered" on each line
What I have was the following method for generating a BufferedImage of the building name labels o or just the first and last name of a person.
private BufferedImage createLabelImage(
String label, Font font, FontRenderContext fontRenderContext, Color labelColor,
Color labelOutlineColor) {
// Determine bounds.
TextLayout textLayout1 = new TextLayout(label, font, fontRenderContext);
Rectangle2D bounds1 = textLayout1.getBounds();
// Get label shape.
Shape labelShape = textLayout1.getOutline(null);
// Create buffered image for label.
int width = (int) (bounds1.getWidth() + bounds1.getX()) + 4;
int height = (int) (bounds1.getHeight()) + 4;
BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
// Get graphics context from buffered image.
Graphics2D g2d = (Graphics2D) bufferedImage.getGraphics();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.translate(2D - bounds1.getX(), 2D - bounds1.getY());
// Draw label outline.
Stroke saveStroke = g2d.getStroke();
g2d.setColor(labelOutlineColor);
g2d.setStroke(new BasicStroke(2, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
g2d.draw(labelShape);
g2d.setStroke(saveStroke);
// Fill label
g2d.setColor(labelColor);
g2d.fill(labelShape);
// Dispose of image graphics context.
g2d.dispose();
return bufferedImage;
}
As you can see, this method can only create a BufferedImage form of label with one line of text only.
As I overlay these BufferedImage labels on a map, they look too long and they overlap one another.
That's why I need to make each label to shape like a square as possible.
Let me try to suggest an algoritm.
Split the label by space to get list of words and measure each word to get array
int[] wordWidths;
int minWidth=max(wordWidths);
int height=the row height const;
int minHeight=height;
int maxHeight=wordWidths.length*height;
int currentWidth=minWidht;
int currentHeight=maxHeight;
while(currentWidth<currentHeight || wordWidths.length>1) {
int mergedWidth=find minimal sum of neighbour words' widths
replace the 2 widths with the mergedWidth reducing the wordWidthssize
currentHeight=wordWidths.length*height;
}
Or you can try to rely on components. I would define a JTextArea instance assigning the label there and trying to play with the wrap reducing width 1 by 1 and measuring preferred height for the width.
When optimal size is achived you can call theWrappedJtextArea.printAll(g) to paint it on your BufferedImage's Graphics.
Would it be possible to get the width of a string based on its font?
So for example, if the font size is 40, and the string was "hi", could you do 2*40.
An example of this would be
startX = 260; startWidth = "Start".length()*getFont().getSize();
startY = getHeight()-startWidth-20;
startHeight = getFont().getSize();
It really depends on the type of font, whether it's monospaced or proportional. Most font now are proportional so you would not simply be able to calculate num_of_chars * n.
The gui interface you are using should give you the width of the container once it has been populated with the given text.
I'm doing some graphics processing and HTML is a perfect choice for styling the displayed content. I'm trying to reuse swing's built in html support, and it works perfectly if I hard code the height passed to View.paint, but I can't figure out how to determine how tall the bounds of the rendered content would be at runtime given a specific width.
Graphics2D g = ...
JLabel label = new JLabel("blah blah blah...");
View view = BasicHTML.createView(label, label.getText());
int minHeight = .... // Calculation magic goes here
Rectangle htmlSize = new Rectangle(0, 0, 50, minHeight);
g.setClip(htmlSize);
view.paint(g, htmlSize);
If I ask the JLabel direction with getPreferredSize() it doesn't consider wrapping at all. If I try using a JEditorPane it returns a larger, but fixed size rectangle.
Thank you.
The height can't be calculated until the width is known. When dealing with Swing components I think you need to do something like:
component.setSize(100, 1);
Dimension size = component.getPreferredSize();
Or maybe you can use the concepts present in this posting:
I have had success with this:
var view = BasicHTML.createHTMLView(new JLabel(), "<html>some html</html>");
System.out.println(view.getPreferredSpan(View.X_AXIS));
System.out.println(view.getPreferredSpan(View.Y_AXIS));