From d43ba17fe97c37d1f12583049638a95c9da26463 Mon Sep 17 00:00:00 2001
From: Andreas Beeker
Date: Wed, 12 Nov 2014 00:19:00 +0000
Subject: [PATCH] Picture method to resize with different scales in width and
height
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1638623 13f79535-47bb-0310-9956-ffa450edef68
---
.../poi/hssf/usermodel/HSSFPicture.java | 216 ++++++++----------
.../poi/hssf/usermodel/HSSFPictureData.java | 30 +++
.../apache/poi/hssf/usermodel/HSSFSheet.java | 18 ++
.../org/apache/poi/ss/usermodel/Picture.java | 73 ++++--
.../apache/poi/ss/usermodel/PictureData.java | 12 +
.../org/apache/poi/ss/usermodel/Sheet.java | 21 +-
.../org/apache/poi/ss/util/ImageUtils.java | 185 ++++++++++++++-
.../java/org/apache/poi/util/Units.java | 14 +-
.../apache/poi/xssf/streaming/SXSSFSheet.java | 13 ++
.../poi/xssf/usermodel/XSSFPicture.java | 155 ++++++-------
.../apache/poi/xssf/usermodel/XSSFSheet.java | 17 ++
.../poi/xssf/usermodel/TestXSSFPicture.java | 38 ++-
.../poi/hssf/usermodel/TestHSSFPicture.java | 49 +++-
.../poi/ss/usermodel/BaseTestPicture.java | 53 +++--
test-data/spreadsheet/resize_compare.xls | Bin 0 -> 23040 bytes
test-data/spreadsheet/resize_compare.xlsx | Bin 0 -> 37715 bytes
16 files changed, 620 insertions(+), 274 deletions(-)
rename src/{ooxml => }/java/org/apache/poi/util/Units.java (78%)
create mode 100644 test-data/spreadsheet/resize_compare.xls
create mode 100644 test-data/spreadsheet/resize_compare.xlsx
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFPicture.java b/src/java/org/apache/poi/hssf/usermodel/HSSFPicture.java
index 2c4b59f7b0..2d2eaa752e 100644
--- a/src/java/org/apache/poi/hssf/usermodel/HSSFPicture.java
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFPicture.java
@@ -21,7 +21,17 @@ import java.awt.Dimension;
import java.io.ByteArrayInputStream;
import java.io.UnsupportedEncodingException;
-import org.apache.poi.ddf.*;
+import org.apache.poi.ddf.DefaultEscherRecordFactory;
+import org.apache.poi.ddf.EscherBSERecord;
+import org.apache.poi.ddf.EscherBlipRecord;
+import org.apache.poi.ddf.EscherClientDataRecord;
+import org.apache.poi.ddf.EscherComplexProperty;
+import org.apache.poi.ddf.EscherContainerRecord;
+import org.apache.poi.ddf.EscherOptRecord;
+import org.apache.poi.ddf.EscherProperties;
+import org.apache.poi.ddf.EscherSimpleProperty;
+import org.apache.poi.ddf.EscherTextboxRecord;
+import org.apache.poi.hssf.model.InternalWorkbook;
import org.apache.poi.hssf.record.CommonObjectDataSubRecord;
import org.apache.poi.hssf.record.EscherAggregate;
import org.apache.poi.hssf.record.ObjRecord;
@@ -29,7 +39,6 @@ import org.apache.poi.ss.usermodel.Picture;
import org.apache.poi.ss.util.ImageUtils;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
-import org.apache.poi.hssf.model.InternalWorkbook;
/**
* Represents a escher picture. Eg. A GIF, JPEG etc...
@@ -44,20 +53,6 @@ public class HSSFPicture extends HSSFSimpleShape implements Picture {
public static final int PICTURE_TYPE_PNG = HSSFWorkbook.PICTURE_TYPE_PNG; // PNG
public static final int PICTURE_TYPE_DIB = HSSFWorkbook.PICTURE_TYPE_DIB; // Windows DIB
- /**
- * width of 1px in columns with default width in units of 1/256 of a character width
- */
- private static final float PX_DEFAULT = 32.00f;
- /**
- * width of 1px in columns with overridden width in units of 1/256 of a character width
- */
- private static final float PX_MODIFIED = 36.56f;
-
- /**
- * Height of 1px of a row
- */
- private static final int PX_ROW = 15;
-
public HSSFPicture(EscherContainerRecord spContainer, ObjRecord objRecord) {
super(spContainer, objRecord);
}
@@ -98,37 +93,7 @@ public class HSSFPicture extends HSSFSimpleShape implements Picture {
}
/**
- * Resize the image
- *
- * Please note, that this method works correctly only for workbooks
- * with default font size (Arial 10pt for .xls).
- * If the default font is changed the resized image can be streched vertically or horizontally.
- *
- *
- * @param scale the amount by which image dimensions are multiplied relative to the original size.
- * resize(1.0)
sets the original size, resize(0.5)
resize to 50% of the original,
- * resize(2.0)
resizes to 200% of the original.
- */
- public void resize(double scale){
- HSSFClientAnchor anchor = (HSSFClientAnchor)getAnchor();
- anchor.setAnchorType(2);
-
- HSSFClientAnchor pref = getPreferredSize(scale);
-
- int row2 = anchor.getRow1() + (pref.getRow2() - pref.getRow1());
- int col2 = anchor.getCol1() + (pref.getCol2() - pref.getCol1());
-
- anchor.setCol2((short)col2);
- anchor.setDx1(0);
- anchor.setDx2(pref.getDx2());
-
- anchor.setRow2(row2);
- anchor.setDy1(0);
- anchor.setDy2(pref.getDy2());
- }
-
- /**
- * Reset the image to the original size.
+ * Reset the image to the dimension of the embedded image
*
*
* Please note, that this method works correctly only for workbooks
@@ -137,7 +102,51 @@ public class HSSFPicture extends HSSFSimpleShape implements Picture {
*
*/
public void resize(){
- resize(1.0);
+ resize(Double.MAX_VALUE);
+ }
+
+ /**
+ * Resize the image proportionally.
+ *
+ * @see #resize(double, double)
+ */
+ public void resize(double scale) {
+ resize(scale,scale);
+ }
+
+ /**
+ * Resize the image
+ *
+ * Please note, that this method works correctly only for workbooks
+ * with default font size (Arial 10pt for .xls).
+ * If the default font is changed the resized image can be streched vertically or horizontally.
+ *
+ *
+ * resize(1.0,1.0)
keeps the original size,
+ * resize(0.5,0.5)
resize to 50% of the original,
+ * resize(2.0,2.0)
resizes to 200% of the original.
+ * resize({@link Double#MAX_VALUE},{@link Double#MAX_VALUE})
resizes to the dimension of the embedded image.
+ *
+ *
+ * @param scaleX the amount by which the image width is multiplied relative to the original width.
+ * @param scaleY the amount by which the image height is multiplied relative to the original height.
+ */
+ public void resize(double scaleX, double scaleY) {
+ HSSFClientAnchor anchor = getClientAnchor();
+ anchor.setAnchorType(2);
+
+ HSSFClientAnchor pref = getPreferredSize(scaleX,scaleY);
+
+ int row2 = anchor.getRow1() + (pref.getRow2() - pref.getRow1());
+ int col2 = anchor.getCol1() + (pref.getCol2() - pref.getCol1());
+
+ anchor.setCol2((short)col2);
+ // anchor.setDx1(0);
+ anchor.setDx2(pref.getDx2());
+
+ anchor.setRow2(row2);
+ // anchor.setDy1(0);
+ anchor.setDy2(pref.getDy2());
}
/**
@@ -158,86 +167,30 @@ public class HSSFPicture extends HSSFSimpleShape implements Picture {
* @since POI 3.0.2
*/
public HSSFClientAnchor getPreferredSize(double scale){
- HSSFClientAnchor anchor = (HSSFClientAnchor)getAnchor();
-
- Dimension size = getImageDimension();
- double scaledWidth = size.getWidth() * scale;
- double scaledHeight = size.getHeight() * scale;
-
- float w = 0;
-
- //space in the leftmost cell
- w += getColumnWidthInPixels(anchor.getCol1())*(1 - (float)anchor.getDx1()/1024);
- short col2 = (short)(anchor.getCol1() + 1);
- int dx2 = 0;
-
- while(w < scaledWidth){
- w += getColumnWidthInPixels(col2++);
- }
-
- if(w > scaledWidth) {
- //calculate dx2, offset in the rightmost cell
- col2--;
- double cw = getColumnWidthInPixels(col2);
- double delta = w - scaledWidth;
- dx2 = (int)((cw-delta)/cw*1024);
- }
- anchor.setCol2(col2);
- anchor.setDx2(dx2);
-
- float h = 0;
- h += (1 - (float)anchor.getDy1()/256)* getRowHeightInPixels(anchor.getRow1());
- int row2 = anchor.getRow1() + 1;
- int dy2 = 0;
-
- while(h < scaledHeight){
- h += getRowHeightInPixels(row2++);
- }
- if(h > scaledHeight) {
- row2--;
- double ch = getRowHeightInPixels(row2);
- double delta = h - scaledHeight;
- dy2 = (int)((ch-delta)/ch*256);
- }
- anchor.setRow2(row2);
- anchor.setDy2(dy2);
-
- return anchor;
+ return getPreferredSize(scale, scale);
}
-
- private float getColumnWidthInPixels(int column){
-
- int cw = getPatriarch().getSheet().getColumnWidth(column);
- float px = getPixelWidth(column);
-
- return cw/px;
- }
-
- private float getRowHeightInPixels(int i){
-
- HSSFRow row = getPatriarch().getSheet().getRow(i);
- float height;
- if(row != null) height = row.getHeight();
- else height = getPatriarch().getSheet().getDefaultRowHeight();
-
- return height/PX_ROW;
- }
-
- private float getPixelWidth(int column){
-
- int def = getPatriarch().getSheet().getDefaultColumnWidth()*256;
- int cw = getPatriarch().getSheet().getColumnWidth(column);
-
- return cw == def ? PX_DEFAULT : PX_MODIFIED;
+
+ /**
+ * Calculate the preferred size for this picture.
+ *
+ * @param scaleX the amount by which image width is multiplied relative to the original width.
+ * @param scaleY the amount by which image height is multiplied relative to the original height.
+ * @return HSSFClientAnchor with the preferred size for this image
+ * @since POI 3.11
+ */
+ public HSSFClientAnchor getPreferredSize(double scaleX, double scaleY){
+ ImageUtils.setPreferredSize(this, scaleX, scaleY);
+ return getClientAnchor();
}
/**
- * Return the dimension of this image
+ * Return the dimension of the embedded image in pixel
*
- * @return image dimension
+ * @return image dimension in pixels
*/
public Dimension getImageDimension(){
- EscherBSERecord bse = getPatriarch().getSheet()._book.getBSERecord(getPictureIndex());
+ InternalWorkbook iwb = getPatriarch().getSheet().getWorkbook().getWorkbook();
+ EscherBSERecord bse = iwb.getBSERecord(getPictureIndex());
byte[] data = bse.getBlipRecord().getPicturedata();
int type = bse.getBlipTypeWin32();
return ImageUtils.getImageDimension(new ByteArrayInputStream(data), type);
@@ -250,7 +203,8 @@ public class HSSFPicture extends HSSFSimpleShape implements Picture {
*/
public HSSFPictureData getPictureData(){
InternalWorkbook iwb = getPatriarch().getSheet().getWorkbook().getWorkbook();
- EscherBlipRecord blipRecord = iwb.getBSERecord(getPictureIndex()).getBlipRecord();
+ EscherBSERecord bse = iwb.getBSERecord(getPictureIndex());
+ EscherBlipRecord blipRecord = bse.getBlipRecord();
return new HSSFPictureData(blipRecord);
}
@@ -301,4 +255,22 @@ public class HSSFPicture extends HSSFSimpleShape implements Picture {
ObjRecord obj = (ObjRecord) getObjRecord().cloneViaReserialise();
return new HSSFPicture(spContainer, obj);
}
+
+ /**
+ * @return the anchor that is used by this picture.
+ */
+ @Override
+ public HSSFClientAnchor getClientAnchor() {
+ HSSFAnchor a = getAnchor();
+ return (a instanceof HSSFClientAnchor) ? (HSSFClientAnchor)a : null;
+ }
+
+
+ /**
+ * @return the sheet which contains the picture shape
+ */
+ @Override
+ public HSSFSheet getSheet() {
+ return getPatriarch().getSheet();
+ }
}
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFPictureData.java b/src/java/org/apache/poi/hssf/usermodel/HSSFPictureData.java
index 93fa26f5ba..267bd2e1d6 100644
--- a/src/java/org/apache/poi/hssf/usermodel/HSSFPictureData.java
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFPictureData.java
@@ -22,6 +22,7 @@ import org.apache.poi.ddf.EscherBitmapBlip;
import org.apache.poi.ddf.EscherBlipRecord;
import org.apache.poi.ddf.EscherMetafileBlip;
import org.apache.poi.ss.usermodel.PictureData;
+import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.util.PngUtils;
/**
@@ -131,4 +132,33 @@ public class HSSFPictureData implements PictureData
return "image/unknown";
}
}
+
+ /**
+ * @return the POI internal image type, -1 if not unknown image type
+ *
+ * @see Workbook#PICTURE_TYPE_DIB
+ * @see Workbook#PICTURE_TYPE_EMF
+ * @see Workbook#PICTURE_TYPE_JPEG
+ * @see Workbook#PICTURE_TYPE_PICT
+ * @see Workbook#PICTURE_TYPE_PNG
+ * @see Workbook#PICTURE_TYPE_WMF
+ */
+ public int getPictureType() {
+ switch (blip.getRecordId()) {
+ case EscherMetafileBlip.RECORD_ID_WMF:
+ return Workbook.PICTURE_TYPE_WMF;
+ case EscherMetafileBlip.RECORD_ID_EMF:
+ return Workbook.PICTURE_TYPE_EMF;
+ case EscherMetafileBlip.RECORD_ID_PICT:
+ return Workbook.PICTURE_TYPE_PICT;
+ case EscherBitmapBlip.RECORD_ID_PNG:
+ return Workbook.PICTURE_TYPE_PNG;
+ case EscherBitmapBlip.RECORD_ID_JPEG:
+ return Workbook.PICTURE_TYPE_JPEG;
+ case EscherBitmapBlip.RECORD_ID_DIB:
+ return Workbook.PICTURE_TYPE_DIB;
+ default:
+ return -1;
+ }
+ }
}
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java
index ce09d8012d..e7ed838ecc 100644
--- a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java
@@ -76,6 +76,16 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
private static final POILogger log = POILogFactory.getLogger(HSSFSheet.class);
private static final int DEBUG = POILogger.DEBUG;
+ /**
+ * width of 1px in columns with default width in units of 1/256 of a character width
+ */
+ private static final float PX_DEFAULT = 32.00f;
+ /**
+ * width of 1px in columns with overridden width in units of 1/256 of a character width
+ */
+ private static final float PX_MODIFIED = 36.56f;
+
+
/**
* Used for compile-time optimization. This is the initial size for the collection of
* rows. It is currently set to 20. If you generate larger sheets you may benefit
@@ -555,6 +565,14 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
return _sheet.getColumnWidth(columnIndex);
}
+ public float getColumnWidthInPixels(int column){
+ int cw = getColumnWidth(column);
+ int def = getDefaultColumnWidth()*256;
+ float px = (cw == def ? PX_DEFAULT : PX_MODIFIED);
+
+ return cw/px;
+ }
+
/**
* get the default column width for the sheet (if the columns do not define their own width) in
* characters
diff --git a/src/java/org/apache/poi/ss/usermodel/Picture.java b/src/java/org/apache/poi/ss/usermodel/Picture.java
index c79260c6ee..58ad448125 100644
--- a/src/java/org/apache/poi/ss/usermodel/Picture.java
+++ b/src/java/org/apache/poi/ss/usermodel/Picture.java
@@ -16,6 +16,9 @@
==================================================================== */
package org.apache.poi.ss.usermodel;
+import java.awt.Dimension;
+
+
/**
* Repersents a picture in a SpreadsheetML document
*
@@ -24,33 +27,61 @@ package org.apache.poi.ss.usermodel;
public interface Picture {
/**
- * Reset the image to the original size.
+ * Reset the image to the dimension of the embedded image
*
- *
- * Please note, that this method works correctly only for workbooks
- * with default font size (Arial 10pt for .xls and Calibri 11pt for .xlsx).
- * If the default font is changed the resized image can be streched vertically or horizontally.
- *
+ * @see #resize(double, double)
*/
void resize();
/**
- * Reset the image to the original size.
+ * Resize the image proportionally.
*
- *
- * Please note, that this method works correctly only for workbooks
- * with default font size (Arial 10pt for .xls and Calibri 11pt for .xlsx).
- * If the default font is changed the resize() procedure can be 'off'.
- *
- *
- * @param scale the amount by which image dimensions are multiplied relative to the original size.
- * resize(1.0)
sets the original size, resize(0.5)
resize to 50% of the original,
- * resize(2.0)
resizes to 200% of the original.
+ * @see #resize(double, double)
*/
void resize(double scale);
+
+ /**
+ * Resize the image.
+ *
+ * Please note, that this method works correctly only for workbooks
+ * with the default font size (Arial 10pt for .xls and Calibri 11pt for .xlsx).
+ * If the default font is changed the resized image can be streched vertically or horizontally.
+ *
+ *
+ * resize(1.0,1.0)
keeps the original size,
+ * resize(0.5,0.5)
resize to 50% of the original,
+ * resize(2.0,2.0)
resizes to 200% of the original.
+ * resize({@link Double#MAX_VALUE},{@link Double#MAX_VALUE})
resizes to the dimension of the embedded image.
+ *
+ *
+ * @param scaleX the amount by which the image width is multiplied relative to the original width.
+ * @param scaleY the amount by which the image height is multiplied relative to the original height.
+ */
+ void resize(double scaleX, double scaleY);
+ /**
+ * Calculate the preferred size for this picture.
+ *
+ * @return XSSFClientAnchor with the preferred size for this image
+ */
ClientAnchor getPreferredSize();
+ /**
+ * Calculate the preferred size for this picture.
+ *
+ * @param scaleX the amount by which image width is multiplied relative to the original width.
+ * @param scaleY the amount by which image height is multiplied relative to the original height.
+ * @return ClientAnchor with the preferred size for this image
+ */
+ ClientAnchor getPreferredSize(double scaleX, double scaleY);
+
+ /**
+ * Return the dimension of the embedded image in pixel
+ *
+ * @return image dimension in pixels
+ */
+ Dimension getImageDimension();
+
/**
* Return picture data for this picture
*
@@ -58,4 +89,14 @@ public interface Picture {
*/
PictureData getPictureData();
+ /**
+ * @return the anchor that is used by this picture
+ */
+ ClientAnchor getClientAnchor();
+
+
+ /**
+ * @return the sheet which contains the picture
+ */
+ Sheet getSheet();
}
diff --git a/src/java/org/apache/poi/ss/usermodel/PictureData.java b/src/java/org/apache/poi/ss/usermodel/PictureData.java
index 954337829d..67d3cefcd1 100644
--- a/src/java/org/apache/poi/ss/usermodel/PictureData.java
+++ b/src/java/org/apache/poi/ss/usermodel/PictureData.java
@@ -37,4 +37,16 @@ public interface PictureData {
* Returns the mime type for the image
*/
String getMimeType();
+
+ /**
+ * @return the POI internal image type, 0 if unknown image type
+ *
+ * @see Workbook#PICTURE_TYPE_DIB
+ * @see Workbook#PICTURE_TYPE_EMF
+ * @see Workbook#PICTURE_TYPE_JPEG
+ * @see Workbook#PICTURE_TYPE_PICT
+ * @see Workbook#PICTURE_TYPE_PNG
+ * @see Workbook#PICTURE_TYPE_WMF
+ */
+ int getPictureType();
}
\ No newline at end of file
diff --git a/src/java/org/apache/poi/ss/usermodel/Sheet.java b/src/java/org/apache/poi/ss/usermodel/Sheet.java
index 57c8785c6c..d1bd67416a 100644
--- a/src/java/org/apache/poi/ss/usermodel/Sheet.java
+++ b/src/java/org/apache/poi/ss/usermodel/Sheet.java
@@ -186,11 +186,26 @@ public interface Sheet extends Iterable {
* using the default font (first font in the workbook)
*
*
- * @param columnIndex - the column to set (0-based)
+ * @param columnIndex - the column to get (0-based)
* @return width - the width in units of 1/256th of a character width
*/
int getColumnWidth(int columnIndex);
+ /**
+ * get the width in pixel
+ *
+ *
+ * Please note, that this method works correctly only for workbooks
+ * with the default font size (Arial 10pt for .xls and Calibri 11pt for .xlsx).
+ * If the default font is changed the column width can be streched
+ *
+ *
+ * @param columnIndex - the column to set (0-based)
+ * @return width in pixels
+ */
+ float getColumnWidthInPixels(int columnIndex);
+
+
/**
* Set the default column width for the sheet (if the columns do not define their own width)
* in characters
@@ -214,7 +229,7 @@ public interface Sheet extends Iterable {
* @return default row height measured in twips (1/20 of a point)
*/
short getDefaultRowHeight();
-
+
/**
* Get the default row height for the sheet (if the rows do not define their own height) in
* points.
@@ -922,7 +937,7 @@ public interface Sheet extends Iterable {
public DataValidationHelper getDataValidationHelper();
- /**
+ /**
* Returns the list of DataValidation in the sheet.
* @return list of DataValidation in the sheet
*/
diff --git a/src/java/org/apache/poi/ss/util/ImageUtils.java b/src/java/org/apache/poi/ss/util/ImageUtils.java
index 76ebc0370c..b470824519 100644
--- a/src/java/org/apache/poi/ss/util/ImageUtils.java
+++ b/src/java/org/apache/poi/ss/util/ImageUtils.java
@@ -16,21 +16,32 @@
==================================================================== */
package org.apache.poi.ss.util;
-import org.w3c.dom.NodeList;
-import org.w3c.dom.Element;
-import org.apache.poi.ss.usermodel.Workbook;
-import org.apache.poi.util.POILogger;
-import org.apache.poi.util.POILogFactory;
+import static org.apache.poi.util.Units.EMU_PER_PIXEL;
-import javax.imageio.ImageReader;
-import javax.imageio.ImageIO;
-import javax.imageio.stream.ImageInputStream;
+import java.awt.Dimension;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.awt.*;
-import java.awt.image.BufferedImage;
import java.util.Iterator;
+import javax.imageio.ImageIO;
+import javax.imageio.ImageReader;
+import javax.imageio.stream.ImageInputStream;
+
+import org.apache.poi.hssf.usermodel.HSSFClientAnchor;
+import org.apache.poi.ss.usermodel.ClientAnchor;
+import org.apache.poi.ss.usermodel.Picture;
+import org.apache.poi.ss.usermodel.PictureData;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.util.POILogFactory;
+import org.apache.poi.util.POILogger;
+import org.apache.poi.util.Units;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
/**
* @author Yegor Kozlov
*/
@@ -60,8 +71,8 @@ public class ImageUtils {
try {
//read the image using javax.imageio.*
ImageInputStream iis = ImageIO.createImageInputStream( is );
- Iterator i = ImageIO.getImageReaders( iis );
- ImageReader r = (ImageReader) i.next();
+ Iterator i = ImageIO.getImageReaders( iis );
+ ImageReader r = i.next();
r.setInput( iis );
BufferedImage img = r.read(0);
@@ -113,4 +124,154 @@ public class ImageUtils {
return new int[]{hdpi, vdpi};
}
+ /**
+ * Calculate and set the preferred size (anchor) for this picture.
+ *
+ * @param scaleX the amount by which image width is multiplied relative to the original width.
+ * @param scaleY the amount by which image height is multiplied relative to the original height.
+ * @return the new Dimensions of the scaled picture in EMUs
+ */
+ public static Dimension setPreferredSize(Picture picture, double scaleX, double scaleY){
+ ClientAnchor anchor = picture.getClientAnchor();
+ boolean isHSSF = (anchor instanceof HSSFClientAnchor);
+ PictureData data = picture.getPictureData();
+ Sheet sheet = picture.getSheet();
+
+ // in pixel
+ Dimension imgSize = getImageDimension(new ByteArrayInputStream(data.getData()), data.getPictureType());
+ // in emus
+ Dimension anchorSize = ImageUtils.getDimensionFromAnchor(picture);
+ final double scaledWidth = (scaleX == Double.MAX_VALUE)
+ ? imgSize.getWidth() : anchorSize.getWidth()/EMU_PER_PIXEL * scaleX;
+ final double scaledHeight = (scaleY == Double.MAX_VALUE)
+ ? imgSize.getHeight() : anchorSize.getHeight()/EMU_PER_PIXEL * scaleY;
+
+ double w = 0;
+ int col2 = anchor.getCol1();
+ int dx2 = 0;
+
+ //space in the leftmost cell
+ w = sheet.getColumnWidthInPixels(col2++);
+ if (isHSSF) {
+ w *= 1 - anchor.getDx1()/1024d;
+ } else {
+ w -= anchor.getDx1()/EMU_PER_PIXEL;
+ }
+
+ while(w < scaledWidth){
+ w += sheet.getColumnWidthInPixels(col2++);
+ }
+
+ if(w > scaledWidth) {
+ //calculate dx2, offset in the rightmost cell
+ double cw = sheet.getColumnWidthInPixels(--col2);
+ double delta = w - scaledWidth;
+ if (isHSSF) {
+ dx2 = (int)((cw-delta)/cw*1024);
+ } else {
+ dx2 = (int)((cw-delta)*EMU_PER_PIXEL);
+ }
+ if (dx2 < 0) dx2 = 0;
+ }
+ anchor.setCol2(col2);
+ anchor.setDx2(dx2);
+
+ double h = 0;
+ int row2 = anchor.getRow1();
+ int dy2 = 0;
+
+ h = getRowHeightInPixels(sheet,row2++);
+ if (isHSSF) {
+ h *= 1 - anchor.getDy1()/256d;
+ } else {
+ h -= anchor.getDy1()/EMU_PER_PIXEL;
+ }
+
+ while(h < scaledHeight){
+ h += getRowHeightInPixels(sheet,row2++);
+ }
+
+ if(h > scaledHeight) {
+ double ch = getRowHeightInPixels(sheet,--row2);
+ double delta = h - scaledHeight;
+ if (isHSSF) {
+ dy2 = (int)((ch-delta)/ch*256);
+ } else {
+ dy2 = (int)((ch-delta)*EMU_PER_PIXEL);
+ }
+ if (dy2 < 0) dy2 = 0;
+ }
+
+ anchor.setRow2(row2);
+ anchor.setDy2(dy2);
+
+ Dimension dim = new Dimension(
+ (int)Math.round(scaledWidth*EMU_PER_PIXEL),
+ (int)Math.round(scaledHeight*EMU_PER_PIXEL)
+ );
+
+ return dim;
+ }
+
+ /**
+ * Calculates the dimensions in EMUs for the anchor of the given picture
+ *
+ * @param picture the picture containing the anchor
+ * @return the dimensions in EMUs
+ */
+ public static Dimension getDimensionFromAnchor(Picture picture) {
+ ClientAnchor anchor = picture.getClientAnchor();
+ boolean isHSSF = (anchor instanceof HSSFClientAnchor);
+ Sheet sheet = picture.getSheet();
+
+ double w = 0;
+ int col2 = anchor.getCol1();
+
+ //space in the leftmost cell
+ w = sheet.getColumnWidthInPixels(col2++);
+ if (isHSSF) {
+ w *= 1 - anchor.getDx1()/1024d;
+ } else {
+ w -= anchor.getDx1()/EMU_PER_PIXEL;
+ }
+
+ while(col2 < anchor.getCol2()){
+ w += sheet.getColumnWidthInPixels(col2++);
+ }
+
+ if (isHSSF) {
+ w += sheet.getColumnWidthInPixels(col2) * anchor.getDx2()/1024d;
+ } else {
+ w += anchor.getDx2()/EMU_PER_PIXEL;
+ }
+
+ double h = 0;
+ int row2 = anchor.getRow1();
+
+ h = getRowHeightInPixels(sheet,row2++);
+ if (isHSSF) {
+ h *= 1 - anchor.getDy1()/256d;
+ } else {
+ h -= anchor.getDy1()/EMU_PER_PIXEL;
+ }
+
+ while(row2 < anchor.getRow2()){
+ h += getRowHeightInPixels(sheet,row2++);
+ }
+
+ if (isHSSF) {
+ h += getRowHeightInPixels(sheet,row2) * anchor.getDy2()/256;
+ } else {
+ h += anchor.getDy2()/EMU_PER_PIXEL;
+ }
+
+ return new Dimension((int)w*EMU_PER_PIXEL, (int)h*EMU_PER_PIXEL);
+ }
+
+
+ private static double getRowHeightInPixels(Sheet sheet, int rowNum) {
+ Row r = sheet.getRow(rowNum);
+ double points = (r == null) ? sheet.getDefaultRowHeightInPoints() : r.getHeightInPoints();
+ return Units.toEMU(points)/EMU_PER_PIXEL;
+ }
}
diff --git a/src/ooxml/java/org/apache/poi/util/Units.java b/src/java/org/apache/poi/util/Units.java
similarity index 78%
rename from src/ooxml/java/org/apache/poi/util/Units.java
rename to src/java/org/apache/poi/util/Units.java
index 3a400399c7..91584b2ad5 100644
--- a/src/ooxml/java/org/apache/poi/util/Units.java
+++ b/src/java/org/apache/poi/util/Units.java
@@ -23,10 +23,20 @@ public class Units {
public static final int EMU_PER_PIXEL = 9525;
public static final int EMU_PER_POINT = 12700;
- public static int toEMU(double value){
- return (int)Math.round(EMU_PER_POINT*value);
+ /**
+ * Converts points to EMUs
+ * @param points points
+ * @return emus
+ */
+ public static int toEMU(double points){
+ return (int)Math.round(EMU_PER_POINT*points);
}
+ /**
+ * Converts EMUs to points
+ * @param emu emu
+ * @return points
+ */
public static double toPoints(long emu){
return (double)emu/EMU_PER_POINT;
}
diff --git a/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFSheet.java b/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFSheet.java
index d676eb599e..2700e16785 100644
--- a/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFSheet.java
+++ b/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFSheet.java
@@ -264,6 +264,19 @@ public class SXSSFSheet implements Sheet, Cloneable
return _sh.getColumnWidth(columnIndex);
}
+ /**
+ * Get the actual column width in pixels
+ *
+ *
+ * Please note, that this method works correctly only for workbooks
+ * with the default font size (Calibri 11pt for .xlsx).
+ *
+ */
+ @Override
+ public float getColumnWidthInPixels(int columnIndex) {
+ return _sh.getColumnWidthInPixels(columnIndex);
+ }
+
/**
* Set the default column width for the sheet (if the columns do not define their own width)
* in characters
diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFPicture.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFPicture.java
index 47402bdc35..1d87faceee 100644
--- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFPicture.java
+++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFPicture.java
@@ -20,15 +20,14 @@ package org.apache.poi.xssf.usermodel;
import java.awt.Dimension;
import java.io.IOException;
-import org.apache.poi.POIXMLDocumentPart;
import org.apache.poi.openxml4j.opc.PackagePart;
import org.apache.poi.openxml4j.opc.PackageRelationship;
import org.apache.poi.ss.usermodel.Picture;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.ImageUtils;
+import org.apache.poi.util.Internal;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
-import org.apache.poi.util.Internal;
import org.openxmlformats.schemas.drawingml.x2006.main.CTBlipFillProperties;
import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps;
import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualPictureProperties;
@@ -40,7 +39,6 @@ import org.openxmlformats.schemas.drawingml.x2006.main.CTTransform2D;
import org.openxmlformats.schemas.drawingml.x2006.main.STShapeType;
import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTPicture;
import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTPictureNonVisual;
-import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCol;
/**
* Represents a picture shape in a SpreadsheetML drawing.
@@ -57,7 +55,7 @@ public final class XSSFPicture extends XSSFShape implements Picture {
*
* This value is the same for default font in Office 2007 (Calibry) and Office 2003 and earlier (Arial)
*/
- private static float DEFAULT_COLUMN_WIDTH = 9.140625f;
+ // private static float DEFAULT_COLUMN_WIDTH = 9.140625f;
/**
* A default instance of CTShape used for creating new shapes.
@@ -140,44 +138,56 @@ public final class XSSFPicture extends XSSFShape implements Picture {
}
/**
- * Reset the image to the original size.
+ * Reset the image to the dimension of the embedded image
*
- *
- * Please note, that this method works correctly only for workbooks
- * with the default font size (Calibri 11pt for .xlsx).
- * If the default font is changed the resized image can be streched vertically or horizontally.
- *
+ * @see #resize(double, double)
*/
public void resize(){
- resize(1.0);
+ resize(Double.MAX_VALUE);
}
/**
- * Reset the image to the original size.
+ * Resize the image proportionally.
+ *
+ * @see #resize(double, double)
+ */
+ public void resize(double scale) {
+ resize(scale, scale);
+ }
+
+ /**
+ * Resize the image relatively to its current size.
*
* Please note, that this method works correctly only for workbooks
* with the default font size (Calibri 11pt for .xlsx).
* If the default font is changed the resized image can be streched vertically or horizontally.
*
+ *
+ * resize(1.0,1.0)
keeps the original size,
+ * resize(0.5,0.5)
resize to 50% of the original,
+ * resize(2.0,2.0)
resizes to 200% of the original.
+ * resize({@link Double#MAX_VALUE},{@link Double#MAX_VALUE})
resizes to the dimension of the embedded image.
+ *
*
- * @param scale the amount by which image dimensions are multiplied relative to the original size.
- * resize(1.0)
sets the original size, resize(0.5)
resize to 50% of the original,
- * resize(2.0)
resizes to 200% of the original.
+ * @param scaleX the amount by which the image width is multiplied relative to the original width,
+ * when set to {@link java.lang.Double#MAX_VALUE} the width of the embedded image is used
+ * @param scaleY the amount by which the image height is multiplied relative to the original height,
+ * when set to {@link java.lang.Double#MAX_VALUE} the height of the embedded image is used
*/
- public void resize(double scale){
- XSSFClientAnchor anchor = (XSSFClientAnchor)getAnchor();
+ public void resize(double scaleX, double scaleY){
+ XSSFClientAnchor anchor = getClientAnchor();
- XSSFClientAnchor pref = getPreferredSize(scale);
+ XSSFClientAnchor pref = getPreferredSize(scaleX,scaleY);
int row2 = anchor.getRow1() + (pref.getRow2() - pref.getRow1());
int col2 = anchor.getCol1() + (pref.getCol2() - pref.getCol1());
anchor.setCol2(col2);
- anchor.setDx1(0);
+ // anchor.setDx1(0);
anchor.setDx2(pref.getDx2());
anchor.setRow2(row2);
- anchor.setDy1(0);
+ // anchor.setDy1(0);
anchor.setDy2(pref.getDy2());
}
@@ -197,71 +207,22 @@ public final class XSSFPicture extends XSSFShape implements Picture {
* @return XSSFClientAnchor with the preferred size for this image
*/
public XSSFClientAnchor getPreferredSize(double scale){
- XSSFClientAnchor anchor = (XSSFClientAnchor)getAnchor();
-
- XSSFPictureData data = getPictureData();
- Dimension size = getImageDimension(data.getPackagePart(), data.getPictureType());
- double scaledWidth = size.getWidth() * scale;
- double scaledHeight = size.getHeight() * scale;
-
- float w = 0;
- int col2 = anchor.getCol1();
- int dx2 = 0;
-
- for (;;) {
- w += getColumnWidthInPixels(col2);
- if(w > scaledWidth) break;
- col2++;
- }
-
- if(w > scaledWidth) {
- double cw = getColumnWidthInPixels(col2 );
- double delta = w - scaledWidth;
- dx2 = (int)(EMU_PER_PIXEL*(cw-delta));
- }
- anchor.setCol2(col2);
- anchor.setDx2(dx2);
-
- double h = 0;
- int row2 = anchor.getRow1();
- int dy2 = 0;
-
- for (;;) {
- h += getRowHeightInPixels(row2);
- if(h > scaledHeight) break;
- row2++;
- }
-
- if(h > scaledHeight) {
- double ch = getRowHeightInPixels(row2);
- double delta = h - scaledHeight;
- dy2 = (int)(EMU_PER_PIXEL*(ch-delta));
- }
- anchor.setRow2(row2);
- anchor.setDy2(dy2);
-
+ return getPreferredSize(scale, scale);
+ }
+
+ /**
+ * Calculate the preferred size for this picture.
+ *
+ * @param scaleX the amount by which image width is multiplied relative to the original width.
+ * @param scaleY the amount by which image height is multiplied relative to the original height.
+ * @return XSSFClientAnchor with the preferred size for this image
+ */
+ public XSSFClientAnchor getPreferredSize(double scaleX, double scaleY){
+ Dimension dim = ImageUtils.setPreferredSize(this, scaleX, scaleY);
CTPositiveSize2D size2d = ctPicture.getSpPr().getXfrm().getExt();
- size2d.setCx((long)(scaledWidth*EMU_PER_PIXEL));
- size2d.setCy((long)(scaledHeight*EMU_PER_PIXEL));
-
- return anchor;
- }
-
- private float getColumnWidthInPixels(int columnIndex){
- XSSFSheet sheet = (XSSFSheet)getDrawing().getParent();
-
- CTCol col = sheet.getColumnHelper().getColumn(columnIndex, false);
- double numChars = col == null || !col.isSetWidth() ? DEFAULT_COLUMN_WIDTH : col.getWidth();
-
- return (float)numChars*XSSFWorkbook.DEFAULT_CHARACTER_WIDTH;
- }
-
- private float getRowHeightInPixels(int rowIndex){
- XSSFSheet sheet = (XSSFSheet)getDrawing().getParent();
-
- XSSFRow row = sheet.getRow(rowIndex);
- float height = row != null ? row.getHeightInPoints() : sheet.getDefaultRowHeightInPoints();
- return height*PIXEL_DPI/POINT_DPI;
+ size2d.setCx((int)dim.getWidth());
+ size2d.setCy((int)dim.getHeight());
+ return getClientAnchor();
}
/**
@@ -283,6 +244,16 @@ public final class XSSFPicture extends XSSFShape implements Picture {
}
}
+ /**
+ * Return the dimension of the embedded image in pixel
+ *
+ * @return image dimension in pixels
+ */
+ public Dimension getImageDimension() {
+ XSSFPictureData picData = getPictureData();
+ return getImageDimension(picData.getPackagePart(), picData.getPictureType());
+ }
+
/**
* Return picture data for this shape
*
@@ -297,4 +268,20 @@ public final class XSSFPicture extends XSSFShape implements Picture {
return ctPicture.getSpPr();
}
+ /**
+ * @return the anchor that is used by this shape.
+ */
+ @Override
+ public XSSFClientAnchor getClientAnchor() {
+ XSSFAnchor a = getAnchor();
+ return (a instanceof XSSFClientAnchor) ? (XSSFClientAnchor)a : null;
+ }
+
+ /**
+ * @return the sheet which contains the picture shape
+ */
+ @Override
+ public XSSFSheet getSheet() {
+ return (XSSFSheet)getDrawing().getParent();
+ }
}
diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java
index ac72c76803..a5a8b311b6 100644
--- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java
+++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java
@@ -704,6 +704,22 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet {
return (int)(width*256);
}
+ /**
+ * Get the actual column width in pixels
+ *
+ *
+ * Please note, that this method works correctly only for workbooks
+ * with the default font size (Calibri 11pt for .xlsx).
+ *
+ */
+ @Override
+ public float getColumnWidthInPixels(int columnIndex) {
+ int styleIdx = getColumnHelper().getColDefaultStyle(columnIndex);
+ CellStyle cs = getWorkbook().getStylesSource().getStyleAt(styleIdx);
+ float widthIn256 = getColumnWidth(columnIndex);
+ return (float)(widthIn256/256.0*XSSFWorkbook.DEFAULT_CHARACTER_WIDTH);
+ }
+
/**
* Get the default column width for the sheet (if the columns do not define their own width) in
* characters.
@@ -730,6 +746,7 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet {
return (short)(getDefaultRowHeightInPoints() * 20);
}
+
/**
* Get the default row height for the sheet measued in point size (if the rows do not define their own height).
*
diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFPicture.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFPicture.java
index f6b05586e6..5415502b12 100644
--- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFPicture.java
+++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFPicture.java
@@ -18,13 +18,25 @@
package org.apache.poi.xssf.usermodel;
import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
import java.util.List;
+import org.apache.poi.hssf.usermodel.HSSFPatriarch;
+import org.apache.poi.hssf.usermodel.HSSFPicture;
+import org.apache.poi.hssf.usermodel.HSSFShape;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.BaseTestPicture;
import org.apache.poi.ss.usermodel.ClientAnchor;
import org.apache.poi.xssf.XSSFITestDataProvider;
import org.apache.poi.xssf.XSSFTestDataSamples;
+import org.junit.Ignore;
+import org.junit.Test;
import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTTwoCellAnchor;
import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.STEditAs;
@@ -37,12 +49,21 @@ public final class TestXSSFPicture extends BaseTestPicture {
super(XSSFITestDataProvider.instance);
}
- public void testResize() {
- baseTestResize(new XSSFClientAnchor(0, 0, 504825, 85725, (short)0, 0, (short)1, 8));
+ @Test
+ public void resize() throws Exception {
+ XSSFWorkbook wb = XSSFITestDataProvider.instance.openSampleWorkbook("resize_compare.xlsx");
+ XSSFDrawing dp = wb.getSheetAt(0).createDrawingPatriarch();
+ List pics = dp.getShapes();
+ XSSFPicture inpPic = (XSSFPicture)pics.get(0);
+ XSSFPicture cmpPic = (XSSFPicture)pics.get(0);
+
+ baseTestResize(inpPic, cmpPic, 2.0, 2.0);
+ wb.close();
}
- public void testCreate(){
+ @Test
+ public void create() throws IOException {
XSSFWorkbook wb = new XSSFWorkbook();
XSSFSheet sheet = wb.createSheet();
XSSFDrawing drawing = sheet.createDrawingPatriarch();
@@ -70,6 +91,8 @@ public final class TestXSSFPicture extends BaseTestPicture {
CTTwoCellAnchor ctShapeHolder = drawing.getCTDrawing().getTwoCellAnchorArray(0);
// STEditAs.ABSOLUTE corresponds to ClientAnchor.DONT_MOVE_AND_RESIZE
assertEquals(STEditAs.ABSOLUTE, ctShapeHolder.getEditAs());
+
+ wb.close();
}
/**
@@ -77,7 +100,8 @@ public final class TestXSSFPicture extends BaseTestPicture {
*
* See Bugzilla 50458
*/
- public void testShapeId(){
+ @Test
+ public void incrementShapeId() throws IOException {
XSSFWorkbook wb = new XSSFWorkbook();
XSSFSheet sheet = wb.createSheet();
XSSFDrawing drawing = sheet.createDrawingPatriarch();
@@ -93,12 +117,15 @@ public final class TestXSSFPicture extends BaseTestPicture {
jpegIdx = wb.addPicture(jpegData, XSSFWorkbook.PICTURE_TYPE_JPEG);
XSSFPicture shape2 = drawing.createPicture(anchor, jpegIdx);
assertEquals(2, shape2.getCTPicture().getNvPicPr().getCNvPr().getId());
+ wb.close();
}
/**
* same image refrerred by mulitple sheets
*/
- public void testMultiRelationShips(){
+ @SuppressWarnings("resource")
+ @Test
+ public void multiRelationShips() throws IOException {
XSSFWorkbook wb = new XSSFWorkbook();
byte[] pic1Data = "test jpeg data".getBytes();
@@ -140,6 +167,7 @@ public final class TestXSSFPicture extends BaseTestPicture {
XSSFPicture shape44 = (XSSFPicture)drawing2.getShapes().get(1);
assertArrayEquals(shape4.getPictureData().getData(), shape44.getPictureData().getData());
+ wb.close();
}
}
diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFPicture.java b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFPicture.java
index 132a45af5c..7a1ae1f1b6 100644
--- a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFPicture.java
+++ b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFPicture.java
@@ -18,7 +18,10 @@
package org.apache.poi.hssf.usermodel;
import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
@@ -32,6 +35,8 @@ import org.apache.poi.ss.usermodel.ClientAnchor;
import org.apache.poi.ss.usermodel.CreationHelper;
import org.apache.poi.ss.usermodel.PictureData;
import org.apache.poi.ss.usermodel.Workbook;
+import org.junit.Test;
+import org.junit.Ignore;
/**
* Test HSSFPicture
.
@@ -44,14 +49,23 @@ public final class TestHSSFPicture extends BaseTestPicture {
super(HSSFITestDataProvider.instance);
}
- public void testResize() {
- baseTestResize(new HSSFClientAnchor(0, 0, 848, 240, (short)0, 0, (short)1, 9));
+ @Test
+ public void resize() throws Exception {
+ HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("resize_compare.xls");
+ HSSFPatriarch dp = wb.getSheetAt(0).createDrawingPatriarch();
+ List pics = dp.getChildren();
+ HSSFPicture inpPic = (HSSFPicture)pics.get(0);
+ HSSFPicture cmpPic = (HSSFPicture)pics.get(1);
+
+ baseTestResize(inpPic, cmpPic, 2.0, 2.0);
+ wb.close();
}
-
+
/**
* Bug # 45829 reported ArithmeticException (/ by zero) when resizing png with zero DPI.
*/
- public void test45829() {
+ @Test
+ public void bug45829() throws IOException {
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sh1 = wb.createSheet();
HSSFPatriarch p1 = sh1.createDrawingPatriarch();
@@ -60,10 +74,14 @@ public final class TestHSSFPicture extends BaseTestPicture {
int idx1 = wb.addPicture( pictureData, HSSFWorkbook.PICTURE_TYPE_PNG );
HSSFPicture pic = p1.createPicture(new HSSFClientAnchor(), idx1);
pic.resize();
+
+ wb.close();
}
- public void testAddPictures(){
+ @SuppressWarnings("resource")
+ @Test
+ public void addPictures() throws IOException {
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sh = wb.createSheet("Pictures");
@@ -154,9 +172,13 @@ public final class TestHSSFPicture extends BaseTestPicture {
assertArrayEquals(data2, ((HSSFPicture)dr.getChildren().get(1)).getPictureData().getData());
assertArrayEquals(data3, ((HSSFPicture)dr.getChildren().get(2)).getPictureData().getData());
assertArrayEquals(data4, ((HSSFPicture)dr.getChildren().get(3)).getPictureData().getData());
+
+ wb.close();
}
- public void testBSEPictureRef(){
+ @SuppressWarnings("unused")
+ @Test
+ public void bsePictureRef() throws IOException {
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sh = wb.createSheet("Pictures");
@@ -180,9 +202,12 @@ public final class TestHSSFPicture extends BaseTestPicture {
HSSFShapeGroup gr = dr.createGroup(new HSSFClientAnchor());
gr.createPicture(new HSSFChildAnchor(), idx1);
assertEquals(bse.getRef(), 3);
+
+ wb.close();
}
- public void testReadExistingImage(){
+ @Test
+ public void readExistingImage(){
HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("drawings.xls");
HSSFSheet sheet = wb.getSheet("picture");
HSSFPatriarch drawing = sheet.getDrawingPatriarch();
@@ -192,7 +217,9 @@ public final class TestHSSFPicture extends BaseTestPicture {
assertEquals(picture.getFileName(), "test");
}
- public void testSetGetProperties(){
+ @SuppressWarnings("resource")
+ @Test
+ public void setGetProperties() throws IOException {
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sh = wb.createSheet("Pictures");
@@ -214,9 +241,12 @@ public final class TestHSSFPicture extends BaseTestPicture {
p1 = (HSSFPicture) dr.getChildren().get(0);
assertEquals(p1.getFileName(), "aaa");
+ wb.close();
}
- public void test49658() throws IOException {
+ @SuppressWarnings("resource")
+ @Test
+ public void bug49658() throws IOException {
// test if inserted EscherMetafileBlip will be read again
HSSFWorkbook wb = new HSSFWorkbook();
@@ -262,6 +292,7 @@ public final class TestHSSFPicture extends BaseTestPicture {
pictureDataOut = wb.getAllPictures().get(2).getData();
assertArrayEquals(wmfNoHeader, pictureDataOut);
+ wb.close();
}
}
diff --git a/src/testcases/org/apache/poi/ss/usermodel/BaseTestPicture.java b/src/testcases/org/apache/poi/ss/usermodel/BaseTestPicture.java
index b19513759d..22247ebdf7 100644
--- a/src/testcases/org/apache/poi/ss/usermodel/BaseTestPicture.java
+++ b/src/testcases/org/apache/poi/ss/usermodel/BaseTestPicture.java
@@ -17,14 +17,19 @@
package org.apache.poi.ss.usermodel;
-import junit.framework.TestCase;
+import static org.junit.Assert.assertEquals;
+
+import java.awt.Dimension;
+import java.io.FileOutputStream;
import org.apache.poi.ss.ITestDataProvider;
+import org.apache.poi.ss.util.ImageUtils;
+import org.apache.poi.util.Units;
/**
* @author Yegor Kozlov
*/
-public abstract class BaseTestPicture extends TestCase {
+public abstract class BaseTestPicture {
private final ITestDataProvider _testDataProvider;
@@ -32,26 +37,32 @@ public abstract class BaseTestPicture extends TestCase {
_testDataProvider = testDataProvider;
}
- public void baseTestResize(ClientAnchor referenceAnchor) {
- Workbook wb = _testDataProvider.createWorkbook();
- Sheet sh1 = wb.createSheet();
- Drawing p1 = sh1.createDrawingPatriarch();
- CreationHelper factory = wb.getCreationHelper();
+ public void baseTestResize(Picture input, Picture compare, double scaleX, double scaleY) {
+ input.resize(scaleX, scaleY);
+
+ ClientAnchor inpCA = input.getClientAnchor();
+ ClientAnchor cmpCA = compare.getClientAnchor();
+
+ Dimension inpDim = ImageUtils.getDimensionFromAnchor(input);
+ Dimension cmpDim = ImageUtils.getDimensionFromAnchor(compare);
- byte[] pictureData = _testDataProvider.getTestDataFileContent("logoKarmokar4.png");
- int idx1 = wb.addPicture( pictureData, Workbook.PICTURE_TYPE_PNG );
- Picture picture = p1.createPicture(factory.createClientAnchor(), idx1);
- picture.resize();
- ClientAnchor anchor1 = picture.getPreferredSize();
+ double emuPX = Units.EMU_PER_PIXEL;
+
+ assertEquals("the image height differs", inpDim.getHeight(), cmpDim.getHeight(), emuPX*6);
+ assertEquals("the image width differs", inpDim.getWidth(), cmpDim.getWidth(), emuPX*6);
+ assertEquals("the starting column differs", inpCA.getCol1(), cmpCA.getCol1());
+ assertEquals("the column x-offset differs", inpCA.getDx1(), cmpCA.getDx1(), 1);
+ assertEquals("the column y-offset differs", inpCA.getDy1(), cmpCA.getDy1(), 1);
+ assertEquals("the ending columns differs", inpCA.getCol2(), cmpCA.getCol2());
+ // can't compare row heights because of variable test heights
+
+ input.resize();
+ inpDim = ImageUtils.getDimensionFromAnchor(input);
+
+ Dimension imgDim = input.getImageDimension();
- //assert against what would BiffViewer print if we insert the image in xls and dump the file
- assertEquals(referenceAnchor.getCol1(), anchor1.getCol1());
- assertEquals(referenceAnchor.getRow1(), anchor1.getRow1());
- assertEquals(referenceAnchor.getCol2(), anchor1.getCol2());
- assertEquals(referenceAnchor.getRow2(), anchor1.getRow2());
- assertEquals(referenceAnchor.getDx1(), anchor1.getDx1());
- assertEquals(referenceAnchor.getDy1(), anchor1.getDy1());
- assertEquals(referenceAnchor.getDx2(), anchor1.getDx2());
- assertEquals(referenceAnchor.getDy2(), anchor1.getDy2());
+ assertEquals("the image height differs", imgDim.getHeight(), inpDim.getHeight()/emuPX, 1);
+ assertEquals("the image width differs", imgDim.getWidth(), inpDim.getWidth()/emuPX, 1);
}
+
}
diff --git a/test-data/spreadsheet/resize_compare.xls b/test-data/spreadsheet/resize_compare.xls
new file mode 100644
index 0000000000000000000000000000000000000000..c41cc4b15dc0ef05adede8a957e5c80fa57adc2e
GIT binary patch
literal 23040
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
LG$FMhd1U(m1JsG_
literal 0
HcmV?d00001
diff --git a/test-data/spreadsheet/resize_compare.xlsx b/test-data/spreadsheet/resize_compare.xlsx
new file mode 100644
index 0000000000000000000000000000000000000000..7fb4437fe86662f6df30d334f2d81571e6cd7ab1
GIT binary patch
literal 37715
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