You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Scala中用ApachePOI 5.0.0将图片居中于Excel单元格

问题:Apache POI 5.0.0 实现图片在Excel单元格内居中

你的代码存在两个核心问题导致图片无法居中:

  1. 在创建Picture实例后才修改ClientAnchor的偏移量,此时图片布局已确定,修改不会生效;
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 21:24:50