I use a Label with text wrap enabled. In this case, the text wraps wrong. The text wraps into 3 lines, when it should be only 2 and the Label size only reflects the 2 lines. I debugged through the code and found the reason for this to be in the Label.layout() method. Not sure if this is a bug or if I am doing something wrong.
In the code below you can see that the text is set twice to the GlyphLayout. The first time it wraps correct, the second time we use the reduced width and it wraps into more lines as before. I think the second time we set the text into the GlyphLayout, the same width should be used.
public class Label extends Widget {
public void layout () {
...
float width = getWidth(), height = getHeight();
...
GlyphLayout layout = this.layout;
float textWidth, textHeight;
if (wrap || text.indexOf("\n") != -1) {
// Set the text into the GlyphLayout. The text is wrapped correctly here
layout.setText(font, text, 0, text.length, Color.WHITE, width, lineAlign, wrap, ellipsis);
textWidth = layout.width;
textHeight = layout.height;
...
} else {
textWidth = width;
textHeight = font.getData().capHeight;
}
...
// Set the text again into the GlyphLayout. This time with the width that we got when we set it the first time
// This time the text is wrapped wrong as it uses less width as it should
layout.setText(font, text, 0, text.length, Color.WHITE, textWidth, lineAlign, wrap, ellipsis);
...
}
}
Make sure to invalidate() the label and then pack() the label so it calculates any new preferred sizes; this may or may not fix the problem.
Looking at the source code of Label then you can see that the last invocation of layout.setText() is always invoked regardless of text wrapping or not.
The previous layout.setText() invocation is used to set the textWidth and textHeight for the later call where those values are actually used.
If wrapping is on or there is a newline character then the width and height is set to that of the label and if it is off then the width is set to the actual text width and the height set to the font data height.
From the above, another problem that may be causing this to happen is if you have scaled the font and/or label. The scaling factor is not being applied from within Label.layout() which may cause the label size to be that of 2 lines whilst the actual text is 3 lines as the width doesn't allow for overflow with wrapping set to on.
If all else fails, I would ensure that your font files are correct and that there are not any characters or data in your text that may cause a new line to occur.
I would also suggest to use another font of the same glyph width and height and see if the problem persists. If it does not then at least you know it is a problem relating to the font and not the label.
Hope this helped you.
This issue has been solved in libgdx 1.9.12
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 am trying to display a TextView in Android such that the text in the view is top-aligned:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Create container layout
FrameLayout layout = new FrameLayout(this);
// Create text label
TextView label = new TextView(this);
label.setTextSize(TypedValue.COMPLEX_UNIT_PX, 25); // 25 pixels tall
label.setGravity(Gravity.TOP + Gravity.CENTER); // Align text top-center
label.setPadding(0, 0, 0, 0); // No padding
Rect bounds = new Rect();
label.getPaint().getTextBounds("gdyl!", 0, 5, bounds); // Measure height
label.setText("good day, world! "+bounds.top+" to "+bounds.bottom);
label.setTextColor (0xFF000000); // Black text
label.setBackgroundColor(0xFF00FFFF); // Blue background
// Position text label
FrameLayout.LayoutParams layoutParams =
new FrameLayout.LayoutParams(300, 25, Gravity.LEFT + Gravity.TOP);
// also 25 pixels tall
layoutParams.setMargins(50, 50, 0, 0);
label.setLayoutParams(layoutParams);
// Compose screen
layout.addView(label);
setContentView(layout);
}
This code outputs the following image:
The things to note:
The blue box is 25 pixels tall, just like requested
The text bounds are also reported as 25 pixels tall as requested (6 - (-19) = 25)
The text does not start at the top of the label, but has some padding above it, ignoring setPadding()
This leads to the text being clipped at the bottom, even though the box technically is tall enough
How do I tell the TextView to start the text at the very top of the box?
I have two restrictions to possible answers:
I do need to keep the text top-aligned, though, so if there is some trick with bottom-aligning or centering it vertically instead, I can't use it, since I have scenarios where the TextView is taller than it needs to be.
I'm a bit of a compatibility-freak, so if possible I'd like to stick to calls that were available in the early Android APIs (preferably 1, but definitely no higher than 7).
TextViews use the abstract class android.text.Layout to draw the text on the canvas:
canvas.drawText(buf, start, end, x, lbaseline, paint);
The vertical offset lbaseline is calculated as the bottom of the line minus the font's descent:
int lbottom = getLineTop(i+1);
int lbaseline = lbottom - getLineDescent(i);
The two called functions getLineTop and getLineDescent are abstract, but a simple implementation can be found in BoringLayout (go figure... :), which simply returns its values for mBottom and mDesc. These are calculated in its init method as follows:
if (includepad) {
spacing = metrics.bottom - metrics.top;
} else {
spacing = metrics.descent - metrics.ascent;
}
if (spacingmult != 1 || spacingadd != 0) {
spacing = (int)(spacing * spacingmult + spacingadd + 0.5f);
}
mBottom = spacing;
if (includepad) {
mDesc = spacing + metrics.top;
} else {
mDesc = spacing + metrics.ascent;
}
Here, includepad is a boolean that specifies whether the text should include additional padding to allow for glyphs that extend past the specified ascent. It can be set (as #ggc pointed out) by the TextView's setIncludeFontPadding method.
If includepad is set to true (the default value), the text is positioned with its baseline given by the top-field of the font's metrics. Otherwise the text's baseline is taken from the descent-field.
So, technically, this should mean that all we need to do is to turn off IncludeFontPadding, but unfortunately this yields the following result:
The reason for this is that the font reports -23.2 as its ascent, while the bounding box reports a top-value of -19. I don't know if this is a "bug" in the font or if it's supposed to be like this. Unfortunately the FontMetrics do not provide any value that matches the 19 reported by the bounding box, even if you try to somehow incorporate the reported screen resolution of 240dpi vs. the definition of font points at 72dpi, so there is no "official" way to fix this.
But, of course, the available information can be used to hack a solution. There are two ways to do it:
with IncludeFontPadding left alone, i.e. set to true:
double top = label.getPaint().getFontMetrics().top;
label.setPadding(0, (int) (top - bounds.top - 0.5), 0, 0);
i.e. the vertical padding is set to compensate for the difference in the y-value reported from the text bounds and the font-metric's top-value. Result:
with IncludeFontPadding set to false:
double ascent = label.getPaint().getFontMetrics().ascent;
label.setPadding(0, (int) (ascent - bounds.top - 0.5), 0, 0);
label.setIncludeFontPadding(false);
i.e. the vertical padding is set to compensate for the difference in the y-value reported from the text bounds and the font-metric's ascent-value. Result:
Note that there is nothing magical about setting IncludeFontPadding to false. Both version should work. The reason they yield different results are slightly different rounding errors when the font-metric's floating-point values are converted to integers. It just so happens that in this particular case it looks better with IncludeFontPadding set to false, but for different fonts or font sizes this may be different. It is probably fairly easy to adjust the calculation of the top-padding to yield the same exact rounding errors as the calculation used by BoringLayout. I haven't done this yet since I'll rather use a "bug-free" font instead, but I might add it later if I find some time. Then, it should be truly irrelevant whether IncludeFontPadding is set to false or true.
If your TextView is inside an other layout, make sure to check if there is enough space between them. You can add a padding at the bottom of your parent view and see if you get your full text. It worked for me!
Example: you have a textView inside a FrameLayout but the FrameLayout is too small and is cutting your textView. Add a padding to your FrameLayout to see if it work.
Edit: Change this line
FrameLayout.LayoutParams layoutParams =
new FrameLayout.LayoutParams(300, 25, Gravity.LEFT + Gravity.TOP);
for this line
FrameLayout.LayoutParams layoutParams =
new FrameLayout.LayoutParams(300, 50, Gravity.LEFT + Gravity.TOP);
This will make the box bigger and, by the same way, let enough space for your text to be shown.
OR add this line
label.setIncludeFontPadding(false);
This will remove surrounding font padding and let the text be seen. But the only thing that dont work in your case is that it wont show entirely letters like 'g' that goes under the line... Maybe that you will have to change the size of the box or the text just a little (like by 2-3) if you really want it to work.
I'm facing a problem while trying to generate a PdfPTable and calculate its height before adding it to a document. The method calculateHeights of PdfPTable returned the height a lot greater than the height of a page (while the table is about 1/4 of page's height), so I wrote a method to calculate the height:
protected Float getVerticalSize() throws DocumentException, ParseException, IOException {
float overallHeight=0.0f;
for(PdfPRow curRow : this.getPdfObject().getRows()) {
float maxHeight = 0.0f;
for(PdfPCell curCell : curRow.getCells()) {
if(curCell.getHeight()>maxHeight) maxHeight=curCell.getHeight();
}
overallHeight+=maxHeight;
}
return overallHeight;
}
where getPdfObject method returns a PdfPTable object.
Using debugger I've discovered that lly and ury coordinate difference (and thus the height) of cell's rectangle is much bigger than it looks after adding a table to a document (for example, one cell is 20 and the other is 38 height while they look like the same on a page). There is nothing in the cell except a paragraph with a chunk in it:
Font f = getFont();
if (f != null) {
int[] color = getTextColor();
if(color != null) f.setColor(color[0],color[1],color[2]);
ch = new Chunk(celltext, f);
par = new Paragraph(ch);
}
cell = new PdfPCell(par);
cell.setHorizontalAlignment(getHorizontalTextAlignment());
cell.setVerticalAlignment(getVerticalTextAlignment());
A table then has a cell added and setWidthPercentage attribute set to a some float.
What am I doing wrong? Why does cell's proportions are different from those I see after generating PDF? Maybe I'm calculating the height wrong? Isn't it the height of a cell on a PDF page should strictly be the difference between lly and ury coordinates
Sorry I haven't shown the exact code, because the PDF is being generated of XML using lots of intermediate steps and objects and it is not very useful "as is" I guess...
Thanks in advance!
The height of table added to a page where the available width is 400 is different from the height of a table added to a page where the available width is 1000. There is no way you can measure the height correctly until the width is defined.
Defining the width can be done by adding the table to the document. Once the table is rendered, the total height is known.
If you want to know the height in advance, you need to define the width in advance. For instance by using:
table.setTotalWidth(400);
table.setLockedWidth(true);
This is explained in the TableHeight example. In table_height.pdf, you see that iText returns a height of 0 before adding a table and a height of 48 after adding the table. iText initially returns 0 because there is no way to determine the actual height.
We then take the same table and we define a total width of 50 (which is much smaller than the original 80% of the available width on the page). Now when we calculate the height of the table with the same contents, iText returns 192 instead of 48. When you look at the table on the page, the cause of the difference in height is obvious.
Inorder to get dynamic table height we should set and lock width of table.
Here, 595 is A4 size paper width.
table.setTotalWidth(595);
table.setLockedWidth(true);
I have a Java Swing application that i want to create a nice component in it like a component in Microsoft word. In Microsoft word you can change the margins of your document like in here :
The trick here is that if you change the Top margins to (Let's say) 1.5" then the Preview image will change to show this, so the lines will move down a bit in the image to show that change in the margins so the user can feel how much his document will be affected by this change. So for example if i change the left margin to (4.0") the image will look like this :
What i did is create 2 images a blank page image + another image that contains lines only(Lines image), like these 2 images :
I inserted each image in a JLabel above each other, and now when i change the JSpinner top margin value, i keep the "blank page" image fixed, but i change the border of the "lines image" to move it down a bit. The trick worked fine for the top margin, but the behavior goes totally wrong if i change the bottom/right/left margins.
Here is my code that i apply when changing any JSpinner value :
private void marginSpinnerStateChanged() {
//1. Get the approximate values of all margins :
int topMargin = (int)( Float.valueOf( topSpinner.getValue().toString() ) * 8 );
int bottomMargin = (int)( Float.valueOf( bottomSpinner.getValue().toString() ) * 8 );
int leftMargin = (int)( Float.valueOf( leftSpinner.getValue().toString() ) * 8 );
int rightMargin = (int)( Float.valueOf( rightSpinner.getValue().toString() ) * 8 );
//2. Apply all specified margins to the lines label :
linesLabel.setBorder( new EmptyBorder( topMargin, leftMargin, bottomMargin, rightMargin ) );
}
Can you help me continue this to work right ?
You could just draw the image on top of the paper and scale the image as you go. So you would override the paintComponent() method of a JComponent to do something like:
g.drawImage(image, x, y, width, height, null);
x - would be the left margin
y - would be the top margin
width - would be (maxWidth - leftMargin - rightMargin)
height - would be (maxHeight - topMargin - bottomMargin)
If you don't like scaling the image you can always use a BufferedImage and then use the getSubImage(...) method to get an image the desired size to be painted.
If you notice, they don't shift the textual image. Instead, they only show half of it. This is simple image manipulation. For a good example, see this.
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));