Apache POI 3.9.0开发Birt XLSX Emitter如何正确设置图片宽高
问题核心原因
你当前代码的适配逻辑存在4处关键错误,是图片异常的直接诱因:
- 硬编码图片高度为116pt,注释掉了从
imageContent读取实际高度的逻辑,导致高度计算完全不符合预期 - 宽高缩放逻辑未考虑图片原始宽高比,仅基于给定宽度和单元格宽度计算比例,极易出现拉伸变形
ClientAnchor的结束行row2直接设置为和起始行row1相同,跨多行的图片会被强制截断- 行高调整逻辑仅修改当前所在行的高度,跨多行场景下总高度完全不足
修复后的核心代码
private void imageData( int type, byte[] data, int column, int colSpan, int rowSpan, int styleId,ImageData image ) { IImageContent imagecontent = image.getImageContent(); this.cell = this.row.getCell(column -1); if (this.cell == null) { this.cell = this.row.createCell(column -1); } this.cell.setCellType(Cell.CELL_TYPE_BLANK); String mimeType = imagecontent.getMIMEType(); if( data != null ) { int imageIdx = wb.addPicture(data, imageType); Drawing drawing = this.sheet.createDrawingPatriarch(); int startCol = this.cell.getColumnIndex(); int startRow = this.cell.getRowIndex(); int endCol = startCol + colSpan; int endRow = startRow + rowSpan; // 1. 计算目标单元格区域总可用宽高(单位mm) double totalColWidthMM = 0.0; for (int i = startCol; i < endCol; i++) { totalColWidthMM += widthUnits2Millimetres((short) this.sheet.getColumnWidth(i)); } // 减去左右各2px边距 转mm totalColWidthMM -= pixels2Millimetres(4); double totalRowHeightPT = 0.0f; for (int i = startRow; i < endRow; i++) { Row row = this.sheet.getRow(i); if (row == null) row = this.sheet.createRow(i); totalRowHeightPT += row.getHeightInPoints(); } // 减去上下各2px边距 转pt double totalRowHeightMM = points2Millimetres(totalRowHeightPT) - pixels2Millimetres(4); // 2. 获取图片原始宽高 计算原始宽高比 double imgOriginalWidthMM = 0.0; double imgOriginalHeightMM = 0.0; if(isAbsolute(imagecontent.getWidth())) { imgOriginalWidthMM = imagecontent.getWidth().convertTo(DimensionType.UNITS_MM); imgOriginalHeightMM = imagecontent.getHeight().convertTo(DimensionType.UNITS_MM); } else if(isPixels(imagecontent.getWidth())) { imgOriginalWidthMM = pixels2Millimetres(imagecontent.getWidth().getMeasure()); imgOriginalHeightMM = pixels2Millimetres(imagecontent.getHeight().getMeasure()); } double imgAspectRatio = imgOriginalWidthMM / imgOriginalHeightMM; double targetAspectRatio = totalColWidthMM / totalRowHeightMM; // 3. 按原始比例缩放图片 适配单元格区域 double targetImgWidthMM, targetImgHeightMM; if (imgAspectRatio > targetAspectRatio) { // 图片更宽 以宽度为准适配 targetImgWidthMM = totalColWidthMM; targetImgHeightMM = targetImgWidthMM / imgAspectRatio; } else { // 图片更高 以高度为准适配 targetImgHeightMM = totalRowHeightMM; targetImgWidthMM = targetImgHeightMM * imgAspectRatio; } // 4. 如果需要调整行高/列宽(无跨列/跨行的场景) if (colSpan == 0) { int targetColWidth = poiColumnWidthFromMM(targetImgWidthMM + pixels2Millimetres(4)); this.sheet.setColumnWidth(startCol, targetColWidth); totalColWidthMM = targetImgWidthMM; } if (rowSpan == 0) { float targetRowHeightPT = millimetres2Points(targetImgHeightMM + pixels2Millimetres(4)); this.cell.getRow().setHeightInPoints(targetRowHeightPT); totalRowHeightMM = targetImgHeightMM; endRow = startRow + 1; } // 5. 计算Anchor的偏移量 int dx2 = (int) (targetImgWidthMM * 36000); // 1mm=36000EMU int dy2 = (int) (millimetres2Points(targetImgHeightMM) * XSSFShape.EMU_PER_POINT); ClientAnchor anchor = this.wb.getCreationHelper().createClientAnchor(); anchor.setCol1(startCol); anchor.setRow1(startRow); anchor.setCol2(startCol); anchor.setRow2(startRow); // 边距偏移 anchor.setDx1(2 * XSSFShape.EMU_PER_PIXEL); anchor.setDy1(2 * XSSFShape.EMU_PER_PIXEL); anchor.setDx2(dx2 + 2 * XSSFShape.EMU_PER_PIXEL); anchor.setDy2(dy2 + 2 * XSSFShape.EMU_PER_PIXEL); anchor.setAnchorType(ClientAnchor.MOVE_DONT_RESIZE); drawing.createPicture(anchor, imageIdx); } } // 补充需要的工具方法 private double points2Millimetres(float pt) { return pt * 0.352778; } private float millimetres2Points(double mm) { return (float) (mm / 0.352778); } private int poiColumnWidthFromMM(double mm) { // POI列宽单位是1/256个字符宽度 这里按默认字体换算即可 可根据实际字体调整 return (int) (mm * 37.7953 / 0.352778); }
关键修改说明
- 优先读取图片原始宽高计算比例,完全删除硬编码116pt的逻辑,保证原始比例不被拉伸
- 支持跨列跨行场景的总宽高计算,适配合并单元格的图片插入需求
- 动态判断适配逻辑:宽图按宽适配,高图按高适配,自动保留边距
- 修正Anchor偏移计算逻辑,避免图片被截断或者溢出单元格
内容的提问来源于stack exchange,提问作者user3458271
相关产品推荐
相关产品推荐

