itext7: Why setting style in table headers propagate the style on entire table cells? - itext7

I'm playing with Table API in iText 7.2.1.
With the following code:
Style header = new Style()
.setBackgroundColor(new DeviceRgb(210, 210, 210))
.setFont(getFontCardo());
final int nCols = 4;
Table table2 = new Table(UnitValue.createPercentArray(nCols)).useAllAvailableWidth();
table2.setMarginLeft(36);
table2.setMarginBottom(36);
//one header row
table2.addHeaderCell("column 1").addStyle(header);
table2.addHeaderCell("column 2");
table2.addHeaderCell("column 3");
table2.addHeaderCell("column 4");
//36 rows
for (int i = 0; i < nCols * 36; i++) {
table2.addCell("cell " + table2.getNumberOfRows() + "," + (i % nCols));
}
document.add(table2);
the result is that all table cells get the style header, while I have set it only on the first header cell.
Same result if I set header style on any other header cell (or all of them), here a snapshot of PDF document:
Why this unexpected (for me) behavior?

Found the solution, my mistake: the method chaining tecnique misled me.
In my code I actually add the style to the entiore table, not the single cell!
Just correct the code like this:
table2.addHeaderCell(new Paragraph().addStyle(header).add("column 1"));
table2.addHeaderCell(new Paragraph().addStyle(header).add("column 2"));
table2.addHeaderCell(new Paragraph().addStyle(header).add("column 3"));
table2.addHeaderCell(new Paragraph().addStyle(header).add("column 4"));

Related

How to create a itext table having cells at user specified positions

I have a requirement to add images in an iText PDF table, but the position of cells (consisting of images) will depend on indexes (row and column number) given by the user. This table could also have empty cells in between if no index for any image is given.
How to add a cell in itext pdf at random position?
I looked out for this at various forums, not successful. I would really appriciate the help.
There is no API in iText's Table class to add a cell to an arbitrary position. The reasoning behind such a decision lies in iText's Table architecture: if a table has not been constructed yet (i.e. it's unknown whether there will be some cells with a rowpan and/or a colpan greater than 1), it's not feasible for iText to know whether it could place a cell at some custom position.
However, the good news is that all the layout-related code is triggered only after the table is added to the document. So, one can always do the following:
create a table
fill it with some presaved empty Cell objects
alter some Cell object as requested
add the table to the document
In the snippet above I will show, hot to add some diagonal content to the table after the cells have been added to it. The same approach could be followed for images.
int numberOfRows = 3;
int numberOfColumns = 3;
Table table = new Table(numberOfColumns);
List<List<Cell>> cells = new ArrayList<>();
for (int i = 0; i < numberOfRows; i++) {
List<Cell> row = new ArrayList<>();
for (int j = 0; j < numberOfColumns; j++) {
Cell cell = new Cell();
row.add(cell);
table.addCell(cell);
}
cells.add(row);
}
// Add some text diagonally
cells.get(0).get(0).add(new Paragraph("Hello"));
cells.get(1).get(1).add(new Paragraph("diagonal"));
cells.get(2).get(2).add(new Paragraph("world!"));
The resultant PDF looks as follows:

Itext7 - How to create pdf with 2 columns and 4 rows per page with images in java

I have following code to create a single pdf with 2 columns and 4 rows. Every cell contains an image.
int labelCount = x;
int columns = 2;
int labelsPerPage = 8;
int rows = labelCount/columns;
int resto = labelCount%columns;
if(resto>0) rows++;
String dest = "path_to_pdf_file";
PdfDocument pdfDoc = new PdfDocument(new PdfWriter(dest));
Document doc = new Document(pdfDoc);
doc.setMargins(0, 0, 0, 0);
Table table = new Table(UnitValue.createPercentArray(columns)).useAllAvailableWidth();
Integer iCount = 0;
for (int i = 1; i <= rows; i++) {
for (int y = 1; y <= columns; y++) {
if(iCount<labelCount) {
String fileName = "name_of_image";
Cell cell = new Cell();
cell.add(new com.itextpdf.layout.element.Image(ImageDataFactory.create("full_path_to_image")).setAutoScale(true));
cell.setBorder(Border.NO_BORDER);
table.addCell(cell);
iCount++;
if(iCount==labelsPerPage-1) {
doc.add(new AreaBreak(AreaBreakType.NEXT_PAGE));
}
}
}
}
doc.add(table);
doc.close();
If number of labels is bigger than defined limit (8 per page), I want a new page to be created with the following labels.
In my code I used
doc.add(new AreaBreak(AreaBreakType.NEXT_PAGE));
but it generate (I don't know why) a blank page at the beginning. In the second page there are labels.
Which is the right way to add a new page dynamically and put remaining content into it?
Thanks
Essentially in your code you add AreaBreak to the document before you add the table, that's why the blank page is inserted before the table. iText 7 does not allow you to insert page breaks within tables at this point.
To achieve the desired result you need to flush the existing table to the document before moving on to the next page, and create a new table for the next page. Essentially you will be adding a bunch of tables separated with AreaBreak.
The pseudocode which does not require a lot of modifications to your code could look as follows:
Table table = new Table();
for (...) {
// populate table
if ("it's time to insert a page break") {
doc.add(table);
doc.add(new AreaBreak(AreaBreakType.NEXT_PAGE));
table = new Table(); // create new table
}
}
doc.add(table);

Google Sheets - Multiple dependent drop-down lists

I am trying to create a dependent list as described and answered (with a script) here.
I would like to achieve that if selecting a certain value (e.g. "First") from a cell in column 1, then the drop-down options from the next cell in the same row should offer a range of values from the column in a different sheet with the same heading as the value in the first - left - cell (i.e. the first sheet is called "Selector" - in which there are dropdowns, in the second sheet called "KAT" I have the options for these dropdowns). This should then be possible for every row depending on the value of each first cell of the row.
I have tried to use and adapt the suggested script and have reviewed the sample files in the article but I apparently lack some basic understanding of the script to be able to adapt and implement it properly.
Could anybody kindly help me with making this dynamic dropdown work properly?
Just to clarify my final intention: I would like to have this script working first to be able to use it on multiple files. My final goal, though, is to make self-filling dropdown lists and selectors, so that I could simply fill in the data in the "Selector" sheet and would then be able to select these same values later in the cells below (depending on the name (value) of the first cell in the row = first cell of the column holding validation range). I hope to be able to achieve this by using either Pivot table or any other formula in the "KAT" sheet that would aggregate my data from "Selector" sheet and feed them back as drop-down options ...).
Thank you for your help.
See the example sheet here
Code I used (as above):
function onEdit()
{
var ss = SpreadsheetApp.getActiveSpreadsheet(),
sheet = ss.getActiveSheet(),
name = sheet.getName();
if (name != 'Selector') return;
var range = sheet.getActiveRange(),
col = range.getColumn();
if (col != 1) return;
var val = range.getValue(),
dv = ss.getSheetByName('KAT'),
data = dv.getDataRange().getValues(),
catCol = data[0].indexOf(val),
list = [];
Logger.log(catCol)
for (var i = 1, len = 100; i < len; i++) // Problem is here, you have too many items in list! Cannot have more 500 items for validation
list.push(data[i][catCol]);
var listRange = dv.getRange(2,catCol +1,dv.getLastRow() - 1, 1)
Logger.log(list)
var cell = sheet.getRange(range.getRow(), col-1)
var rule = SpreadsheetApp.newDataValidation()
.requireValueInRange(listRange) // Use requireValueIn Range instead to fix the problem
.build();
cell.setDataValidation(rule);
Logger.log(cell.getRow())
}
This question deals with dynamic dropdown lists. A previous question and answer on StackOverflow (Google Sheets - Dependent drop-down lists) were referenced, and code from that answer was being unsuccessfully re-purposed.
The code in the question was not working for one reason: Line 20
var cell = sheet.getRange(range.getRow(), col-1)
In the referenced code, the dropdown list begins in Column F (col=6). The dependant dropdowns ranged to the left so the definition of the dependant column was "col-1". In the questioner's scenario, the dropdown list begins in Column A (col=1) and the dependant dropdowns range from left to right. However, this line of code was not changed to take into account the different layout. Rather than "col-1", it should be "col+1".
Other matters
In addition to this, lines 16 and 17 perform a loop to create an array that might be used for the dependant dropdown. However the loop is redundant because the dropdown is actual defined by creating and assigning a range on the "KAT" sheet.
Cell A2 of KAT includes a formula:
=sort(unique(Selector!$A$2:$A),1,true)
This may appear to be useful because it automatically adds any new dropdown value entered in "Selector" to a list of values in KAT. In reality it is unproductive, because the dependant dropdown build by the code works vertically rather than horizontally. So an additional row added to KAT does not, of itself, contribute to building the dependant dropdown.
The following code works to build the dependant drop down list. I have deliberately left a number of "Logger" entries in the code to assist the questioner in understanding how the code works.
function onEdit() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var name = sheet.getName();
if (name != 'Selector') return;
var range = sheet.getActiveRange();
var col = range.getColumn();
var dropdownrow = range.getRow(); // added for debugging and informationm
if (col != 1) return;
var val = range.getValue();
Logger.log("the cursor is in 'Selector' in cell = " + range.getA1Notation()); //DEBUG
Logger.log("That's row " + dropdownrow + ", and column " + col + ". The value selected = " + val); // DEBUG
var dv = ss.getSheetByName('KAT');
var data = dv.getDataRange().getValues();
var catCol = data[0].indexOf(val);
var list = [];
var KAT_data = dv.getDataRange();
var KAT_data_len = KAT_data.getLastRow(); // added to give 'for' loop a sensible range
Logger.log("The data range on KAT is " + KAT_data.getA1Notation() + ", and the last row of data = " + KAT_data_len); //DEBUG
Logger.log("KAT data = '" + data + "'"); // DEBUG
Logger.log("Found the dropdown cell value of '" + val + "' in KAT as item #" + catCol); //DEBUG
for (var i = 1, len = KAT_data_len; i < len; i++) { // Irrelevant because the data validation range is obtained by defining a range on KAT
// Problem is here, the unique command in A2 creates a blank row
// Logger.log("i="+i+", data = "+data[i][catCol]); // DEBUG
list.push(data[i][catCol]);
}
var listRange = dv.getRange(2, catCol + 1, dv.getLastRow() - 1, 1);
Logger.log("FWIW, this is the list after the loop= " + list); // DEBUG
Logger.log("The contents for the new data validation range (taken from KAT) is " + listRange.getA1Notation()); // DEBUG
Logger.log("The new validation range gets added to col = " + (col + 1)); // DEBUG
//var cell = sheet.getRange(range.getRow(), col-1); // governs the next validation range. Example validation worked right to left, but this sheet works left to right. So must ADD 1, not subtract 1.
var cell = sheet.getRange(range.getRow(), col + 1);
Logger.log("The cell to be assigned the new validation range will be " + cell.getA1Notation()); // DEBUG
var rule = SpreadsheetApp.newDataValidation().requireValueInRange(listRange).build(); // Build validation rule
cell.setDataValidation(rule); // assign validation range to new cell
}
Is this code worthwhile?
The code, as written and referenced, is limited to creating only one level of dependant dropdowns. To this extent it has very limited value. A different approach to creating dependant dropdowns is justified.
"How do you do dynamic / dependent drop downs in Google Sheets?" on StackOverflow has been a meeting place for discussing and updating techniques for dynamic dependant dropdowns since 2014. The latest update was in February 2018 by Max Makhrov. Thye code described here may be useful for the questioner.

How to get the XPath of a grid cell using Firebug?

I am working on a UI. My job is to automate it. I came across the following grid.
When you click on any cell under the Rule column, a browse button appears.
I am supposed to automate this scenario. So, using Firebug I am trying to extract the XPath of that cell.
I used Firebug's inspector to locate that particular cell, so that I can write the XPath for it, but I am unable to locate that cell. Instead, the entire row is getting selected, as shown in next images.
How should I approach this problem?
below code might help you to verify the grid values,
public void verifyTableValues(String expectedValue
) {
try {
//List of Fields values which you want to verify
List<String> expectedValues = Arrays.asList(expectedValue.split("#"));
// you will get the number of rows in Table Select the xpath to /tr
String tableRow = driver.findElements(By.xpath(//table row constant));
int tableRwCnt = getCount(tableRow);
// you will get the number of Columns in Table Select the xpath to /td
String tableColumn = driver.findElements(By.xpath(//table Column constant));
int tableColumnCnt = getCount(tableColumn);
for (int cnt = 1; cnt <= tableRwCnt; cnt++) {
for (int listCount = 1; listCount <= tableColumnCnt; listCount++) {
String actualVal = findElement(By.xpath(tableColumn + "[" + cnt + "]/td["
+ expectedValues.get(listCount) + "]").getText();
String expectdVal = expectedValues.get(listCount);
Assert.assertEquals("Value from table doent Match", expectdVal, actualVal);
}
}
} catch (Exception e) {
// code for exception
}
}
Parameter: expectedValue = test1#test2#test4#test4 (Grid values)

Appending ="0xxxx" to excel cell while exporting using NPOI to maintain leading zero, or how to set cell as text

I am trying to maintain the leading zeros while exporting a column holding phone numbers to excel using NPOI in an asp.net mvc 3 application. I have read here; http://creativyst.com/Doc/Articles/CSV/CSV01.htm#CSVAndExcel that I can append ="[some number beginning with 0]" to have excel maintain the zeros. I have also read that if I set the cell as text it will maintain the zero. I have been unsuccessful in my attempts to do this. Here is my code;
public ActionResult Export(int page, string orderBy, string filter)
{
//Get the data representing the current grid state - page, sort and filter
GridModel model = Model().ToGridModel(page, 10, orderBy, string.Empty, filter);
var orders = model.Data.Cast<Advertiser>();
//Create new Excel workbook
var workbook = new HSSFWorkbook();
//Create new Excel sheet
var sheet = workbook.CreateSheet();
//(Optional) set the width of the columns
sheet.SetColumnWidth(0, 10 * 256);
sheet.SetColumnWidth(1, 50 * 256);
sheet.SetColumnWidth(2, 50 * 256);
sheet.SetColumnWidth(3, 50 * 256);
//Create a header row
var headerRow = sheet.CreateRow(0);
//Set the column names in the header row
headerRow.CreateCell(0).SetCellValue("Name");
headerRow.CreateCell(1).SetCellValue("Phone");
headerRow.CreateCell(5).SetCellValue("Company Name");
headerRow.CreateCell(7).SetCellValue("Address 1");
headerRow.CreateCell(8).SetCellValue("Address 2");
headerRow.CreateCell(9).SetCellValue("Address 3");
headerRow.CreateCell(10).SetCellValue("Address 4");
headerRow.CreateCell(11).SetCellValue("Post Code");
headerRow.CreateCell(14).SetCellValue("Email");
headerRow.CreateCell(16).SetCellValue("Website");
headerRow.CreateCell(19).SetCellValue("Listing Type");
//(Optional) freeze the header row so it is not scrolled
sheet.CreateFreezePane(0, 1, 0, 1);
int rowNumber = 1;
//Populate the sheet with values from the grid data
foreach (Advertiser order in orders)
{
//Create a new row
var row = sheet.CreateRow(rowNumber++);
//Set values for the cells
row.CreateCell(0).SetCellValue(order.AdvertiserName);
row.CreateCell(1).SetCellValue(order.Phone);
row.CreateCell(3).SetCellValue(order.CompanyName);
row.CreateCell(5).SetCellValue(order.Address1);
row.CreateCell(6).SetCellValue(order.Address2);
row.CreateCell(7).SetCellValue(order.Address3);
row.CreateCell(8).SetCellValue(order.Address4);
row.CreateCell(9).SetCellValue(order.Postcode);
row.CreateCell(10).SetCellValue(order.AdvertiserEmail);
row.CreateCell(11).SetCellValue(order.Website);
row.CreateCell(12).SetCellValue(order.listing.type);
}
//Write the workbook to a memory stream
MemoryStream output = new MemoryStream();
workbook.Write(output);
//Return the result to the end user
return File(output.ToArray(), //The binary data of the XLS file
"application/vnd.ms-excel", //MIME type of Excel files
"Advertisers.xls"); //Suggested file name in the "Save as" dialog which will be displayed to the end user
}
}
I have tried the setCellTyoe method in various places with no luck.
I don't mind how it's done, I just want to maintain the leading zeros when the sheet is exported.
I can not test this but did you try setting the type in the createCell call? If all else fails you could resort to the tried and true hack of putting a single quote before the leading zero:
'00185
This will force the cell to text and excel should only display it on edit.
Changed the data type from string to int and NPOI set the cell as text, keeping the leading zero.

Resources