Short question
I'm searching a Java library which can convert a worksheet into an image.
My use case
I have a pptx file. That file has on one slide an excel file embedded. In "presentation modus" (I mean when you don't edit the excel), an image representation is shown. That image is generated by PowerPoint and stored in the pptx file in /ppt/images/*.emf. If the data of the excel file gets changed (that's what I do programmatically), the image isn't up to date anymore. So you have to generate a new image of the worksheet and replace the old image by the new one.
Already found Options
convert the excel to pdf with iText and convert the
pdf to an image
Aspose
I wonder if there are alternatives. Any hint is really appreciated.
You can convert your excel file to HTML (e.g. like this) and then convert it to an image (e.g with this library).
Another option is SmartXLS, but it doesen't work in java.awt.headless=true modus and that's a no go for us.
Finally we will do it with aspose.
#spilymp Thanks for the support.
Here is the snipped that will do it just in case that the aspose link won't work anymore:
public void generateImages(final String sourcePath) {
try {
Workbook workbook = new Workbook(sourcePath);
List<Worksheet> worksheets = getAllWorksheets(workbook);
if (worksheets != null) {
int noOfImages = 0;
for (Worksheet worksheet : worksheets) {
if (worksheet.getCells().getCount() > 0 || worksheet.getCharts().getCount() > 0 || worksheet.getPictures().getCount() > 0) {
String imageFilePath = sourcePath + "_output_" + (noOfImages++) + ".jpeg";
SheetRender sr = new SheetRender(worksheet, getImageOrPrintOptions());
sr.toImage(0, imageFilePath);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Returns all worksheets present in given workbook.
*
* #param workbook
* #return all worksheets present in given workbook.
*/
private 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;
}
/**
* Returns ImageOrPrintOptions for png images
*
* #return
*/
private ImageOrPrintOptions getImageOrPrintOptions() {
ImageOrPrintOptions imgOptions = new ImageOrPrintOptions();
imgOptions.setImageFormat(ImageFormat.getJpeg());
imgOptions.setOnePagePerSheet(true);
return imgOptions;
}
Related
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();
}
}
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.
I have an gwt web application on linux .In server side ,I tried to insert some images to excel file by Apache poi 3.10.
I got inputstream of image files from db and send it to existing excel file .
the code is:
Drawing patriarch = null;
private void addImages(InputStream in, HSSFWorkbook requestReport, HSSFSheet sheet, int row, int col) throws IOException {
CreationHelper helper = requestReport.getCreationHelper();
ClientAnchor anchor = helper.createClientAnchor();
byte[] bytes = IOUtils.toByteArray(in);
int pictureIndex = requestReport.addPicture(bytes, HSSFWorkbook.PICTURE_TYPE_PNG);
in.close();
if (patriarch == null) {
patriarch = sheet.createDrawingPatriarch();
}
anchor.setAnchorType(2);
anchor.setRow1(row);
anchor.setCol1(col);
Picture picture = patriarch.createPicture(anchor, pictureIndex);
picture.resize();
picture.setLineStyle(HSSFPicture.LINESTYLE_DASHDOTGEL);
}
In this case that I get Inputstream from DB, I get NoSuchElementException on picture.resize().
On the other hand if I use image from disk to insert, there is no error and image is not shown.
in = new FileInputStream("/test.jpeg")
Any help would be appreciated.
The disk image is not shown because you don't set the second column and row in your anchor. You should add:
anchor.setRow2(row + 1);
anchor.setCol2(col + 1);
I don't know what's the reason why the input stream from your DB gives an error. The error suggests that the input stream is empty (doesn't contain data), but the reason cannot be determined from your post.
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.
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.
}
}