diff --git a/src/documentation/content/xdocs/hssf/quick-guide.xml b/src/documentation/content/xdocs/hssf/quick-guide.xml index f5e396036..6c86ed2df 100644 --- a/src/documentation/content/xdocs/hssf/quick-guide.xml +++ b/src/documentation/content/xdocs/hssf/quick-guide.xml @@ -24,6 +24,7 @@ Busy Developers' Guide to HSSF Features + @@ -988,7 +989,26 @@ anchor.setAnchorType( 2 ); patriarch.createPicture(anchor, loadPicture( "src/resources/logos/logoKarmokar4.png", wb )); -

Reading images from a workbook:

+

Creating an image and setting its anchor to the actual width and height:

+ + HSSFPatriarch patriarch = sheet5.createDrawingPatriarch(); + + HSSFPicture picture = patriarch.createPicture(new HSSFClientAnchor(), loadPicture( "src/resources/logos/logoKarmokar4.png", wb )); + picture.resize(); + +

or

+ + HSSFPatriarch patriarch = sheet5.createDrawingPatriarch(); + + HSSFPicture picture = patriarch.createPicture(new HSSFClientAnchor(), loadPicture( "src/resources/logos/logoKarmokar4.png", wb )); + HSSFClientAnchor prefferedSize = picture.getPrefferedSize(); + picture.setAnchor(prefferedSize); + + + HSSFPicture.resize() works only for JPEG and PNG. Other formats are not yet supported. + + +

Reading images from a workbook:

HSSFWorkbook wb; diff --git a/src/examples/src/org/apache/poi/hssf/usermodel/examples/OfficeDrawing.java b/src/examples/src/org/apache/poi/hssf/usermodel/examples/OfficeDrawing.java index ebee3850a..21319f873 100644 --- a/src/examples/src/org/apache/poi/hssf/usermodel/examples/OfficeDrawing.java +++ b/src/examples/src/org/apache/poi/hssf/usermodel/examples/OfficeDrawing.java @@ -166,6 +166,8 @@ public class OfficeDrawing anchor = new HSSFClientAnchor(0,0,1023,255,(short)6,2,(short)8,7); anchor.setAnchorType( 2 ); HSSFPicture picture = patriarch.createPicture(anchor, loadPicture( "src/resources/logos/logoKarmokar4s.png", wb )); + //Reset the image to the original size. + picture.resize(); picture.setLineStyle( picture.LINESTYLE_DASHDOTGEL ); } diff --git a/src/java/org/apache/poi/hssf/model/Workbook.java b/src/java/org/apache/poi/hssf/model/Workbook.java index 28970820d..972aa0901 100644 --- a/src/java/org/apache/poi/hssf/model/Workbook.java +++ b/src/java/org/apache/poi/hssf/model/Workbook.java @@ -2218,7 +2218,12 @@ public class Workbook implements Model public WindowOneRecord getWindowOne() { return windowOne; } - + + public EscherBSERecord getBSERecord(int pictureIndex) + { + return (EscherBSERecord)escherBSERecords.get(pictureIndex-1); + } + public int addBSERecord(EscherBSERecord e) { createDrawingGroup(); diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFPatriarch.java b/src/java/org/apache/poi/hssf/usermodel/HSSFPatriarch.java index 4dfa70075..e383ed558 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFPatriarch.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFPatriarch.java @@ -90,6 +90,7 @@ public class HSSFPatriarch HSSFPicture shape = new HSSFPicture(null, anchor); shape.setPictureIndex( pictureIndex ); shape.anchor = anchor; + shape.patriarch = this; shapes.add(shape); return shape; } diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFPicture.java b/src/java/org/apache/poi/hssf/usermodel/HSSFPicture.java index 9425d54d7..79649b06a 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFPicture.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFPicture.java @@ -16,23 +16,40 @@ */ package org.apache.poi.hssf.usermodel; +import org.apache.poi.ddf.EscherBSERecord; +import org.apache.poi.util.POILogFactory; +import org.apache.poi.util.POILogger; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + +import javax.imageio.ImageIO; +import javax.imageio.ImageReader; +import javax.imageio.stream.ImageInputStream; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.Iterator; + /** * Represents a escher picture. Eg. A GIF, JPEG etc... * * @author Glen Stampoultzis - * @version $Id$ + * @author Yegor Kozlov (yegor at apache.org) */ public class HSSFPicture extends HSSFSimpleShape { - public static final int PICTURE_TYPE_EMF = 0; // Windows Enhanced Metafile - public static final int PICTURE_TYPE_WMF = 1; // Windows Metafile - public static final int PICTURE_TYPE_PICT = 2; // Macintosh PICT - public static final int PICTURE_TYPE_JPEG = 3; // JFIF - public static final int PICTURE_TYPE_PNG = 4; // PNG - public static final int PICTURE_TYPE_DIB = 5; // Windows DIB + public static final int PICTURE_TYPE_EMF = HSSFWorkbook.PICTURE_TYPE_EMF; // Windows Enhanced Metafile + public static final int PICTURE_TYPE_WMF = HSSFWorkbook.PICTURE_TYPE_WMF; // Windows Metafile + public static final int PICTURE_TYPE_PICT = HSSFWorkbook.PICTURE_TYPE_PICT; // Macintosh PICT + public static final int PICTURE_TYPE_JPEG = HSSFWorkbook.PICTURE_TYPE_JPEG; // JFIF + 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 int pictureIndex; + HSSFPatriarch patriarch; + + private static final POILogger log = POILogFactory.getLogger(HSSFPicture.class); /** * Constructs a picture object. @@ -52,4 +69,111 @@ public class HSSFPicture { this.pictureIndex = pictureIndex; } + + /** + * Reset the image to the original size. + * + * @since POI 3.0.2 + */ + public void resize(){ + HSSFClientAnchor anchor = (HSSFClientAnchor)getAnchor(); + anchor.setAnchorType(2); + + HSSFClientAnchor pref = getPrefferedSize(); + + 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()); + } + + /** + * Calculate the preffered size for this picture. + * + * @return HSSFClientAnchor with the preffered size for this image + * @since POI 3.0.2 + */ + public HSSFClientAnchor getPrefferedSize(){ + HSSFClientAnchor anchor = new HSSFClientAnchor(); + + EscherBSERecord bse = (EscherBSERecord)patriarch.sheet.book.getBSERecord(pictureIndex); + byte[] data = bse.getBlipRecord().getPicturedata(); + int type = bse.getBlipTypeWin32(); + switch (type){ + //we can calculate the preffered size only for JPEG and PNG + //other formats like WMF, EMF and PICT are not supported in Java + case HSSFWorkbook.PICTURE_TYPE_JPEG: + case HSSFWorkbook.PICTURE_TYPE_PNG: + BufferedImage img = null; + ImageReader r = null; + try { + //read the image using javax.imageio.* + ImageInputStream iis = ImageIO.createImageInputStream( new ByteArrayInputStream(data) ); + Iterator i = ImageIO.getImageReaders( iis ); + r = (ImageReader) i.next(); + r.setInput( iis ); + img = r.read(0); + + int[] dpi = getResolution(r); + int imgWidth = img.getWidth()*96/dpi[0]; + int imgHeight = img.getHeight()*96/dpi[1]; + + //Excel measures cells in units of 1/256th of a character width. + //The cell width calculated based on this info is always "off". + //A better approach seems to be to use empirically obtained cell width and row height + int cellwidth = 64; + int rowheight = 17; + + int col2 = imgWidth/cellwidth; + int row2 = imgHeight/rowheight; + + int dx2 = (int)((float)(imgWidth % cellwidth)/cellwidth * 1024); + int dy2 = (int)((float)(imgHeight % rowheight)/rowheight * 256); + + anchor.setCol2((short)col2); + anchor.setDx2(dx2); + + anchor.setRow2(row2); + anchor.setDy2(dy2); + + } catch (IOException e){ + //silently return if ImageIO failed to read the image + log.log(POILogger.WARN, e); + img = null; + } + + break; + } + return anchor; + } + + /** + * The metadata of PNG and JPEG can contain the width of a pixel in millimeters. + * Return the the "effective" dpi calculated as 25.4/HorizontalPixelSize + * and 25.4/VerticalPixelSize. Where 25.4 is the number of mm in inch. + * + * @return array of two elements: {horisontalPdi, verticalDpi}. + * {96, 96} is the default. + */ + protected 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}; + } + } diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java index 9d7c287cb..069e4efd8 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java @@ -83,8 +83,8 @@ public class HSSFSheet private Sheet sheet; private TreeMap rows; - private Workbook book; - private HSSFWorkbook workbook; + protected Workbook book; + protected HSSFWorkbook workbook; private int firstrow; private int lastrow; private static POILogger log = POILogFactory.getLogger(HSSFSheet.class); diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFPicture.java b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFPicture.java new file mode 100644 index 000000000..45f92647b --- /dev/null +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFPicture.java @@ -0,0 +1,79 @@ +/* +* 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.hssf.usermodel; + +import junit.framework.TestCase; + +import java.io.IOException; +import java.io.FileInputStream; +import java.io.ByteArrayOutputStream; + +/** + * Test HSSFPicture. + * + * @author Yegor Kozlov (yegor at apache.org) + */ +public class TestHSSFPicture extends TestCase{ + + public void testResize() throws Exception { + HSSFWorkbook wb = new HSSFWorkbook(); + HSSFSheet sh1 = wb.createSheet(); + HSSFPatriarch p1 = sh1.createDrawingPatriarch(); + + int idx1 = loadPicture( "src/resources/logos/logoKarmokar4.png", wb); + HSSFPicture picture1 = p1.createPicture(new HSSFClientAnchor(), idx1); + HSSFClientAnchor anchor1 = picture1.getPrefferedSize(); + + //assert against what would BiffViewer print if we insert the image in xls and dump the file + assertEquals(0, anchor1.getCol1()); + assertEquals(0, anchor1.getRow1()); + assertEquals(1, anchor1.getCol2()); + assertEquals(9, anchor1.getRow2()); + assertEquals(0, anchor1.getDx1()); + assertEquals(0, anchor1.getDy1()); + assertEquals(848, anchor1.getDx2()); + assertEquals(240, anchor1.getDy2()); + } + + /** + * Copied from org.apache.poi.hssf.usermodel.examples.OfficeDrawing + */ + private static int loadPicture( String path, HSSFWorkbook wb ) throws IOException + { + int pictureIndex; + FileInputStream fis = null; + ByteArrayOutputStream bos = null; + try + { + fis = new FileInputStream( path); + bos = new ByteArrayOutputStream( ); + int c; + while ( (c = fis.read()) != -1) + bos.write( c ); + pictureIndex = wb.addPicture( bos.toByteArray(), HSSFWorkbook.PICTURE_TYPE_PNG ); + } + finally + { + if (fis != null) + fis.close(); + if (bos != null) + bos.close(); + } + return pictureIndex; + } + +}