I'm creating Excel file in Java using Apache POI, it works but the inserted image is anchored to columns / rows. There are 2 issues with this method:
The image doesn't keep the original size (get stretched a little bit).
When resizing the columns/rows the image get stretched. Is there any way to insert an image to Excel without anchor? (same as using Excel / Insert / Picture)
I'm using the following example from Apache POI document page:
//create a new workbook
Workbook wb = new XSSFWorkbook(); //or new HSSFWorkbook();
//add picture data to this workbook.
InputStream is = new FileInputStream("image1.jpeg");
byte[] bytes = IOUtils.toByteArray(is);
int pictureIdx = wb.addPicture(bytes, Workbook.PICTURE_TYPE_JPEG);
is.close();
CreationHelper helper = wb.getCreationHelper();
//create sheet
Sheet sheet = wb.createSheet();
// Create the drawing patriarch. This is the top level container for all shapes.
Drawing drawing = sheet.createDrawingPatriarch();
//add a picture shape
ClientAnchor anchor = helper.createClientAnchor();
//set top-left corner of the picture,
//subsequent call of Picture#resize() will operate relative to it
anchor.setCol1(3);
anchor.setRow1(2);
Picture pict = drawing.createPicture(anchor, pictureIdx);
//auto-size picture relative to its top-left corner
pict.resize();
//save workbook
String file = "picture.xls";
if(wb instanceof XSSFWorkbook) file += "x";
FileOutputStream fileOut = new FileOutputStream(file);
wb.write(fileOut);
fileOut.close();
Thanks for your answer.
Proably that because of this comment in method 'resize' javadoc:
Please note, that this method works correctly only for workbooks with the default font size (Arial 10pt for .xls and Calibri 11pt for .xlsx). If the default font is changed the resized image can be streched vertically or horizontally.
Related
I follow the sample and add pictures as watermark like the following:
private void addWaterMark4AllSheets() {
final BufferedImage image = FontImage.createWatermarkImage();
// Export to byte stream B
final ByteArrayOutputStream os = new ByteArrayOutputStream();
try {
ImageIO.write(image, "png", os);
} catch (final IOException e) {
e.printStackTrace();
}
final XSSFWorkbook wb = (XSSFWorkbook) workBook;
final int pictureIdx = wb.addPicture(os.toByteArray(), Workbook.PICTURE_TYPE_PNG);
final POIXMLDocumentPart poixmlDocumentPart = wb.getAllPictures().get(pictureIdx);
// ((XSSFSheet )(schreiben.getSheet()).
for (int i = 0; i < workBook.getNumberOfSheets(); i++) {// Get each Sheet
final XSSFSheet sheet = wb.getSheetAt(i);
final PackagePartName ppn = poixmlDocumentPart.getPackagePart().getPartName();
final String relType = XSSFRelation.IMAGES.getRelation();
// add relation from sheet to the picture data
final PackageRelationship pr = sheet.getPackagePart().addRelationship(ppn, TargetMode.INTERNAL, relType,
null);
// set background picture to sheet
sheet.getCTWorksheet().addNewPicture().setId(pr.getId());
}
}
In general the approach works quite fine. A picture is added into the Excel. But the appearance is different.
In Excel: the image is displayed in the background during the editing (of the sheets). But it is NOT displayed when I print the sheet.
In LibreOffice (7.1): the image is NOT displayed during the editing (of the sheet) - but is printed.
Is there a chance to fix it for working in both Spreadsheets?
There is nothing what apache poi could change as this behavior is by design in the different spreadsheet softwares.
Your linked code example does not creating watermarks. Watermark functionality is not available in Microsoft Excel. Instead it adds background pictures to sheets.
Microsoft itself states: You cannot print a background graphic for a Excel worksheet. So using Microsoft Excel the sheets background graphics only are visible in Excel GUI but not in prints.
In LibreOffice those background graphics are called watermarks but Libreoffice states in Defining Graphics or Colors in the Background of Pages (Watermark):
In spreadsheets this background appears only in the print behind the
cells not formatted elsewhere.
So using Libreoffice the sheets background graphics are visible in prints only.
So what you had observed is by design.
In Add or remove a sheet background Microsoft describes methods to mimic a watermark in Excel. The usage of a picture in a header or footer to mimic a watermark using apache poi is described in apache POI adding watermark in Excel workbook. But this also means a print watermark. So the watermark is visible in print preview and print only. It is not visible in sheet's GUI. And the option to add picture in a header or footer to mimic a watermark is Excel only. LibreOffice does not provide that feature.
Conclusion:
There is not a possibility to have a functionality similar to a watermark which works in Excel and LibreOffice the same way.
I followed the approach to add a new image with the POI.
cell.getRow().setHeight(img.getHeightForExcel());
sheet.setColumnWidth(cell.getColumnIndex(), img.getWidthForExcel());
final int picID = workBook.addPicture(bytes, Workbook.PICTURE_TYPE_PNG);
/* Create the drawing container */
final XSSFDrawing drawing = (XSSFDrawing) sheet.createDrawingPatriarch();
// ========adding image START
final XSSFClientAnchor myAnchor = new XSSFClientAnchor();
myAnchor.setAnchorType(AnchorType.DONT_MOVE_AND_RESIZE);
/* Define top left corner, and we can resize picture suitable from there */
myAnchor.setCol1(cell.getColumnIndex()); // Column start
myAnchor.setRow1(rowNum - 1); // Row start
myAnchor.setCol2(cell.getColumnIndex() + 2); // Column end (covers two columns)
myAnchor.setRow2(rowNum); // Row end
/* Invoke createPicture and pass the anchor point and ID */
final XSSFPicture myPicture = drawing.createPicture(myAnchor, picID);
In principal this works quite well. I specify the width of the column at the beginning with the width of the image. (The height as well).
The major problem I'm facing is that as soon as I run autoadjust like
for (; i < max; i++) {
xlsWorkbook.getSheet().autoSizeColumn(i);
}
I get the problem that the first two columns are resized as well. But with this the width of the image is resized as well. Since the width might be quite long (or quite narrow) I don't want to affect the image size.
Is there a way to set the width of the image despite the column width?
If you don't want resizing the image when column widths are changing, then you cannot using that approach. This approach explicitly tells that the image shall be sized as the cell size it is anchored to. So if that cell size changes, the pictures size changes too.
You might think that ClientAnchor.AnchorType.DONT_MOVE_AND_RESIZE should protect the image from resizing. But this only is valuable when opened in Excel GUI. Apache poi does not respect ClientAnchor.AnchorType while auto sizing the columns. May be this will change in later versions. But in current version apache poi 5.0.0 it does not.
So to fulfill your requirement you set only a one cell anchor. That is only anchor.setCol1 and anchor.setRow1 as the upper left position of the picture. Then you need resizing the picture later to set the bottom right position. You must do that resizing after all column widths and row heights are set. So after auto sizing the columns. Else the resizing the columns will resizing the picture again.
Complete example:
import org.apache.poi.xssf.usermodel.*;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.util.IOUtils;
import java.io.InputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
class ImageTest {
public static void main(String[] args) throws Exception {
Workbook wb = new XSSFWorkbook();
Sheet sheet = wb.createSheet("My Sample Excel");
//FileInputStream obtains input bytes from the image file
InputStream inputStream = new FileInputStream("./logo.png");
//Get the contents of an InputStream as a byte[].
byte[] bytes = IOUtils.toByteArray(inputStream);
//Adds a picture to the workbook
int pictureIdx = wb.addPicture(bytes, Workbook.PICTURE_TYPE_PNG);
//close the input stream
inputStream.close();
//Returns an object that handles instantiating concrete classes
CreationHelper helper = wb.getCreationHelper();
//Creates the top-level drawing patriarch.
Drawing drawing = sheet.createDrawingPatriarch();
//Create an anchor that is attached to the worksheet
ClientAnchor anchor = helper.createClientAnchor();
//Set anchor type; only valuable in Excel GUI
anchor.setAnchorType(ClientAnchor.AnchorType.MOVE_DONT_RESIZE);
//Create an anchor with upper left cell only
anchor.setCol1(1); //Column B
anchor.setRow1(2); //Row 3
//Create a picture
Picture pict = drawing.createPicture(anchor, pictureIdx);
//Reset the image to the original size
//pict.resize(); // don't do this before autosize column
//Create cell in column B to auto sizing that column
Cell cell = sheet.createRow(0).createCell(1);
cell.setCellValue("12345678901234567890");
sheet.autoSizeColumn(1);
//Reset the image to the original size
//pict.resize();
//Reset the image to half the original size
pict.resize(0.5);
//Write the Excel file
FileOutputStream fileOut = null;
fileOut = new FileOutputStream("./myFile.xlsx");
wb.write(fileOut);
fileOut.close();
}
}
I have an image that is embedded in my solution and is used on the main form of a Winforms app, and also for pasting into a spreadsheet. The image size is 156X121.
I put it on the sheet like so:
var ms = new MemoryStream();
_logo.Save(ms, ImageFormat.Png);
ms.Position = 0;
pivotTableSheet.Pictures.Add(0, _grandTotalsColumnPivotTable, ms);
Yet when it is on the sheet, it stretches out and spills into neighboring cells, partially obscuring other data:
As you can see, the size is no longer 156X121. The height has been increased by 25%. Why? And how can I prevent that?
This code:
MessageBox.Show(string.Format("image height is {0}", _logo.Height));
MessageBox.Show(string.Format("image width is {0}", _logo.Width));
...showed me "126" as the height and "151" as the width, matching the image as it is in the project. So why is the original size changed? Is there a property I can set to leave the size alone and not stretch it? Or how can I prevent this gumbification of the image?
It's bizarre to me that the image is one size (126X151), its original size is purported to be 1.26" X 1.63", and its size after being scaled is 1.57" X 1.63".
Who or what is allowing this 25% increase in Height?
NOTE: If I select the "Reset" button in the image's "Size and Properties" dialog, it shrinks up as I want it to be, setting the Height "back" to 100% from 125%. Is there a way to do this "Reset" programmatically?
UPDATE
Based on the answer, I tried this:
var ms = new MemoryStream();
//_logo.Height = 121; <= cannot set; Height is a readonly property
_logo.Save(ms, ImageFormat.Png);
ms.Position = 0;
pivotTableSheet.Pictures.Add(0, _grandTotalsColumnPivotTable, ms);
Picture pic = pivotTableSheet.Pictures[0];
//Workbook.Worksheets[0].Pictures[0]; <= does not compile
pic.HeightScale = 100;
pic.WidthScale = 100;
(Workbook.Worksheets[0] does not compile for me).
It makes no difference; the image is still stretching vertically.
UPDATE 2
I realized I needed "Workbook" to be "workBook" due to this:
private static Workbook workBook;
...and so I tried this:
Picture pic = workBook.Worksheets[1].Pictures[0]; // Worksheet 0 is the data sheet that feeds the PivotTable and subsequently gets hidden, so need to use 1
pic.Height = 121;
pic.WidthScale = 100;
...but it still gumbifies the image vertically. So does replacing "pic.Height = 121" with "pic.HeightScale = 100;"
So this is the code currently, which adds the image, but in a vertically gumbified manner:
var ms = new MemoryStream();
//_logo.Height = 121; readonly
_logo.Save(ms, ImageFormat.Png);
ms.Position = 0;
pivotTableSheet.Pictures.Add(0, _grandTotalsColumnPivotTable, ms);
Picture pic = workBook.Worksheets[1].Pictures[0]; // Worksheet 0 is the data sheet that feeds the PivotTable
//pic.Height = 121;
pic.HeightScale = 100;
pic.WidthScale = 100;
Please use this code to reset it to original height.
Picture pic = wb.Worksheets[0].Pictures[0];
pic.HeightScale = 100;
pic.WidthScale = 100;
Note: I am working as Developer Evangelist at Aspose
I am trying to paste a 'JPEG' image file to Excel 2013 32-bit version. But after writing the workbook when I am opening it I am getting an error "The image cannot currently be displayed". The image file is very large.
I am using Windows 7 64 bit with Microsoft Excel 32 bit verison.
here is the portion of code to paste the image following the apache POI documentation. -
XSSFDrawing drawing = sheet.createDrawingPatriarch();
InputStream graphImage = new FileInputStream(fileName);
//Byte array to store the Image in Byte format
byte[] bytes = IOUtils.toByteArray(graphImage);
//Image Id
int imageId = frameWorkbook.addPicture(bytes,Workbook.PICTURE_TYPE_JPEG);
graphImage.close();
//Instance of ClientAnchor
ClientAnchor anchor = new XSSFClientAnchor();
anchor.setAnchorType(ClientAnchor.DONT_MOVE_AND_RESIZE);
//Setting start position of the Image
anchor.setCol1(5);
anchor.setRow1(5);
//Instance of XSSFpicture to paint the image
XSSFPicture picOfGraph = drawing.createPicture(anchor,imageId);
picOfGraph.resize();
This code works fine with small size image but if the image size gets larger then I am getting the error????Any solution??
I have a asp.net application in that I have grid view and I export that grid-view data into Excel it is working fine. But, now I want add the image(image getting from Server map-path) to the Excel sheet while export grid view data to Excel
How can I bind the image to excel?
Try the following code, i have tested it on local IIS, it is working properly and including the image like Header Image/Logo on top of the grid data.
Response.ContentType = "application/vnd.ms-excel";
Response.AddHeader("Content-Disposition", "attachment; filename=test.xls;");
StringWriter stringWrite = new StringWriter();
HtmlTextWriter htmlWrite = new HtmlTextWriter(stringWrite);
dgrExport.DataSource = dtExport;
dgrExport.DataBind();
dgrExport.RenderControl(htmlWrite);
string headerTable = #"<Table><tr><td><img src=""D:\\Folder\\1.jpg"" \></td></tr></Table>";
Response.Write(headerTable);
Response.Write(stringWrite.ToString());
Response.End();
you can adjust your image's height and width as per your requirement. Same height and width setting will be required for the <TD> tag.