]> source.dussan.org Git - poi.git/commitdiff
SL Common - Fix gradient fills
authorAndreas Beeker <kiwiwings@apache.org>
Sun, 13 Oct 2019 13:30:21 +0000 (13:30 +0000)
committerAndreas Beeker <kiwiwings@apache.org>
Sun, 13 Oct 2019 13:30:21 +0000 (13:30 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1868407 13f79535-47bb-0310-9956-ffa450edef68

src/java/org/apache/poi/sl/draw/DrawPaint.java
src/java/org/apache/poi/sl/draw/PathGradientPaint.java
src/java/org/apache/poi/sl/draw/geom/ArcToCommand.java
src/ooxml/java/org/apache/poi/xslf/util/PPTX2PNG.java
src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFill.java
src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFShape.java

index 25affcedf884cf46abc2f7cae07ac5af78ad8fcf..f674aa13171e033dec35325f4328c13a59c0fc95 100644 (file)
@@ -17,6 +17,8 @@
 
 package org.apache.poi.sl.draw;
 
+import static org.apache.poi.sl.draw.geom.ArcToCommand.convertOoxml2AwtAngle;
+
 import java.awt.Color;
 import java.awt.Graphics2D;
 import java.awt.LinearGradientPaint;
@@ -443,6 +445,8 @@ public class DrawPaint {
 
         Rectangle2D anchor = DrawShape.getAnchor(graphics, shape);
 
+        angle = convertOoxml2AwtAngle(-angle, anchor.getWidth(), anchor.getHeight());
+
         AffineTransform at = AffineTransform.getRotateInstance(Math.toRadians(angle), anchor.getCenterX(), anchor.getCenterY());
 
         double diagonal = Math.sqrt(Math.pow(anchor.getWidth(),2) + Math.pow(anchor.getHeight(),2));
index 2281bbff83ade7f8449a9b2f8b4540b50d32a8c6..a26abadd2ef6b774ba21b75c6b0af751acfe8636 100644 (file)
 
 package org.apache.poi.sl.draw;
 
-import java.awt.*;
+import java.awt.AlphaComposite;
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.LinearGradientPaint;
 import java.awt.MultipleGradientPaint.ColorSpaceType;
 import java.awt.MultipleGradientPaint.CycleMethod;
-import java.awt.geom.*;
-import java.awt.image.*;
+import java.awt.Paint;
+import java.awt.PaintContext;
+import java.awt.Rectangle;
+import java.awt.RenderingHints;
+import java.awt.Shape;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Area;
+import java.awt.geom.IllegalPathStateException;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+import java.awt.image.BufferedImage;
+import java.awt.image.ColorModel;
+import java.awt.image.Raster;
+import java.awt.image.WritableRaster;
 
 import org.apache.poi.util.Internal;
 
@@ -174,14 +190,17 @@ class PathGradientPaint implements Paint {
             int[] rgb = new int[cm.getNumComponents()];
 
             for (int i = gradientSteps-1; i>=0; i--) {
-                img2.getPixel(i, 0, rgb);
+                img2.getPixel(gradientSteps-i-1, 0, rgb);
                 Color c = new Color(rgb[0],rgb[1],rgb[2]);
                 if (rgb.length == 4) {
                     // it doesn't work to use just a color with transparency ...
-                    graphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC, rgb[3]/255.0f));                           
+                    graphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC, rgb[3]/255.0f));
                 }
                 graphics.setStroke(new BasicStroke(i+1, capStyle, joinStyle));
                 graphics.setColor(c);
+                if (i == gradientSteps-1) {
+                    graphics.fill(shape);
+                }
                 graphics.draw(shape);
             }
             
index 7d0feb366e80be5757b10463c13c17336c938de1..9ff57e6da98bffc0f4112de697e52f196821da17 100644 (file)
@@ -26,6 +26,7 @@ import java.awt.geom.Path2D;
 import java.awt.geom.Point2D;
 
 import org.apache.poi.sl.draw.binding.CTPath2DArcTo;
+import org.apache.poi.util.Internal;
 
 /**
  * ArcTo command within a shape path in DrawingML:
@@ -89,15 +90,16 @@ public class ArcToCommand implements PathCommand {
      *            |270/-90                     |90/-270 (5400000)
      * </pre>
      *
-     * @param ooAngle the angle in OOXML units
-     * @param width the half width of the bounding box
-     * @param height the half height of the bounding box
+     * @param ooAngle the angle in OOXML units divided by 60000
+     * @param width the width of the bounding box
+     * @param height the height of the bounding box
      *
      * @return the angle in degrees
      *
      * @see <a href="http://www.onlinemathe.de/forum/Problem-bei-Winkelberechnungen-einer-Ellipse">unskew angle</a>
      **/
-    private double convertOoxml2AwtAngle(double ooAngle, double width, double height) {
+    @Internal
+    public static double convertOoxml2AwtAngle(double ooAngle, double width, double height) {
         double aspect = (height / width);
         // reverse angle for awt
         double awtAngle = -ooAngle;
index 4c7e27d5586b1f98151a7061b313b4919251f4d2..5a2b8dbe7e202455307b36138ac019707a65e598 100644 (file)
@@ -225,7 +225,7 @@ public class PPTX2PNG {
                 proxy.setSlideNo(slideNo);
                 if (!quiet) {
                     String title = proxy.getTitle();
-                    System.out.println("Rendering slide " + (slideNo + 1) + (title == null ? "" : ": " + title.trim()));
+                    System.out.println("Rendering slide " + slideNo + (title == null ? "" : ": " + title.trim()));
                 }
 
                 GenericRecord gr = proxy.getRoot();
@@ -301,8 +301,6 @@ public class PPTX2PNG {
 
     private interface MFProxy extends Closeable {
         void parse(File file) throws IOException;
-//        boolean isEmpty();
-//        void dumpRecords(Writer writer) throws IOException;
 //        Iterable<HwmfEmbedded> getEmbeddings();
         Dimension2D getSize();
 
index b87d2869bef73f2dd118b417726acc7e766cc97a..3b505f703eb1aa06d14976aed9349a368d568b67 100644 (file)
@@ -20,6 +20,8 @@ package org.apache.poi.hslf.usermodel;
 import java.awt.Color;
 import java.io.ByteArrayInputStream;
 import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
 import org.apache.poi.ddf.AbstractEscherOptRecord;
@@ -229,6 +231,19 @@ public final class HSLFFill {
         return new FillStyle() {
             @Override
             public PaintStyle getPaint() {
+                AbstractEscherOptRecord opt = shape.getEscherOptRecord();
+
+                EscherSimpleProperty hitProp = HSLFShape.getEscherProperty(opt, EscherPropertyTypes.FILL__NOFILLHITTEST);
+                int propVal = (hitProp == null) ? 0 : hitProp.getPropertyValue();
+
+                EscherSimpleProperty masterProp = HSLFShape.getEscherProperty(opt, EscherPropertyTypes.SHAPE__MASTER);
+
+                if (!FILL_USE_FILLED.isSet(propVal) && masterProp != null) {
+                    int masterId = masterProp.getPropertyValue();
+                    HSLFShape o = shape.getSheet().getMasterSheet().getShapes().stream().filter(s -> s.getShapeId() == masterId).findFirst().orElse(null);
+                    return o != null ? o.getFillStyle().getPaint() : null;
+                }
+
                 final int fillType = getFillType();
                 // TODO: fix gradient types, this mismatches with the MS-ODRAW definition ...
                 // need to handle (not only) the type (radial,rectangular,linear),
@@ -265,9 +280,61 @@ public final class HSLFFill {
 
     private GradientPaint getGradientPaint(final GradientType gradientType) {
         AbstractEscherOptRecord opt = shape.getEscherOptRecord();
+
+        EscherSimpleProperty p = HSLFShape.getEscherProperty(opt, EscherPropertyTypes.FILL__NOFILLHITTEST);
+        int propVal = (p == null) ? 0 : p.getPropertyValue();
+
+        if (FILL_USE_FILLED.isSet(propVal) && !FILL_FILLED.isSet(propVal)) {
+            return null;
+        }
+
+
         final EscherArrayProperty ep = HSLFShape.getEscherProperty(opt, EscherPropertyTypes.FILL__SHADECOLORS);
         final int colorCnt = (ep == null) ? 0 : ep.getNumberOfElementsInArray();
 
+        final List<Color> colors = new ArrayList<>();
+        final List<Float> fractions = new ArrayList<>();
+
+        // TODO: handle palette colors and alpha(?) value
+        if (colorCnt == 0) {
+            colors.add(getBackgroundColor());
+            colors.add(getForegroundColor());
+            fractions.add(0f);
+            fractions.add(1f);
+        } else {
+            ep.forEach(data -> {
+                EscherColorRef ecr = new EscherColorRef(data, 0, 4);
+                colors.add(shape.getColor(ecr));
+                double pos = Units.fixedPointToDouble(LittleEndian.getInt(data, 4));
+                fractions.add((float)pos);
+            });
+        }
+
+        int focus = getFillFocus();
+        if (focus == 100 || focus == -100) {
+            Collections.reverse(colors);
+        } else if (focus != 0) {
+            if (focus < 0) {
+                focus = 100+focus;
+            }
+            // TODO: depending on fill focus, rotation with shape and other escher properties
+            // there are still a lot of cases where we get the gradients wrong
+            List<Color> reflectedColors = new ArrayList<>(colors.subList(1,colors.size()));
+            Collections.reverse(reflectedColors);
+            colors.addAll(0, reflectedColors);
+
+            final List<Float> fractRev = new ArrayList<>();
+            for (int i=fractions.size()-2; i >= 0; i--) {
+                float val = (float)(1 - fractions.get(i) * focus / 100.);
+                fractRev.add(val);
+            }
+            for (int i=0; i<fractions.size(); i++) {
+                float val = (float)(fractions.get(i) * focus / 100.);
+                fractions.set(i, val);
+            }
+            fractions.addAll(fractRev);
+        }
+
         return new GradientPaint() {
             @Override
             public double getGradientAngle() {
@@ -277,24 +344,10 @@ public final class HSLFFill {
                 int rot = shape.getEscherProperty(EscherPropertyTypes.FILL__ANGLE);
                 return 90-Units.fixedPointToDouble(rot);
             }
-            
+
             @Override
             public ColorStyle[] getGradientColors() {
-                ColorStyle[] cs;
-                if (colorCnt == 0) {
-                    cs = new ColorStyle[2];
-                    cs[0] = wrapColor(getBackgroundColor());
-                    cs[1] = wrapColor(getForegroundColor());
-                } else {
-                    cs = new ColorStyle[colorCnt];
-                    int idx = 0;
-                    // TODO: handle palette colors and alpha(?) value 
-                    for (byte[] data : ep) {
-                        EscherColorRef ecr = new EscherColorRef(data, 0, 4);
-                        cs[idx++] = wrapColor(shape.getColor(ecr));
-                    }
-                }
-                return cs;
+                return colors.stream().map(this::wrapColor).toArray(ColorStyle[]::new);
             }
             
             private ColorStyle wrapColor(Color col) {
@@ -303,16 +356,9 @@ public final class HSLFFill {
             
             @Override
             public float[] getGradientFractions() {
-                float[] frc;
-                if (colorCnt == 0) {
-                    frc = new float[]{0, 1};
-                } else {
-                    frc = new float[colorCnt];
-                    int idx = 0;
-                    for (byte[] data : ep) {
-                        double pos = Units.fixedPointToDouble(LittleEndian.getInt(data, 4));
-                        frc[idx++] = (float)pos;
-                    }
+                float[] frc = new float[fractions.size()];
+                for (int i = 0; i<fractions.size(); i++) {
+                    frc[i] = fractions.get(i);
                 }
                 return frc;
             }
@@ -370,6 +416,20 @@ public final class HSLFFill {
         return prop == null ? FILL_SOLID : prop.getPropertyValue();
     }
 
+    /**
+     * The fillFocus property specifies the relative position of the last color in the shaded fill.
+     * Its used to specify the center of an reflected fill. 0 = no reflection, 50 = reflected in the middle.
+     * If fillFocus is less than 0, the relative position of the last color is outside the shape,
+     * and the relative position of the first color is within the shape.
+     *
+     * @return a percentage in the range of -100 .. 100; defaults to 0
+     */
+    public int getFillFocus() {
+        AbstractEscherOptRecord opt = shape.getEscherOptRecord();
+        EscherSimpleProperty prop = HSLFShape.getEscherProperty(opt, EscherPropertyTypes.FILL__FOCUS);
+        return prop == null ? 0 : prop.getPropertyValue();
+    }
+
     void afterInsert(HSLFSheet sh){
         AbstractEscherOptRecord opt = shape.getEscherOptRecord();
         EscherSimpleProperty p = HSLFShape.getEscherProperty(opt, EscherPropertyTypes.FILL__PATTERNTEXTURE);
@@ -420,7 +480,7 @@ public final class HSLFFill {
         EscherSimpleProperty p = HSLFShape.getEscherProperty(opt, EscherPropertyTypes.FILL__NOFILLHITTEST);
         int propVal = (p == null) ? 0 : p.getPropertyValue();
 
-        return (FILL_USE_FILLED.isSet(propVal) && FILL_FILLED.isSet(propVal))
+        return (!FILL_USE_FILLED.isSet(propVal) || (FILL_USE_FILLED.isSet(propVal) && FILL_FILLED.isSet(propVal)))
             ? shape.getColor(EscherPropertyTypes.FILL__FILLCOLOR, EscherPropertyTypes.FILL__FILLOPACITY)
             : null;
     }
@@ -464,7 +524,7 @@ public final class HSLFFill {
         EscherSimpleProperty p = HSLFShape.getEscherProperty(opt, EscherPropertyTypes.FILL__NOFILLHITTEST);
         int propVal = (p == null) ? 0 : p.getPropertyValue();
 
-        return (FILL_USE_FILLED.isSet(propVal) && FILL_FILLED.isSet(propVal))
+        return (!FILL_USE_FILLED.isSet(propVal) || (FILL_USE_FILLED.isSet(propVal) && FILL_FILLED.isSet(propVal)))
             ? shape.getColor(EscherPropertyTypes.FILL__FILLBACKCOLOR, EscherPropertyTypes.FILL__FILLOPACITY)
             : null;
     }
index 9ddf070963a48a9ee2ecdf0a23f612204548d1d9..03458c4b2abed885fe0064575d5762138f4c3088 100644 (file)
@@ -507,21 +507,21 @@ public abstract class HSLFShape implements Shape<HSLFShape,HSLFTextParagraph> {
         // a different color type
         switch (sis) {
             case FILL_COLOR: {
-                return getFill().getForegroundColor();
+                return getColor(EscherPropertyTypes.FILL__FILLCOLOR, EscherPropertyTypes.FILL__FILLOPACITY);
             }
             case LINE_OR_FILL_COLOR: {
                 Color col = null;
                 if (this instanceof HSLFSimpleShape) {
-                    col = ((HSLFSimpleShape)this).getLineColor();
+                    col = getColor(EscherPropertyTypes.LINESTYLE__COLOR, EscherPropertyTypes.LINESTYLE__OPACITY);
                 }
                 if (col == null) {
-                    col = getFill().getForegroundColor();
+                    col = getColor(EscherPropertyTypes.FILL__FILLCOLOR, EscherPropertyTypes.FILL__FILLOPACITY);
                 }
                 return col;
             }
             case LINE_COLOR: {
                 if (this instanceof HSLFSimpleShape) {
-                    return ((HSLFSimpleShape)this).getLineColor();
+                    return getColor(EscherPropertyTypes.LINESTYLE__COLOR, EscherPropertyTypes.LINESTYLE__OPACITY);
                 }
                 break;
             }
@@ -536,7 +536,7 @@ public abstract class HSLFShape implements Shape<HSLFShape,HSLFTextParagraph> {
                 break;
             }
             case FILL_BACKGROUND_COLOR: {
-                return getFill().getBackgroundColor();
+                return getColor(EscherPropertyTypes.FILL__FILLBACKCOLOR, EscherPropertyTypes.FILL__FILLOPACITY);
             }
             case LINE_BACKGROUND_COLOR: {
                 if (this instanceof HSLFSimpleShape) {
@@ -545,9 +545,9 @@ public abstract class HSLFShape implements Shape<HSLFShape,HSLFTextParagraph> {
                 break;
             }
             case FILL_OR_LINE_COLOR: {
-                Color col = getFill().getForegroundColor();
+                Color col = getColor(EscherPropertyTypes.FILL__FILLCOLOR, EscherPropertyTypes.FILL__FILLOPACITY);
                 if (col == null && this instanceof HSLFSimpleShape) {
-                    col = ((HSLFSimpleShape)this).getLineColor();
+                    col = getColor(EscherPropertyTypes.LINESTYLE__COLOR, EscherPropertyTypes.LINESTYLE__OPACITY);
                 }
                 return col;
             }