Hello I have created excel sheet by using POI. I've added picture(jpg-file) in the next way:
Workbook wb = new HSSFWorkbook();
CreationHelper helper = wb.getCreationHelper();
//...
InputStream is = new FileInputStream("img.jpg");
byte[] bytes = IOUtils.toByteArray(is);
int picIdx = wb.addPicture(bytes, Workbook.PICTURE_TYPE_JPEG);
Drawing drawing = sheet.createDrawingPatriarch();
ClientAnchor anchor = helper.createClientAnchor();
anchor.setCol1(5);
anchor.setRow1(5);
Picture pict = drawing.createPicture(anchor, picIdx);
pict.resize();
Now I want the picture to fit into that cell, but i don't want to change its aspect ratio. the scales i can put into resize are in respect to the cell, which obviously can have a different ratio. I tried to calculate the scales but the problem is, that I can't get or set the row height in pixel, only in pt and i can't compute the cells ratio either bc the i can't get width and height in the same unit... Any suggestions?
There is a Units class in POI, which provide convertion between pixel and point.
And here are some methods might be helpful to set cell's width and height.
1.Centimeters to Pixels
public static int cmToPx(double cm) {
return (int) Math.round(cm * 96 / 2.54D);
}
96
is my Monitor's DPI (check yours from dpilove)
and 1 inch = 2.54 centimeters
2.Centimeters to RowHeight
public static int cmToH(double cm) {
return (int) (Units.pixelToPoints(cmToPx(cm)) * 20); //POI's Units
}
Usage: sheet.setDefaultRowHeight(cmToH(1.0))
Reference: HSSFRow#getHeightInPoints
Set the height in "twips" or 1/20th of a point.
3.Centimeters to ColumnWidth
public static int cmToW(double cm) {
return (int) Math.round(((cmToPx(cm) - 5.0D) / 8 * 7 + 5) / 7 * 256);
}
Usage: sheet.setColumnWidth(cmToW(1.0))
Use (px - 5.0D) / 8
to convert from pixels to points in excel width.(When drag column's width in excel, pixels and points will show around your cursor)
Reference: HSSFSheet#setColumnWidth
Set the width (in units of 1/256th of a character width)
Excel uses the following formula (Section 3.3.1.12 of the OOXML spec):
// Excel width, not character width
width = Truncate([{Number of Visible Characters} * {Maximum Digit Width} + {5 pixel padding}]/{Maximum Digit Width} * 256) / 256
Using the Calibri font as an example, the maximum digit width of 11 point font size is 7 pixels (at 96 dpi). If you set a column width to be eight characters wide, e.g. setColumnWidth(columnIndex, 8*256), then the actual value of visible characters (the value shown in Excel) is derived from the following equation:
Truncate([numChars * 7 + 5] / 7 * 256) / 256 = 8;
Use XSSFClientAnchor to resize a picture to fill the cell and keep its ratio:
// set padding between picture and gridlines so gridlines would not covered by the picture
private static final double PADDING_SIZE = 10;
private static final int PADDING = Units.toEMU(PADDING_SIZE);
/**
* Draw Image inside specific cell
*
* @param wb workbook
* @param sheet sheet
* @param cellW cell width in pixels
* @param cellH cell height in pixels
* @param imgPath image path
* @param col the column (0 based) of the first cell.
* @param row the row (0 based) of the first cell.
* @param colSize the column size of cell
* @param rowSize the row size of cell
*/
public static void drawImageInCell(SXSSFWorkbook wb, SXSSFSheet sheet, int cellW, int cellH,
String imgPath, int col, int row, int colSize, int rowSize) throws IOException {
Path path = Paths.get(imgPath);
BufferedImage img = ImageIO.read(path.toFile());
int[] anchorArray = calCellAnchor(Units.pixelToPoints(cellW), Units.pixelToPoints(cellH),
img.getWidth(), img.getHeight());
XSSFClientAnchor anchor = new XSSFClientAnchor(anchorArray[0], anchorArray[1], anchorArray[2],
anchorArray[3], (short) col, row, (short) (col + colSize), row + rowSize);
int index = wb.addPicture(Files.readAllBytes(path), XSSFWorkbook.PICTURE_TYPE_JPEG);
sheet.createDrawingPatriarch().createPicture(anchor, index);
}
/**
* calculate POI cell anchor
*
* @param cellX cell width in excel points
* @param cellY cell height in excel points
* @param imgX image width
* @param imgY image height
*/
public static int[] calCellAnchor(double cellX, double cellY, int imgX, int imgY) {
// assume Y has fixed padding first
return calCoordinate(true, cellX, cellY, imgX, imgY);
}
/**
* calculate cell coordinate
*
* @param fixTop is Y has fixed padding
*/
private static int[] calCoordinate(boolean fixTop, double cellX, double cellY, int imgX, int imgY) {
double ratio = ((double) imgX) / imgY;
int x = (int) Math.round(Units.toEMU(cellY - 2 * PADDING_SIZE) * ratio);
x = (Units.toEMU(cellX) - x) / 2;
if (x < PADDING) {
return calCoordinate(false, cellY, cellX, imgY, imgX);
}
return calDirection(fixTop, x);
}
/**
* calculate X's direction
*
* @param fixTop is Y has fixed padding
* @param x X's padding
*/
private static int[] calDirection(boolean fixTop, int x) {
if (fixTop) {
return new int[] { x, PADDING, -x, -PADDING };
} else {
return new int[] { PADDING, x, -PADDING, -x };
}
}
I am using Base64 image data and I do it like this, for me it adds at given col/row and resizes it properly, also I dont want to to fit the cell it can axpand to whatever to right and bottom:
byte[] imageBase64Data = base64DataString.getBytes();
byte[] imageRawData = Base64.getDecoder().decode(imageBase64Data);
int imgWidth = 1920; // only initial if not known
int imgHeight = 1080; // only initial if not known
try {
BufferedImage img = ImageIO.read(new ByteArrayInputStream(imageRawData));
imgWidth = img.getWidth();
imgHeight = img.getHeight();
} catch (IOException e) {
e.printStackTrace();
}
int pictureIdx = workbook.addPicture(imageRawData, Workbook.PICTURE_TYPE_PNG);
CreationHelper helper = workbook.getCreationHelper();
Drawing drawing = sheet.createDrawingPatriarch();
ClientAnchor anchor = helper.createClientAnchor();
anchor.setCol1(desiredCol);
anchor.setRow1(desiredRow);
Picture picture = drawing.createPicture(anchor, pictureIdx);
picture.resize(0.7 * imgWidth / XSSFShape.PIXEL_DPI, 5 * imgHeight / XSSFShape.PIXEL_DPI);