diff --git a/src/scratchpad/src/org/apache/poi/hslf/blip/BitmapPainter.java b/src/scratchpad/src/org/apache/poi/hslf/blip/BitmapPainter.java index ecc368c2b..aae83bf6e 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/blip/BitmapPainter.java +++ b/src/scratchpad/src/org/apache/poi/hslf/blip/BitmapPainter.java @@ -22,6 +22,9 @@ import org.apache.poi.hslf.model.Picture; import org.apache.poi.util.POILogger; import org.apache.poi.util.POILogFactory; + + + /* ==================================================================== Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with @@ -39,7 +42,9 @@ import org.apache.poi.util.POILogFactory; limitations under the License. ==================================================================== */ import javax.imageio.ImageIO; + import java.awt.*; +import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; @@ -54,15 +59,36 @@ public final class BitmapPainter implements ImagePainter { public void paint(Graphics2D graphics, PictureData pict, Picture parent) { BufferedImage img; try { - img = ImageIO.read(new ByteArrayInputStream(pict.getData())); - } - catch (Exception e){ + img = ImageIO.read(new ByteArrayInputStream(pict.getData())); + } catch (Exception e) { logger.log(POILogger.WARN, "ImageIO failed to create image. image.type: " + pict.getType()); return; } + boolean isClipped = true; + Insets clip = parent.getBlipClip(); + if (clip == null) { + isClipped = false; + clip = new Insets(0,0,0,0); + } + + int iw = img.getWidth(); + int ih = img.getHeight(); + Rectangle anchor = parent.getLogicalAnchor2D().getBounds(); - graphics.drawImage(img, anchor.x, anchor.y, anchor.width, anchor.height, null); + + double cw = (100000-clip.left-clip.right) / 100000.0; + double ch = (100000-clip.top-clip.bottom) / 100000.0; + double sx = anchor.getWidth()/(iw*cw); + double sy = anchor.getHeight()/(ih*ch); + double tx = anchor.getX()-(iw*sx*clip.left/100000.0); + double ty = anchor.getY()-(ih*sy*clip.top/100000.0); + AffineTransform at = new AffineTransform(sx, 0, 0, sy, tx, ty) ; + + Shape clipOld = graphics.getClip(); + if (isClipped) graphics.clip(anchor.getBounds2D()); + graphics.drawRenderedImage(img, at); + graphics.setClip(clipOld); } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/Picture.java b/src/scratchpad/src/org/apache/poi/hslf/model/Picture.java index eac973ef6..ea6777dee 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/Picture.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/Picture.java @@ -18,6 +18,7 @@ package org.apache.poi.hslf.model; import java.awt.Graphics2D; +import java.awt.Insets; import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; @@ -264,4 +265,40 @@ public class Picture extends SimpleShape { graphics.setTransform(at); } -} + + /** + * Returns the clipping values as percent ratio relatively to the image size. + * The anchor specified by {@link #getLogicalAnchor2D()} is the displayed size, + * i.e. the size of the already clipped image + * + * @return the clipping as insets converted/scaled to 100000 (=100%) + */ + public Insets getBlipClip() { + EscherOptRecord opt = getEscherOptRecord(); + + double top = getFractProp(opt, EscherProperties.BLIP__CROPFROMTOP); + double bottom = getFractProp(opt, EscherProperties.BLIP__CROPFROMBOTTOM); + double left = getFractProp(opt, EscherProperties.BLIP__CROPFROMLEFT); + double right = getFractProp(opt, EscherProperties.BLIP__CROPFROMRIGHT); + + // if all crop values are zero (the default) then no crop rectangle is set, return null + return (top==0 && bottom==0 && left==0 && right==0) + ? null + : new Insets((int)(top*100000), (int)(left*100000), (int)(bottom*100000), (int)(right*100000)); + } + + /** + * @return the fractional property or 0 if not defined + * + * @see 2.2.1.6 FixedPoint + */ + private static double getFractProp(EscherOptRecord opt, short propertyId) { + EscherSimpleProperty prop = (EscherSimpleProperty)getEscherProperty(opt, propertyId); + if (prop == null) return 0; + int fixedPoint = prop.getPropertyValue(); + int i = (fixedPoint >> 16); + int f = (fixedPoint >> 0) & 0xFFFF; + double fp = i + f/65536.0; + return fp; + } +} \ No newline at end of file diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/model/AllHSLFModelTests.java b/src/scratchpad/testcases/org/apache/poi/hslf/model/AllHSLFModelTests.java index aac72a28e..71cd5f570 100644 --- a/src/scratchpad/testcases/org/apache/poi/hslf/model/AllHSLFModelTests.java +++ b/src/scratchpad/testcases/org/apache/poi/hslf/model/AllHSLFModelTests.java @@ -17,39 +17,35 @@ package org.apache.poi.hslf.model; -import junit.framework.Test; -import junit.framework.TestSuite; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; /** * Collects all tests from the package org.apache.poi.hslf.model. - * - * @author Josh Micich */ +@RunWith(Suite.class) +@Suite.SuiteClasses({ + TestBackground.class, + TestFreeform.class, + TestHeadersFooters.class, + TestHyperlink.class, + TestImagePainter.class, + TestLine.class, + TestMovieShape.class, + TestOleEmbedding.class, + TestPPFont.class, + TestPPGraphics2D.class, + TestPicture.class, + TestSetBoldItalic.class, + TestShapes.class, + TestSheet.class, + TestSlideChangeNotes.class, + TestSlideMaster.class, + TestSlides.class, + TestTable.class, + TestTextRun.class, + TestTextRunReWrite.class, + TestTextShape.class +}) public class AllHSLFModelTests { - - public static Test suite() { - TestSuite result = new TestSuite(AllHSLFModelTests.class.getName()); - result.addTestSuite(TestBackground.class); - result.addTestSuite(TestFreeform.class); - result.addTestSuite(TestHeadersFooters.class); - result.addTestSuite(TestHyperlink.class); - result.addTestSuite(TestImagePainter.class); - result.addTestSuite(TestLine.class); - result.addTestSuite(TestMovieShape.class); - result.addTestSuite(TestOleEmbedding.class); - result.addTestSuite(TestPPFont.class); - result.addTestSuite(TestPPGraphics2D.class); - result.addTestSuite(TestPicture.class); - result.addTestSuite(TestSetBoldItalic.class); - result.addTestSuite(TestShapes.class); - result.addTestSuite(TestSheet.class); - result.addTestSuite(TestSlideChangeNotes.class); - result.addTestSuite(TestSlideMaster.class); - result.addTestSuite(TestSlides.class); - result.addTestSuite(TestTable.class); - result.addTestSuite(TestTextRun.class); - result.addTestSuite(TestTextRunReWrite.class); - result.addTestSuite(TestTextShape.class); - return result; - } } diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/model/TestPicture.java b/src/scratchpad/testcases/org/apache/poi/hslf/model/TestPicture.java index 3444ae5fe..fabf63bee 100644 --- a/src/scratchpad/testcases/org/apache/poi/hslf/model/TestPicture.java +++ b/src/scratchpad/testcases/org/apache/poi/hslf/model/TestPicture.java @@ -17,26 +17,38 @@ package org.apache.poi.hslf.model; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; + +import java.awt.Dimension; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; + import javax.imageio.ImageIO; -import junit.framework.TestCase; - +import org.apache.poi.POIDataSamples; import org.apache.poi.ddf.EscherBSERecord; import org.apache.poi.hslf.HSLFSlideShow; import org.apache.poi.hslf.usermodel.PictureData; import org.apache.poi.hslf.usermodel.SlideShow; -import org.apache.poi.POIDataSamples; +import org.apache.poi.util.JvmBugs; +import org.junit.Ignore; +import org.junit.Test; /** * Test Picture shape. * * @author Yegor Kozlov */ -public final class TestPicture extends TestCase { +public final class TestPicture { private static POIDataSamples _slTests = POIDataSamples.getSlideShowInstance(); /** @@ -44,7 +56,8 @@ public final class TestPicture extends TestCase { * This is important when the same image appears multiple times in a slide show. * */ - public void testMultiplePictures() throws Exception { + @Test + public void multiplePictures() throws Exception { SlideShow ppt = new SlideShow(); Slide s = ppt.createSlide(); @@ -78,7 +91,8 @@ public final class TestPicture extends TestCase { * Picture#getEscherBSERecord threw NullPointerException if EscherContainerRecord.BSTORE_CONTAINER * was not found. The correct behaviour is to return null. */ - public void test46122() { + @Test + public void bug46122() { SlideShow ppt = new SlideShow(); Slide slide = ppt.createSlide(); @@ -92,7 +106,8 @@ public final class TestPicture extends TestCase { pict.draw(graphics); } - public void testMacImages() throws Exception { + @Test + public void macImages() throws Exception { HSLFSlideShow hss = new HSLFSlideShow(_slTests.openResourceAsStream("53446.ppt")); PictureData[] pictures = hss.getPictures(); @@ -128,4 +143,47 @@ public final class TestPicture extends TestCase { } } } + + @Test + @Ignore("Just for visual validation - antialiasing is different on various systems") + public void bug54541() throws Exception { +// InputStream xis = _slTests.openResourceAsStream("54542_cropped_bitmap.pptx"); +// XMLSlideShow xss = new XMLSlideShow(xis); +// xis.close(); +// +// Dimension xpg = xss.getPageSize(); +// for(XSLFSlide slide : xss.getSlides()) { +// BufferedImage img = new BufferedImage(xpg.width, xpg.height, BufferedImage.TYPE_INT_RGB); +// Graphics2D graphics = img.createGraphics(); +// fixFonts(graphics); +// slide.draw(graphics); +// ImageIO.write(img, "PNG", new File("testx.png")); +// } +// +// System.out.println("########################"); + + InputStream is = _slTests.openResourceAsStream("54541_cropped_bitmap.ppt"); + SlideShow ss = new SlideShow(is); + is.close(); + + Dimension pg = ss.getPageSize(); + int i=1; + for(Slide slide : ss.getSlides()) { + BufferedImage img = new BufferedImage(pg.width, pg.height, BufferedImage.TYPE_INT_RGB); + Graphics2D graphics = img.createGraphics(); + fixFonts(graphics); + slide.draw(graphics); + ImageIO.write(img, "PNG", new File("test"+(i++)+".png")); + } + } + + @SuppressWarnings("unchecked") + private void fixFonts(Graphics2D graphics) { + if (!JvmBugs.hasLineBreakMeasurerBug()) return; + Map fontMap = (Map)graphics.getRenderingHint(TextPainter.KEY_FONTMAP); + if (fontMap == null) fontMap = new HashMap(); + fontMap.put("Calibri", "Lucida Sans"); + fontMap.put("Cambria", "Lucida Bright"); + graphics.setRenderingHint(TextPainter.KEY_FONTMAP, fontMap); + } } diff --git a/test-data/slideshow/54541_cropped_bitmap.ppt b/test-data/slideshow/54541_cropped_bitmap.ppt new file mode 100644 index 000000000..3c631a16f Binary files /dev/null and b/test-data/slideshow/54541_cropped_bitmap.ppt differ