You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@poi.apache.org by fa...@apache.org on 2022/09/27 12:09:11 UTC

svn commit: r1904301 - in /poi/trunk: poi-ooxml/src/test/java/org/apache/poi/xslf/TestXSLFBugs.java poi/src/main/java/org/apache/poi/sl/draw/DrawPaint.java test-data/slideshow/minimal-gradient-fill-issue.pptx

Author: fanningpj
Date: Tue Sep 27 12:09:11 2022
New Revision: 1904301

URL: http://svn.apache.org/viewvc?rev=1904301&view=rev
Log:
[bug-66278] Bug with multiple gradient stops at the exact same location causing a rendering failure (AWT -> not enough colors in gradient). Thanks to Gareth Floodgate. This closes #385

Added:
    poi/trunk/test-data/slideshow/minimal-gradient-fill-issue.pptx   (with props)
Modified:
    poi/trunk/poi-ooxml/src/test/java/org/apache/poi/xslf/TestXSLFBugs.java
    poi/trunk/poi/src/main/java/org/apache/poi/sl/draw/DrawPaint.java

Modified: poi/trunk/poi-ooxml/src/test/java/org/apache/poi/xslf/TestXSLFBugs.java
URL: http://svn.apache.org/viewvc/poi/trunk/poi-ooxml/src/test/java/org/apache/poi/xslf/TestXSLFBugs.java?rev=1904301&r1=1904300&r2=1904301&view=diff
==============================================================================
--- poi/trunk/poi-ooxml/src/test/java/org/apache/poi/xslf/TestXSLFBugs.java (original)
+++ poi/trunk/poi-ooxml/src/test/java/org/apache/poi/xslf/TestXSLFBugs.java Tue Sep 27 12:09:11 2022
@@ -25,6 +25,11 @@ import static org.junit.jupiter.api.Asse
 import static org.junit.jupiter.api.Assumptions.assumeFalse;
 
 import java.awt.Color;
+import java.awt.LinearGradientPaint;
+import java.awt.MultipleGradientPaint;
+import java.awt.Paint;
+import java.awt.RadialGradientPaint;
+import java.awt.geom.Point2D;
 import java.awt.geom.Rectangle2D;
 import java.io.File;
 import java.io.FileInputStream;
@@ -247,9 +252,9 @@ class TestXSLFBugs {
     @Test
     void bug62587() throws IOException {
         Object[][] pics = {
-            {"santa.wmf", PictureType.WMF, XSLFRelation.IMAGE_WMF},
-            {"tomcat.png", PictureType.PNG, XSLFRelation.IMAGE_PNG},
-            {"clock.jpg", PictureType.JPEG, XSLFRelation.IMAGE_JPEG}
+                {"santa.wmf", PictureType.WMF, XSLFRelation.IMAGE_WMF},
+                {"tomcat.png", PictureType.PNG, XSLFRelation.IMAGE_PNG},
+                {"clock.jpg", PictureType.JPEG, XSLFRelation.IMAGE_JPEG}
         };
 
         try (XMLSlideShow ppt1 = new XMLSlideShow()) {
@@ -298,12 +303,12 @@ class TestXSLFBugs {
             XSLFSlide slide1 = ppt1.getSlides().get(0);
 
             Optional<XSLFShape> shapeToDelete1 =
-                slide1.getShapes().stream().filter(s -> s instanceof XSLFPictureShape).findFirst();
+                    slide1.getShapes().stream().filter(s -> s instanceof XSLFPictureShape).findFirst();
 
             assertTrue(shapeToDelete1.isPresent());
             slide1.removeShape(shapeToDelete1.get());
             assertTrue(slide1.getRelationParts().stream()
-                .allMatch(rp -> "rId1,rId3".contains(rp.getRelationship().getId())));
+                    .allMatch(rp -> "rId1,rId3".contains(rp.getRelationship().getId())));
 
             assertNotNull(ppt1.getPackage().getPart(ppn));
         }
@@ -311,20 +316,20 @@ class TestXSLFBugs {
         try (XMLSlideShow ppt2 = openSampleDocument("bug60499.pptx")) {
             XSLFSlide slide2 = ppt2.getSlides().get(0);
             Optional<XSLFShape> shapeToDelete2 =
-                slide2.getShapes().stream().filter(s -> s instanceof XSLFPictureShape).skip(1).findFirst();
+                    slide2.getShapes().stream().filter(s -> s instanceof XSLFPictureShape).skip(1).findFirst();
             assertTrue(shapeToDelete2.isPresent());
             slide2.removeShape(shapeToDelete2.get());
             assertTrue(slide2.getRelationParts().stream()
-                .allMatch(rp -> "rId1,rId2".contains(rp.getRelationship().getId())));
+                    .allMatch(rp -> "rId1,rId2".contains(rp.getRelationship().getId())));
             assertNotNull(ppt2.getPackage().getPart(ppn));
         }
 
         try (XMLSlideShow ppt3 = openSampleDocument("bug60499.pptx")) {
             XSLFSlide slide3 = ppt3.getSlides().get(0);
             slide3.getShapes().stream()
-                .filter(s -> s instanceof XSLFPictureShape)
-                .collect(Collectors.toList())
-                .forEach(slide3::removeShape);
+                    .filter(s -> s instanceof XSLFPictureShape)
+                    .collect(Collectors.toList())
+                    .forEach(slide3::removeShape);
             assertNull(ppt3.getPackage().getPart(ppn));
         }
     }
@@ -754,8 +759,8 @@ class TestXSLFBugs {
     void bug59273() throws IOException {
         try (XMLSlideShow ppt = openSampleDocument("bug59273.potx")) {
             ppt.getPackage().replaceContentType(
-                XSLFRelation.PRESENTATIONML_TEMPLATE.getContentType(),
-                XSLFRelation.MAIN.getContentType()
+                    XSLFRelation.PRESENTATIONML_TEMPLATE.getContentType(),
+                    XSLFRelation.MAIN.getContentType()
             );
 
             try (XMLSlideShow rwPptx = writeOutAndReadBack(ppt)) {
@@ -879,7 +884,7 @@ class TestXSLFBugs {
     @Test
     void bug62051() throws IOException {
         final Function<List<XSLFShape>, int[]> ids = (shapes) ->
-            shapes.stream().mapToInt(Shape::getShapeId).toArray();
+                shapes.stream().mapToInt(Shape::getShapeId).toArray();
 
         try (final XMLSlideShow ppt = new XMLSlideShow()) {
             final XSLFSlide slide = ppt.createSlide();
@@ -997,11 +1002,11 @@ class TestXSLFBugs {
         assumeFalse(xslfOnly);
 
         final double[][] clips = {
-            { 50.999999999999986, 51.0, 298.0, 98.0 },
-            { 51.00000000000003, 51.0, 298.0, 98.0 },
-            { 51.0, 51.0, 298.0, 98.0 },
-            { 250.02000796164992, 93.10370370370373, 78.61839367617523, 55.89629629629627 },
-            { 79.58198774450841, 53.20887318960063, 109.13118501448272, 9.40935058567127 },
+                { 50.999999999999986, 51.0, 298.0, 98.0 },
+                { 51.00000000000003, 51.0, 298.0, 98.0 },
+                { 51.0, 51.0, 298.0, 98.0 },
+                { 250.02000796164992, 93.10370370370373, 78.61839367617523, 55.89629629629627 },
+                { 79.58198774450841, 53.20887318960063, 109.13118501448272, 9.40935058567127 },
         };
 
         DummyGraphics2d dgfx = new DummyGraphics2d(new NullPrintStream()) {
@@ -1031,13 +1036,13 @@ class TestXSLFBugs {
     public void bug65228() throws IOException {
         try (XMLSlideShow ppt = openSampleDocument("bug65228.pptx")) {
             TextRun.TextCap act = ppt.getSlides().stream()
-                .flatMap(s -> s.getShapes().stream())
-                .filter(s -> "März 2021\u2026".equals(s.getShapeName()))
-                .map(XSLFTextShape.class::cast)
-                .flatMap(s -> s.getTextParagraphs().stream())
-                .flatMap(s -> s.getTextRuns().stream())
-                .map(XSLFTextRun::getTextCap)
-                .findFirst().orElse(null);
+                    .flatMap(s -> s.getShapes().stream())
+                    .filter(s -> "März 2021\u2026".equals(s.getShapeName()))
+                    .map(XSLFTextShape.class::cast)
+                    .flatMap(s -> s.getTextParagraphs().stream())
+                    .flatMap(s -> s.getTextRuns().stream())
+                    .map(XSLFTextRun::getTextCap)
+                    .findFirst().orElse(null);
             assertEquals(TextRun.TextCap.ALL, act);
         }
     }
@@ -1120,4 +1125,97 @@ class TestXSLFBugs {
             }
         }
     }
+
+    @Test
+    void identicalGradientStopsBug() throws IOException {
+
+        final ArrayList<LinearGradientPaint> linearGradients = new ArrayList<>();
+        final ArrayList<RadialGradientPaint> radialGradients = new ArrayList<>();
+        final DummyGraphics2d dgfx = new DummyGraphics2d(new NullPrintStream())
+        {
+            public void setPaint(final Paint paint) {
+                if (paint instanceof LinearGradientPaint) {
+                    linearGradients.add((LinearGradientPaint) paint);
+                }
+                if (paint instanceof RadialGradientPaint) {
+                    radialGradients.add((RadialGradientPaint) paint);
+                }
+            }
+        };
+
+        final List<LinearGradientPaint> expectedLinearGradients = Arrays.asList(
+                new LinearGradientPaint(new Point2D.Double(30.731732283464567, 138.7317322834646),
+                        new Point2D.Double(122.91549846753813, 46.54796609939099),
+                        new float[] { 0.0f, 0.99999994f, 1.0f },
+                        new Color[] { new Color(81, 124, 252, 255),
+                                new Color(81, 124, 252, 255),
+                                new Color(17,21,27, 204) }),
+                new LinearGradientPaint(new Point2D.Double(174.7317322834646, 138.73173228346457),
+                        new Point2D.Double(266.9154984675381, 46.547966099391004),
+                        new float[] { 0.0f, 0.00000005f, 1.0f },
+                        new Color[] { new Color(17,21,27, 204),
+                                new Color(81, 124, 252, 255),
+                                new Color(81, 124, 252, 255) }),
+                new LinearGradientPaint(new Point2D.Double(318.73173228346457, 138.73173228346462),
+                        new Point2D.Double(410.9154984675381, 46.547966099391004),
+                        new float[] { 0.0f, 0.5f, 0.50000006f, 1.0f },
+                        new Color[] { new Color(17,21,27, 204),
+                                new Color(17,21,27, 204),
+                                new Color(81, 124, 252, 255),
+                                new Color(81, 124, 252, 255) })
+        );
+
+        final List<RadialGradientPaint> expectedRadialGradients = Arrays.asList(
+                new RadialGradientPaint(new Point2D.Double(30.731732283464567, 138.7317322834646),
+                        108.0f, new Point2D.Double(122.91549846753813, 46.54796609939099),
+                        new float[] { 0.0f, 0.5f, 0.50000006f, 1.0f },
+                        new Color[] { new Color(17,21,27, 204),
+                                new Color(17,21,27, 204),
+                                new Color(81, 124, 252, 255),
+                                new Color(81, 124, 252, 255)  },
+                        MultipleGradientPaint.CycleMethod.NO_CYCLE),
+                new RadialGradientPaint(new Point2D.Double(228.73173228346457, 226.9755905511811),
+                        108.0f, new Point2D.Double(282.73173228346457, 280.9755905511811),
+                        new float[] { 0.0f, 0.00000005f, 1.0f },
+                        new Color[] { new Color(17,21,27, 204),
+                                new Color(81, 124, 252, 255),
+                                new Color(81, 124, 252, 255)  },
+                        MultipleGradientPaint.CycleMethod.NO_CYCLE),
+                new RadialGradientPaint(new Point2D.Double(84.73173228346457, 226.9755905511811),
+                        108.0f, new Point2D.Double(138.73173228346457, 280.9755905511811),
+                        new float[] { 0.0f, 0.99999994f, 1.0f },
+                        new Color[] { new Color(81, 124, 252, 255),
+                                new Color(81, 124, 252, 255),
+                                new Color(17,21,27, 204) },
+                        MultipleGradientPaint.CycleMethod.NO_CYCLE)
+        );
+
+        try (XMLSlideShow slideShowModel = openSampleDocument("minimal-gradient-fill-issue.pptx")) {
+            // Render the first (and only) slide.
+            slideShowModel.getSlides().get(0).draw(dgfx);
+
+            // Test that the linear gradients have the expected data (stops modified)
+            assertEquals(3, linearGradients.size());
+            for (int i = 0 ; i < expectedLinearGradients.size() ; i++) {
+                final LinearGradientPaint expected = expectedLinearGradients.get(i);
+                final LinearGradientPaint actual = linearGradients.get(i);
+                assertEquals(expected.getStartPoint(), expected.getStartPoint());
+                assertEquals(expected.getEndPoint(), expected.getEndPoint());
+                assertArrayEquals(expected.getFractions(), actual.getFractions());
+                assertArrayEquals(expected.getColors(), actual.getColors());
+            }
+
+            // Test that the radial gradients have the expected data (stops modified)
+            assertEquals(3, radialGradients.size());
+            for (int i = 0 ; i < expectedRadialGradients.size() ; i++) {
+                final RadialGradientPaint expected = expectedRadialGradients.get(i);
+                final RadialGradientPaint actual = radialGradients.get(i);
+                assertEquals(expected.getCenterPoint(), expected.getCenterPoint());
+                assertEquals(expected.getFocusPoint(), expected.getFocusPoint());
+                assertArrayEquals(expected.getFractions(), actual.getFractions());
+                assertArrayEquals(expected.getColors(), actual.getColors());
+            }
+        }
+    }
+
 }

Modified: poi/trunk/poi/src/main/java/org/apache/poi/sl/draw/DrawPaint.java
URL: http://svn.apache.org/viewvc/poi/trunk/poi/src/main/java/org/apache/poi/sl/draw/DrawPaint.java?rev=1904301&r1=1904300&r2=1904301&view=diff
==============================================================================
--- poi/trunk/poi/src/main/java/org/apache/poi/sl/draw/DrawPaint.java (original)
+++ poi/trunk/poi/src/main/java/org/apache/poi/sl/draw/DrawPaint.java Tue Sep 27 12:09:11 2022
@@ -613,7 +613,21 @@ public class DrawPaint {
         // need to remap the fractions, because Java doesn't like repeating fraction values
         Map<Float,Color> m = new TreeMap<>();
         for (float fraction : fill.getGradientFractions()) {
-            m.put(fraction, styles.next());
+            float gradientFraction = fraction;
+
+            // Multiple gradient stops at the same location
+            // can lead to failure when creating AWT gradient, especially
+            // if there are only two stops and they are both on the exact
+            // same location.
+            //   (The example of (only) 2 stops at exactly the same location will cause:
+            //    java.lang.IllegalArgumentException: User must specify at least 2 colors).
+            //
+            // To fix this we nudge the stop a teeny tiny bit.
+            if (m.containsKey(gradientFraction)) {
+                gradientFraction += (gradientFraction == 1.0 ? -1.0 : 1.0) * 0.00000005;
+            }
+
+            m.put(gradientFraction, styles.next());
         }
 
         return init.apply(toArray(m.keySet()), m.values().toArray(new Color[0]));

Added: poi/trunk/test-data/slideshow/minimal-gradient-fill-issue.pptx
URL: http://svn.apache.org/viewvc/poi/trunk/test-data/slideshow/minimal-gradient-fill-issue.pptx?rev=1904301&view=auto
==============================================================================
Binary file - no diff available.

Propchange: poi/trunk/test-data/slideshow/minimal-gradient-fill-issue.pptx
------------------------------------------------------------------------------
--- svn:mime-type (added)
+++ svn:mime-type Tue Sep 27 12:09:11 2022
@@ -0,0 +1 @@
+application/vnd.openxmlformats-officedocument.presentationml.presentation



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@poi.apache.org
For additional commands, e-mail: commits-help@poi.apache.org