如何在Scala中用ApachePOI 5.0.0将图片居中于Excel单元格
问题:Apache POI 5.0.0 实现图片在Excel单元格内居中
你的代码存在两个核心问题导致图片无法居中:
- 在创建
Picture实例后才修改ClientAnchor的偏移量,此时图片布局已确定,修改不会生效; setDx1/setDy1的参数单位不是像素,而是EMU(English Metric Unit),直接传入像素值会导致偏移计算完全错误(1像素 = 9525 EMU)。
另外,picture.resize()会自动覆盖ClientAnchor的参数,建议直接通过锚点参数控制图片的大小和位置,而非依赖resize方法。
以下是修正后的代码:
import org.apache.poi.util.Units import org.apache.poi.xssf.usermodel.{XSSFSheet, XSSFPicture, XSSFDrawing, XSSFClientAnchor} import java.io.{File, FileInputStream} import java.nio.file.{Files, Path} import javax.imageio.ImageIO import java.awt.Dimension def addImageToCell(imagePath: String, sheet: XSSFSheet, columnIdx: Int, rowNum: Int): Unit = { val fis = Files.newInputStream(Path.of(imagePath)) val pictureBytes = org.apache.commons.io.IOUtils.toByteArray(fis) val pictureIdx = sheet.getWorkbook.addPicture(pictureBytes, org.apache.poi.ss.usermodel.Workbook.PICTURE_TYPE_PNG) fis.close() val helper = sheet.getWorkbook.getCreationHelper val drawing = sheet.createDrawingPatriarch().asInstanceOf[XSSFDrawing] // 初始化锚点,绑定到目标单元格范围 val anchor = helper.createClientAnchor().asInstanceOf[XSSFClientAnchor] anchor.setCol1(columnIdx) anchor.setRow1(rowNum) anchor.setCol2(columnIdx + 1) anchor.setRow2(rowNum + 1) anchor.setAnchorType(org.apache.poi.ss.usermodel.ClientAnchor.AnchorType.MOVE_AND_RESIZE) // 获取图片原始尺寸 val bufferedImg = ImageIO.read(new File(imagePath)) val originalWidth = bufferedImg.getWidth val originalHeight = bufferedImg.getHeight // 获取单元格的像素尺寸 val cellWidthPx = Math.round(sheet.getColumnWidthInPixels(columnIdx)) val cellHeightPx = Math.round(sheet.getRow(rowNum).getHeightInPoints / 72 * 96) // 点转像素(96DPI) // 计算缩放比例,确保图片完整放入单元格 val scaleX = cellWidthPx.toDouble / originalWidth val scaleY = cellHeightPx.toDouble / originalHeight val scale = Math.min(scaleX, scaleY) val revisedScale = Math.max(scale, 0.1) // 计算缩放后的图片尺寸(像素) val scaledWidthPx = (originalWidth * revisedScale).toInt val scaledHeightPx = (originalHeight * revisedScale).toInt // 计算居中偏移量(像素),转成EMU单位 val dx1 = Units.pixelToEMU((cellWidthPx - scaledWidthPx) / 2) val dy1 = Units.pixelToEMU((cellHeightPx - scaledHeightPx) / 2) // 设置图片右下角的偏移,确保图片大小正确 val dx2 = Units.pixelToEMU(cellWidthPx - scaledWidthPx - (cellWidthPx - scaledWidthPx) / 2) val dy2 = Units.pixelToEMU(cellHeightPx - scaledHeightPx - (cellHeightPx - scaledHeightPx) / 2) anchor.setDx1(dx1) anchor.setDy1(dy1) anchor.setDx2(-dx2) // 负数值表示从单元格右侧向左偏移 anchor.setDy2(-dy2) // 负数值表示从单元格底部向上偏移 // 创建图片 val picture = drawing.createPicture(anchor, pictureIdx).asInstanceOf[XSSFPicture] }
关键修改说明:
- 提前配置锚点参数:在创建
Picture前完成所有ClientAnchor的设置,包括偏移量和单元格范围; - 单位转换:使用
Units.pixelToEMU()将像素偏移量转换为POI要求的EMU单位; - 锚点范围与偏移配合:通过
setCol2/setRow2将锚点绑定到单元格的右下角,再通过dx2/dy2的负偏移控制图片实际大小,确保图片在单元格内居中且不超出边界; - 移除
picture.resize():避免该方法覆盖锚点配置,完全通过锚点参数控制图片尺寸。
内容的提问来源于stack exchange,提问作者kajoe14
相关产品推荐
相关产品推荐

