]> source.dussan.org Git - poi.git/commitdiff
bug 55075,51233: create drawings with correct height when rows are custom height
authorJaven O'Neal <onealj@apache.org>
Sun, 9 Oct 2016 10:02:36 +0000 (10:02 +0000)
committerJaven O'Neal <onealj@apache.org>
Sun, 9 Oct 2016 10:02:36 +0000 (10:02 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1763950 13f79535-47bb-0310-9956-ffa450edef68

src/ooxml/java/org/apache/poi/util/OOXMLLite.java
src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFDrawing.java [new file with mode: 0644]
src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFPicture.java [new file with mode: 0644]
src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFSheet.java
src/testcases/org/apache/poi/hssf/usermodel/TestHSSFWorkbook.java
src/testcases/org/apache/poi/ss/usermodel/BaseTestWorkbook.java

index afb60f600e469421983e9ba7976d48f09810c52d..5d783e0878ab144bff9a8f9b04a70eef1eb0a8b9 100644 (file)
@@ -98,7 +98,6 @@ public final class OOXMLLite {
                 "BaseTestXCell",
                 "BaseTestXSSFPivotTable",
                 "TestSXSSFWorkbook\\$\\d",
-                "TestSXSSFWorkbook\\$NullOutputStream",
                 "TestUnfixedBugs",
                 "MemoryUsage",
                 "TestDataProvider",
diff --git a/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFDrawing.java b/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFDrawing.java
new file mode 100644 (file)
index 0000000..2bb226c
--- /dev/null
@@ -0,0 +1,62 @@
+/* ====================================================================
+   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.xssf.streaming;
+
+import org.apache.poi.ss.usermodel.Chart;
+import org.apache.poi.ss.usermodel.ClientAnchor;
+import org.apache.poi.ss.usermodel.Comment;
+import org.apache.poi.ss.usermodel.Drawing;
+import org.apache.poi.xssf.usermodel.XSSFDrawing;
+import org.apache.poi.xssf.usermodel.XSSFPicture;
+
+/**
+ * Streaming version of Drawing.
+ * Delegates most tasks to the non-streaming XSSF code.
+ * TODO: Potentially, Comment and Chart need a similar streaming wrapper like Picture.
+ */
+public class SXSSFDrawing implements Drawing {
+    private final SXSSFWorkbook _wb;
+    private final XSSFDrawing _drawing;
+
+    public SXSSFDrawing(SXSSFWorkbook workbook, XSSFDrawing drawing) {
+        this._wb = workbook;
+        this._drawing = drawing;
+    }
+
+    @Override
+    public SXSSFPicture createPicture(ClientAnchor anchor, int pictureIndex) {
+        XSSFPicture pict = _drawing.createPicture(anchor, pictureIndex);
+        return new SXSSFPicture(_wb, pict);
+    }
+
+    @Override
+    public Comment createCellComment(ClientAnchor anchor) {
+        return _drawing.createCellComment(anchor);
+    }
+
+    @Override
+    public Chart createChart(ClientAnchor anchor) {
+        return _drawing.createChart(anchor);
+    }
+
+    @Override
+    public ClientAnchor createAnchor(int dx1, int dy1, int dx2, int dy2, int col1, int row1, int col2, int row2) {
+        return _drawing.createAnchor(dx1, dy1, dx2, dy2, col1, row1, col2, row2);
+    }
+}
+
diff --git a/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFPicture.java b/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFPicture.java
new file mode 100644 (file)
index 0000000..eaabbb1
--- /dev/null
@@ -0,0 +1,274 @@
+/* ====================================================================
+   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.xssf.streaming;
+
+import org.apache.poi.openxml4j.opc.PackagePart;
+import org.apache.poi.ss.usermodel.ClientAnchor;
+import org.apache.poi.ss.usermodel.Picture;
+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.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.xssf.usermodel.*;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTShapeProperties;
+import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTPicture;
+import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCol;
+
+import java.awt.*;
+import java.io.IOException;
+
+/**
+ * Streaming version of Picture.
+ * Most of the code is a copy of the non-streaming XSSFPicture code.
+ * This is necessary as a private method getRowHeightInPixels of that class needs to be changed, which is called by a method call chain nested several levels.
+ *
+ * The main change is to access the rows in the SXSSF sheet, not the always empty rows in the XSSF sheet when checking the row heights.
+ */
+public final class SXSSFPicture implements Picture {
+    private static final POILogger logger = POILogFactory.getLogger(SXSSFPicture.class);
+    /**
+     * Column width measured as the number of characters of the maximum digit width of the
+     * numbers 0, 1, 2, ..., 9 as rendered in the normal style's font. There are 4 pixels of margin
+     * padding (two on each side), plus 1 pixel padding for the gridlines.
+     *
+     * 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 final SXSSFWorkbook _wb;
+    private final XSSFPicture _picture;
+
+    SXSSFPicture(SXSSFWorkbook _wb, XSSFPicture _picture) {
+        this._wb = _wb;
+        this._picture = _picture;
+    }
+
+    /**
+     * Return the underlying CTPicture bean that holds all properties for this picture
+     *
+     * @return the underlying CTPicture bean
+     */
+    @Internal
+    public CTPicture getCTPicture(){
+        return _picture.getCTPicture();
+    }
+
+    /**
+     * Reset the image to the original size.
+     *
+     * <p>
+     * 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.
+     * </p>
+     */
+    @Override
+    public void resize(){
+        resize(1.0);
+    }
+
+    /**
+     * Reset the image to the original size.
+     * <p>
+     * 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.
+     * </p>
+     *
+     * @param scale the amount by which image dimensions are multiplied relative to the original size.
+     * <code>resize(1.0)</code> sets the original size, <code>resize(0.5)</code> resize to 50% of the original,
+     * <code>resize(2.0)</code> resizes to 200% of the original.
+     */
+    @Override
+    public void resize(double scale){
+        XSSFClientAnchor anchor = getAnchor();
+
+        XSSFClientAnchor pref = getPreferredSize(scale);
+
+        int row2 = anchor.getRow1() + (pref.getRow2() - pref.getRow1());
+        int col2 = anchor.getCol1() + (pref.getCol2() - pref.getCol1());
+
+        anchor.setCol2(col2);
+        anchor.setDx1(0);
+        anchor.setDx2(pref.getDx2());
+
+        anchor.setRow2(row2);
+        anchor.setDy1(0);
+        anchor.setDy2(pref.getDy2());
+    }
+
+    /**
+     * Calculate the preferred size for this picture.
+     *
+     * @return XSSFClientAnchor with the preferred size for this image
+     */
+    @Override
+    public XSSFClientAnchor getPreferredSize(){
+        return getPreferredSize(1.0);
+    }
+
+    /**
+     * Calculate the preferred size for this picture.
+     *
+     * @param scale the amount by which image dimensions are multiplied relative to the original size.
+     * @return XSSFClientAnchor with the preferred size for this image
+     */
+    public XSSFClientAnchor getPreferredSize(double scale){
+        XSSFClientAnchor anchor = 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)(XSSFShape.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)(XSSFShape.EMU_PER_PIXEL * (ch - delta));
+        }
+        anchor.setRow2(row2);
+        anchor.setDy2(dy2);
+
+        CTPositiveSize2D size2d =  getCTPicture().getSpPr().getXfrm().getExt();
+        size2d.setCx((long)(scaledWidth * XSSFShape.EMU_PER_PIXEL));
+        size2d.setCy((long)(scaledHeight * XSSFShape.EMU_PER_PIXEL));
+
+        return anchor;
+    }
+
+    private float getColumnWidthInPixels(int columnIndex){
+        XSSFSheet sheet = 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) {
+        // THE FOLLOWING THREE LINES ARE THE MAIN CHANGE compared to the non-streaming version: use the SXSSF sheet,
+               // not the XSSF sheet (which never contais rows when using SXSSF)
+        XSSFSheet xssfSheet = getParent();
+        SXSSFSheet sheet = _wb.getSXSSFSheet(xssfSheet);
+        Row row = sheet.getRow(rowIndex);
+        float height = row != null ?  row.getHeightInPoints() : sheet.getDefaultRowHeightInPoints();
+        return height * XSSFShape.PIXEL_DPI / XSSFShape.POINT_DPI;
+    }
+    /**
+     * Return the dimension of this image
+     *
+     * @param part the package part holding raw picture data
+     * @param type type of the picture: {@link Workbook#PICTURE_TYPE_JPEG},
+     * {@link Workbook#PICTURE_TYPE_PNG} or {@link Workbook#PICTURE_TYPE_DIB}
+     *
+     * @return image dimension in pixels
+     */
+    protected static Dimension getImageDimension(PackagePart part, int type){
+        try {
+            return ImageUtils.getImageDimension(part.getInputStream(), type);
+        } catch (IOException e){
+            //return a "singulariry" if ImageIO failed to read the image
+            logger.log(POILogger.WARN, e);
+            return new Dimension();
+        }
+    }
+
+    /**
+     * Return picture data for this shape
+     *
+     * @return picture data for this shape
+     */
+    @Override
+    public XSSFPictureData getPictureData() {
+        return _picture.getPictureData();
+    }
+
+    protected CTShapeProperties getShapeProperties(){
+        return getCTPicture().getSpPr();
+    }
+
+    private XSSFSheet getParent() {
+        return (XSSFSheet)_picture.getDrawing().getParent();
+    }
+
+    private XSSFClientAnchor getAnchor() {
+        return (XSSFClientAnchor)_picture.getAnchor();
+    }
+
+    @Override
+    public void resize(double scaleX, double scaleY) {
+        _picture.resize(scaleX, scaleY);
+    }
+
+    @Override
+    public XSSFClientAnchor getPreferredSize(double scaleX, double scaleY) {
+        return _picture.getPreferredSize(scaleX, scaleY);
+    }
+
+    @Override
+    public Dimension getImageDimension() {
+        return _picture.getImageDimension();
+    }
+
+    @Override
+    public XSSFClientAnchor getClientAnchor() {
+        XSSFAnchor a = getAnchor();
+        return (a instanceof XSSFClientAnchor) ? (XSSFClientAnchor)a : null;
+    }
+    
+    public XSSFDrawing getDrawing() {
+        return _picture.getDrawing();
+    }
+
+    @Override
+    public XSSFSheet getSheet() {
+        return _picture.getSheet();
+    }
+}
index e217dd2e6c7032e4f1536dee1a978f93a80a7ad4..2c9fb6e372a237f7bf51fcae556a727bad10bfc4 100644 (file)
@@ -1704,7 +1704,7 @@ public class SXSSFSheet implements Sheet
     @Override
     public Drawing createDrawingPatriarch()
     {
-        return _sh.createDrawingPatriarch();
+        return new SXSSFDrawing((SXSSFWorkbook)getWorkbook(), _sh.createDrawingPatriarch());
     }
 
 
index 07fd7910f826c5bb3276c50d2e8c8631a74bbb0d..55e1d77db68d935a2947f52057b672fa21bc406a 100644 (file)
@@ -73,6 +73,7 @@ import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.LittleEndian;
 import org.apache.poi.util.RecordFormatException;
 import org.apache.poi.util.TempFile;
+import org.junit.Ignore;
 import org.junit.Test;
 
 /**
@@ -1315,4 +1316,12 @@ public final class TestHSSFWorkbook extends BaseTestWorkbook {
         assertEquals(3, wb.getNumberOfSheets());
         wb.close();
     }
+    
+    @Ignore
+    @Test
+    @Override
+    public void createDrawing() throws Exception {
+        super.createDrawing();
+        // the dimensions for this image are different than for XSSF and SXSSF
+    }
 }
index 52f542281ab95289d85fb78e42b36d265d39ab0a..2c7c15add6c01b03423700c90d8b68cd0a425765 100644 (file)
@@ -26,15 +26,19 @@ import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.util.ConcurrentModificationException;
 import java.util.Iterator;
 
+import org.apache.poi.POIDataSamples;
 import org.apache.poi.hssf.HSSFTestDataSamples;
 import org.apache.poi.ss.ITestDataProvider;
+import org.apache.poi.ss.usermodel.ClientAnchor.AnchorType;
 import org.apache.poi.ss.util.CellRangeAddress;
 import org.apache.poi.util.NullOutputStream;
+import org.apache.poi.util.TempFile;
 import org.junit.Test;
 
 public abstract class BaseTestWorkbook {
@@ -895,5 +899,59 @@ public abstract class BaseTestWorkbook {
 
         wb.close();
     }
+    
+    // bug 51233 and 55075: correctly size image if added to a row with a custom height
+    @Test
+    public void createDrawing() throws Exception {
+        Workbook wb = _testDataProvider.createWorkbook();
+        Sheet sheet = wb.createSheet("Main Sheet");
+        Row row0 = sheet.createRow(0);
+        Row row1 = sheet.createRow(1);
+        Cell cell = row1.createCell(0);
+        row0.createCell(1);
+        row1.createCell(0);
+        row1.createCell(1);
+
+        byte[] pictureData = _testDataProvider.getTestDataFileContent("logoKarmokar4.png");
+
+        int handle = wb.addPicture(pictureData, Workbook.PICTURE_TYPE_PNG);
+        Drawing drawing = sheet.createDrawingPatriarch();
+        CreationHelper helper = wb.getCreationHelper();
+        ClientAnchor anchor = helper.createClientAnchor();
+        anchor.setAnchorType(AnchorType.DONT_MOVE_AND_RESIZE);
+        anchor.setCol1(0);
+        anchor.setRow1(0);
+        Picture picture = drawing.createPicture(anchor, handle);
+
+        row0.setHeightInPoints(144);
+        // set a column width so that XSSF and SXSSF have the same width (default widths may be different otherwise)
+        sheet.setColumnWidth(0, 100*256);
+        picture.resize();
+        
+        // The actual dimensions don't matter as much as having XSSF and SXSSF produce the same size drawings
+        
+        // Check drawing height
+        assertEquals(0, anchor.getRow1());
+        assertEquals(0, anchor.getRow2());
+        assertEquals(0, anchor.getDy1());
+        assertEquals(1609725, anchor.getDy2()); //HSSF: 225
+        
+        // Check drawing width
+        assertEquals(0, anchor.getCol1());
+        assertEquals(0, anchor.getCol2());
+        assertEquals(0, anchor.getDx1());
+        assertEquals(1114425, anchor.getDx2()); //HSSF: 171
+        
+        final boolean writeOut = false;
+        if (writeOut) {
+            String ext = "." + _testDataProvider.getStandardFileNameExtension();
+            String prefix = wb.getClass().getName() + "-createDrawing";
+            File f = TempFile.createTempFile(prefix, ext);
+            FileOutputStream out = new FileOutputStream(f);
+            wb.write(out);
+            out.close();
+        }
+        wb.close();
+    }
 
 }