apache poi XSSFClientAnchor没有定位图片相对于dx1,dy1,dx2,dy2

ocebsuys  于 2023-10-23  发布在  Apache
关注(0)|答案(1)|浏览(408)

我正在尝试使用apach-poi version 3.16将图像添加到excel。我可以用HSSFWorkbookXSSFWorkbook来实现。但是当我试图为图像添加间距时,即如果我在XSSFClientAnchor上设置dx1dy1dx2dy2坐标,则不会生效。同样的事情也发生在HSSFClientAnchor上。我附上这两个类和相应的Excel文件生成。请问你可以帮助我如何使用XSSFClientAnchor实现相同的结果。
HSSF类

  1. package poisamples;
  2. import java.io.ByteArrayOutputStream;
  3. import java.io.File;
  4. import java.io.FileInputStream;
  5. import java.io.FileOutputStream;
  6. import java.io.IOException;
  7. import org.apache.poi.ss.usermodel.ClientAnchor.AnchorType;
  8. import org.apache.poi.hssf.usermodel.HSSFClientAnchor;
  9. import org.apache.poi.hssf.usermodel.HSSFPatriarch;
  10. import org.apache.poi.hssf.usermodel.HSSFPicture;
  11. import org.apache.poi.hssf.usermodel.HSSFSheet;
  12. import org.apache.poi.hssf.usermodel.HSSFWorkbook;
  13. public class HSSFImage {
  14. public static void main(String[] args) throws IOException {
  15. String imageFile = "test.png";
  16. String outputFile = "image-sutpid.xls";
  17. HSSFWorkbook workbook = new HSSFWorkbook();
  18. HSSFSheet sheet = workbook.createSheet("Image");
  19. HSSFClientAnchor anchor = new HSSFClientAnchor(100,100,100,100,(short)0, (short)0, (short)0, (short)3);
  20. sheet.setColumnWidth(0, 6000);
  21. anchor.setAnchorType(AnchorType.DONT_MOVE_AND_RESIZE);
  22. int index = sheet.getWorkbook().addPicture(imageToBytes(imageFile), HSSFWorkbook.PICTURE_TYPE_PNG);
  23. HSSFPatriarch patriarch = sheet.createDrawingPatriarch();
  24. HSSFPicture picture = patriarch.createPicture(anchor, index);
  25. picture.resize();
  26. FileOutputStream fos = new FileOutputStream(outputFile);
  27. workbook.write(fos);
  28. }
  29. private static byte[] imageToBytes(String imageFilename) throws IOException {
  30. File imageFile;
  31. FileInputStream fis = null;
  32. ByteArrayOutputStream bos;
  33. int read;
  34. try {
  35. imageFile = new File(imageFilename);
  36. fis = new FileInputStream(imageFile);
  37. bos = new ByteArrayOutputStream();
  38. while ((read = fis.read()) != -1) {
  39. bos.write(read);
  40. }
  41. return (bos.toByteArray());
  42. } finally {
  43. if (fis != null) {
  44. try {
  45. fis.close();
  46. fis = null;
  47. } catch (IOException ioEx) {
  48. // Nothing to do here
  49. }
  50. }
  51. }
  52. }
  53. }

XSSF类

  1. package poisamples;
  2. import java.io.ByteArrayOutputStream;
  3. import java.io.File;
  4. import java.io.FileInputStream;
  5. import java.io.FileOutputStream;
  6. import java.io.IOException;
  7. import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
  8. import org.apache.poi.xssf.usermodel.XSSFDrawing;
  9. import org.apache.poi.xssf.usermodel.XSSFPicture;
  10. import org.apache.poi.xssf.usermodel.XSSFSheet;
  11. import org.apache.poi.xssf.usermodel.XSSFWorkbook;
  12. import org.apache.poi.ss.usermodel.ClientAnchor.AnchorType;
  13. public class XSSFImage {
  14. public static void main(String[] args) throws IOException {
  15. String imageFile = "test.png";
  16. String outputFile = "image-sutpid.xlsx";
  17. XSSFWorkbook workbook = new XSSFWorkbook();
  18. XSSFSheet sheet = workbook.createSheet("Image");
  19. XSSFClientAnchor anchor = new XSSFClientAnchor(100,100,100,100,0, 0, 0, 3);
  20. sheet.setColumnWidth(0, 6000);
  21. anchor.setAnchorType(AnchorType.DONT_MOVE_AND_RESIZE);
  22. int index = sheet.getWorkbook().addPicture(imageToBytes(imageFile), XSSFWorkbook.PICTURE_TYPE_PNG);
  23. XSSFDrawing patriarch = sheet.createDrawingPatriarch();
  24. XSSFPicture picture = patriarch.createPicture(anchor, index);
  25. picture.resize();
  26. FileOutputStream fos = new FileOutputStream(outputFile);
  27. workbook.write(fos);
  28. }
  29. private static byte[] imageToBytes(String imageFilename) throws IOException {
  30. File imageFile;
  31. FileInputStream fis = null;
  32. ByteArrayOutputStream bos;
  33. int read;
  34. try {
  35. imageFile = new File(imageFilename);
  36. fis = new FileInputStream(imageFile);
  37. bos = new ByteArrayOutputStream();
  38. while ((read = fis.read()) != -1) {
  39. bos.write(read);
  40. }
  41. return (bos.toByteArray());
  42. } finally {
  43. if (fis != null) {
  44. try {
  45. fis.close();
  46. fis = null;
  47. } catch (IOException ioEx) {
  48. // Nothing to do here
  49. }
  50. }
  51. }
  52. }
  53. }

HSSF结果:

XSSF结果:

使用的图像:

e7arh2l6

e7arh2l61#

问题是微软使用的不同的奇怪测量单位,以及二进制文件系统*.xls和Office Open XML *.xlsx不仅在文件存储方面而且在一般方法上都有很大不同。
ClientAnchor所述:“注意- XSSF和HSSF的坐标系略有不同,XSSF中的值要大Units.EMU_PER_PIXEL”。但这并不是全部真相。dxdy的含义完全不同。在二进制文件系统*.xls中,这些值取决于column-width/default column-widthrow-height/default row-height的因子。不要问我在我的例子中使用的因子14.75。这只是Trial&Error。
关于你的代码,要提到的是,如果你想将图片缩放到它的原始大小,那么只需要一个单元格锚。这将锚定图片的左上边缘。如果锚将确定图片的大小,则仅需要两个单元锚。然后,锚中的第一个单元格锚定图片的左上边缘,而锚中的第二个单元格锚定图片的右下边缘。
下面的示例使用1/256th of a character width作为dx的度量单位,因为列宽也在此度量单位中。它使用point作为dy的测量单位,因为行高也在这个测量单位中。

  1. import java.io.*;
  2. import org.apache.poi.ss.usermodel.*;
  3. import org.apache.poi.ss.usermodel.ClientAnchor.AnchorType;
  4. import org.apache.poi.util.IOUtils;
  5. import org.apache.poi.util.Units;
  6. import org.apache.poi.xssf.usermodel.XSSFWorkbook;
  7. import org.apache.poi.xssf.usermodel.XSSFSheet;
  8. import org.apache.poi.hssf.usermodel.HSSFWorkbook;
  9. import org.apache.poi.hssf.usermodel.HSSFSheet;
  10. public class CreateExcelWithPictures {
  11. private static Picture drawImageOnExcelSheet(Sheet sheet,
  12. int col1, int row1, int dx1/*1/256th of a character width*/, int dy1/*points*/,
  13. int col2, int row2, int dx2/*1/256th of a character width*/, int dy2/*points*/,
  14. String pictureurl, int picturetype, boolean resize) throws Exception {
  15. int DEFAULT_COL_WIDTH = 10 * 256;
  16. float DEFAULT_ROW_HEIGHT = 12.75f;
  17. Row row = sheet.getRow(row1);
  18. float rowheight1 = (row!=null)?row.getHeightInPoints():DEFAULT_ROW_HEIGHT;
  19. row = sheet.getRow(row2);
  20. float rowheight2 = (row!=null)?row.getHeightInPoints():DEFAULT_ROW_HEIGHT;
  21. int colwidth1 = sheet.getColumnWidth(col1);
  22. int colwidth2 = sheet.getColumnWidth(col2);
  23. InputStream is = new FileInputStream(pictureurl);
  24. byte[] bytes = IOUtils.toByteArray(is);
  25. int pictureIdx = sheet.getWorkbook().addPicture(bytes, picturetype);
  26. is.close();
  27. CreationHelper helper = sheet.getWorkbook().getCreationHelper();
  28. Drawing drawing = sheet.createDrawingPatriarch();
  29. ClientAnchor anchor = helper.createClientAnchor();
  30. anchor.setAnchorType(AnchorType.DONT_MOVE_AND_RESIZE);
  31. anchor.setRow1(row1); //first anchor determines upper left position
  32. if (sheet instanceof XSSFSheet) {
  33. anchor.setDy1(dy1 * Units.EMU_PER_POINT);
  34. } else if (sheet instanceof HSSFSheet) {
  35. anchor.setDy1((int)Math.round(dy1 * Units.PIXEL_DPI / Units.POINT_DPI * 14.75 * DEFAULT_ROW_HEIGHT / rowheight1));
  36. }
  37. anchor.setCol1(col1);
  38. if (sheet instanceof XSSFSheet) {
  39. anchor.setDx1((int)Math.round(dx1 * Units.EMU_PER_PIXEL * Units.DEFAULT_CHARACTER_WIDTH / 256f));
  40. } else if (sheet instanceof HSSFSheet) {
  41. anchor.setDx1((int)Math.round(dx1 * Units.DEFAULT_CHARACTER_WIDTH / 256f * 14.75 * DEFAULT_COL_WIDTH / colwidth1));
  42. }
  43. if (!resize) {
  44. anchor.setRow2(row2); //second anchor determines bottom right position
  45. if (sheet instanceof XSSFSheet) {
  46. anchor.setDy2(dy2 * Units.EMU_PER_POINT);
  47. } else if (sheet instanceof HSSFSheet) {
  48. anchor.setDy2((int)Math.round(dy2 * Units.PIXEL_DPI / Units.POINT_DPI * 14.75 * DEFAULT_ROW_HEIGHT / rowheight2));
  49. }
  50. anchor.setCol2(col2);
  51. if (sheet instanceof XSSFSheet) {
  52. anchor.setDx2((int)Math.round(dx2 * Units.EMU_PER_PIXEL * Units.DEFAULT_CHARACTER_WIDTH / 256f));
  53. } else if (sheet instanceof HSSFSheet) {
  54. anchor.setDx2((int)Math.round(dx2 * Units.DEFAULT_CHARACTER_WIDTH / 256f * 14.75 * DEFAULT_COL_WIDTH / colwidth2));
  55. }
  56. }
  57. Picture picture = drawing.createPicture(anchor, pictureIdx);
  58. if (resize) picture.resize();
  59. return picture;
  60. }
  61. public static void main(String[] args) throws Exception {
  62. Workbook workbook = new XSSFWorkbook();
  63. //Workbook workbook = new HSSFWorkbook();
  64. Sheet sheet = workbook.createSheet("Sheet1");
  65. sheet.setColumnWidth(1, 6000/*1/256th of a character width*/);
  66. Row row = sheet.createRow(0);
  67. row.setHeightInPoints(100/*points*/);
  68. row = sheet.createRow(10);
  69. row.setHeightInPoints(50/*points*/);
  70. Picture picture;
  71. //two cell anchor in the same cell (B1) used without resizing the picture
  72. picture = drawImageOnExcelSheet(sheet,
  73. 1, 0, 1000/*1/256th of a character width*/, 10/*points*/,
  74. 1, 0, 5000/*1/256th of a character width*/, 90/*points*/,
  75. "mikt1.png", Workbook.PICTURE_TYPE_PNG, false);
  76. //one cell anchor (B3) used with resizing the picture
  77. picture = drawImageOnExcelSheet(sheet,
  78. 1, 2, 1000/*1/256th of a character width*/, 10/*points*/,
  79. 0, 0, 0, 0,
  80. "mikt1.png", Workbook.PICTURE_TYPE_PNG, true);
  81. //two cell anchor (B10 to B12) used without resizing the picture
  82. picture = drawImageOnExcelSheet(sheet,
  83. 1, 9, 1000/*1/256th of a character width*/, 10/*points*/,
  84. 1, 11, 5000/*1/256th of a character width*/, 10/*points*/,
  85. "mikt1.png", Workbook.PICTURE_TYPE_PNG, false);
  86. if (workbook instanceof XSSFWorkbook) {
  87. workbook.write(new FileOutputStream("image-sutpid.xlsx"));
  88. } else if (workbook instanceof HSSFWorkbook) {
  89. workbook.write(new FileOutputStream("image-sutpid.xls"));
  90. }
  91. workbook.close();
  92. }
  93. }

至少找到了*-xls二进制文件格式的dxdy的定义。它在2.5.193 OfficeArtClientAnchorSheet中定义。
dx:值表示为单元格宽度的1024次方。
dy:该值表示为该单元格高度的256次方。
有了这个,代码应该是这样的:

  1. import java.io.*;
  2. import org.apache.poi.ss.usermodel.*;
  3. import org.apache.poi.ss.usermodel.ClientAnchor.AnchorType;
  4. import org.apache.poi.util.IOUtils;
  5. import org.apache.poi.util.Units;
  6. import org.apache.poi.xssf.usermodel.XSSFWorkbook;
  7. import org.apache.poi.xssf.usermodel.XSSFSheet;
  8. import org.apache.poi.hssf.usermodel.HSSFWorkbook;
  9. import org.apache.poi.hssf.usermodel.HSSFSheet;
  10. public class CreateExcelWithPictures {
  11. private static Picture drawImageOnExcelSheet(Sheet sheet,
  12. int col1, int row1, int dx1/*1/256th of a character width*/, int dy1/*points*/,
  13. int col2, int row2, int dx2/*1/256th of a character width*/, int dy2/*points*/,
  14. String pictureurl, int picturetype, boolean resize) throws Exception {
  15. int DEFAULT_COL_WIDTH = 10 * 256;
  16. float DEFAULT_ROW_HEIGHT = 12.75f;
  17. Row row = sheet.getRow(row1);
  18. float rowheight1 = (row!=null)?row.getHeightInPoints():DEFAULT_ROW_HEIGHT;
  19. row = sheet.getRow(row2);
  20. float rowheight2 = (row!=null)?row.getHeightInPoints():DEFAULT_ROW_HEIGHT;
  21. int colwidth1 = sheet.getColumnWidth(col1);
  22. int colwidth2 = sheet.getColumnWidth(col2);
  23. InputStream is = new FileInputStream(pictureurl);
  24. byte[] bytes = IOUtils.toByteArray(is);
  25. int pictureIdx = sheet.getWorkbook().addPicture(bytes, picturetype);
  26. is.close();
  27. CreationHelper helper = sheet.getWorkbook().getCreationHelper();
  28. Drawing drawing = sheet.createDrawingPatriarch();
  29. ClientAnchor anchor = helper.createClientAnchor();
  30. anchor.setAnchorType(AnchorType.DONT_MOVE_AND_RESIZE);
  31. anchor.setRow1(row1); //first anchor determines upper left position
  32. if (sheet instanceof XSSFSheet) {
  33. anchor.setDy1(dy1 * Units.EMU_PER_POINT);
  34. } else if (sheet instanceof HSSFSheet) {
  35. anchor.setDy1((int)Math.round(dy1 * Units.PIXEL_DPI / Units.POINT_DPI * 256f / (rowheight1 * Units.PIXEL_DPI / Units.POINT_DPI)));
  36. }
  37. anchor.setCol1(col1);
  38. if (sheet instanceof XSSFSheet) {
  39. anchor.setDx1((int)Math.round(dx1 * Units.EMU_PER_PIXEL * Units.DEFAULT_CHARACTER_WIDTH / 256f));
  40. } else if (sheet instanceof HSSFSheet) {
  41. anchor.setDx1((int)Math.round(dx1 * Units.DEFAULT_CHARACTER_WIDTH / 256f * 1024f / (colwidth1 * Units.DEFAULT_CHARACTER_WIDTH / 256f)));
  42. }
  43. if (!resize) {
  44. anchor.setRow2(row2); //second anchor determines bottom right position
  45. if (sheet instanceof XSSFSheet) {
  46. anchor.setDy2(dy2 * Units.EMU_PER_POINT);
  47. } else if (sheet instanceof HSSFSheet) {
  48. anchor.setDy2((int)Math.round(dy2 * Units.PIXEL_DPI / Units.POINT_DPI * 256f / (rowheight2 * Units.PIXEL_DPI / Units.POINT_DPI)));
  49. }
  50. anchor.setCol2(col2);
  51. if (sheet instanceof XSSFSheet) {
  52. anchor.setDx2((int)Math.round(dx2 * Units.EMU_PER_PIXEL * Units.DEFAULT_CHARACTER_WIDTH / 256f));
  53. } else if (sheet instanceof HSSFSheet) {
  54. anchor.setDx2((int)Math.round(dx2 * Units.DEFAULT_CHARACTER_WIDTH / 256f * 1024f / (colwidth2 * Units.DEFAULT_CHARACTER_WIDTH / 256f)));
  55. }
  56. }
  57. Picture picture = drawing.createPicture(anchor, pictureIdx);
  58. if (resize) picture.resize();
  59. return picture;
  60. }
  61. public static void main(String[] args) throws Exception {
  62. //Workbook workbook = new XSSFWorkbook();
  63. Workbook workbook = new HSSFWorkbook();
  64. Sheet sheet = workbook.createSheet("Sheet1");
  65. sheet.setColumnWidth(1, 6000/*1/256th of a character width*/);
  66. Row row = sheet.createRow(0);
  67. row.setHeightInPoints(100/*points*/);
  68. row = sheet.createRow(10);
  69. row.setHeightInPoints(50/*points*/);
  70. Picture picture;
  71. //two cell anchor in the same cell (B1) used without resizing the picture
  72. picture = drawImageOnExcelSheet(sheet,
  73. 1, 0, 1000/*1/256th of a character width*/, 10/*points*/,
  74. 1, 0, 5000/*1/256th of a character width*/, 90/*points*/,
  75. "mikt1.png", Workbook.PICTURE_TYPE_PNG, false);
  76. //one cell anchor (B3) used with resizing the picture
  77. picture = drawImageOnExcelSheet(sheet,
  78. 1, 2, 1000/*1/256th of a character width*/, 10/*points*/,
  79. 0, 0, 0, 0,
  80. "mikt1.png", Workbook.PICTURE_TYPE_PNG, true);
  81. //two cell anchor (B10 to B12) used without resizing the picture
  82. picture = drawImageOnExcelSheet(sheet,
  83. 1, 9, 1000/*1/256th of a character width*/, 10/*points*/,
  84. 1, 11, 5000/*1/256th of a character width*/, 10/*points*/,
  85. "mikt1.png", Workbook.PICTURE_TYPE_PNG, false);
  86. if (workbook instanceof XSSFWorkbook) {
  87. workbook.write(new FileOutputStream("image-sutpid.xlsx"));
  88. } else if (workbook instanceof HSSFWorkbook) {
  89. workbook.write(new FileOutputStream("image-sutpid.xls"));
  90. }
  91. workbook.close();
  92. }
  93. }

但是,最好将所有长度都以像素为测量单位,以避免从pt和/或字符宽度的256分之一转换为像素。例如Why the same image export excel using HSSFWorkbook can use SXSSFWorkbook can not

展开查看全部

相关问题