Apache POI - How to set correct column width in Word table - apache-poi

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:

Related

PHPExcel Add new rows without override existing info

Is possible add new rows in the middle of this sheet?
...without affect or overwrite the information at the bottom of the document?
I know that is possible create the bottom info manually, but I have to upload this Excel each month (with different header) and set the start row.
I've tried copy the last rows but duplicateStyle doesn't copy borders and backgrounds
# library
$this->load->library('excel');
$path = './assets/files/uploads/form.xls';
$excel = PHPExcel_IOFactory::load( $path );
$excel->setActiveSheetIndex(1);
$row = 10;
$total = 25;
if( $total >= 20 )
{
$cellValues = $excel->getActiveSheet()->rangeToArray( 'A30:L32' );
$excel->getActiveSheet()->fromArray( $cellValues, null, 'A33' );
$excel->getActiveSheet()->duplicateStyle( $excel->getActiveSheet()->getStyle( 'A30:L30'), 'A32:L32' );
}
There is a Worksheet method called insertNewRowBefore() (and a corresponding method for columns called insertNewColumnBefore()) that do this.
$excel->getActiveSheet()->insertNewRowBefore(10, 5);
will insert 5 new rows into the active worksheet, before row 10... effectively, it pushes row 10 down to row 15, row 11 down to row 16, row 12 down to row 17, etc; adjusting formulae and other cell references accordingly.
Likewise
$excel->getActiveSheet()->insertNewColumnBefore('B');
will insert a single new column (the default for both insertNewRowBefore() and insertNewColumnBefore() is a single row or a single column) before column B.
In the examples folder, 05featuredemo.php and 30template.php demonstrate the use of these methods
And
$excel->getActiveSheet()->duplicateStyle(
$excel->getActiveSheet()->getStyle('A30'),
'A31:A100'
);
should copy all style elements (including borders and background) from a single cell to range of cells; but it won't copy different styles from a range of cells to a new range of cells.

Number and cell Formatting in apache poi

I am creating excel sheet using apache poi. I have numbers like - 337499.939437217, which I want to show as it is in excel without rounding off. Also the cell format should be number (for some columns) and currency (for some columns).
Please suggest which BuiltinFormats should I use to achieve this.
Many thanks for the help.
At first you need to know how to use DataFormats. Then you need to know the guidelines for customizing a number format.
For your number -337499.939437217 which will be displayed rounded with general number format, you could use format #.###############. The # means a digit which will be displayed only if needed (is not leading zero and/or is not zero as last decimal digit) - see guidelines. So the whole format means show up to 15 decimal digits if needed but only as much as needed.
For currency you should really using a built in number format for currency. So the currency symbol depends on the locale settings of Excel. The following BuiltinFormats are usable with apache poi. Using a built in number format you need only the hexadecimal format numbers.
Example:
import java.io.*;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
public class CreateNumberFormats {
public static void main(String[] args) throws Exception {
Workbook wb = new XSSFWorkbook();
Sheet sheet = wb.createSheet("format sheet");
CellStyle style;
DataFormat format = wb.createDataFormat();
Row row;
Cell cell;
short rowNum = 0;
short colNum = 0;
row = sheet.createRow(rowNum++);
cell = row.createCell(colNum);
cell.setCellValue(-337499.939437217); // general format
style = wb.createCellStyle();
style.setDataFormat(format.getFormat("#.###############")); // custom number format
row = sheet.createRow(rowNum++);
cell = row.createCell(colNum);
cell.setCellValue(-337499.939437217);
cell.setCellStyle(style);
row = sheet.createRow(rowNum++);
cell = row.createCell(colNum);
cell.setCellValue(123.456789012345);
cell.setCellStyle(style);
row = sheet.createRow(rowNum++);
cell = row.createCell(colNum);
cell.setCellValue(123456789.012345);
cell.setCellStyle(style);
style = wb.createCellStyle();
style.setDataFormat((short)0x7); // builtin currency format
row = sheet.createRow(rowNum++);
cell = row.createCell(colNum);
cell.setCellValue(-1234.5678);
cell.setCellStyle(style);
sheet.autoSizeColumn(0);
FileOutputStream fileOut = new FileOutputStream("CreateNumberFormats.xlsx");
wb.write(fileOut);
fileOut.close();
wb.close();
}
}

How to Set the Background Color of a Cell in a MigraDoc Table

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).

Read from a specific row onwards from Excel File

I have got a Excel file having around 7000 rows approx to read. And Excel file contains Table of Contents and the actual contents data in details below.
I would like to avoid all rows for Table of Content and start from actual content data to read. This is because if I need to read data for "CPU_INFO" the loop and search string occurrence twice 1] from Table of Content and 2] from actual Content.
So I would like to know if there is any way I can point to Start Row Index to start reading data content for Excel File , thus skipping whole of Table Of Content Section?
As taken from the Apache POI documentation on iterating over rows and cells:
In some cases, when iterating, you need full control over how missing or blank rows or cells are treated, and you need to ensure you visit every cell and not just those defined in the file. (The CellIterator will only return the cells defined in the file, which is largely those with values or stylings, but it depends on Excel).
In cases such as these, you should fetch the first and last column information for a row, then call getCell(int, MissingCellPolicy) to fetch the cell. Use a MissingCellPolicy to control how blank or null cells are handled.
If we take the example code from that documentation, and tweak it for your requirement to start on row 7000, and assuming you want to not go past 15k rows, we get:
// Decide which rows to process
int rowStart = Math.min(7000, sheet.getFirstRowNum());
int rowEnd = Math.max(1500, sheet.getLastRowNum());
for (int rowNum = rowStart; rowNum < rowEnd; rowNum++) {
Row r = sheet.getRow(rowNum);
int lastColumn = Math.max(r.getLastCellNum(), MY_MINIMUM_COLUMN_COUNT);
for (int cn = 0; cn < lastColumn; cn++) {
Cell c = r.getCell(cn, Row.RETURN_BLANK_AS_NULL);
if (c == null) {
// The spreadsheet is empty in this cell
} else {
// Do something useful with the cell's contents
}
}
}

Javafx TableView formatting

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;
}
});

Resources