iText cell widths seem to behave inconsistently - java

I have a function which I've been using for a good while in java which calculates cell widths for a pdftable from a given array of string values.
It works very well, and I recently wrote a version of the function in c# and it doesn't give the expected result (i.e text is wrapped to multiple lines) - both java and c# code shown below any help much appreciated
This is the Java version;
float[] CalculateCellWidths(String[] CellHeaders, Font CellFont)
{
float[] CellWidths = new float[CellHeaders.length];
for (int i = 0; i < CellHeaders.length; i++)
{
CellWidths[i] = CellFont.getCalculatedBaseFont(true).getWidthPoint(CellHeaders[i], CellFont.getCalculatedSize());
}
return CellWidths;
}
This is the C# version;
float[] CalculateCellWidths(String[] CellHeaders, iTextSharp.text.Font CellFont)
{
float[] CellWidths = new float[CellHeaders.Length];
for (int i = 0; i < CellHeaders.Length; i++)
{
CellWidths[i] = CellFont.GetCalculatedBaseFont(true).GetWidthPoint(CellHeaders[i], CellFont.CalculatedSize);
}
return CellWidths;
}

I see from your comment that the problem has been solved, but that you don't know why, so here's a small explanation about LockWidth.
There are two ways to define the width of a table. Either the table takes a percentage of the available width. This is a relative width that depends on page size and page margins. Historically, this percentage is 80% and the table is centered. You can change this width percentage with the setWidthPercentage() method. If you use a method to set the widths of the invidual columns, those widths will be treated as relative widths, for instance { 10, 10, 20 } means that you have a table with 3 columns where the first two columns take a quarter of the available width and the third column takes half.
You can also set the absolute width of a table. You can set the total width with the setTotalWidth() method. You can also define the absolute widths of the columns. However, these absolute widths are ignored in favor of the default width percentage as long as you don't "lock" the width. This is what happens if you use setLockedWidth(true) (Java) or table.LockedWidth = true (C#).
Based on your comment, I think the problem was caused by locking the width of the columns so that absolute values were used instead of relative values.

Related

JTable setPreferredWidth() for columns displaying incorrectly

I have a JTable with an AbstractTabelModel displayed. I tried to create a void method in my project that sets the width of each column to the length of the longest value in the column. Here is the method that I am using right now, where "accountWindow" is the JTable:
public void setColumnWidths(){
accountWindow.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
for (int i = 0; i < accountWindow.getColumnCount(); i++){
int greatestStringLength = 2;
for (int z = 0; z < accountWindow.getRowCount(); z++){
if (accountWindow.getValueAt(z, i).toString().length() > greatestStringLength){
System.out.println("Width SET");
greatestStringLength = accountWindow.getValueAt(z, i).toString().length();
accountWindow.getColumnModel().getColumn(i).setPreferredWidth(greatestStringLength);
}
//System.out.println(accountWindow.getValueAt(z, i).toString());
//System.out.println("Greatest Value: " + greatestValueWidth);
}
}
}
The method is called correctly in my Controller class (MVC), but it is setting the width of each column to essentially 0. Method is called after the table is updated and the fireTableData() method is called and account information is displayed. I have added the JTable to a scroll pane. Any comments or suggestions would be appreciated.
The width needs to be specified in pixels not characters in the String.
if (accountWindow.getValueAt(z, i).toString().length() > greatestStringLength){
Looks to me like you are just getting the number of characters in the String, not the width it takes to render the String. That is 20 characters will NOT render in 20 pixels. The width of the String will vary for different Fonts.
Check out Table Column Adjuster for the solution on how to determine the rendered width. It gives a simple usage and a custom class that you can use which has more features.
Both solutions will actually invoke the renderer used by the table to determine the maximum width.

How to calculate the height of an element?

I am generating pdf files by XML data.
I calculate the height of a paragraph element as :
float paraWidth = 0.0f;
for (Object o : el.getChunks()) {
paraWidth += ((Chunk) o).getWidthPoint();
}
float paraHeight = paraWidth/PageSize.A4.getWidth();
But this method does not works correctly.
Can you give me an idea?
Your question is strange. According to the header of your question, you want to know the height of a string, but your code shows that you are asking for the width of a String.
Please take a look at the FoobarFilmFestival example.
If bf is a BaseFont instance, then you can use:
float ascent = bf.getAscentPoint("Some String", 12);
float descent = bf.getDescentPoint("Some String", 12);
This will return the height above the baseline and the height below the baseline, when we use a font size of 12. As you probably know, the font size is an indication of the average height. It's not the actual height. It's just a number we work with.
The total height will be:
float height = ascent - descent;
Or maybe you want to know the number of lines taken by a Paragraph and multiply that with the leading. In that case, there are different possibilities. As it's not clear from your question what you want (height of chunks, width of chunks, vertical position of the baseline,...), you won't get any better answers than the ones that are already given. Please rephrase your question if the height of the glyphs in a Chunk wasn't what you expected.
Firstly why you iterating over Chunk collection casted to Object ? If all elements of this collection are Chunk, use this:
for (Chunk c : el.getChunks()) {
paraWidth += c.getWidthPoint();
}
What do you mean saying method does not works correctly ?

Apache POI autosizing a percent formatted cell

I'm having some issues with autosizing cells. After my program populates and formats each accordingly, it autosizes the cells to make it look visually acceptable. The issue is, when the cell is formatted as percentage, it auto-sizes the cell without taking in consideration the fact that there's a '%' character, so we end up getting #### on the cells until you expand the cell. Is there a way to autosize it WHILE taking in consideration that extra '%' character?
EDIT:
So this is what happens, the left VAR has been autosized correctly for whatever reasons, but the VAR on the right hasn't.
EDIT2:
I noticed that this ONLY happens when the cell value is 0.00% So all the values in the column for VAR that has #### were 0s and some of the values in the left column for VAR were non-zeros.
While this may not be an ideal solution, one possibility is to grab the size of the string you are inserting into the cell (ie. "50.00%" = size of 6), and set the cell width based manually based on that.
I'm afraid this is not supported in Apache POI, as it disregards the custom cell formatting when trying to calculate column width (it only takes font characteristics and rotation into consideration, but no DataFormat).
As a workaround you can try to write your custom autoSizeColumn method as a modification of the one currently implemented, i.e.:
public void autoSizeColumn(Sheet sheet, int column, int plusMinusChars) {
double width = SheetUtil.getColumnWidth(sheet, column, false);
if (width != -1) {
width += plusMinusChars;
width *= 256;
int maxColumnWidth = 255*256; // The maximum column width for an individual cell is 255 characters
if (width > maxColumnWidth) {
width = maxColumnWidth;
}
sheet.setColumnWidth(column, (int)(width));
}
}
Then you can call it with an extra '%' character to be printed out:
//4th column autosized + 1 character in width to accomodate for '%'
autoSizeColumn(sheet, 3, 1);

Using itext for printing at absolute positions

What is the recommended way of printing a text document as a pdf using absolute positioning ?
I am having a table that I have to print. I am also having the data type lengths and starting positions of the columns.
Since the existing table was a character based, there was no problem in its positioning. But even after using a monotype font (Courier, 10) I am not able to properly position the data and last column(s) of each row erroneously skip to the next line.
In order to present my data as close as the character one, I divided the page into different columns(based on its page size) and then add the contents at the desired place. I am adding chunks of data into the paragraph.
paragraph.add(new Chunk(new VerticalPositionMark(), columnNo*ptUnit, false));
I have tried to tweak the page size, font size and margin lengths, but the data is not properly displayed. Have you encountered any such problems ? please do share your thoughts.
Have you tried ColumnText
When i want to write a paragraph and I do know the amount of lines...I do a cycle incrementing (even it says incrementing and is minus is because the pdf is from "south" to "north" (0 - height) the y in a proportion of the fontsize, something like this
//_valueArray is my string[]
//fontSize is the value of the Size of the font...
//1.5 it's just a magic number :) that give me the space line that i need
//cbLocal is the PdfContentByte of the pdf
for (i = 0; i < _valueArray.Length; i++)
{
var p = new Phrase(_valueArray[i], font);
ColumnText.ShowTextAligned(cbLocal, align, p, x, y, 0);
if (i + 1 != _valueArray.Length)
{
y = y - (fontSize*1.5f);
}
}

Java JTable setting Column Width

I have a JTable in which I set the column size as follows:
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
table.getColumnModel().getColumn(0).setPreferredWidth(27);
table.getColumnModel().getColumn(1).setPreferredWidth(120);
table.getColumnModel().getColumn(2).setPreferredWidth(100);
table.getColumnModel().getColumn(3).setPreferredWidth(90);
table.getColumnModel().getColumn(4).setPreferredWidth(90);
table.getColumnModel().getColumn(6).setPreferredWidth(120);
table.getColumnModel().getColumn(7).setPreferredWidth(100);
table.getColumnModel().getColumn(8).setPreferredWidth(95);
table.getColumnModel().getColumn(9).setPreferredWidth(40);
table.getColumnModel().getColumn(10).setPreferredWidth(400);
This works fine, but when the table is maximized, I get empty space to the right of the last column. Is it possible to make the last column resize to the end of the window when resized?
I found AUTO_RESIZE_LAST_COLUMN property in docs but it does not work.
Edit: JTable is in a JScrollPane its prefered size is set.
What happens if you call setMinWidth(400) on the last column instead of setPreferredWidth(400)?
In the JavaDoc for JTable, read the docs for doLayout() very carefully. Here are some choice bits:
When the method is called as a result of the resizing of an enclosing window, the
resizingColumn is null. This means that resizing has taken place "outside" the JTable
and the change - or "delta" - should be distributed to all of the columns regardless of
this JTable's automatic resize mode.
This might be why AUTO_RESIZE_LAST_COLUMN didn't help you.
Note: When a JTable makes adjustments to the widths of the columns it respects their
minimum and maximum values absolutely.
This says that you might want to set Min == Max for all but the last columns, then set Min = Preferred on the last column and either not set Max or set a very large value for Max.
With JTable.AUTO_RESIZE_OFF, the table will not change the size of any of the columns for you, so it will take your preferred setting. If it is your goal to have the columns default to your preferred size, except to have the last column fill the rest of the pane, You have the option of using the JTable.AUTO_RESIZE_LAST_COLUMN autoResizeMode, but it might be most effective when used with TableColumn.setMaxWidth() instead of TableColumn.setPreferredWidth() for all but the last column.
Once you are satisfied that AUTO_RESIZE_LAST_COLUMN does in fact work, you can experiment with a combination of TableColumn.setMaxWidth() and TableColumn.setMinWidth()
JTable.AUTO_RESIZE_LAST_COLUMN is defined as "During all resize operations, apply adjustments to the last column only" which means you have to set the autoresizemode at the end of your code, otherwise setPreferredWidth() won't affect anything!
So in your case this would be the correct way:
table.getColumnModel().getColumn(0).setPreferredWidth(27);
table.getColumnModel().getColumn(1).setPreferredWidth(120);
table.getColumnModel().getColumn(2).setPreferredWidth(100);
table.getColumnModel().getColumn(3).setPreferredWidth(90);
table.getColumnModel().getColumn(4).setPreferredWidth(90);
table.getColumnModel().getColumn(6).setPreferredWidth(120);
table.getColumnModel().getColumn(7).setPreferredWidth(100);
table.getColumnModel().getColumn(8).setPreferredWidth(95);
table.getColumnModel().getColumn(9).setPreferredWidth(40);
table.getColumnModel().getColumn(10).setPreferredWidth(400);
table.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
Use this method
public static void setColumnWidths(JTable table, int... widths) {
TableColumnModel columnModel = table.getColumnModel();
for (int i = 0; i < widths.length; i++) {
if (i < columnModel.getColumnCount()) {
columnModel.getColumn(i).setMaxWidth(widths[i]);
}
else break;
}
}
Or extend the JTable class:
public class Table extends JTable {
public void setColumnWidths(int... widths) {
for (int i = 0; i < widths.length; i++) {
if (i < columnModel.getColumnCount()) {
columnModel.getColumn(i).setMaxWidth(widths[i]);
}
else break;
}
}
}
And then
table.setColumnWidths(30, 150, 100, 100);
Reading the remark of Kleopatra (her 2nd time she suggested to have a look at javax.swing.JXTable, and now I Am sorry I didn't have a look the first time :) )
I suggest you follow the link
I had this solution for the same problem: (but I suggest you follow the link above)
On resize the table, scale the table column widths to the current table total width.
to do this I use a global array of ints for the (relative) column widths):
private int[] columnWidths=null;
I use this function to set the table column widths:
public void setColumnWidths(int[] widths){
int nrCols=table.getModel().getColumnCount();
if(nrCols==0||widths==null){
return;
}
this.columnWidths=widths.clone();
//current width of the table:
int totalWidth=table.getWidth();
int totalWidthRequested=0;
int nrRequestedWidths=columnWidths.length;
int defaultWidth=(int)Math.floor((double)totalWidth/(double)nrCols);
for(int col=0;col<nrCols;col++){
int width = 0;
if(columnWidths.length>col){
width=columnWidths[col];
}
totalWidthRequested+=width;
}
//Note: for the not defined columns: use the defaultWidth
if(nrRequestedWidths<nrCols){
log.fine("Setting column widths: nr of columns do not match column widths requested");
totalWidthRequested+=((nrCols-nrRequestedWidths)*defaultWidth);
}
//calculate the scale for the column width
double factor=(double)totalWidth/(double)totalWidthRequested;
for(int col=0;col<nrCols;col++){
int width = defaultWidth;
if(columnWidths.length>col){
//scale the requested width to the current table width
width=(int)Math.floor(factor*(double)columnWidths[col]);
}
table.getColumnModel().getColumn(col).setPreferredWidth(width);
table.getColumnModel().getColumn(col).setWidth(width);
}
}
When setting the data I call:
setColumnWidths(this.columnWidths);
and on changing I call the ComponentListener set to the parent of the table (in my case the JScrollPane that is the container of my table):
public void componentResized(ComponentEvent componentEvent) {
this.setColumnWidths(this.columnWidths);
}
note that the JTable table is also global:
private JTable table;
And here I set the listener:
scrollPane=new JScrollPane(table);
scrollPane.addComponentListener(this);
This code is worked for me without setAutoResizeModes.
TableColumnModel columnModel = jTable1.getColumnModel();
columnModel.getColumn(1).setPreferredWidth(170);
columnModel.getColumn(1).setMaxWidth(170);
columnModel.getColumn(2).setPreferredWidth(150);
columnModel.getColumn(2).setMaxWidth(150);
columnModel.getColumn(3).setPreferredWidth(40);
columnModel.getColumn(3).setMaxWidth(40);
fireTableStructureChanged();
will default the resize behavior ! If this method is called somewhere in your code AFTER you did set the column resize properties all your settings will be reset. This side effect can happen indirectly. F.e. as a consequence of the linked data model being changed in a way this method is called, after properties are set.
No need for the option, just make the preferred width of the last column the maximum and it will take all the extra space.
table.getColumnModel().getColumn(0).setPreferredWidth(27);
table.getColumnModel().getColumn(1).setPreferredWidth(120);
table.getColumnModel().getColumn(2).setPreferredWidth(100);
table.getColumnModel().getColumn(3).setPreferredWidth(90);
table.getColumnModel().getColumn(4).setPreferredWidth(90);
table.getColumnModel().getColumn(6).setPreferredWidth(120);
table.getColumnModel().getColumn(7).setPreferredWidth(100);
table.getColumnModel().getColumn(8).setPreferredWidth(95);
table.getColumnModel().getColumn(9).setPreferredWidth(40);
table.getColumnModel().getColumn(10).setPreferredWidth(Integer.MAX_INT);
Use this code. It worked for me. I considered for 3 columns. Change the loop value for your code.
TableColumn column = null;
for (int i = 0; i < 3; i++) {
column = table.getColumnModel().getColumn(i);
if (i == 0)
column.setMaxWidth(10);
if (i == 2)
column.setMaxWidth(50);
}

Categories