286 lines
10 KiB
Java
286 lines
10 KiB
Java
/* ====================================================================
|
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
|
contributor license agreements. See the NOTICE file distributed with
|
|
this work for additional information regarding copyright ownership.
|
|
The ASF licenses this file to You under the Apache License, Version 2.0
|
|
(the "License"); you may not use this file except in compliance with
|
|
the License. You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
==================================================================== */
|
|
package org.apache.poi.ss.util;
|
|
|
|
import static org.apache.poi.util.Units.EMU_PER_PIXEL;
|
|
|
|
import java.awt.Dimension;
|
|
import java.awt.image.BufferedImage;
|
|
import java.io.ByteArrayInputStream;
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
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
|
|
*/
|
|
public class ImageUtils {
|
|
private static final POILogger logger = POILogFactory.getLogger(ImageUtils.class);
|
|
|
|
public static final int PIXEL_DPI = 96;
|
|
|
|
/**
|
|
* Return the dimension of this image
|
|
*
|
|
* @param is the stream containing the image data
|
|
* @param type type of the picture: {@link org.apache.poi.ss.usermodel.Workbook#PICTURE_TYPE_JPEG},
|
|
* {@link org.apache.poi.ss.usermodel.Workbook#PICTURE_TYPE_PNG} or {@link org.apache.poi.ss.usermodel.Workbook#PICTURE_TYPE_DIB}
|
|
*
|
|
* @return image dimension in pixels
|
|
*/
|
|
public static Dimension getImageDimension(InputStream is, int type){
|
|
Dimension size = new Dimension();
|
|
|
|
switch (type){
|
|
//we can calculate the preferred size only for JPEG, PNG and BMP
|
|
//other formats like WMF, EMF and PICT are not supported in Java
|
|
case Workbook.PICTURE_TYPE_JPEG:
|
|
case Workbook.PICTURE_TYPE_PNG:
|
|
case Workbook.PICTURE_TYPE_DIB:
|
|
try {
|
|
//read the image using javax.imageio.*
|
|
ImageInputStream iis = ImageIO.createImageInputStream( is );
|
|
try {
|
|
Iterator<ImageReader> i = ImageIO.getImageReaders( iis );
|
|
ImageReader r = i.next();
|
|
try {
|
|
r.setInput( iis );
|
|
BufferedImage img = r.read(0);
|
|
|
|
int[] dpi = getResolution(r);
|
|
|
|
//if DPI is zero then assume standard 96 DPI
|
|
//since cannot divide by zero
|
|
if (dpi[0] == 0) dpi[0] = PIXEL_DPI;
|
|
if (dpi[1] == 0) dpi[1] = PIXEL_DPI;
|
|
|
|
size.width = img.getWidth()*PIXEL_DPI/dpi[0];
|
|
size.height = img.getHeight()*PIXEL_DPI/dpi[1];
|
|
} finally {
|
|
r.dispose();
|
|
}
|
|
} finally {
|
|
iis.close();
|
|
}
|
|
|
|
} catch (IOException e) {
|
|
//silently return if ImageIO failed to read the image
|
|
logger.log(POILogger.WARN, e);
|
|
}
|
|
|
|
break;
|
|
default:
|
|
logger.log(POILogger.WARN, "Only JPEG, PNG and DIB pictures can be automatically sized");
|
|
}
|
|
return size;
|
|
}
|
|
|
|
/**
|
|
* The metadata of PNG and JPEG can contain the width of a pixel in millimeters.
|
|
* Return the the "effective" dpi calculated as <code>25.4/HorizontalPixelSize</code>
|
|
* and <code>25.4/VerticalPixelSize</code>. Where 25.4 is the number of mm in inch.
|
|
*
|
|
* @return array of two elements: <code>{horisontalPdi, verticalDpi}</code>.
|
|
* {96, 96} is the default.
|
|
*/
|
|
public static int[] getResolution(ImageReader r) throws IOException {
|
|
int hdpi=96, vdpi=96;
|
|
double mm2inch = 25.4;
|
|
|
|
NodeList lst;
|
|
Element node = (Element)r.getImageMetadata(0).getAsTree("javax_imageio_1.0");
|
|
lst = node.getElementsByTagName("HorizontalPixelSize");
|
|
if(lst != null && lst.getLength() == 1) hdpi = (int)(mm2inch/Float.parseFloat(((Element)lst.item(0)).getAttribute("value")));
|
|
|
|
lst = node.getElementsByTagName("VerticalPixelSize");
|
|
if(lst != null && lst.getLength() == 1) vdpi = (int)(mm2inch/Float.parseFloat(((Element)lst.item(0)).getAttribute("value")));
|
|
|
|
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 *= 1d - anchor.getDx1()/1024d;
|
|
} else {
|
|
w -= anchor.getDx1()/(double)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()/(double)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()/(double)EMU_PER_PIXEL;
|
|
}
|
|
|
|
while(col2 < anchor.getCol2()){
|
|
w += sheet.getColumnWidthInPixels(col2++);
|
|
}
|
|
|
|
if (isHSSF) {
|
|
w += sheet.getColumnWidthInPixels(col2) * anchor.getDx2()/1024d;
|
|
} else {
|
|
w += anchor.getDx2()/(double)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()/(double)EMU_PER_PIXEL;
|
|
}
|
|
|
|
while(row2 < anchor.getRow2()){
|
|
h += getRowHeightInPixels(sheet,row2++);
|
|
}
|
|
|
|
if (isHSSF) {
|
|
h += getRowHeightInPixels(sheet,row2) * anchor.getDy2()/256;
|
|
} else {
|
|
h += anchor.getDy2()/(double)EMU_PER_PIXEL;
|
|
}
|
|
|
|
w *= EMU_PER_PIXEL;
|
|
h *= EMU_PER_PIXEL;
|
|
|
|
return new Dimension((int)Math.rint(w), (int)Math.rint(h));
|
|
}
|
|
|
|
|
|
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)EMU_PER_PIXEL;
|
|
}
|
|
}
|