在Apache POI中将对象嵌入Excel中时,如何保持预览图像的原始大小?

时间:2018-07-18 03:34:28

标签: java excel apache-poi

来自this answer的有关使用Apache POI将对象嵌入Excel电子表格的信息:

Workbook wb1 = new XSSFWorkbook();
Sheet sh = wb1.createSheet();
int picIdx = wb1.addPicture(getSamplePng(), Workbook.PICTURE_TYPE_PNG);
byte samplePPTX[] = getSamplePPT(true);
int oleIdx = wb1.addOlePackage(samplePPTX, "dummy.pptx", "dummy.pptx", "dummy.pptx");

Drawing<?> pat = sh.createDrawingPatriarch();
ClientAnchor anchor = pat.createAnchor(0, 0, 0, 0, 1, 1, 3, 6);
pat.createObjectData(anchor, oleIdx, picIdx);

这将使用getSamplePng()中的图像作为预览图像创建嵌入的对象。该图像在行= 1,列= 1处锚定到单元格,并延伸到行= 6,列=3。除非图像宽高比完全匹配,否则会导致图像失真。

Picture接口提供了resize方法,但是似乎没有办法从Picture获取ObjectData实例。

如果图像尺寸在其锚定的单元格尺寸之内,则可以进行以下操作:

ClientAnchor anchor = drawing.createAnchor(0, 
                                           0, 
                                           imgDim.width * Units.EMU_PER_PIXEL,
                                           imgDim.height * Units.EMU_PER_PIXEL,
                                           columnIndex,
                                           rowIndex,
                                           columnIndex,
                                           rowIndex);

如果图像大于单元格尺寸,则此方法不起作用;图像最终伸展到细胞的边界。

我也尝试过setAnchorType(AnchorType.MOVE_DONT_RESIZE),但似乎没有任何作用。

使用createObjectData嵌入对象时如何将预览图像的大小调整为原始大小?

1 个答案:

答案 0 :(得分:0)

XSSFClientAnchor类提供了一个构造函数,该构造函数允许提供左上角的单元格(作为CTPoint2D对象)和对象大小(以CTPositiveSize2D的形式)。但是,此构造方法受保护。

解决方法是将该类扩展为一个接受左上角单元坐标以及宽度和高度(以EMU单位为单位)的类:

class SizedClientAnchor extends XSSFClientAnchor
{
    private SizedClientAnchor(XSSFSheet sheet, int columnIndex, int rowIndex, int w, int h)
    {
        super((XSSFSheet) cell.getSheet(), createCTMarker(columnIndex, rowIndex), getSize2D(w, h));
    }

    private static CTMarker createCTMarker(int columnIndex, int rowIndex)
    {
        CTMarker ret = CTMarker.Factory.newInstance();
        ret.setCol(columnIndex);
        ret.setColOff(0);
        ret.setRow(rowIndex);
        ret.setRowOff(0);
        return ret;
    }

    private static CTPositiveSize2D getSize2D(int w, int h)
    {
        CTPositiveSize2D ret = CTPositiveSize2D.Factory.newInstance();
        ret.setCx(w);
        ret.setCy(h);
        return ret;
    }
}

原始代码如下所示:

ClientAnchor anchor = new SizedClientAnchor(sh, 
                                            columnIndex, 
                                            rowIndex, 
                                            imgDim.width * Units.EMU_PER_PIXEL,
                                            imgDim.height * Units.EMU_PER_PIXEL);

但是请注意,如果图像所经过的列的字体与“标准”字体(Calibri或Arial 11pt)不同,则结果图像的宽度将不正确。

上浆码位于calcCell()中的XSSFClientAnchor中。这使用了Units.columnWidthToEMU(),而后者又使用了Units.charactersToEMU()。它使用DEFAULT_CHARACTER_WIDTH,据记录它仅适用于默认字体(Calibri或Arial)。

此问题的解决方法将涉及重新实现Units.charactersToEMU()XSSFClientAnchor.calcCell()以应用于适用单元格的适用字体。