XSSFClientAnchor的最大dy值

使用Apache-POI,我试图插入一个图像在一个大的字体(Calibri-32)半行左上angular的Excel工作表。
使用众所周知的公式,我发现XSSFClientAnchor内的dy1值应该是XSSFClientAnchor
但是,打开Excel文件时,出现错误,说明内容不可用。
接受警告, 图像显示正确无论如何

经过一些testing,我发现dy的最大值,没有得到Excel的错误,似乎是190,500
我使用的字体的行高为55像素。
行的中间因此是0.5*55*Units.EMU_PER_PIXEL=261,938

使用更小的字体时会发生同样的问题,但在图像的行尾附近开始图像。
在所有情况下,如果dy1的值大于190500,我会得到一个错误。

有没有人有线索?

更新:
我从xlsx文件中提取了xml ,并且在某处注意到一个负的cy值。 我对xlsx的内容并不熟悉,但我希望对其他人有用:

 <xdr:wsDr xmlns:xdr="http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing" xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"> <xdr:twoCellAnchor editAs="oneCell"> <xdr:from> <xdr:col>2</xdr:col> <xdr:colOff>147637</xdr:colOff> <xdr:row>0</xdr:row> <xdr:rowOff>261937</xdr:rowOff> </xdr:from> <xdr:to> <xdr:col>5</xdr:col> <xdr:colOff>2309812</xdr:colOff> <xdr:row>13</xdr:row> <xdr:rowOff>14287</xdr:rowOff> </xdr:to> <xdr:pic> <xdr:nvPicPr> <xdr:cNvPr id="1" name="Picture 1" descr="Picture"/> <xdr:cNvPicPr> <a:picLocks noChangeAspect="true"/> </xdr:cNvPicPr> </xdr:nvPicPr> <xdr:blipFill> <a:blip r:embed="rId1"/> <a:stretch> <a:fillRect/> </a:stretch> </xdr:blipFill> <xdr:spPr> <a:xfrm> <a:off x="147637" y="261937"/> <a:ext cx="195263" cy="-71437"/> </a:xfrm> <a:prstGeom prst="rect"> <a:avLst/> </a:prstGeom> </xdr:spPr> </xdr:pic> <xdr:clientData/> </xdr:twoCellAnchor> </xdr:wsDr> 

更新2:
以下代码显示错误。 如果dy1大于190500并且row2等于row1 + 1,则会发生

 /********************************************************************************************************************** * Package specification *********************************************************************************************************************/ package test; /********************************************************************************************************************** * Import definitions *********************************************************************************************************************/ import java.awt.Desktop; import java.io.*; import org.apache.poi.ss.usermodel.*; import org.apache.poi.util.*; import org.apache.poi.xssf.streaming.*; import org.apache.poi.xssf.usermodel.*; /********************************************************************************************************************** * This class implements a Minimal, Complete and Verifiable example for the problem of the maximum dy value for the * {@link XSSFClientAnchor}. *********************************************************************************************************************/ public class TestPictureOffset { /******************************************************************************************************************** * This constants represents the name of the file with the picture to import within the sheet. *******************************************************************************************************************/ private static final String FILENAME_PICTURE = "./excel.png"; /******************************************************************************************************************** * These constants represents the width and height of the big cell within the sheet. *******************************************************************************************************************/ private static final short BIG_CELL_COLUMN_WIDTH_IN_PIXELS = 317; private static final short BIG_CELL_ROW_HEIGHT_IN_PIXELS = 56; /******************************************************************************************************************** * This constants represents the default height of a cell within the sheet. *******************************************************************************************************************/ private static final short DEFAULT_ROW_HEIGHT_IN_PIXELS = 20; /******************************************************************************************************************** * This method places the specified picture on the sheet. *******************************************************************************************************************/ private static void setPicture(int picture_index, SXSSFSheet sheet) { // ----------------- // Initialize anchor // ----------------- XSSFClientAnchor anchor; anchor = (XSSFClientAnchor)sheet.getWorkbook().getCreationHelper().createClientAnchor(); anchor.setAnchorType(XSSFClientAnchor.AnchorType.MOVE_AND_RESIZE); // ----------------------------- // Set position // THIS IS WHERE THE FUN HAPPENS // ----------------------------- anchor.setCol1(1); anchor.setRow1(0); anchor.setDx1((int)(0.5 * BIG_CELL_COLUMN_WIDTH_IN_PIXELS * Units.EMU_PER_PIXEL)); anchor.setDy1((int)(0.4 * BIG_CELL_ROW_HEIGHT_IN_PIXELS * Units.EMU_PER_PIXEL)); anchor.setCol2(anchor.getCol1() + 1); anchor.setRow2(anchor.getRow1() + 1); // Fails if dy1 > 190500 //anchor.setRow2(anchor.getRow1() + 2); // OK independently from dy1 anchor.setDx2(0); anchor.setDy2(0); // ---------------------- // Show some measurements // ---------------------- System.out.println("Got dy1: " + anchor.getDy1()); System.out.println("Maximum dy in default cell: " + (DEFAULT_ROW_HEIGHT_IN_PIXELS * Units.EMU_PER_PIXEL)); // ---------------- // Draw the picture // ---------------- sheet.createDrawingPatriarch().createPicture(anchor, picture_index); } // setPicture /******************************************************************************************************************** * This method runs the application. *******************************************************************************************************************/ private static void run() throws Exception { // --------------- // Create workbook // --------------- SXSSFWorkbook workbook; workbook = new SXSSFWorkbook(); workbook.setCompressTempFiles(true); // ------------ // Create sheet // ------------ SXSSFSheet sheet; sheet = workbook.createSheet("TestSheet"); sheet.trackAllColumnsForAutoSizing(); // -------------------------- // Create style with big font // -------------------------- Font font; XSSFCellStyle style; font = workbook.createFont(); font.setFontHeightInPoints((short)32); style = (XSSFCellStyle)workbook.createCellStyle(); style.setFont(font); // ------------------- // Write something big // ------------------- SXSSFRow row; SXSSFCell cell; row = sheet.createRow(0); cell = row.createCell(1); cell.setCellStyle(style); cell.setCellValue("Hello everybody"); // ----------------------- // Auto resize this column // ----------------------- sheet.autoSizeColumn(1); // ------------ // Load picture // ------------ InputStream input_stream; byte[] bytes; input_stream = new FileInputStream(FILENAME_PICTURE); bytes = IOUtils.toByteArray(input_stream); input_stream.close(); // --------------- // Add to workbook // --------------- int picture_index; picture_index = workbook.addPicture(bytes, SXSSFWorkbook.PICTURE_TYPE_PNG); // ------------------------- // Position picture in sheet // ------------------------- setPicture(picture_index, sheet); // ------------- // Save workbook // ------------- File output_file; FileOutputStream output_stream; output_file = new File("testxls.xlsx"); output_stream = new FileOutputStream(output_file); workbook.write(output_stream); output_stream.close(); workbook.close(); // ------- // Open it // ------- Desktop.getDesktop().open(output_file); } // run /******************************************************************************************************************** * MAIN *******************************************************************************************************************/ public static void main(String[] args) { try { run(); } catch (Exception exception) { exception.printStackTrace(); } } // main } // class TestPictureOffset 

所以我们需要确定消极cy可能来自哪里。

起初:你不应该使用常量BIG_CELL_COLUMN_WIDTH_IN_PIXELS和BIG_CELL_ROW_HEIGHT_IN_PIXELS。 相反,如同apache poi一样,使用sheet.getColumnWidthInPixels和ImageUtils.getRowHeightInPixels来计算宽度和高度。

而且你需要根据字体高度来设置行高。 否则,该行保持默认高度,直到Excel正在渲染工作表。 而在默认行高中,如果大于190500, dy1在行外。 dy1对于这一行它不是有效的dy1

XSSFDrawing.java中有私有CTTransform2D createXfrm(XSSFClientAnchor锚) ,它计算包含cyxfrm 。 在那里你可以看到,只有当height + anchor.getDy2()anchor.getDy1()更低时,才会出现负的cy

使用XSSF这就足够了。 但是使用SXSSF ,使用ImageUtils.getRowHeightInPixels(sheet, row)private CTTransform2D createXfrm(XSSFClientAnchor anchor)计算行高度似乎是错误的。 我怀疑这是因为行stream的方式,行高不是已经在表中已经知道。 所以我们需要一个解决方法。 设置大字体所需的默认行高,然后设置图片,然后重置默认行高。

以下为我工作:

 /********************************************************************************************************************** * Import definitions *********************************************************************************************************************/ import java.awt.Desktop; import java.io.*; import org.apache.poi.ss.usermodel.*; import org.apache.poi.util.*; import org.apache.poi.xssf.streaming.*; import org.apache.poi.xssf.usermodel.*; import org.apache.poi.ss.util.ImageUtils; /********************************************************************************************************************** * This class implements a Minimal, Complete and Verifiable example for the problem of the maximum dy value for the * {@link XSSFClientAnchor}. *********************************************************************************************************************/ public class TestPictureOffset { /******************************************************************************************************************** * This constants represents the name of the file with the picture to import within the sheet. *******************************************************************************************************************/ private static final String FILENAME_PICTURE = "./excel.png"; /******************************************************************************************************************** * These constants represents the width and height of the big cell within the sheet. *******************************************************************************************************************/ // Don't do that. Instead do the same as apache poi does and use // sheet.getColumnWidthInPixels and // ImageUtils.getRowHeightInPixels to calculate width and height private static final short BIG_CELL_COLUMN_WIDTH_IN_PIXELS = 317; private static final short BIG_CELL_ROW_HEIGHT_IN_PIXELS = 56; /******************************************************************************************************************** * This constants represents the default height of a cell within the sheet. *******************************************************************************************************************/ private static final short DEFAULT_ROW_HEIGHT_IN_PIXELS = 20; /******************************************************************************************************************** * This method places the specified picture on the sheet. *******************************************************************************************************************/ private static void setPicture(int picture_index, Sheet sheet) { // ----------------- // Initialize anchor // ----------------- ClientAnchor anchor; anchor = sheet.getWorkbook().getCreationHelper().createClientAnchor(); anchor.setAnchorType(ClientAnchor.AnchorType.MOVE_AND_RESIZE); // ----------------------------- // Set position // THIS IS WHERE THE FUN HAPPENS // ----------------------------- anchor.setCol1(1); anchor.setRow1(0); //anchor.setDx1((int)(0.5 * BIG_CELL_COLUMN_WIDTH_IN_PIXELS * Units.EMU_PER_PIXEL)); //anchor.setDy1((int)(0.4 * BIG_CELL_ROW_HEIGHT_IN_PIXELS * Units.EMU_PER_PIXEL)); anchor.setDx1((int)(0.5 * sheet.getColumnWidthInPixels(1) * Units.EMU_PER_PIXEL)); anchor.setDy1((int)(0.4 * ImageUtils.getRowHeightInPixels(sheet, 0) * Units.EMU_PER_PIXEL)); anchor.setCol2(anchor.getCol1() + 1); anchor.setRow2(anchor.getRow1() + 1); // Fails if dy1 > 190500 //anchor.setRow2(anchor.getRow1() + 2); // OK independently from dy1 anchor.setDx2(0); anchor.setDy2(0); // ---------------------- // Show some measurements // ---------------------- System.out.println("Got dy1: " + anchor.getDy1()); System.out.println("Maximum dy in default cell: " + (DEFAULT_ROW_HEIGHT_IN_PIXELS * Units.EMU_PER_PIXEL)); // ---------------- // Draw the picture // ---------------- sheet.createDrawingPatriarch().createPicture(anchor, picture_index); } // setPicture /******************************************************************************************************************** * This method runs the application. *******************************************************************************************************************/ private static void run() throws Exception { // --------------- // Create workbook // --------------- SXSSFWorkbook workbook; workbook = new SXSSFWorkbook(); workbook.setCompressTempFiles(true); // ------------ // Create sheet // ------------ SXSSFSheet sheet; sheet = workbook.createSheet("TestSheet"); sheet.trackAllColumnsForAutoSizing(); // -------------------------- // Create style with big font // -------------------------- Font font; CellStyle style; font = workbook.createFont(); font.setFontHeightInPoints((short)32); style = workbook.createCellStyle(); style.setFont(font); // ------------------- // Write something big // ------------------- SXSSFRow row; SXSSFCell cell; row = sheet.createRow(0); cell = row.createCell(1); cell.setCellStyle(style); cell.setCellValue("Hello everybody"); // ----------------------- // Set row heigth according to the fonts height // ----------------------- row.setHeightInPoints(workbook.getFontAt(style.getFontIndex()).getFontHeightInPoints()*1.275f); // ----------------------- // Auto resize this column // ----------------------- sheet.autoSizeColumn(1); // ------------ // Load picture // ------------ InputStream input_stream; byte[] bytes; input_stream = new FileInputStream(FILENAME_PICTURE); bytes = IOUtils.toByteArray(input_stream); input_stream.close(); // --------------- // Add to workbook // --------------- int picture_index; picture_index = workbook.addPicture(bytes, SXSSFWorkbook.PICTURE_TYPE_PNG); // ------------------------- // Position picture in sheet // ------------------------- // Workaround for SXSSFSheet which has not the correct row height in // private CTTransform2D createXfrm(XSSFClientAnchor anchor) // because of the streaming manner. // So set default row height that big: sheet.setDefaultRowHeight(sheet.getRow(0).getHeight()); // set the picture then: setPicture(picture_index, sheet); // and reset the default row height after that: sheet.setDefaultRowHeight((short)(15*20)); // ------------- // Save workbook // ------------- File output_file; FileOutputStream output_stream; output_file = new File("testxls.xlsx"); output_stream = new FileOutputStream(output_file); workbook.write(output_stream); output_stream.close(); workbook.close(); workbook.dispose(); // ------- // Open it // ------- Desktop.getDesktop().open(output_file); } // run /******************************************************************************************************************** * MAIN *******************************************************************************************************************/ public static void main(String[] args) { try { run(); } catch (Exception exception) { exception.printStackTrace(); } } // main } // class TestPictureOffset