Changing XLSX form control location with Apache POI - java

I've some number of xlsm files containing form controls. I'd like to programmatically move a particular button down a few rows on each sheet. My first hope was to do something like this:
FileInputStream inputStream = new FileInputStream(new File("t.xlsm"));
XSSFWorkbook wb = new XSSFWorkbook(inputStream);
XSSFSheet xs = (XSSFSheet)wb.getSheetAt(1);
RelationPart rp = xs.getRelationParts().get(0);
XSSFDrawing drawing = (XSSFDrawing)rp.getDocumentPart();
for(XSSFShape sh : drawing.getShapes()){
XSSFClientAnchor a = (XSSFClientAnchor)sh.getAnchor();
if (sh.getShapeName().equals("Button 2")) {
a.setRow1(a.getRow1()+10);
a.setRow2(a.getRow2()+10);
}
}
However, the shape objects given by XSSFDrawing.getShapes() are copies and any changes to them are not reflected in the document after a wb.write().
I tried a couple other approaches, such as getting the CTShape and parsing the XML within but things quickly got hairy.
Is there a recommended way to manage form controls like this via POI?

I ended up fiddling directly with the XML:
wb = new XSSFWorkbook(new File(xlsmFile));
XSSFSheet s = wb.getSheet("TWO");
XmlObject[] subobj = s.getCTWorksheet().selectPath(declares+
" .//mc:AlternateContent/mc:Choice/main:controls/mc:AlternateContent/mc:Choice/main:control");
String targetButton = "Button 2";
int rowsDown = 10;
for (XmlObject obj : subobj) {
XmlCursor cursor = obj.newCursor();
cursor.push();
String attrName = cursor.getAttributeText(new QName("name"));
if (attrName.equals(targetButton)) {
cursor.selectPath(declares+" .//main:from/xdr:row");
if (!cursor.toNextSelection()) {
throw new Exception();
}
int newRow = Integer.parseInt(cursor.getTextValue()) + rowsDown;
cursor.setTextValue(Integer.toString(newRow));
cursor.pop();
cursor.selectPath(declares+" .//main:to/xdr:row");
if (!cursor.toNextSelection()) {
throw new Exception();
}
newRow = Integer.parseInt(cursor.getTextValue()) + rowsDown;
cursor.setTextValue(Integer.toString(newRow));
}
cursor.dispose();
}
This moves the named button down 10 rows. I had to discover the button name (which may not be easy to do via Excel, I inspected the file directly). I'm guessing this is going to be very sensitive to the version of Excel in use.

Related

Cell style is lost or not displayed in Excel 97-2003 (.xls)

I am using the Apache POI library to export data to Excel. I have tried all the latest versions (3.17, 4.1.2, and 5.2.1).
I have a problem with Excel 97 (.xls) format in relation to cell styles. The cell style somehow is lost (or not displayed) after a certain number of columns.
Here is my sample code:
private void exportXls() {
try (
OutputStream os = new FileOutputStream("test.xls");
Workbook wb = new HSSFWorkbook();) {
Sheet sh = wb.createSheet("test");
Row r = sh.createRow(0);
for (int i = 0; i < 50; i++) {
Cell c = r.createCell(i);
c.setCellValue(i + 1);
CellStyle cs = wb.createCellStyle();
cs.setFillBackgroundColor(IndexedColors.WHITE.index);
cs.setFillPattern(FillPatternType.SOLID_FOREGROUND);
cs.setFillForegroundColor(IndexedColors.LIGHT_BLUE.getIndex());
c.setCellStyle(cs);
}
wb.write(os);
os.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
And the result as viewed by MS Excel 2019
Viewed by MS Excel
As you can see, the style/format is lost after cell 43rd.
But, when I open the same file by other applications like XLS Viewer Free (from Microsoft Store) or Google Sheets (online), the style/format still exists and is displayed well.
Viewed by XLS Viewer Free
Viewed by Google Sheets
Could anyone please tell me what is going on here?
Did I miss something in my code?
Is there any hidden setting in MS Excel that causes this problem?
Thank you.
Creating cell styles for each single cell is not a good idea using apache poi. Cell styles are stored on workbook level in Excel. The sheets and cells share the cell styles if possible.
And there are limits for maximum count of different cell styles in all Excel versions. The limit for the binary *.xls is less than the one for the OOXML *.xlsx.
The limit alone cannot be the only reason for the result you have. But it seems as if Excel is not very happy with the 50 exactly same cell styles in workbook. Those are memory waste as only one shared style would be necessary as all the 50 cells share the same style.
Solutions are:
Do creating the cell styles on workbook level outside cell creating loops and only set the styles to the cells in the loop.
Example:
private static void exportXlsCorrect() {
try (
OutputStream os = new FileOutputStream("testCorrect.xls");
Workbook wb = new HSSFWorkbook();) {
CellStyle cs = wb.createCellStyle();
cs.setFillBackgroundColor(IndexedColors.WHITE.index);
cs.setFillPattern(FillPatternType.SOLID_FOREGROUND);
cs.setFillForegroundColor(IndexedColors.LIGHT_BLUE.getIndex());
Sheet sh = wb.createSheet("test");
Row r = sh.createRow(0);
for (int i = 0; i < 50; i++) {
Cell c = r.createCell(i);
c.setCellValue(i + 1);
c.setCellStyle(cs);
}
wb.write(os);
os.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
Sometimes it is not really possible to know all possible needed cell styles before creating the cells. Then CellUtil can be used. This has a method CellUtil.setCellStyleProperties which is able to set specific style properties to cells. Doing that new cell styles are created on workbook level only if needed. If already present, the present cell styles are used.
Example:
private static void exportXlsUsingCellUtil() {
try (
OutputStream os = new FileOutputStream("testUsingCellUtil.xls");
Workbook wb = new HSSFWorkbook();) {
Sheet sh = wb.createSheet("test");
Row r = sh.createRow(0);
for (int i = 0; i < 50; i++) {
Cell c = r.createCell(i);
c.setCellValue(i + 1);
java.util.Map<java.lang.String,java.lang.Object> properties = new java.util.HashMap<java.lang.String,java.lang.Object>();
properties.put(org.apache.poi.ss.util.CellUtil.FILL_BACKGROUND_COLOR, IndexedColors.WHITE.index);
properties.put(org.apache.poi.ss.util.CellUtil.FILL_FOREGROUND_COLOR, IndexedColors.LIGHT_BLUE.getIndex());
properties.put(org.apache.poi.ss.util.CellUtil.FILL_PATTERN, FillPatternType.SOLID_FOREGROUND);
org.apache.poi.ss.util.CellUtil.setCellStyleProperties(c, properties);
}
wb.write(os);
os.flush();
} catch (Exception e) {
e.printStackTrace();
}
}

Java Check workbook contains a specific spreadsheet or not using Apache POI

I'm using apache POI to create a excel file which contains multiple spreadsheet. Firstly, this method should check the workbook is empty or not after check its existence. If empty, create new sheet name "sheet1"; if not, check any sheets named "sheet1" in this workbook. If yes, adding new rows to the sheet; if not, create a sheet name "sheet1". Below is my current code, which I think is not the right method to achieve my goal:
File file = new File("Main Data.xlsx");
// Retrieve the workbook for the main report
XSSFWorkbook workbook;
// Check file existence
if (file.exists() == false) {
// Create new file if it does not exist
workbook = new XSSFWorkbook();
} else {
try (
// Make current input to exist file
InputStream is = new FileInputStream(file)) {
workbook = new XSSFWorkbook(is);
}
}
XSSFSheet spreadsheet;
// Check if the workbook is empty or not
if (workbook.getNumberOfSheets() != 0) {
for (int i = 0; i < workbook.getNumberOfSheets(); i++) {
if (workbook.getSheetName(i).equals(" Enc " + deviceLabel.getText() + " Info. ")) {
spreadsheet = workbook.getSheet(" Enc " + deviceLabel.getText() + " Info. ");
} else spreadsheet = workbook.createSheet(" Enc " + deviceLabel.getText() + " Info. ");
}
}
else {
// Create new sheet to the workbook if empty
spreadsheet = workbook.createSheet(" Enc " + deviceLabel.getText() + " Info. ");
}
// Error here (variable spreadsheet may not have been initialized
spreadsheet.shiftRows(0, spreadsheet.getLastRowNum(), 23);
Tow problems I am getting.
The for loop is obviously wrong to achieve my goal. Is there any way I can check empty spreadsheet or named spreadsheet exists already in the same time in one if statement?
e.g.
if ((workbook.getNumberOfSheets() == 0) && !workbook.getSheet("sheet1").exists()) {
workbook.createSheet("sheet1");
} else workbook.getSheet("sheet1")
I keep getting error that said spreadsheet is not initialized, but if I delete the for loop, the error will disappear.
I figured my problem with following code:
File file = new File("Main Data.xlsx");
// Retrieve the workbook for the main report
XSSFWorkbook workbook;
// Check file existence
if (file.exists() == false) {
// Create new file if it does not exist
workbook = new XSSFWorkbook();
} else {
try (
// Make current input to exist file
InputStream is = new FileInputStream(file)) {
workbook = new XSSFWorkbook(is);
}
}
XSSFSheet spreadsheet = workbook.getSheet("sheet1");
// Check if the workbook is empty or not
if (workbook.getNumberOfSheets() != 0) {
for (int i = 0; i < workbook.getNumberOfSheets(); i++) {
if (workbook.getSheetName(i).equals("sheet1")) {
spreadsheet = workbook.getSheet("sheet1");
} else spreadsheet = workbook.createSheet("sheet1");
}
}
else {
// Create new sheet to the workbook if empty
spreadsheet = workbook.createSheet("sheet1");
}
// Shift rows down if exist
spreadsheet.shiftRows(0, spreadsheet.getLastRowNum(), 23);
You can simply check if a sheet is created or not with
workbook.getSheetIndex(sheetName)
returns -1 if the sheet is not in the work book or Index if it is present in the workbook.
You have declared workbook as final, it can not be initial for work!
your code is wrong a priori. You probably add pluses yourself for your answers?
public XSSFSheet factorySheet(final String name) {
return Optional.ofNullable((XSSFSheet) workbook.getSheet(name))
.orElseGet(()->(XSSFSheet) workbook.createSheet(name));
}

How do I get column names when using XSSFReader to parse XML? [duplicate]

How to get the column names in an Excel file using Apache POI, to make sure that the columns are ordered as expected.
There is a convenience method for this:
CellReference.convertNumToColString(cell.getColumnIndex());
To get the full name:
private static String getCellName(Cell cell)
{
return CellReference.convertNumToColString(cell.getColumnIndex()) + (cell.getRowIndex() + 1);
}
Or this:
cell.getSheet().getRow(0).getCell(currentcellIndex)
.getRichStringCellValue().toString()
Row indexes start from 0.
Apache POI, translating Excel column number to letter
FileInputStream fis = new FileInputStream(
new File("D:\\workspace\\Writesheet.xlsx"));
#SuppressWarnings("resource")
XSSFWorkbook workbook = new XSSFWorkbook(fis);
XSSFSheet spreadsheet = workbook.getSheetAt(0);
int lastcell=spreadsheet.getRow(0).getLastCellNum();
//Non empty Last cell Number or index return
for(int i=0;i<=lastcell;i++)
{
try
{
System.out.println(CellReference.convertNumToColString(i));
}catch(Exception e)
{}
}
fis.close();
To get cell name:
String cellName = new CellAddress(intRow, intCol).toString();

convert xls to image

I am trying to create multiple and complex Table in PPT (not PPTX), si Im using POI Apache HSLF, The problem is that I have multiple kinds of tables with multiple headers sometimes,
I think then to create my tables on xls files than convert them to image and finally embed them on my generated PPT
I know it is a complex theory, but what I need now is to transform my XLS to image
any help please
Thanks
You cant do it with POI but You can convert or copy charts(graphs) using J XL or Aspose Cells(Aspose is not free).
This is the code snippet to extract excel chart to image
public class ExportChartToImage
{
public static void main(String[] args) throws Exception
{
//Start Excel
Application excelApp = new Application();
excelApp.setVisible(true);
//Create test workbook
Workbook workbook = excelApp.createWorkbook("/home/tejus/Desktop/Chart Test");
//Get the first (and the only) worksheet
final Worksheet worksheet1 = workbook.getWorksheet(1);
//Fill-in the first worksheet with sample data
worksheet1.getCell("A1").setValue("Date");
worksheet1.getCell("A2").setValue("March 1");
worksheet1.getCell("A3").setValue("March 8");
worksheet1.getCell("A4").setValue("March 15");
worksheet1.getCell("B1").setValue("Customer");
worksheet1.getCell("B2").setValue("Smith");
worksheet1.getCell("B3").setValue("Jones");
worksheet1.getCell("B4").setValue("James");
worksheet1.getCell("C1").setValue("Sales");
worksheet1.getCell("C2").setValue("23");
worksheet1.getCell("C3").setValue("17");
worksheet1.getCell("C4").setValue("39");
excelApp.getOleMessageLoop().doInvokeAndWait(new Runnable()
{
public void run()
{
final Variant unspecified = Variant.createUnspecifiedParameter();
final Int32 localeID = new Int32(LocaleID.LOCALE_SYSTEM_DEFAULT);
Range sourceDataNativePeer = worksheet1.getRange("A1:C4").getPeer();
_Worksheet worksheetNativePeer = worksheet1.getPeer();
IDispatch chartObjectDispatch = worksheetNativePeer.chartObjects(unspecified, localeID);
ChartObjectsImpl chartObjects = new ChartObjectsImpl(chartObjectDispatch);
ChartObject chartObject = chartObjects.add(new DoubleFloat(100), new DoubleFloat(150), new DoubleFloat(300), new DoubleFloat(225));
_Chart chart = chartObject.getChart();
chart.setSourceData(sourceDataNativePeer, new Variant(XlRowCol.xlRows));
BStr fileName = new BStr("/home/tejus/Desktop/chart.gif");
Variant filterName = new Variant("gif");
Variant interactive = new Variant(false);
chart.export(fileName, filterName, interactive);
chart.setAutoDelete(false);
chart.release();
chartObject.setAutoDelete(false);
chartObject.release();
chartObjects.setAutoDelete(false);
chartObjects.release();
chartObjectDispatch.setAutoDelete(false);
chartObjectDispatch.release();
}
});
System.out.println("Press 'Enter' to terminate the application");
System.in.read();
//Close the MS Excel application.
boolean saveChanges = false;
workbook.close(saveChanges);
boolean forceQuit = true;
excelApp.close(forceQuit);
}
}
i used J excel
There are 3rd party products like aspose which provides API's for converting worksheets to image file. However you could start by creating a rendering engine which does the geometry layout and write all the data that are available on the sheet to a canvas and then create a converter which gets the pixel data from the canvas and renders to a file.

Modifying existing excel using jxl

I m not able to edit the existing excel sheet using jxl.
It always creates a new one.
Can anyone please help me out with it.
Please give a small sample code.
jxl is designed for increased read efficiency (since this is the primary use of the API). In order to improve performance, data which relates to output information (eg. all the formatting information such as fonts) is not interpreted when the spreadsheet is read, since this is superfluous when interrogating the raw data values.
However, if we need to modify this spreadsheet a handle to the various write interfaces is needed, which can be obtained using the copy method.
Workbook workbook = Workbook.getWorkbook(new File("myfile.xls"));
WritableWorkbook copy = Workbook.createWorkbook(new File("temp.xls"), workbook);
This copies the information that has already been read in as well as performing the additional processing to interpret the fields that are necessary to for writing spreadsheets. The disadvantage of this read-optimized strategy is that we have two spreadsheets held in memory rather than just one, thus doubling the memory requirements.
But after this, you can do whatever you want. Like:
WritableSheet sheet2 = copy.getSheet(1);
WritableCell cell = sheet2.getWritableCell(1, 2);
if (cell.getType() == CellType.LABEL)
{
Label l = (Label) cell;
l.setString("modified cell");
}
copy.write();
copy.close();
workbook.close();
Note: this is directly taken from Andy Khan's tutorial page.
I know that this is quite an old question, but if anyone will encounter the same problem, then to preserve the correct formatting (font type, colouring, etc. )
you should save the cell format before casting it to Label, and then force the cell to the previous formatting.
Code:
CellFormat cfm = cell.getCellFormat();
Label l = (Label) cell;
l.setString("modified cell");
cell.setCellFormat(cfm);
//there is god example of it, you can copy in ur project and check it out, to
//understand how it works
Workbook wk = Workbook.getWorkbook(new File("ex.xls"));
//
WritableWorkbook wkr = Workbook.createWorkbook(new File("modifed.xls"), wk);
/* second line makes copy of wk excel file object /creates a readable spreadsheet.
both are now similar and i can Modify exiting wkr spreadsheets */
//next 2 line retrieve sheet number 0 and cell (1,1)
WritableSheet getsht = wkr.getSheet(0);
WritableCell getcl = getsht.getWritableCell(1, 1);
//making own font
WritableFont ft = new WritableFont(WritableFont.ARIAL, 20 , WritableFont.BOLD, true , UnderlineStyle.SINGLE);
//making Format, which uses font
WritableCellFormat form = new WritableCellFormat( ft);
Number nb = ( Number ) getcl ;
nb.setCellFormat( form );
wkr.write();
wkr.close();
I personally use this code to append the xls file and create one if it doesn't exist.
Using jxl 2.6:
public class Excel {
private String fileName = "excel_file.xls";
private String sheetName = "sheet1";
private WritableWorkbook writableWorkbook;
private int rowCount;
private Workbook wb;
// assigns checks if file exists or not, both cases we assign it to a WritableWorkbook // object so that we can write to it.
private void assignWorkBook() throws IOException, BiffException {
// File f = new File(System.getProperty("user.dir") +"\\"+fileName);
File inp = new File(fileName);
try{
wb = Workbook.getWorkbook(inp);
writableWorkbook = Workbook.createWorkbook(inp, wb);
} catch (FileNotFoundException e){
writableWorkbook = Workbook.createWorkbook(inp); //Create a new one
}
}
public int getRowCount() {
return rowCount;
}
// this function writes a vector to an excel file, checks if there is already a sheet
// with that name or not, and uses it. then we have to close the Workbook object before
// we could write to the file, and then we save the file.
// That is, the file is always saved after writing to it.
public void writeRow(Vector<String> playerVector) throws WriteException, IOException, BiffException {
assignWorkBook();
WritableSheet excelSheet;
if(writableWorkbook.getNumberOfSheets() == 0) {
excelSheet = writableWorkbook.createSheet(sheetName, 0);
}
else {
excelSheet = writableWorkbook.getSheet(sheetName);
}
rowCount = excelSheet.getRows();
int colCount = 0;
for(String playerStat:playerVector) {
Label label = new Label(colCount++, rowCount, playerStat);
excelSheet.addCell(label);
}
if(wb != null) {
wb.close();
}
writableWorkbook.write();
writableWorkbook.close(); //everytime save it.
}
}

Categories