如何解决XSSFWorkbook插入EMF图片后无法正常调整尺寸的问题
解决思路
POI 原生不支持读取 EMF 格式图片的原始维度,所以调用resize()或者直接修改ImageDimension都会失效,变通方案是跳过自带的尺寸计算逻辑,手动计算ClientAnchor的结束列、结束行以及偏移量,直接固定图片的展示尺寸。
具体实现代码
private void addNormalFigure(XSSFSheet sheet, byte[] imgData){ XSSFWorkbook w = sheet.getWorkbook(); int picIdx = w.addPicture(imgData, Workbook.PICTURE_TYPE_EMF); CreationHelper helper = w.getCreationHelper(); XSSFDrawing drawing = sheet.createDrawingPatriarch(); ClientAnchor anchor = helper.createClientAnchor(); // 固定起始位置:A列,第4行 anchor.setCol1(0); anchor.setRow1(4); // 起始位置无偏移 anchor.setDx1(0); anchor.setDy1(0); // 目标尺寸:2英寸宽,2英寸高,先转成EMU单位(1英寸=914400 EMU) long targetWidthEmu = inchToEmu(2.0); long targetHeightEmu = inchToEmu(2.0); // 计算结束列和列偏移 int col2 = anchor.getCol1(); long remainingWidth = targetWidthEmu; while (remainingWidth > 0 && col2 < 16384) { // Excel最大列数是16384 long colWidthEmu = getColumnWidthEmu(sheet, col2); if (remainingWidth <= colWidthEmu) { anchor.setDx2((int) remainingWidth); break; } else { remainingWidth -= colWidthEmu; col2++; } } anchor.setCol2(col2); // 计算结束行和行偏移 int row2 = anchor.getRow1(); long remainingHeight = targetHeightEmu; while (remainingHeight > 0 && row2 < 1048576) { // Excel最大行数是1048576 long rowHeightEmu = getRowHeightEmu(sheet, row2); if (remainingHeight <= rowHeightEmu) { anchor.setDy2((int) remainingHeight); break; } else { remainingHeight -= rowHeightEmu; row2++; } } anchor.setRow2(row2); // 直接创建图片即可,不需要再调用resize drawing.createPicture(anchor, picIdx); } // 英寸转EMU单位 private long inchToEmu(double inch) { return (long) (inch * 914400); } // 获取指定列的宽度,单位EMU private long getColumnWidthEmu(XSSFSheet sheet, int colIndex) { int widthPx = sheet.getColumnWidthInPixels(colIndex); // 像素转EMU:96像素=1英寸=914400EMU return (long) (widthPx * 914400.0 / 96); } // 获取指定行的高度,单位EMU private long getRowHeightEmu(XSSFSheet sheet, int rowIndex) { XSSFRow row = sheet.getRow(rowIndex); float heightPt = row == null ? sheet.getDefaultRowHeightInPoints() : row.getHeightInPoints(); // 磅转EMU:72磅=1英寸=914400EMU return (long) (heightPt * 914400.0 / 72); }
注意事项
- 该方案直接固定图片的展示尺寸,不需要依赖POI读取EMF原始维度的能力,所有格式的图片都可以用这个方案固定尺寸
- 如果需要按图片原始比例缩放,可以提前用第三方工具(如batik)读取EMF的原始宽高比,再按比例换算目标尺寸即可
- 代码里已经兼容了默认行高、默认列宽的场景,不需要提前给对应行列设置固定宽高
内容的提问来源于stack exchange,提问作者dodobird
相关产品推荐
相关产品推荐

