简体   繁体   English

Apache POI Excel工作表:在调整图片大小的同时保持其比例

[英]Apache POI Excel sheet: resize a picture while keeping its ratio

Hello I have created excel sheet by using POI. 您好,我已经使用POI创建了Excel工作表。 I've added picture(jpg-file) in the next way: 我以以下方式添加了picture(jpg-file):

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? 我试图计算比例,但问题是,我无法获得或设置行高度(以像素为单位),仅能以pt为单位,而且我也无法计算出单元格的比例,因为我无法获得宽度和高度。同一单位...有什么建议吗?

There is a Units class in POI, which provide convertion between pixel and point. POI中有一个Units类,它提供像素和点之间的转换。
And here are some methods might be helpful to set cell's width and height. 这里有一些方法可能对设置单元格的宽度和高度有用。

1.Centimeters to Pixels 1.厘米为像素

public static int cmToPx(double cm) {
    return (int) Math.round(cm * 96 / 2.54D);
}

96 is my Monitor's DPI (check yours from dpilove ) 96是我的显示器的DPI(请从dpilove检查您的DPI)
and 1 inch = 2.54 centimeters 和1英寸= 2.54厘米

2.Centimeters to RowHeight 2.厘米为行高

public static int cmToH(double cm) {
    return (int) (Units.pixelToPoints(cmToPx(cm)) * 20); //POI's Units
}

Usage: sheet.setDefaultRowHeight(cmToH(1.0)) 用法: sheet.setDefaultRowHeight(cmToH(1.0))
Reference: HSSFRow#getHeightInPoints 参考: HSSFRow#getHeightInPoints

Set the height in "twips" or 1/20th of a point. 将高度设置为“缇”或点的1/20。

3.Centimeters to ColumnWidth 3.厘米为列宽

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)) 用法: 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) 使用(px - 5.0D) / 8将像素转换为excel宽度的点(在excel中拖动列的宽度时,像素和点将显示在光标周围)
Reference: HSSFSheet#setColumnWidth 参考: HSSFSheet#setColumnWidth

Set the width ( in units of 1/256th of a character width ) 设置宽度( 以字符宽度的1/256为单位

Excel uses the following formula (Section 3.3.1.12 of the OOXML spec): Excel使用以下公式(OOXML规范的3.3.1.12节):

// 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). 以Calibri字体为例,11点字体大小的最大数字宽度为7像素(96 dpi)。 If you set a column width to be eight characters wide, eg setColumnWidth(columnIndex, 8*256), then the actual value of visible characters (the value shown in Excel) is derived from the following equation: 如果将列宽设置为八个字符宽,例如setColumnWidth(columnIndex,8 * 256),则可见字符的实际值(Excel中显示的值)由以下公式得出:

Truncate([numChars * 7 + 5] / 7 * 256) / 256 = 8;

Use XSSFClientAnchor to resize a picture to fill the cell and keep its ratio: 使用XSSFClientAnchor调整图片大小以填充单元格并保持其比例:

// 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: 我正在使用Base64图像数据,并且这样做是这样的,对我来说,它会在给定的col / row处添加并正确调整其大小,而且我也不想适合它可以左右左右固定的单元格:

    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);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM