I am currently importing excel data into a vector in Java, however now i want to group that data so that similar data with lets say the same id is grouped (in my case matter number).
here is my relevant import code:
public class FileChooser extends javax.swing.JFrame {
private String path ="";
public FileChooser() {
initComponents();
}
private static Vector importExcelSheet(String fileName)
{
Vector cellVectorHolder = new Vector();
try
{
Workbook workBook = WorkbookFactory.create(new FileInputStream(fileName));
Sheet sheet = workBook.getSheetAt(0);
Iterator rowIter = sheet.rowIterator();
while(rowIter.hasNext())
{
XSSFRow row = (XSSFRow) rowIter.next();
Iterator cellIter = row.cellIterator();
Vector cellStoreVector=new Vector();
while(cellIter.hasNext())
{
XSSFCell cell = (XSSFCell) cellIter.next();
cellStoreVector.addElement(cell);
}
cellVectorHolder.addElement(cellStoreVector);
}
}
catch (Exception e)
{
System.out.println(e.getMessage());
}
return cellVectorHolder;
}
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
JFileChooser chooser = new JFileChooser();
chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
int option = chooser.showOpenDialog(this); // parentComponent must a component like JFrame, JDialog...
if (option == JFileChooser.APPROVE_OPTION) {
File selectedFile = chooser.getSelectedFile();
path = selectedFile.getAbsolutePath();
jTextField1.setText(path);
}
}
private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {
Vector dataHolder = importExcelSheet(path);
Enumeration e = dataHolder.elements(); // get all vector elements
while (e.hasMoreElements()) { // step through all vector elements
Object obj = e.nextElement();
System.out.println(obj);
}// TODO add your handling code here:
}
This selects the excel spreadsheet and extracts all the values to a vector and prints them out, what i want to know is, can i group the same data in the vector and output that data instead of the whole thing.
So lets say i have a spreadsheet of cellphone contracts, and i want to select all the contracts of the Samsung galaxy s3 and not all the contracts, how would i do that?
You're suffering from Object Denial :-)
As I read your code, you have a Vector of rows containing a Vector of columns, is that correct? And if so, does one of the columns contain the phone model?
Anyways, you could be doing something along the lines of:
// TODO seriously consider something else than Vector to store the rows and columns!
Map<String,Vector> map = new HashMap<String,Vector>()
while(rowIter.hasNext())
{
boolean isFirst = false;
String phoneModel = "";
while( cellIter.hasNext() )
{
XSSFCell cell = (XSSFCell) cellIter.next();
if ( isFirst ) { phoneModel = cell.getTheTextContentOrWhatever(); isFirst = false; }
cellStoreVector.addElement(cell);
}
if ( map.get( phoneModel ) == null ) { map.put( phoneModel , new Vector() ); }
map.get( phoneModel ).add( cellStoreVector );
}
Then, the map keys will be your phones, and the value will be the Vector with the rows for that phone. It is not what I consider pretty code and needs works in terms of error handling, but you can work from there.
Cheers,
Related
How i can validate xlsx file header as well as row?
Suppose there will be 10 column,I want some column should be Integer and some column should be String.If any validation fail then that file should not be proceed ,If all validation pass then we need to update record into our table based on meterno and boundary_id.
If meterno and boundary_id combination is not present for some record then,it should not be insert into our table.For those we need to create new xlsx file(Not Update record) and save into our local directory.
Note:-CT,PT,MF should be Number and rest all string.
Please find attache xlsx file.
#Controller
public class UploadExeclController {
// all key value pair mapping ( holding column name and its respective value per row.
Map<String,Object> records = new HashMap<String,Object>();
#RequestMapping(value = "/uploadBoundaryFile", method = { RequestMethod.GET, RequestMethod.POST })
public #ResponseBody void uploadBoundaryFile(HttpServletRequest request, HttpServletResponse response,
ModelMap model) {
try {
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
MultipartFile myFile = multipartRequest.getFile("fileUpload");
String fileName = myFile.getOriginalFilename();
FileInputStream file = new FileInputStream(getTempFile(myFile));
// Get the workbook instance for XLSX file
XSSFWorkbook workbook = new XSSFWorkbook(file);
// Get first sheet from the workbook
XSSFSheet sheet = workbook.getSheetAt(0);
// GET the header row
Row headerRow = sheet.getRow(0);
// Get iterator to all the rows in current sheet
//Iterator<Row> rowIterator = sheet.iterator();
// LIst of headers from excel
List<String> headers = new ArrayList<String>();
Iterator<Cell> cells = headerRow.cellIterator();
while (cells.hasNext()) {
Cell cell = (Cell) cells.next();
RichTextString value = cell.getRichStringCellValue();
headers.add(value.getString());
}
// validate the template
Object[] headerValidation = validateTempalte(headers);
// if validation fails then write back the message to user.
if((Boolean) headerValidation[0]==false){
List<String> headerValidationMsg = (List<String>) headerValidation[1];
return ;
}
// Assign a number to header.This is done so that when we reading excel value we can identiy the cell value belongs to which column
Map<String,Integer> headerSeqNumber = assignHeaderSeqNumber(headers);
//Get iterator to all the rows in current sheet
int rowNumber=0;
Iterator<Row> rowIterator = sheet.iterator();
while (rowIterator.hasNext()) {
Row row = (Row) rowIterator.next();
if(rowNumber==0){
rowNumber++;
continue;
}
System.out.println("Row no "+rowNumber);
Iterator<String> columnsIterator = headerSeqNumber.keySet().iterator();
while (columnsIterator.hasNext()) {
String name = columnsIterator.next();
System.out.println("Read columnName "+name);
int cellType=row.getCell(headerSeqNumber.get(name)).getCellType();
switch (cellType) {
case Cell.CELL_TYPE_STRING:
RichTextString value= row.getCell(headerSeqNumber.get(name)).getRichStringCellValue();
String val=value!=null ?value.getString().trim():null;
val=val.replaceAll(":", "");
val=val.replaceAll("'", "");
records.put(name,val);
break;
case Cell.CELL_TYPE_NUMERIC:
if (DateUtil.isCellDateFormatted(row.getCell(headerSeqNumber.get(name)))) {
Date date = row.getCell(headerSeqNumber.get(name)).getDateCellValue();
// records.put(name,Utils.convertDateToString(date, "dd-MMM-yyyy"));
}else{
double numericVal= row.getCell(headerSeqNumber.get(name)).getNumericCellValue();
records.put(name,numericVal);
}
break;
case Cell.CELL_TYPE_BLANK:
System.err.println(" blank cell type ");
records.put(name,null);
break;
default:
System.err.println(" NEither string no number "+row.getCell(headerSeqNumber.get(name)).getCellType());
System.err.println(" value "+row.getCell(headerSeqNumber.get(name)).getStringCellValue());
break;
}
}
// once a row is read validate each cell
Object[] validationResults=validateRecords(records);
Boolean isDataValid =(Boolean) validationResults[0];
List<String> vMessages =new ArrayList<String>();
// if data is valid then update the value in db.
if (isDataValid) {
System.out.println(" Valid data sending for upating record");
// String msg = executeProcedure(records);
// vMessages.add(msg);
}else{
// if there is validation issues then add the messages to list.
if(validationResults[1]!=null){
vMessages = (List<String>) validationResults[1];
}
System.err.println(" data is invalid");
}
// errorMessages.put(rowNumber, vMessages);
rowNumber++;
}
} catch (Exception e) {
e.printStackTrace();
}
}
public File getTempFile(MultipartFile multipartFile) {
CommonsMultipartFile commonsMultipartFile = (CommonsMultipartFile) multipartFile;
FileItem fileItem = commonsMultipartFile.getFileItem();
DiskFileItem diskFileItem = (DiskFileItem) fileItem;
String absPath = diskFileItem.getStoreLocation().getAbsolutePath();
File file = new File(absPath);
// trick to implicitly save on disk small files (<10240 bytes by default)
if (!file.exists()) {
try {
file.createNewFile();
multipartFile.transferTo(file);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return file;
}
You can use CellType type = cell.getCellType(); to check whether its numeric or string.
CellType type = cell.getCellType();
if (type == CellType.STRING) {
// your job which needs to be done
}
If meterno and boundary_id combination is not present for some record , for this condition just write null check or some logic once you have implemented the logic mentioned above.
Posting method implementation which can validate every cell value of a particular row, let's say Header in this case. Based on validation rule can store validation errors to respond.
private Map<String, ProductDetailResponse.ErrorDetail> validateCpiDataFile(MultipartFile file) {
Map<String, ProductDetailResponse.ErrorDetail> errorMap = new HashMap<>();
List<String> headerNames = Arrays.asList("Index", "Portfolio", "Time Period", "Year");
DataFormatter dataFormatter = new DataFormatter();
if (Objects.isNull(file) && file.isEmpty()) {
throw new Exception(HttpStatus.NOT_ACCEPTABLE, service.getErrorProperties().getCpiFileUploadErrorCode(), service.getErrorProperties().getCpiFileUploadErrorMessage());
} else {
try {
XSSFWorkbook cpiWorkbook = new XSSFWorkbook(file.getInputStream());
Sheet sheet = cpiWorkbook.getSheetAt(0);
int colCount = 0;
int cellCount = sheet.rowIterator().next().getLastCellNum() - sheet.rowIterator().next().getFirstCellNum();
if (cellCount == headerNames.size()) {
for (Cell cell : sheet.getRow(0)) {
String cellValue = dataFormatter.formatCellValue(cell); // from apache poi 5.2.0 on
if (!headerNames.get(colCount).trim().equalsIgnoreCase(cellValue.trim())) {
ProductDetailResponse.ErrorDetail error = new ProductDetailResponse
.ErrorDetail(ProductDetailResponse.ValidationsType.INVALID_HEADER.getValue(), cell.getRowIndex(), "Header Value and Order is mismatched with specified format.");
errorMap.put(cellValue, error);
}
colCount++;
}
} else {
log.error("Number of column mismatched from specified format. Please verify file headers.");
throw new Exception(HttpStatus.NOT_ACCEPTABLE, service.getErrorProperties().getCpiFileUploadErrorCode(), service.getErrorProperties().getCpiFileUploadErrorMessage());
}
} catch (IOException | EncryptedDocumentException e) {
log.error("Exception at validateCpiDataFile() while validating CPI data file. Exception: [{}]", e.getMessage());
throw new Exception(HttpStatus.NOT_ACCEPTABLE, service.getErrorProperties().getCpiFileUploadErrorCode(), service.getErrorProperties().getCpiFileUploadErrorMessage());
}
}
return errorMap;
}
I've the following code where fetching of data from excel sheet is done and then this data is added to list as below:
List<HashMap<String,String>> mydata = new ArrayList<>();
try
{
FileInputStream fs = new FileInputStream(filepath);
XSSFWorkbook workbook = new XSSFWorkbook(fs);
XSSFSheet sheet = workbook.getSheet(sheetName);
Row HeaderRow = sheet.getRow(0);
for(int i=1;i<sheet.getPhysicalNumberOfRows();i++)
{
Row currentRow = sheet.getRow(i);
HashMap<String,String> currentHash = new HashMap<String,String>();
for(int j=0;j<currentRow.getPhysicalNumberOfCells();j++)
{
Cell currentCell = currentRow.getCell(j);
switch (currentCell.getCellType())
{
case Cell.CELL_TYPE_STRING:
currentHash.put(HeaderRow.getCell(j).getStringCellValue(), currentCell.getStringCellValue());
break;
}
}
mydata.add(currentHash);
I've another class where we are making use Properties class and setting the property :
public List<HashMap<String,String>> datamap;
public static Properties prop;
public void read_Data_FromExcel(String arg1) throws Throwable {
datamap = DataHelper.data("C:/FINALORDER/ORDERING_Tools/ordering-tools/src/test/resources/Test451.xlsx","Sheet1");
prop=new Properties();
int index = Integer.parseInt(arg1)-2;
FileInputStream fs = new FileInputStream("C:\\FINALORDER\\ORDERING_Tools\\ordering-tools\\src\\test\\resources\\Test451.xlsx");
XSSFWorkbook workbook = new XSSFWorkbook(fs);
XSSFSheet sheet = workbook.getSheet("Sheet1");
Row HeaderRow = sheet.getRow(0);
for(int i=0;i<HeaderRow.getPhysicalNumberOfCells();i++) {
prop.setProperty(HeaderRow.getCell(i).toString(), datamap.get(index).get(HeaderRow.getCell(i).toString()));
Using the above code i m fetching the data from excel and then inputting that data into the fields present in the screen like below:
try {
String remark=ExcelSteps.prop.getProperty("Remark");
mosdpOrderSummaryEditPage.tbx_remarks.type(remark);
}catch(Exception e) {
}
The issue is if there are many fields like remark,name,address,id then i have to fetch them one by one using the above code as for remark which is sort of redundant work and increasing code unnecessarily , is there a way by which i can reduce the code or make any generic method.
You can create separate column and get those through
ExcelSteps.prop.getProperty("Here you should pass the column data");
By this code will automatically get remark,name,address,id field names dynamically.
:)
I am using Java 8, excel and apache poi for my project. There are certain cell values that I am interested in extracting from excel using java. I am trying to detect text which is strikeout in the excel cells, but the format of text is little different that is why I am facing some problems.
Below is how data laid out in my excel sheet:
After extacting this data from excel, I always save it in string arraylist format like this a = [text 1, text 2, text 3]. code is mentioned below if you want to see how I am storing data in this arraylist.
What I want:
I want to ignore all those texts which are strikeout, so in above case I expect to have output like this [text 2, text 3] for first picture and second picture.
What I tried:
For the sake of just detecting strikeout values, I tried below code first:
XSSFRichTextString text = new XSSFRichTextString(a.get(0));
XSSFFont font = text.getFontAtIndex(0);
Boolean font_striked = font.getStrikeout();
but above code is not working as font_striked returns null, it must return true or false
The code which partially works in my case on single line cell values is:
boolean striked_out = sheet.getRow(row_index).getCell(column_index).getCellStyle().
getFont().getStrikeout();
This code only works if there is single line value in the cell and not with bullet list as shown above. It fails as it is not made for such kind of text.
P.S
I believe that if somehow I am able to detect even a single strikeout string in bullet points from arraylist, I can make it work for all the data.
As per the answer below I have updated my question adding following code to show how I make my string arraylist
How I convert data in excel into Arraylist:
String value_header = cell.getStringCellValue();
String[] newline_split = value_header.split("-");
for (int i = 0; i < newline_split.length; i++){
final_values = newline_split[i].
replace("\n"," ").replaceAll("\\s{2,}", " ").trim();
XSSFRichTextString text = new XSSFRichTextString(final_values);
XSSFFont font = text.getFontAtIndex(0);
Boolean font_striked = font.getStrikeout();
} // for ends here
You will need to get the RichTextString first, then go through all FormattingRuns, check whether it is stroked out and only if not, then get the appropriated substring and put it into the List:
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.usermodel.CellType.*;
import org.apache.poi.xssf.usermodel.*;
import java.io.FileInputStream;
import java.util.List;
import java.util.ArrayList;
class ReadExcelRichTextCells {
public static void main(String[] args) throws Exception {
Workbook wb = WorkbookFactory.create(new FileInputStream("ExcelRichTextCells.xlsx"));
Sheet sheet = wb.getSheetAt(0);
for (Row row : sheet) {
for (Cell cell : row) {
switch (cell.getCellTypeEnum()) {
case STRING:
XSSFRichTextString richtextstring = (XSSFRichTextString)cell.getRichStringCellValue();
String textstring = richtextstring.getString();
List<String> textparts = new ArrayList<String>();
if (richtextstring.hasFormatting()) {
for (int i = 0; i < richtextstring.numFormattingRuns(); i++) {
if (richtextstring.getFontOfFormattingRun(i)==null || !richtextstring.getFontOfFormattingRun(i).getStrikeout()) {
int indexofformattingrun = richtextstring.getIndexOfFormattingRun(i);
String textpart = textstring.substring(indexofformattingrun,
indexofformattingrun + richtextstring.getLengthOfFormattingRun(i));
String[] textpart_split = textpart.split("-");
for (int j = 0; j < textpart_split.length; j++){
String text = textpart_split[j].replace("\n", "").trim();
if (!"".equals(text)) textparts.add(text);
}
}
}
} else {
textparts.add(textstring);
}
System.out.println(textparts);
break;
//...
default:
System.out.println("default cell"); //should never occur
}
}
}
wb.close();
}
}
This is how to get the strikethrough in Excel with VBA:
Public Sub IsMyActivecellStriked()
Debug.Print ActiveCell.Font.Strikethrough
End Sub
If you have something like this:
Then the you should find a way to access the characters and check for them. Like this:
Option Explicit
Public Sub TestMe()
Dim strRange As String
Dim varArr As Variant
Dim varStr As Variant
Dim lngStart As Long
Dim lngEnd As Long
strRange = [a1]
varArr = Split(strRange, Chr(10))
For Each varStr In varArr
lngStart = InStr(1, strRange, varStr)
Debug.Print [a1].Characters(Start:=lngStart, Length:=Len(varStr)).Font.Strikethrough
Debug.Print [a1].Characters(Start:=lngStart, Length:=Len(varStr)).Text
Next varStr
End Sub
This will give you the following in the immediate window:
False
aaa
True
bbb
True
ccc
False
ddd
This should be possible to be translated into Java with the POI library.
As per I understanding above question Question (plz Correct me if I am wrong..!)
It should show whether your text in cell is strikethrough or not. ( TRUE or FALSE)
Below I have created a demo with that :
public class ApachePOI {
public static void main(String[] args) {
//Using workbook
XSSFWorkbook workbook;
try {
//Access excel file as workbook
workbook = new XSSFWorkbook(new FileInputStream(new File("/testExcelfile.xlsx")));
// first sheet of excel file
XSSFSheet xssfFirstSheet = workbook.getSheetAt(0);
//Check for A1 cell that strikethrough or not
boolean strikedOutTextStatus = xssfFirstSheet.getRow(0).getCell(0).getCellStyle().getFont().getStrikeout();
//print status of A1 cell text
System.out.println(strikedOutTextStatus);
// UPDATED CODE
if(strikedOutTextStatus){
String cellStringValue = xssfFirstSheet.getRow(0).getCell(0).getStringCellValue();
System.out.println("cell Value : "+cellStringValue.replace("-", "").replace(" ", ""));
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
I am trying to convert an excel to Json and it should save in some local directory. I am able to save the Json file but All are getting Null values. It is reading Excel row by row and there itself it is changing it into JSON format and saving again it is going to next row and replacing the previous Json file with current row data. It is in while loop so it is iterating and replacing the file. If i move the converter code from while, only one row data is coming that to null values with column names (json data) is coming rather than 34 row data(full data).
Here is the code. Please suggest me how can i achieve the whole excel data to be converted and should save the file in local directory.
public static void uploadXLS(MultipartFile file, Document doc)
throws IOException {
Products products = new Products();
List<Products> productsList = new ArrayList<Products>();
logger.info("uploadExcel method");
HSSFWorkbook wb = null;
try {
wb= new HSSFWorkbook(file.getInputStream());
System.out.println("workbook: "+wb);
HSSFSheet sheet = wb.getSheetAt(0);
System.out.println("worksheet: "+sheet);
HSSFRow row;
Iterator<Row> iterator = sheet.iterator();
while (iterator.hasNext()) {
products = new Products();
Row nextRow = iterator.next();
Iterator<Cell> cellIterator = nextRow.cellIterator();
Cell cell = cellIterator.next();
Iterator cells = nextRow.cellIterator();
cell=(HSSFCell) cells.next();
if (cell.getCellType() == HSSFCell.CELL_TYPE_STRING)
{
System.out.print(cell.getStringCellValue()+" ");
}
else if(cell.getCellType() == HSSFCell.CELL_TYPE_NUMERIC)
{
System.out.print(cell.getNumericCellValue()+" ");
}
else if(HSSFDateUtil.isCellDateFormatted(cell)){
Date date = HSSFDateUtil.getJavaDate(cell.getNumericCellValue());
} else
{
//U Can Handel Boolean, Formula, Errors
}
products.setId(new DataFormatter().formatCellValue(nextRow.getCell(0)));
products.setProductId(new DataFormatter().formatCellValue(nextRow.getCell(1)));
products.setPopularity((new DataFormatter().formatCellValue(nextRow.getCell(12))));
products.setRelevance((new DataFormatter().formatCellValue(nextRow.getCell(13))));
products.setShortlisted((new DataFormatter().formatCellValue(nextRow.getCell(14))));
products.setLikes((new DataFormatter().formatCellValue(nextRow.getCell(15))));
products.setCreateDt((new DataFormatter().formatCellValue(nextRow.getCell(16))));
products.setPageId((new DataFormatter().formatCellValue(nextRow.getCell(17))));
products.setStyleName(nextRow.getCell(18).getStringCellValue());
products.setStyleId(nextRow.getCell(19).getStringCellValue());
products.setPriceRange(nextRow.getCell(20).getStringCellValue());
products.setPriceId(nextRow.getCell(21).getStringCellValue());
// products.setDefaultPrice(nextRow.getCell(22).getStringCellValue());
products.setDefaultMaterial(nextRow.getCell(23).getStringCellValue());
products.setDefaultFinish((new DataFormatter().formatCellValue(nextRow.getCell(24))));
productsList.add(products);
System.out.println(productsList.add(products));
// JSON CONVERTER
ObjectMapper mapper = new ObjectMapper();
System.out.println("productsList: "+products);
DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
Date date = new Date();
String location = dateFormat.format(date);
System.out.println("productsList final: "+products);
// Convert object to JSON string
String jsonInString = mapper.writeValueAsString(products);
System.out.println("JsonInString " +jsonInString);
// Convert object to JSON string and pretty print
jsonInString = mapper.writerWithDefaultPrettyPrinter()
.writeValueAsString(products);
mapper.writeValue(new File("D:\\"+location+"products.json"), productsList);
}
} catch (JsonGenerationException e) {
e.printStackTrace();
} catch (JsonMappingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
finally {
}
}
Am attaching the screen shot, here you can see that, there 34 rows in excel. It is doing well and displaying the values but the last Json generated file have null values. All 34 rows data is having null values :(
Thank a lot in Advance :). Hoping that anyone can get me out of this issue.
First of all you have to re-instantiate the Product object every time , otherwise there is only object at the end in the list.
.....
Iterator<Row> iterator = sheet.iterator();
while (iterator.hasNext()) {
products = new Products(); // re-instantiation.
Row nextRow = iterator.next();
Iterator<Cell> cellIterator = nextRow.cellIterator();
.....
Products is containing single product information. If possible then rename it to Product.
I want to convert my excel file as well as its entites(charts, tables, images) to jpeg/png images. Currently using aspose for that. Here is my code
public static int excelToImages(final String sourceFilePath, final String outFilePrefix) throws Exception {
int noOfImages = 0;
Workbook workbook = getWorkbook(sourceFilePath);
List<Worksheet> worksheets = getAllWorksheets(workbook);
if (worksheets != null) {
for (Worksheet worksheet : worksheets) {
if (worksheet.getCells().getCount() > 0) {
String outFilePath = FileUtils.getAbsoluteFilePath(outFilePrefix + (noOfImages++));
SheetRender sr = new SheetRender(worksheet, getImageOrPrintOptions());
sr.toImage(0, outFilePath);
}
}
}
return noOfImages;
}
private static ImageOrPrintOptions getImageOrPrintOptions() {
ImageOrPrintOptions imgOptions = new ImageOrPrintOptions();
imgOptions.setImageFormat(ImageFormat.getJpeg());
imgOptions.setOnePagePerSheet(true);
return imgOptions;
}
private static List<Worksheet> getAllWorksheets(final Workbook workbook) {
List<Worksheet> worksheets = new ArrayList<Worksheet>();
WorksheetCollection worksheetCollection = workbook.getWorksheets();
for (int i = 0; i < worksheetCollection.getCount(); i++) {
worksheets.add(worksheetCollection.get(i));
}
return worksheets;
}
My problem is that size of output image is either split into multiple A4 size or single 1 sheet depends upon the value of
imgOptions.setOnePagePerSheet(true);
Can anybody tell me how I can customize the size of output image file?
You can try it with imgOptions.setOnlyArea(true);. That will set the size of the image to the minimal that's needed to put everything to the image. But I'm not sure if the generated image is split into A4 parts.