dodobird
dodobird

Reputation: 320

Resizing an EMF image in XSSFWorkbook

I am trying to resize an EMF image after inserting it into an XSSFWorkbook. There is support resizing PNG, JPEG and DIB images.

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();
    // Column : A
    anchor.setCol1(0);
    // Row : 4
    anchor.setRow1(4);
    Picture pic = drawing.createPicture(anchor, picIdx);
    double height = 2.0;
    double width = 2.0;
    // sets the anchor.col2 to 0 and anchor.row2 to 0
    pic.getImageDimension().setSize(inchToPixel(width), inchToPixel(height));
    // pic.resize(); -> this too sets the anchor.col2 to 0 and anchor.row2 to 0
}

private double inchToPixel(double in) {
    return in * 96.0;
}

Is there any way we can workaround this problem?

Upvotes: 0

Views: 889

Answers (1)

Axel Richter
Axel Richter

Reputation: 61945

Apache poi not provides to get image dimensions from Picture of type Workbook.PICTURE_TYPE_EMF. The used ImageUtils method getImageDimension only is able to get dimensions from JPEG, PNG and DIB pictures,

But apache poi provides org.apache.poi.hemf.usermodel.HemfPicture. So

import org.apache.poi.hemf.usermodel.HemfPicture;
...
HemfPicture hemfPicture = new HemfPicture(new ByteArrayInputStream(imgData));
System.out.println(hemfPicture.getSize()); 
...

will print the correct dimensions.

But then it lacks a possibility to set those dimensions to the anchor. There is a method scaleCell in ImageUtils but not public accessible. So only way would be to copy source code of that method into your code.

Following complete example does that. It uses methos scaleCell having same code as in ImageUtils. The method resizeHemfPicture resizes the Picture to the HemfPicture dimension.

This is tested and works using apache poi 5.0.0 and Java 12.

import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.xssf.usermodel.*;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.util.IOUtils;
import org.apache.poi.util.Units;
import org.apache.poi.ss.util.ImageUtils;

import org.apache.poi.hemf.usermodel.HemfPicture;

import java.util.function.Consumer;
import java.util.function.Function;

import java.awt.Dimension;

import java.io.FileInputStream;
import java.io.ByteArrayInputStream;
import java.io.FileOutputStream;

class CreateExcelEMFPicture {
 
 public static double getRowHeightInPixels(Sheet sheet, int rowNum) {
  Row r = sheet.getRow(rowNum);
  double points = (r == null) ? sheet.getDefaultRowHeightInPoints() : r.getHeightInPoints();
  return Units.toEMU(points)/(double)Units.EMU_PER_PIXEL;
 }

 private static void scaleCell(final double targetSize,
                               final int startCell,
                               final int startD,
                               Consumer<Integer> endCell,
                               Consumer<Integer> endD,
                               final int hssfUnits,
                               Function<Integer,Number> nextSize) { 
  if (targetSize < 0) {
   throw new IllegalArgumentException("target size < 0");
  }

  int cellIdx = startCell;
  double dim, delta;
  for (double totalDim = 0, remDim;; cellIdx++, totalDim += remDim) {
   dim = nextSize.apply(cellIdx).doubleValue();
   remDim = dim;
   if (cellIdx == startCell) {
    if (hssfUnits > 0) {
     remDim *= 1 - startD/(double)hssfUnits;
    } else {
     remDim -= startD/(double)Units.EMU_PER_PIXEL;
    }
   }
   delta = targetSize - totalDim;
   if (delta < remDim) {
    break;
   }
  }

  double endDval;
  if (hssfUnits > 0) {
   endDval = delta/dim * (double)hssfUnits;
  } else {
   endDval = delta * Units.EMU_PER_PIXEL;
  }
  if (cellIdx == startCell) {
   endDval += startD;
  }

  endCell.accept(cellIdx);
  endD.accept((int)Math.rint(endDval));
 }

 static void resizeHemfPicture(Picture picture, HemfPicture hemfPicture) {
  ClientAnchor anchor = picture.getClientAnchor();
  boolean isHSSF = (anchor instanceof HSSFClientAnchor);
  double height = hemfPicture.getSize().getHeight();
  double width = hemfPicture.getSize().getWidth();
  Sheet sheet = picture.getSheet();

  int WIDTH_UNITS = 1024;
  int HEIGHT_UNITS = 256;

  scaleCell(width, anchor.getCol1(), anchor.getDx1(), anchor::setCol2, anchor::setDx2, isHSSF ? WIDTH_UNITS : 0, sheet::getColumnWidthInPixels);
  scaleCell(height, anchor.getRow1(), anchor.getDy1(), anchor::setRow2, anchor::setDy2, isHSSF ? HEIGHT_UNITS : 0, (row) -> getRowHeightInPixels(sheet, row));

 }

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

  Workbook workbook = new XSSFWorkbook(); String filePath = "./CreateExcelEMFPicture.xlsx";
  //Workbook workbook = new HSSFWorkbook(); String filePath = "./CreateExcelEMFPicture.xls";

  CreationHelper helper = workbook.getCreationHelper();

  //add picture data to this workbook.
  FileInputStream is = new FileInputStream("./image.emf");
  byte[] bytes = IOUtils.toByteArray(is);
  int pictureIdx = workbook.addPicture(bytes, Workbook.PICTURE_TYPE_EMF);
  is.close();

  HemfPicture hemfPicture = new HemfPicture(new ByteArrayInputStream(bytes));
System.out.println(hemfPicture.getSize()); // returns correct dimension


  Sheet sheet = workbook.createSheet("Sheet1");

  Drawing drawing = sheet.createDrawingPatriarch();

  ClientAnchor anchor = helper.createClientAnchor();

  anchor.setCol1(0);
  anchor.setRow1(4); 

  Picture picture = drawing.createPicture(anchor, pictureIdx);
System.out.println(picture.getImageDimension()); // 0, 0
System.out.println(ImageUtils.getDimensionFromAnchor(picture)); // 0, 0
  //picture.resize(); //will not work
  resizeHemfPicture(picture, hemfPicture);
System.out.println(ImageUtils.getDimensionFromAnchor(picture)); // correct dimension

  FileOutputStream out = new FileOutputStream(filePath);
  workbook.write(out);
  out.close();
  workbook.close();

 }
}

Upvotes: 1

Related Questions