GOAL: Print a table with each row having varying row heights to GUI and to a physical printer using custom renderers.
I am able to print the JTable data to a table within my Java Swing GUI by extending the TableCellRenderer using a customized renderer to change the row height for each row depending on the height needed for each row.
int height = stringValue.length * 22;
if(height > table.getRowHeight(row)) {
table.setRowHeight(row, height);
}
This works for the GUI display and outputs perfectly to my screen.
However, when I want to physically print a paper copy of the entire table, I am unable to correctly print the new row heights using the jtable.print() method. Instead, the data is being cut off because only a single row height is used for all rows. i.e. it ignores my new inputs (from the looks of it).
Related
I created a table with iText 5.5.13.2 (latest iText5 version) and I'm filling it with text and images that are read from a specific folder on the same PC:
Paragraph p = new Paragraph();
p.add(new Phrase("This is a new paragraph!"));
PdfPTable table = new PdfPTable(2);
table.setWidthPercentage(100);
for(int i=0;i<imageArr.size();i++) { //imageArr.size()%2==0!
PdfPCell cell = new PdfPCell();
String name = imageArr.get(i);
String path = imgFolder + File.separator + name;
File f = new File(path);
if(f.isFile()) {
Image img = Image.getInstance(path);
//cell.setCalculatedHeight(50);
cell.addElement(img);
} else {
cell.addElement(new Phrase(name));
}
cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
cell.setHorizontalAlignment(Element.ALIGN_MIDDLE);
//cell.setCalculatedHeight(50);
table.addCell(cell);
}
p.add(table);
doc.add(p);
Both columns in the table use the same width (which is great) and big images are automatically resized to fit the width (which is also great), the only thing that's not working:
The cells should all be a certain height and the big images should resize accordingly (while still keeping the proper height/width ratio). It doesn't seem to matter if I use setCalculatedHeight before or after I add the image to the cell (only doing one or the other, see code above), the cell always sets its height according to the image's height, so rows with only text or images in landscape format are always smaller than rows with images in portrait format.
Small images are also resized (stretched), even while using img.setScaleToFitLineWhenOverflow(false) and img.setScaleToFitHeight(false), but even then the height isn't set properly.
I already tried to use a Chunk (cell.addElement(new Chunk(img, 0, 0))) but then the images are tiny and my height setting is still ignored.
How do I set the cell's height and make the images resize accordingly?
I managed to find a solution. A lot of testing was involved and even now I'm not 100% sure why it was behaving the way it did most of the time.
First of all: Do not add the element with addElement because once you call this with a PdfPCell, iText switches from "Text Mode" (seems to affect images too) to "Composite Mode" and from then on ignores all the alignment,... settings for that cell, including horizontal alignment for text - you can find a more detailed explanation by the original iText developer here (with examples here). Instead do whatever you want to do with the Image first and only then create the PdfPCell with that image. Afterwards the cell can be edited - using the table.getDefaultCell() won't work though, the changes to it won't have any effect on the cells created with the code below.
My working code:
float docWidth = doc.getPageSize().getWidth() - doc.leftMargin() - doc.rightMargin();
float docHeight = doc.getPageSize().getHeight() - doc.topMargin() - doc.bottomMargin();
float docWidthDiv2 = docWidth/2 - 10;
float docHeightDiv2 = docHeight/2 - 10;
PdfPCell cell = null;
if(f.isFile()) {
Image img = Image.getInstance(path);
//img.scaleAbsolute(100, 50);
if(img.getWidth() < docWidthDiv2 && img.getHeight < docHeightDiv2) {
cell = new PdfPCell(img, false);
} else {
cell = new PdfPCell(img, true);
}
} else {
cell = new PdfPCell(new Phrase(name));
}
cell.setFixedHeight(50); //"setCalculatedHeight" doesn't work
Why do I compare the image's width to docWidthDiv2 and the image's height to docHeightDiv2?
There are a lot of combinations for setting the cell's height but none show 100% of the behavior I expected: Really big images should be scaled down to fit the width of the column (more important for images in landscape mode) but also respect the cell's fixed height (more important for images in portrait mode), while still keeping their aspect ratio. Small images that already fit the cell comfortably should not be scaled at all.
The documentation for new PDfPCell(Image image, boolean fit) describes the fit parameter with:
true to fit the image to the cell
In my case true resizes the image (while still respecting its aspect ratio and the cell's height) until it touches two opposite sides of the cell, hence: Big images are reduced in size and small images are stretched.
With false the aspect ratio of the image and the cell's height are still respected but while small images keep their size, big images in landscape mode "bleed" into the neighboring cell (and setScaleToFitLineWhenOverflow doesn't help) and big images in portrait mode might not even be displayed at all (when they're too tall for the cell).
To not stretch small images but decrease the size of big images, a combination of both is needed. I only added the -10, so a potential default padding won't mess with it. If you want to add text before or after the table, then you have to deduct its height from docHeightDiv2 too.
As mentioned, there are also other combinations I tested, the most important information I took away from it:
If the cell's height is set before the image is added, then the image'll overwrite the height, no matter if it's smaller (cell shrinks in height) or bigger (cell's height increases) than the cell.
There are a couple of combinations that can be used, between the parameter, setting the image size and setting the cell's height but with most of them the images either keep their original size (e.g. 2000x1000 won't be completely visible on the page) or they're increased in size until they touch two opposite sides of the cell (which also increases the height of the cell). In the end there's only one combination left that's still useful (in my opinion) - an example:
img.scaleAbsolute(100, 50);
cell = new PdfPCell(img, false);
cell.setVerticalAlignment(Element.ALIGN_MIDDLE); //"center" doesn't work here
cell.setHorizontalAlignment(Element.ALIGN_CENTER); //"middle" doesn't work here
cell.setFixedHeight(150);
This'll create an image with a size of 100x50 (the original aspect ratio is ignored) in the center of a cell that's 150 units tall (= padding of 50 units above and below the image).
Additional information about iText's table:
Columns share the available width of the table equally and there's no need to change it, even if the first cell contains a really small image and the second a really big one. The only thing that you have to pay attention to, in that regard, is the number of cells that are added - rows always have to be completely filled, so a table with 3 columns has to contain 3 cells per row, otherwise that row won't be printed into the pdf file (the same way an empty new page also won't be printed). It's possible to create empty extra cells to fill the rest of the row:
PdfPCell extra = new PdfPCell();
extra.setFixedHeight(50);
table.addCell(extra);
After i added a MenuButton I can not I can not get correct size
The tableView after load looks like this
How can i set each column's width to equal inner content width of that column ? OR Right blank space should be distributed in a balanced way
I can not do them both
tableColumns[i].setMinWidth(tableColumns[i].minWidthProperty().get() + 50);//does not work
tableView.columnResizePolicyProperty().set(...)//not work too
How do I make the last column in a JavaFX TableView take the remaining space.
I have tried table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); but this makes the column sizes equal. I want only the last column to grow when the window width increases.
If you want all the columns in your tableview to fill up the window space available to the tableview AND be dynamically resized to adjust to changing the size of the window, then you need to bind the column properties to the tableview's width.
For example, say I have 5 columns in a tableview. I want them to always be a fixed percentage of the available width (so that when the window is resized, the proportions of the columns remain constant). You can easily form your own column sizing rules using the same property binding idiom (eg. I want the last column to take all the remaining space).
In the initialize() method of the controller that has my TableView control, I could do the following:
void initialize() {
// Initialize your logic here: all #FXML variables will have been injected
:
:
// TableView column control variables are prefixed with "tco"
tcoLast.setCellValueFactory(new PropertyValueFactory<Client.Expand, String>("lastName"));
tcoFirst.setCellValueFactory(new PropertyValueFactory<Client.Expand, String>("firstName"));
tcoDoB.setCellValueFactory(new PropertyValueFactory<Client.Expand, Integer>("doB"));
tcoMRN.setCellValueFactory(new PropertyValueFactory<Client.Expand, String>("defMRN"));
tcoGen.setCellValueFactory(new PropertyValueFactory<Client.Expand, String>("gender"));
// Cell factories for rendering certain columns in the TableView
tcoLast.setCellFactory(new ClientNameTableCellFactory());
tcoFirst.setCellFactory(new ClientNameTableCellFactory());
tcoDoB.setCellFactory(new ClientDoBTableCellFactory());
// Set fixed column widths that resize automatically
// Values are weighted to be a fraction of a total of 41 (arbitrary)
tcoLast.prefWidthProperty().bind(tbvMatches.widthProperty().multiply(11.0/41.0));
tcoFirst.prefWidthProperty().bind(tbvMatches.widthProperty().multiply(11.0/41.0));
tcoDoB.prefWidthProperty().bind(tbvMatches.widthProperty().multiply(8.0/41.0));
tcoMRN.prefWidthProperty().bind(tbvMatches.widthProperty().multiply(8.0/41.0));
tcoGen.prefWidthProperty().bind(tbvMatches.widthProperty().multiply(2.0/41.0));
:
:
}
I have a Swing JTable. It is large and users can scroll up and down to see the row of data they want to work on. When the user stops scrolling, I want to know the row number of the JTable that is the first visible row that the user can see. I want to use this to scroll back to this position in the table after the user is through additional operations (I know how to do the scrolling piece - its getting the first visible row that has me stumped).
You first need to find out which part of the table is visible and then map the visual coordinates to the underlying row:
JViewport viewport = scrollPane.getViewport();
Point p = viewport.getViewPosition();
int rowIndex = table.rowAtPoint(p);
In addition to that you might want to experiment with offsets to p (such as, e.g., offsetting it by half a row height etc.) depending on the behavior you want to achieve when the first visible row is only partly visible.
Using netbeans I was able to show the content of my database using Jtable, but the problem is that when I run the file I got the Jtable but not all of the content of the indivdual record appear entirely. Because some columns has a paragraph entry not a few words
You'd have to be able to know the number of lines & the text height used by the jtable. From there you can set the row height (globally for the table, or for individual rows).
You would then need a table cell renderer capable of displaying multi line content.
Depending on your requirements, you'd might be better of displaying a one line summary of the cell & allow for tooltips to display the full content (depending on the size of te content) or a popup window with a none editable text component
UPDATE
Sorry for the short comment, I was on my IPad.
You have two choices, depending on what state your UI is in. You can grab a reference to the FontMatrics from the JTables graphics context. This will only work if the JTable has been realised (rendered on the screen)
FontMetrics fm = myTable.getFontMetrcis(myTable.getFont());
int height = fm.getHeight();
This example, of course, assumes you are using the same font as the JTable. If not, you'll need to supply the correct font.
Or, if the UI hasn't been realised yet, you'll need to construct a compatible image a extract the font metrics from it. This is a little more complicated as it begins to deal with the graphics configuration and devices...
BufferedImage img = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration().createCompatibleImage(1, 1, Transparency.TRANSLUCENT);
Graphics2D g2d = img.createGraphics();
FontMetrics fm = g2d.getFontMetrics(font);
int height = fm.getHeight();
g2d.dispose();
Once you have the height of the font, you should be able to calculate the height of the text, assuming the text is broken up into lines (or you can split the lines your self). Now if that's not the case (or you want to provide you own word/line wrapping), this become increasingly complicated.
You can check here http://docs.oracle.com/javase/tutorial/2d/text/drawmulstring.html for how to render text using graphics2D for hints (you can use this to split text into groups as you need)
You might also want to check out http://www.jroller.com/santhosh/entry/multiline_in_table_cell_editing1 which shows a great idea for a multi line editor.