We have a lot of excel reports generated by apache poi. Some of them contains comments in headers. Because of many reports we want to create generic solution for adding comments. As we noticed comments can be added to cells by code like this:
public static void addComment(final Workbook workbook, final Sheet sheet, final Cell cell, final Row row,
final String comment) {
final CreationHelper factory = workbook.getCreationHelper();
final Drawing drawing = sheet.createDrawingPatriarch();
final ClientAnchor anchor = factory.createClientAnchor();
anchor.setCol1(cell.getColumnIndex());
anchor.setCol2(cell.getColumnIndex() + 3);
anchor.setRow1(row.getRowNum());
anchor.setRow2(row.getRowNum() + 5);
final Comment cellComment = drawing.createCellComment(anchor);
final RichTextString richText = factory.createRichTextString(comment);
cellComment.setString(richText);
cell.setCellComment(cellComment);
}
We also noticed that comment box size can by set using columns/rows index - this is the main problem for us because if first column has 100px and the second one has 1000px then comment width will be 1000px. Here is our question - is there a possibility to set comment size with pixels instead of columns/rows indexes using apache poi? Or maybe there is some way to automatically calculate comments size with apache poi?
ClientAnchor supports offsets, at least the specific implementations do:
public XSSFClientAnchor(int dx1, int dy1, int dx2, int dy2, int col1, int row1, int col2, int row2)
dx1 is offset from the left, dx2 is offset from the right, dy1 is offset from the top, dy2 is offset from the bottom (col1,row1 is starting row col, col2, row2 is ending row col, non inclusive).
So to reduce the width, simply assign a non zero value to dx2.
dx1,dx2,dy1 and dy2 are in English Metric Units (EMU). There are 9525 pixels per EMU, so you have to use fairly large values.
Related
I am setting the width of a column this way:
private static readonly int RESTAURANT_LOCATION_COLUMN_WIDTH = 28;
. . .
deliveryPerformanceWorksheet.Column(RESTAURANT_LOCATION_COLUMN).Width = RESTAURANT_LOCATION_COLUMN_WIDTH;
The end user wants the width to be precisely 225 pixels.
The width value is obviously not in pixels; is there some calculation for converting the value used to pixels to cause the value assigned to equate to a specific pixel count?
Easy as eating gooseberry pie:
deliveryPerformanceWorksheet.Cells.SetColumnWidthPixel(RESTAURANT_LOCATION_COLUMN, 225);
I have an existing Word document containing a table. The first row of the table has two cells, but all the other rows have four cells and each cell has a different width.
I need to insert new rows via POI that also have four cells with widths that match those of the existing 4-cell rows.
The basic code is:
XWPFTable table = doc.getTableArray(0);
XWPFTableRow oldRow = table.getRow(2);
table.insertNewTableRow(3);
XWPFTableRow newRow = table.getRow(3);
XWPFTableCell cell;
for (int i = 0; i < oldRow.getTableCells().size(); i++) {
cell = newRow.createCell();
CTTblWidth cellWidth = cell.getCTTc().addNewTcPr().addNewTcW();
BigInteger width = oldRow.getCell(i).getCTTc().getTcPr().getTcW().getW();
cellWidth.setW(width); // sets width
XWPFRun run = cell.getParagraphs().get(0).createRun();
run.setText("NewRow C" + i);
}
The result of this is that row 3 has four cells but their widths do not match those of row 2. The total new row width ends up being the same as the total width of the first three cells of row 2. (Sorry, I don't know how to paste the Word table here).
However, if I first manually edit the source document so that the first table row also has four cells, then everything works perfectly. Similarly, if I get a reference to an existing row and add it to the table, then the cell widths are also correct (but I have the same row object twice so can't modify it).
It seems that the number of cells in the first row influences how other rows are inserted. Does this make sense to anyone and can you suggest how to override it? Also, is there a document anywhere that I can study to understand how this works? Thanks.
Accordiing to your mention: "The first row of the table has two cells, but all the other rows have four cells and each cell has a different width." I suspect this will be a very messy table. Although Word is supporting such tables, I would try to avoid such. But if it must be, you need to know that there is a table grid also for those messy tables. Unzip the *.docx and have a look at /word/document.xml there you will find it.
So if we want to insert rows into such messy tables, we also must respect the table grid. For this there is a GridSpan element in the CTTcPr. This we must also copy from the oldRow and not only copy the CTTblWidth.
Also the CTTblWidth has not only a width but also a type. This we also should copy.
Example:
The source.docx looks like this:
As you see the table grid has 10 columns in total. "Cell 2 1" spans 3 columns, "Cell 2 2" spans 3 columns, "Cell 2 3" spans 0 columns (is its own column), "Cell 2 4" spans 3 columns.
With code:
import java.io.*;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblWidth;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTcPr;
import java.math.BigInteger;
public class WordInsertTableRow {
public static void main(String[] args) throws IOException, InvalidFormatException {
XWPFDocument doc = new XWPFDocument(new FileInputStream("source.docx"));
XWPFTable table = doc.getTableArray(0);
XWPFTableRow oldRow = table.getRow(2);
table.insertNewTableRow(3);
XWPFTableRow newRow = table.getRow(3);
XWPFTableCell cell;
for (int i = 0; i < oldRow.getTableCells().size(); i++) {
cell = newRow.createCell();
CTTcPr ctTcPr = cell.getCTTc().addNewTcPr();
CTTblWidth cellWidth = ctTcPr.addNewTcW();
cellWidth.setType(oldRow.getCell(i).getCTTc().getTcPr().getTcW().getType()); // sets type of width
BigInteger width = oldRow.getCell(i).getCTTc().getTcPr().getTcW().getW();
cellWidth.setW(width); // sets width
if (oldRow.getCell(i).getCTTc().getTcPr().getGridSpan() != null) {
ctTcPr.setGridSpan(oldRow.getCell(i).getCTTc().getTcPr().getGridSpan()); // sets grid span if any
}
XWPFRun run = cell.getParagraphs().get(0).createRun();
run.setText("NewRow C" + i);
}
doc.write(new FileOutputStream("result.docx"));
doc.close();
System.out.println("Done");
}
}
The result.docx looks like:
I have a MigraDoc table where I specify a row height of 0.75cm, and the text is vertically-aligned in the middle of the cell. When I set cell.Format.Shading.Color to something non-white, there is still a portion of the cell near the border that is shown as white around all four sides.
I discovered I can remove the white section to the left and right of the text by setting column.LeftPadding = 0 and column.RightPadding = 0. However, I cannot figure out how to get the white stripes at the top/bottom of the text to disappear without affecting the vertical alignment of the text. If I change the paragraph line height to 0.75cm, the stripes disappear, but the text is then bottom-aligned within the cell. I cannot set the column shading color because each cell in the column contains a different color. Does anyone know a way to force the paragraph to fill the cell vertically (or otherwise get the background color to be uniform within the cell)?
Here is a sample of my code (in C#) where table is of type MigraDoc.DocumentObjectModel.Tables.Table:
...
// Add a column at index #2
var column = table.AddColumn();
column.LeftPadding = 0;
column.RightPadding = 0;
// Add more columns
...
// Iterate through the data printed in each row
foreach (var rowData in myData)
{
// Create a row for the data
var row = table.AddRow();
row.Height = ".75cm";
row.Format.Font.Size = 11;
row.VerticalAlignment = VerticalAlignment.Center;
...
// The following is for illustrative purposes... the actual
// colors and text is determined by the data within the cell
var cell = row.Cells[2];
cell.Format.Shading.Color = Colors.Black;
cell.Format.Font.Color = Colors.White;
var paragraph = cell.AddParagraph("Example");
...
}
Try cell.Shading.Color instead of cell.Format.Shading.Color - the former sets the colour of the cell, the latter sets the colour of the text background (and the padding of the cell will then have a different colour).
I'm working with the DynamicPDF library to typeset tabular data and I have an incomplete understanding of how the width property of Table2 and Cell2 objects works. Specifically, I don't know whether the width set by the property is an internal width, or an external one. For a Table2 object, I don't know whether it includes the left and right borders of the table. For a Cell2 object, I don't know whether it includes the left and right borders or whether it includes the left and right padding. For that matter, I have an incomplete understanding of why we are permitted to set the width of the table at all - surely the width follows logically from the widths of the table columns, in which case the proper way to modify the table's width is by modifying the column widths?
It would be useful to know whether the table width in particular includes its borders, because I want to typeset tables that are the width of the page (or rather, the width of the area inside the page margins). If the Table2's width propery includes the border width, then the correct way to do this is to set the width to the page width. If not, then the correct way is to subtract the left and right border widths first. If I do it wrong, then the table will be either too wide or too narrow.
These pages seem to be up-to-date documentation, but they don't specify whether the borders/padding are included (they just say "Gets or sets the width of the table." and "Gets the width of the cell.") This page has examples of use of the Table2 class, but all of them use hard-coded widths.
The table width is independent of column widths. Table width cannot be modified by modifying the column widths. When generating the tables with dynamic data, setting table width allows you to specify a region in which the content is rendered and anything that falls outside of these boundaries is overflowed into a new table. For example if the sum of the column widths is greater than the table width, the columns that do not fall within the table boundaries are pushed out into the overflow table which can be accessed using GetOverflowColumns().
Border width is not included in the table width. On the other hand, cell width includes the cell border width and padding of the cell. Here is the code that demonstrates how different widths are set.
Document document = new Document();
Page page = new Page();
Table2 table = new Table2(0, 0, 300, 500);
table.Border.Width = 5;
table.Border.Color = RgbColor.Red;
table.Columns.Add(100);
table.Columns.Add(100);
table.Columns.Add(100);
Row2 row = table.Rows.Add();
Cell2 cell = row.Cells.Add("Cell 1");
cell.Padding = 5;
cell.Border.Width = 5;
cell.Border.Color = RgbColor.Blue;
Cell2 cell2 = row.Cells.Add("Cell 2");
cell2.Padding = 5;
cell2.Border.Width = 5;
cell2.Border.Color = RgbColor.Green;
Cell2 cell3 = row.Cells.Add("Cell 3");
cell3.Padding = 5;
cell3.Border.Width = 5;
cell3.Border.Color = RgbColor.DarkOrange;
page.Elements.Add(table);
page.Elements.Add(new LayoutGrid());
page.Elements.Add(new Label("Table Width: " + table.Width.ToString(), 0, 50, 200, 20));
document.Pages.Add(page);
document.Draw("Table2.pdf");
Disclaimer: I work for ceTe Software, the company that develops DynamicPDF libraries.
My fxml file has the following declaration:
< TableView fx:id="myTable" prefHeight="756.0" prefWidth="472.0" />
then in Java code, I add the columns and then setItems as usual. This works as expected.
The only other code which affects the table is:
myTable.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
which nicely automatically re-sizes the columns. But I can't figure out how to do the following:
When I add say 1 or 10 items to the table, those appear as expected in the first 1 or 10 rows of the table, but the number of rows in the table are always 21. Rest of the rows are just empty. I want the table to have only 1 row if I set 1 item or 10 rows if I set 10 items. How do I achieve this ?
All the columns are of the same size. I can manually re-size them, but I want columns to auto-fit according to their size. For example if I have 2 columns one with integer from 1-10 and another with text description, they both have equal size. How do I tell it to autofit the column size according the the row contents ?
Thanks for the response!
The table fills up the layout with empty rows. Try hiding them by adding css
.table-row-cell:empty {
-fx-background-color: -fx-background;
}
.table-row-cell:empty .table-cell {
-fx-border-width: 0px;
}
For #2 you can check the length of text in the cells in the cell value factory but I have title problems like that. You can read the data set and figure out what it should be. These are both approximations depending on font size.
Added this, my play cellValueFactory where I tried out some things.
TableColumn<LineItem,String> amountCol = new TableColumn<>("Amount");
amountCol.setPrefWidth(amountCol.getText().length()*20);
amountCol.setCellValueFactory(new Callback<CellDataFeatures<LineItem, String>, ObservableValue<String>>() {
#Override
public ObservableValue<String> call(CellDataFeatures<LineItem, String> p) {
SimpleStringProperty ssp = new SimpleStringProperty(String.format("%.4f", p.getValue().getAmount()));
amountCol.setPrefWidth(Math.max(amountCol.getPrefWidth(), ssp.get().length()*20));
return ssp;
}
});