首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Apache POI -在合并区域内保持原始图片比例

Apache POI -在合并区域内保持原始图片比例
EN

Stack Overflow用户
提问于 2020-03-09 11:57:38
回答 1查看 2.9K关注 0票数 0

我需要把一张图片放进一个合并的细胞区域的中心。合并的单元格区域总是只有一列宽,但多行高。

如果图片不适合在单元格区域内,它需要调整大小,同时保持原来的比例。

代码语言:javascript
复制
    int pictureIdx = wb.addPicture(bytes, pictype);
    CreationHelper helper = wb.getCreationHelper();
    Drawing drawing = sheet.createDrawingPatriarch();
    ClientAnchor anchor = helper.createClientAnchor();
    anchor.setAnchorType(ClientAnchor.AnchorType.DONT_MOVE_AND_RESIZE);
    anchor.setCol1(column);
    anchor.setRow1(row);
    anchor.setRow2(row+rowspan);
    anchor.setCol2(column+1);
    Picture pict = drawing.createPicture(anchor, pictureIdx);
    pict.resize();

    short rowHeight = (short)0;
    for (int i = row; i<row+rowspan; i++ ) {
        rowHeight += sheet.getRow(i).getHeight();
    }
    int rowHeightPx = heightUnits2Pixel(rowHeight); // (from [https://stackoverflow.com/a/31837639/1320704][1] )
    int columnWidthPx = (int) (sheet.getColumnWidthInPixels(column));
    int pictWidthPx = pict.getImageDimension().width;
    int pictHeightPx = pict.getImageDimension().height;
    float scale = 1;
    if (pictHeightPx > rowHeightPx) {
        float tmpscale = (float)rowHeightPx / (float)pictHeightPx;
        if (tmpscale < scale) scale = tmpscale;
    }
    if (pictWidthPx > columnWidthPx) {
        float tmpscale = (float)columnWidthPx / (float)pictWidthPx;
        if (tmpscale < scale) scale = tmpscale;
    }
    anchor.setDx1(
        (int) ((sheet.getColumnWidthInPixels(column)- pict.getImageDimension().width) /2)
    );
    pict.resize(scale, scale);

结果如下:

正如你所看到的,我没有达到预期的效果。这幅画是正方形(256x256),尽管x和y的比例尺是一样的,但它还是被扭曲了,而且它的高度不匹配。而且它不是中心的。

为了进行比较,下面是原来大小相同的图片:

这就是我真正想要达到的目标:

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-03-10 08:35:43

第一个问题是,如果您想使用Picture.resize,那么只创建一个左上角单元格Col1Row1的锚点。只有一个单元格锚,因为右下角锚位置取决于大小。

第二个问题是,您应该只在左上锚位置是众所周知的包容性Dx1Dy1之后才进行调整。

但主要的问题是,您的heightUnits2Pixel没有得到正确的像素高度行。应该更好地使用Row.getHeightInPoints。这将获得pt中的行高,但可以使用rowHeightPt * Units.PIXEL_DPI / Units.POINT_DPI将其转换为px。见https://poi.apache.org/apidocs/dev/org/apache/poi/util/Units.html

使用它,至少刻度因子的计算是正确的,因为它现在有正确的像素行高。

要将锚的Dx1设置为水平中心位置,还有一个额外的问题。DxDy的含义在XSSFHSSF之间有很大的不同。

XSSF中,它是单元EMU中的水平中心位置。因此,您需要为horCenterPosPx * Units.EMU_PER_PIXEL计算Dx。见https://poi.apache.org/apidocs/dev/org/apache/poi/util/Units.html

HSSF中,Dx值依赖于column-width / default column-width因子,Dy值依赖于row-height / default row-height因子。见apache poi XSSFClientAnchor不定位图片相对于dx1,dy1,dx2,dy2

首先,为了计算垂直中心位置,需要确定锚的正确Row1。即使合并了,也会出现不同的行。因此,锚的Row1需要是图片开始的正确行。到垂直中心位置的其余像素是Dy1

下面是完整的示例,对我来说是有效的。它将各种不同大小、缩放和中心的图像放入合并范围A3:A12中。

代码语言:javascript
复制
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.*;

import org.apache.poi.util.IOUtils;
import org.apache.poi.util.Units;

import java.io.InputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;

class CenterImageTest {

 static void putPictureCentered(Sheet sheet, String picturePath, int pictureType, int column, int startRow, int rowspan) throws Exception {
  Workbook wb = sheet.getWorkbook();

  //load the picture
  InputStream inputStream = new FileInputStream(picturePath);
  byte[] bytes = IOUtils.toByteArray(inputStream);
  int pictureIdx = wb.addPicture(bytes, pictureType);
  inputStream.close();

  //create an anchor with upper left cell column/startRow, only one cell anchor since bottom right depends on resizing
  CreationHelper helper = wb.getCreationHelper();
  ClientAnchor anchor = helper.createClientAnchor();
  anchor.setCol1(column);
  anchor.setRow1(startRow);

  //create a picture anchored to Col1 and Row1
  Drawing drawing = sheet.createDrawingPatriarch();
  Picture pict = drawing.createPicture(anchor, pictureIdx);

  //get the picture width in px
  int pictWidthPx = pict.getImageDimension().width;
  //get the picture height in px
  int pictHeightPx = pict.getImageDimension().height;

  //get column width of column in px
  float columnWidthPx = sheet.getColumnWidthInPixels(column);

  //get the heights of all merged rows in px
  float[] rowHeightsPx = new float[startRow+rowspan];
  float rowsHeightPx = 0f;
  for (int r = startRow; r < startRow+rowspan; r++) {
   Row row = sheet.getRow(r);
   float rowHeightPt = row.getHeightInPoints();
   rowHeightsPx[r-startRow] = rowHeightPt * Units.PIXEL_DPI / Units.POINT_DPI;
   rowsHeightPx += rowHeightsPx[r-startRow];
  }

  //calculate scale
  float scale = 1;
  if (pictHeightPx > rowsHeightPx) {
   float tmpscale = rowsHeightPx / (float)pictHeightPx;
   if (tmpscale < scale) scale = tmpscale;
  }
  if (pictWidthPx > columnWidthPx) {
   float tmpscale = columnWidthPx / (float)pictWidthPx;
   if (tmpscale < scale) scale = tmpscale;
  }

  //calculate the horizontal center position
  int horCenterPosPx = Math.round(columnWidthPx/2f - pictWidthPx*scale/2f);
  //set the horizontal center position as Dx1 of anchor
  if (wb instanceof XSSFWorkbook) {
   anchor.setDx1(horCenterPosPx * Units.EMU_PER_PIXEL); //in unit EMU for XSSF
  } else if (wb instanceof HSSFWorkbook) {
   //see https://stackoverflow.com/questions/48567203/apache-poi-xssfclientanchor-not-positioning-picture-with-respect-to-dx1-dy1-dx/48607117#48607117 for HSSF
   int DEFAULT_COL_WIDTH = 10 * 256;
   anchor.setDx1(Math.round(horCenterPosPx * Units.DEFAULT_CHARACTER_WIDTH / 256f * 14.75f * DEFAULT_COL_WIDTH / columnWidthPx));
  }

  //calculate the vertical center position
  int vertCenterPosPx = Math.round(rowsHeightPx/2f - pictHeightPx*scale/2f);
  //get Row1
  Integer row1 = null;
  rowsHeightPx = 0f;
  for (int r = 0; r < rowHeightsPx.length; r++) {
   float rowHeightPx = rowHeightsPx[r];
   if (rowsHeightPx + rowHeightPx > vertCenterPosPx) {
    row1 = r + startRow;
    break;
   }
   rowsHeightPx += rowHeightPx;
  }
  //set the vertical center position as Row1 plus Dy1 of anchor
  if (row1 != null) {
   anchor.setRow1(row1);
   if (wb instanceof XSSFWorkbook) {
    anchor.setDy1(Math.round(vertCenterPosPx - rowsHeightPx) * Units.EMU_PER_PIXEL); //in unit EMU for XSSF
   } else if (wb instanceof HSSFWorkbook) {
    //see https://stackoverflow.com/questions/48567203/apache-poi-xssfclientanchor-not-positioning-picture-with-respect-to-dx1-dy1-dx/48607117#48607117 for HSSF
    float DEFAULT_ROW_HEIGHT = 12.75f;
    anchor.setDy1(Math.round((vertCenterPosPx - rowsHeightPx) * Units.PIXEL_DPI / Units.POINT_DPI * 14.75f * DEFAULT_ROW_HEIGHT / rowHeightsPx[row1]));
   }
  }

  //resize the picture to it's native size
  pict.resize();
  //if it must scaled down, then scale
  if (scale < 1) {
   pict.resize(scale);
  }
 }

 public static void main(String[] args) throws Exception {

  Workbook wb = new HSSFWorkbook(); String resultName = "CenterImageTest.xls";
  //Workbook wb = new XSSFWorkbook(); String resultName = "CenterImageTest.xlsx";
  Sheet sheet = wb.createSheet("Sheet1");

  int column = 2;
  int columnWidth = 100; //in default character widths
  int startRow = 2;
  int rowspan = 10;

  //========================prepare sheet
  //create cell A1 and set cell value
  Row row = sheet.createRow(1);
  Cell cell = row.createCell(column);
  cell.setCellValue("Picture of product");

  //create the cells with different heights
  for (int r = startRow; r < startRow+rowspan; r++) {
   row = sheet.createRow(r);
   row.setHeightInPoints(12 + r*2);
  }
  //merge cells
  sheet.addMergedRegion(new CellRangeAddress(startRow,startRow+rowspan-1,column,column));
  //========================end prepare sheet

  //set column width of column in default character widths
  sheet.setColumnWidth(column, columnWidth * 256);
  //put image centered
  //String picturePath = "./logo.png"; // small image
  //putPictureCentered(sheet, picturePath, Workbook.PICTURE_TYPE_PNG, column, startRow, rowspan);
  String picturePath = "./Bilder/Sample-jpg-image-1.jpg"; // bigger image
  putPictureCentered(sheet, picturePath, Workbook.PICTURE_TYPE_JPEG, column, startRow, rowspan);

  FileOutputStream fileOut = new FileOutputStream(resultName);
  wb.write(fileOut);
  fileOut.close();
  wb.close();

 }
}

如果图片很小,并且为锚设置了Picture.resize,那么似乎在Dx1中有一个bug。刻度因子scale根据此代码正确计算,但pict.resize(scale)不按比例缩放高度和宽度。它使用正确的因子缩放高度。但它的宽度不是使用相同的因素,而是一个更大的因素。

我已经在bug.cgi?id=64213上注册了bug。

为了避免错误的Picture.resize,我们可以计算完整的两个细胞锚点。我们有Col1,并且已经在计算Dx1Row1Dy1,以得到图片的左上角。现在,我们可以额外计算Dx2Row2Dy2,以获得图片的右下角。

下面是我的putPictureCentered示例:

代码语言:javascript
复制
...
/*
  //resize the picture to it's native size
  pict.resize();
  //if it must scaled down, then scale
  if (scale < 1) {
   pict.resize(scale);
  }
*/

  //set Col2 of anchor the same as Col1 as all is in one column
  anchor.setCol2(column);

  //calculate the horizontal end position of picture
  int horCenterEndPosPx = Math.round(horCenterPosPx + pictWidthPx*scale);
  //set set the horizontal end position as Dx2 of anchor
  if (wb instanceof XSSFWorkbook) {
   anchor.setDx2(horCenterEndPosPx * Units.EMU_PER_PIXEL); //in unit EMU for XSSF
  } else if (wb instanceof HSSFWorkbook) {
   //see https://stackoverflow.com/questions/48567203/apache-poi-xssfclientanchor-not-positioning-picture-with-respect-to-dx1-dy1-dx/48607117#48607117 for HSSF
   int DEFAULT_COL_WIDTH = 10 * 256;
   anchor.setDx2(Math.round(horCenterEndPosPx * Units.DEFAULT_CHARACTER_WIDTH / 256f * 14.75f * DEFAULT_COL_WIDTH / columnWidthPx));
  }  

  //calculate the vertical end position of picture
  int vertCenterEndPosPx = Math.round(vertCenterPosPx + pictHeightPx*scale);
  //get Row2
  Integer row2 = null;
  rowsHeightPx = 0f;
  for (int r = 0; r < rowHeightsPx.length; r++) {
   float rowHeightPx = rowHeightsPx[r];
   if (rowsHeightPx + rowHeightPx > vertCenterEndPosPx) {
    row2 = r + startRow;
    break;
   }
   rowsHeightPx += rowHeightPx;
  }

  //set the vertical end position as Row2 plus Dy2 of anchor
  if (row2 != null) {
   anchor.setRow2(row2);
   if (wb instanceof XSSFWorkbook) {
    anchor.setDy2(Math.round(vertCenterEndPosPx - rowsHeightPx) * Units.EMU_PER_PIXEL); //in unit EMU for XSSF
   } else if (wb instanceof HSSFWorkbook) {
    //see https://stackoverflow.com/questions/48567203/apache-poi-xssfclientanchor-not-positioning-picture-with-respect-to-dx1-dy1-dx/48607117#48607117 for HSSF
    float DEFAULT_ROW_HEIGHT = 12.75f;
    anchor.setDy2(Math.round((vertCenterEndPosPx - rowsHeightPx) * Units.PIXEL_DPI / Units.POINT_DPI * 14.75f * DEFAULT_ROW_HEIGHT / rowHeightsPx[row1]));
   }
  }
...
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/60600112

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档