You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@poi.apache.org by ki...@apache.org on 2019/11/11 23:06:30 UTC

svn commit: r1869669 - in /poi: site/src/documentation/content/xdocs/ trunk/src/java/org/apache/poi/sl/draw/ trunk/src/java/org/apache/poi/sl/usermodel/ trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/ trunk/src/scratchpad/src/org/apache/poi/hslf/us...

Author: kiwiwings
Date: Mon Nov 11 23:06:29 2019
New Revision: 1869669

URL: http://svn.apache.org/viewvc?rev=1869669&view=rev
Log:
#63918 - Fix texture fill - scale stretched images correctly

Added:
    poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFGradientPaint.java   (with props)
    poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTexturePaint.java   (with props)
Modified:
    poi/site/src/documentation/content/xdocs/changes.xml
    poi/trunk/src/java/org/apache/poi/sl/draw/DrawTexturePaint.java
    poi/trunk/src/java/org/apache/poi/sl/usermodel/PaintStyle.java
    poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShape.java
    poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFill.java

Modified: poi/site/src/documentation/content/xdocs/changes.xml
URL: http://svn.apache.org/viewvc/poi/site/src/documentation/content/xdocs/changes.xml?rev=1869669&r1=1869668&r2=1869669&view=diff
==============================================================================
--- poi/site/src/documentation/content/xdocs/changes.xml (original)
+++ poi/site/src/documentation/content/xdocs/changes.xml Mon Nov 11 23:06:29 2019
@@ -86,8 +86,11 @@
           when referring to both H??F and X??F formats.
     -->
 
-    <!-- release version="4.1.2" date="2020-01-??">
-    </release -->
+    <release version="4.1.2" date="2020-01-??">
+      <actions>
+        <action dev="PD" fixes-bug="63918" context="SL_Common XSLF">Fix texture fill - scale stretched images correctly</action>
+      </actions>
+    </release>
 
     <release version="4.1.1" date="2019-10-20">
       <summary>

Modified: poi/trunk/src/java/org/apache/poi/sl/draw/DrawTexturePaint.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/sl/draw/DrawTexturePaint.java?rev=1869669&r1=1869668&r2=1869669&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/sl/draw/DrawTexturePaint.java (original)
+++ poi/trunk/src/java/org/apache/poi/sl/draw/DrawTexturePaint.java Mon Nov 11 23:06:29 2019
@@ -17,10 +17,13 @@
 
 package org.apache.poi.sl.draw;
 
+import java.awt.AlphaComposite;
+import java.awt.Graphics2D;
 import java.awt.PaintContext;
 import java.awt.Rectangle;
 import java.awt.RenderingHints;
 import java.awt.Shape;
+import java.awt.TexturePaint;
 import java.awt.geom.AffineTransform;
 import java.awt.geom.Dimension2D;
 import java.awt.geom.Point2D;
@@ -28,7 +31,9 @@ import java.awt.geom.Rectangle2D;
 import java.awt.image.BufferedImage;
 import java.awt.image.ColorModel;
 
+import org.apache.poi.sl.usermodel.Insets2D;
 import org.apache.poi.sl.usermodel.PaintStyle;
+import org.apache.poi.util.Dimension2DDouble;
 
 /* package */ class DrawTexturePaint extends java.awt.TexturePaint {
     private final PaintStyle.TexturePaint fill;
@@ -36,6 +41,9 @@ import org.apache.poi.sl.usermodel.Paint
     private final double flipX, flipY;
     private final boolean isBitmapSrc;
 
+    private static final Insets2D INSETS_EMPTY = new Insets2D(0,0,0,0);
+
+
     DrawTexturePaint(BufferedImage txtr, Shape shape, PaintStyle.TexturePaint fill, double flipX, double flipY, boolean isBitmapSrc) {
         // deactivate scaling/translation in super class, by specifying the dimension of the texture
         super(txtr, new Rectangle2D.Double(0,0,txtr.getWidth(),txtr.getHeight()));
@@ -49,13 +57,10 @@ import org.apache.poi.sl.usermodel.Paint
     @Override
     public PaintContext createContext(ColorModel cm, Rectangle deviceBounds, Rectangle2D userBounds, AffineTransform xform, RenderingHints hints) {
 
-        final double usr_w, usr_h;
-
+        final Dimension2D userDim = new Dimension2DDouble();
+        final Rectangle2D usedBounds;
         if (fill.isRotatedWithShape() || shape == null) {
-            usr_w = userBounds.getWidth();
-            usr_h = userBounds.getHeight();
-
-            xform.translate(userBounds.getX(), userBounds.getY());
+            usedBounds = userBounds;
         } else {
             AffineTransform	transform = new AffineTransform(xform);
 
@@ -73,22 +78,111 @@ import org.apache.poi.sl.usermodel.Paint
 
             // TODO: check if approximation via rotating only the bounds (instead of the shape) is sufficient
             transform = AffineTransform.getRotateInstance(rad, userBounds.getCenterX(), userBounds.getCenterY());
-            Rectangle2D newBounds = transform.createTransformedShape(shape).getBounds2D();
-            usr_w = newBounds.getWidth();
-            usr_h = newBounds.getHeight();
+            usedBounds = transform.createTransformedShape(shape).getBounds2D();
+        }
+        userDim.setSize(usedBounds.getWidth(), usedBounds.getHeight());
+        xform.translate(usedBounds.getX(), usedBounds.getY());
+
+        BufferedImage bi = getImage(usedBounds);
 
-            xform.translate(newBounds.getX(), newBounds.getY());
+        if (fill.getStretch() != null) {
+            TexturePaint tp = new TexturePaint(bi, new Rectangle2D.Double(0, 0, bi.getWidth(), bi.getHeight()));
+            return tp.createContext(cm, deviceBounds, usedBounds, xform, hints);
+        } else if (fill.getScale() != null) {
+            AffineTransform newXform = getTiledInstance(usedBounds, (AffineTransform) xform.clone());
+            TexturePaint tp = new TexturePaint(bi, new Rectangle2D.Double(0, 0, bi.getWidth(), bi.getHeight()));
+            return tp.createContext(cm, deviceBounds, userBounds, newXform, hints);
+        } else {
+            return super.createContext(cm, deviceBounds, userBounds, xform, hints);
         }
+    }
 
-        final Dimension2D scale = fill.getScale();
+    public BufferedImage getImage(Rectangle2D userBounds) {
+        BufferedImage bi = super.getImage();
+        final Insets2D insets = fill.getInsets();
+        final Insets2D stretch = fill.getStretch();
 
+        if ((insets == null || INSETS_EMPTY.equals(insets)) && (stretch == null)) {
+            return bi;
+        }
+
+        if (insets != null && !INSETS_EMPTY.equals(insets)) {
+            final int width = bi.getWidth();
+            final int height = bi.getHeight();
+
+            bi = bi.getSubimage(
+                (int)(Math.max(insets.left,0)/100_000 * width),
+                (int)(Math.max(insets.top,0)/100_000 * height),
+                (int)((100_000-Math.max(insets.left,0)-Math.max(insets.right,0))/100_000 * width),
+                (int)((100_000-Math.max(insets.top,0)-Math.max(insets.bottom,0))/100_000 * height)
+            );
+
+            int addTop = (int)(Math.max(-insets.top, 0)/100_000 * height);
+            int addLeft = (int)(Math.max(-insets.left, 0)/100_000 * width);
+            int addBottom = (int)(Math.max(-insets.bottom, 0)/100_000 * height);
+            int addRight = (int)(Math.max(-insets.right, 0)/100_000 * width);
+
+            // handle outsets
+            if (addTop > 0 || addLeft > 0 || addBottom > 0 || addRight > 0) {
+                int[] buf = new int[bi.getWidth()*bi.getHeight()];
+                bi.getRGB(0, 0, bi.getWidth(), bi.getHeight(), buf, 0, bi.getWidth());
+                BufferedImage borderBi = new BufferedImage(bi.getWidth()+addLeft+addRight, bi.getHeight()+addTop+addBottom, bi.getType());
+                borderBi.setRGB(addLeft, addTop, bi.getWidth(), bi.getHeight(), buf, 0, bi.getWidth());
+                bi = borderBi;
+            }
+        }
+
+        if (stretch != null) {
+            Rectangle2D srcBounds = new Rectangle2D.Double(
+                0, 0, bi.getWidth(), bi.getHeight()
+            );
+
+            Rectangle2D dstBounds = new Rectangle2D.Double(
+                stretch.left/100_000 * userBounds.getWidth(),
+                stretch.top/100_000 * userBounds.getHeight(),
+                (100_000-stretch.left-stretch.right)/100_000 * userBounds.getWidth(),
+                (100_000-stretch.top-stretch.bottom)/100_000 * userBounds.getHeight()
+            );
+
+            BufferedImage stretchBi = new BufferedImage((int)userBounds.getWidth(), (int)userBounds.getHeight(), BufferedImage.TYPE_INT_ARGB);
+            Graphics2D g = stretchBi.createGraphics();
+
+            g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+            g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
+            g.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_SPEED);
+            g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
+            g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
+
+            g.setComposite(AlphaComposite.Clear);
+            g.fillRect(0, 0, stretchBi.getWidth(), stretchBi.getHeight());
+            g.setComposite(AlphaComposite.SrcOver);
+
+            AffineTransform at = new AffineTransform();
+            at.translate(dstBounds.getCenterX(), dstBounds.getCenterY());
+            at.scale(dstBounds.getWidth()/srcBounds.getWidth(), dstBounds.getHeight()/srcBounds.getHeight());
+            at.translate(-srcBounds.getCenterX(), -srcBounds.getCenterY());
+
+            g.drawRenderedImage(bi, at);
+
+            g.dispose();
+
+            bi = stretchBi;
+        }
+
+        return bi;
+    }
+
+    private AffineTransform getTiledInstance(final Rectangle2D usedBounds, final AffineTransform xform) {
         final BufferedImage bi = getImage();
-        final double img_w = bi.getWidth() * (scale == null ? 1 : scale.getWidth())/flipX;
-        final double img_h = bi.getHeight() * (scale == null ? 1 : scale.getHeight())/flipY;
+        final Dimension2D scale = fill.getScale();
+        assert(scale != null);
+        final double img_w = bi.getWidth() * (scale.getWidth() == 0 ? 1 : scale.getWidth())/flipX;
+        final double img_h = bi.getHeight() * (scale.getHeight() == 0 ? 1 : scale.getHeight())/flipY;
 
         // Alignment happens after the scaling but before any offset.
         PaintStyle.TextureAlignment ta = fill.getAlignment();
         final double alg_x, alg_y;
+        final double usr_w = usedBounds.getWidth(), usr_h = usedBounds.getHeight();
         switch (ta == null ? PaintStyle.TextureAlignment.TOP_LEFT : ta) {
             case BOTTOM:
                 alg_x = (usr_w-img_w)/2;
@@ -140,10 +234,8 @@ import org.apache.poi.sl.usermodel.Paint
             xform.translate(offset.getX(),offset.getY());
         }
 
-        if (scale != null) {
-            xform.scale(scale.getWidth()/(isBitmapSrc ? flipX : 1.),scale.getHeight()/(isBitmapSrc ? flipY : 1.));
-        }
+        xform.scale(scale.getWidth()/(isBitmapSrc ? flipX : 1.),scale.getHeight()/(isBitmapSrc ? flipY : 1.));
 
-        return super.createContext(cm, deviceBounds, userBounds, xform, hints);
+        return xform;
     }
 }

Modified: poi/trunk/src/java/org/apache/poi/sl/usermodel/PaintStyle.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/sl/usermodel/PaintStyle.java?rev=1869669&r1=1869668&r2=1869669&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/sl/usermodel/PaintStyle.java (original)
+++ poi/trunk/src/java/org/apache/poi/sl/usermodel/PaintStyle.java Mon Nov 11 23:06:29 2019
@@ -138,5 +138,37 @@ public interface PaintStyle {
 
 
         default TextureAlignment getAlignment() { return null; }
+
+        /**
+         * Specifies the portion of the blip or image that is used for the fill.<p>
+         *
+         * Each edge of the image is defined by a percentage offset from the edge of the bounding box.
+         * A positive percentage specifies an inset and a negative percentage specifies an outset.<p>
+         *
+         * The percentage are ints based on 100000, so 100% = 100000.<p>
+         *
+         * So, for example, a left offset of 25% specifies that the left edge of the image is located
+         * to the right of the bounding box's left edge by 25% of the bounding box's width.
+         *
+         * @return the cropping insets of the source image
+         */
+        default Insets2D getInsets() {
+            return null;
+        }
+
+        /**
+         * The stretch specifies the edges of a fill rectangle.<p>
+         *
+         * Each edge of the fill rectangle is defined by a perentage offset from the corresponding edge
+         * of the picture's bounding box. A positive percentage specifies an inset and a negative percentage
+         * specifies an outset.<p>
+         *
+         * The percentage are ints based on 100000, so 100% = 100000.
+         *
+         * @return the stretching in the destination image
+         */
+        default Insets2D getStretch() {
+            return null;
+        }
     }
 }

Added: poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFGradientPaint.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFGradientPaint.java?rev=1869669&view=auto
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFGradientPaint.java (added)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFGradientPaint.java Mon Nov 11 23:06:29 2019
@@ -0,0 +1,107 @@
+/* ====================================================================
+   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.xslf.usermodel;
+
+import java.util.Arrays;
+
+import org.apache.poi.sl.usermodel.ColorStyle;
+import org.apache.poi.sl.usermodel.PaintStyle;
+import org.apache.poi.util.Internal;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTGradientFillProperties;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTGradientStop;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTSchemeColor;
+import org.openxmlformats.schemas.drawingml.x2006.main.STPathShadeType;
+
+@Internal
+public class XSLFGradientPaint implements PaintStyle.GradientPaint {
+
+    private final CTGradientFillProperties gradFill;
+    final ColorStyle[] cs;
+    final float[] fractions;
+
+    public XSLFGradientPaint(final CTGradientFillProperties gradFill, CTSchemeColor phClr, final XSLFTheme theme) {
+        this.gradFill = gradFill;
+
+        final CTGradientStop[] gs = gradFill.getGsLst() == null ?
+                new CTGradientStop[0] : gradFill.getGsLst().getGsArray();
+
+        Arrays.sort(gs, (o1, o2) -> {
+            int pos1 = o1.getPos();
+            int pos2 = o2.getPos();
+            return Integer.compare(pos1, pos2);
+        });
+
+        cs = new ColorStyle[gs.length];
+        fractions = new float[gs.length];
+
+        int i=0;
+        for (CTGradientStop cgs : gs) {
+            CTSchemeColor phClrCgs = phClr;
+            if (phClrCgs == null && cgs.isSetSchemeClr()) {
+                phClrCgs = cgs.getSchemeClr();
+            }
+            cs[i] = new XSLFColor(cgs, theme, phClrCgs).getColorStyle();
+            fractions[i] = cgs.getPos() / 100000.f;
+            i++;
+        }
+
+    }
+
+
+    @Override
+    public double getGradientAngle() {
+        return (gradFill.isSetLin())
+                ? gradFill.getLin().getAng() / 60000.d
+                : 0;
+    }
+
+    @Override
+    public ColorStyle[] getGradientColors() {
+        return cs;
+    }
+
+    @Override
+    public float[] getGradientFractions() {
+        return fractions;
+    }
+
+    @Override
+    public boolean isRotatedWithShape() {
+        return gradFill.getRotWithShape();
+    }
+
+    @Override
+    public PaintStyle.GradientPaint.GradientType getGradientType() {
+        if (gradFill.isSetLin()) {
+            return PaintStyle.GradientPaint.GradientType.linear;
+        }
+
+        if (gradFill.isSetPath()) {
+            /* TODO: handle rect path */
+            STPathShadeType.Enum ps = gradFill.getPath().getPath();
+            if (ps == STPathShadeType.CIRCLE) {
+                return PaintStyle.GradientPaint.GradientType.circular;
+            } else if (ps == STPathShadeType.SHAPE) {
+                return PaintStyle.GradientPaint.GradientType.shape;
+            }
+        }
+
+        return PaintStyle.GradientPaint.GradientType.linear;
+    }
+
+}

Propchange: poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFGradientPaint.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShape.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShape.java?rev=1869669&r1=1869668&r2=1869669&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShape.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShape.java Mon Nov 11 23:06:29 2019
@@ -20,40 +20,26 @@
 package org.apache.poi.xslf.usermodel;
 
 import java.awt.Graphics2D;
-import java.awt.geom.Dimension2D;
-import java.awt.geom.Point2D;
 import java.awt.geom.Rectangle2D;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Arrays;
 
-import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
 import org.apache.poi.openxml4j.opc.PackagePart;
-import org.apache.poi.openxml4j.opc.PackageRelationship;
 import org.apache.poi.sl.draw.DrawFactory;
 import org.apache.poi.sl.draw.DrawPaint;
-import org.apache.poi.sl.usermodel.ColorStyle;
 import org.apache.poi.sl.usermodel.MasterSheet;
 import org.apache.poi.sl.usermodel.PaintStyle;
-import org.apache.poi.sl.usermodel.PaintStyle.GradientPaint;
-import org.apache.poi.sl.usermodel.PaintStyle.TexturePaint;
 import org.apache.poi.sl.usermodel.PlaceableShape;
 import org.apache.poi.sl.usermodel.Placeholder;
 import org.apache.poi.sl.usermodel.PlaceholderDetails;
 import org.apache.poi.sl.usermodel.Shape;
 import org.apache.poi.sl.usermodel.SimpleShape;
 import org.apache.poi.util.Beta;
-import org.apache.poi.util.Dimension2DDouble;
 import org.apache.poi.util.Internal;
-import org.apache.poi.util.Units;
 import org.apache.poi.xslf.model.PropertyFetcher;
 import org.apache.poi.xslf.usermodel.XSLFPropertiesDelegate.XSLFFillProperties;
 import org.apache.xmlbeans.XmlCursor;
 import org.apache.xmlbeans.XmlObject;
-import org.openxmlformats.schemas.drawingml.x2006.main.CTBlip;
 import org.openxmlformats.schemas.drawingml.x2006.main.CTBlipFillProperties;
 import org.openxmlformats.schemas.drawingml.x2006.main.CTGradientFillProperties;
-import org.openxmlformats.schemas.drawingml.x2006.main.CTGradientStop;
 import org.openxmlformats.schemas.drawingml.x2006.main.CTGroupShapeProperties;
 import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps;
 import org.openxmlformats.schemas.drawingml.x2006.main.CTSchemeColor;
@@ -62,9 +48,6 @@ import org.openxmlformats.schemas.drawin
 import org.openxmlformats.schemas.drawingml.x2006.main.CTSolidColorFillProperties;
 import org.openxmlformats.schemas.drawingml.x2006.main.CTStyleMatrix;
 import org.openxmlformats.schemas.drawingml.x2006.main.CTStyleMatrixReference;
-import org.openxmlformats.schemas.drawingml.x2006.main.CTTileInfoProperties;
-import org.openxmlformats.schemas.drawingml.x2006.main.STPathShadeType;
-import org.openxmlformats.schemas.drawingml.x2006.main.STTileFlipMode;
 import org.openxmlformats.schemas.presentationml.x2006.main.CTBackgroundProperties;
 import org.openxmlformats.schemas.presentationml.x2006.main.CTPicture;
 import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder;
@@ -153,6 +136,7 @@ public abstract class XSLFShape implemen
     protected PaintStyle getFillPaint() {
         final XSLFTheme theme = getSheet().getTheme();
         final boolean hasPlaceholder = getPlaceholder() != null;
+
         PropertyFetcher<PaintStyle> fetcher = new PropertyFetcher<PaintStyle>() {
             @Override
             public boolean fetch(XSLFShape shape) {
@@ -411,159 +395,12 @@ public abstract class XSLFShape implemen
 
     @SuppressWarnings("WeakerAccess")
     protected static PaintStyle selectPaint(final CTBlipFillProperties blipFill, final PackagePart parentPart) {
-        final CTBlip blip = blipFill.getBlip();
-        return new TexturePaint() {
-            private PackagePart getPart() {
-                try {
-                    String blipId = blip.getEmbed();
-                    PackageRelationship rel = parentPart.getRelationship(blipId);
-                    return parentPart.getRelatedPart(rel);
-                } catch (InvalidFormatException e) {
-                    throw new RuntimeException(e);
-                }
-            }
-
-            @Override
-            public InputStream getImageData() {
-                try {
-                    return getPart().getInputStream();
-                } catch (IOException e) {
-                    throw new RuntimeException(e);
-                }
-            }
-
-            @Override
-            public String getContentType() {
-                if (blip == null || !blip.isSetEmbed() || blip.getEmbed().isEmpty()) {
-                    return null;
-                }
-                /* TOOD: map content-type */
-                return getPart().getContentType();
-            }
-
-            @Override
-            public int getAlpha() {
-                return (blip.sizeOfAlphaModFixArray() > 0)
-                    ? blip.getAlphaModFixArray(0).getAmt()
-                    : 100000;
-            }
-
-            @Override
-            public boolean isRotatedWithShape() {
-                return blipFill.isSetRotWithShape() && blipFill.getRotWithShape();
-            }
-
-            @Override
-            public Dimension2D getScale() {
-                CTTileInfoProperties tile = blipFill.getTile();
-                return (tile == null) ? null : new Dimension2DDouble(
-                    tile.isSetSx() ? tile.getSx()/100_000. : 1,
-                    tile.isSetSy() ? tile.getSy()/100_000. : 1);
-            }
-
-            @Override
-            public Point2D getOffset() {
-                CTTileInfoProperties tile = blipFill.getTile();
-                return (tile == null) ? null : new Point2D.Double(
-                        tile.isSetTx() ? Units.toPoints(tile.getTx()) : 0,
-                        tile.isSetTy() ? Units.toPoints(tile.getTy()) : 0);
-            }
-
-            @Override
-            public FlipMode getFlipMode() {
-                CTTileInfoProperties tile = blipFill.getTile();
-                switch (tile == null || tile.getFlip() == null ? STTileFlipMode.INT_NONE : tile.getFlip().intValue()) {
-                    default:
-                    case STTileFlipMode.INT_NONE:
-                        return FlipMode.NONE;
-                    case STTileFlipMode.INT_X:
-                        return FlipMode.X;
-                    case STTileFlipMode.INT_Y:
-                        return FlipMode.Y;
-                    case STTileFlipMode.INT_XY:
-                        return FlipMode.XY;
-                }
-            }
-
-            @Override
-            public TextureAlignment getAlignment() {
-                CTTileInfoProperties tile = blipFill.getTile();
-                return (tile == null || !tile.isSetAlgn()) ? null
-                    : TextureAlignment.fromOoxmlId(tile.getAlgn().toString());
-            }
-        };
+        return new XSLFTexturePaint(blipFill, parentPart);
     }
 
     @SuppressWarnings("WeakerAccess")
     protected static PaintStyle selectPaint(final CTGradientFillProperties gradFill, CTSchemeColor phClr, final XSLFTheme theme) {
-
-        @SuppressWarnings("deprecation")
-        final CTGradientStop[] gs = gradFill.getGsLst() == null ?
-                new CTGradientStop[0] : gradFill.getGsLst().getGsArray();
-
-        Arrays.sort(gs, (o1, o2) -> {
-            int pos1 = o1.getPos();
-            int pos2 = o2.getPos();
-            return Integer.compare(pos1, pos2);
-        });
-
-        final ColorStyle[] cs = new ColorStyle[gs.length];
-        final float[] fractions = new float[gs.length];
-
-        int i=0;
-        for (CTGradientStop cgs : gs) {
-            CTSchemeColor phClrCgs = phClr;
-            if (phClrCgs == null && cgs.isSetSchemeClr()) {
-                phClrCgs = cgs.getSchemeClr();
-            }
-            cs[i] = new XSLFColor(cgs, theme, phClrCgs).getColorStyle();
-            fractions[i] = cgs.getPos() / 100000.f;
-            i++;
-        }
-
-        return new GradientPaint() {
-
-            @Override
-            public double getGradientAngle() {
-                return (gradFill.isSetLin())
-                    ? gradFill.getLin().getAng() / 60000.d
-                    : 0;
-            }
-
-            @Override
-            public ColorStyle[] getGradientColors() {
-                return cs;
-            }
-
-            @Override
-            public float[] getGradientFractions() {
-                return fractions;
-            }
-
-            @Override
-            public boolean isRotatedWithShape() {
-                return gradFill.getRotWithShape();
-            }
-
-            @Override
-            public GradientType getGradientType() {
-                if (gradFill.isSetLin()) {
-                    return GradientType.linear;
-                }
-
-                if (gradFill.isSetPath()) {
-                    /* TODO: handle rect path */
-                    STPathShadeType.Enum ps = gradFill.getPath().getPath();
-                    if (ps == STPathShadeType.CIRCLE) {
-                        return GradientType.circular;
-                    } else if (ps == STPathShadeType.SHAPE) {
-                        return GradientType.shape;
-                    }
-                }
-
-                return GradientType.linear;
-            }
-        };
+        return new XSLFGradientPaint(gradFill, phClr, theme);
     }
 
     @SuppressWarnings("WeakerAccess")

Added: poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTexturePaint.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTexturePaint.java?rev=1869669&view=auto
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTexturePaint.java (added)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTexturePaint.java Mon Nov 11 23:06:29 2019
@@ -0,0 +1,154 @@
+/* ====================================================================
+   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.xslf.usermodel;
+
+import java.awt.geom.Dimension2D;
+import java.awt.geom.Point2D;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.function.Supplier;
+
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+import org.apache.poi.openxml4j.opc.PackagePart;
+import org.apache.poi.openxml4j.opc.PackageRelationship;
+import org.apache.poi.sl.usermodel.Insets2D;
+import org.apache.poi.sl.usermodel.PaintStyle;
+import org.apache.poi.util.Dimension2DDouble;
+import org.apache.poi.util.Internal;
+import org.apache.poi.util.Units;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTBlip;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTBlipFillProperties;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTRelativeRect;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTTileInfoProperties;
+import org.openxmlformats.schemas.drawingml.x2006.main.STTileFlipMode;
+
+@Internal
+public class XSLFTexturePaint implements PaintStyle.TexturePaint {
+    private final CTBlipFillProperties blipFill;
+    private final PackagePart parentPart;
+    private final CTBlip blip;
+
+    public XSLFTexturePaint(final CTBlipFillProperties blipFill, final PackagePart parentPart) {
+        this.blipFill = blipFill;
+        this.parentPart = parentPart;
+        blip = blipFill.getBlip();
+    }
+
+
+    private PackagePart getPart() {
+        try {
+            String blipId = blip.getEmbed();
+            PackageRelationship rel = parentPart.getRelationship(blipId);
+            return parentPart.getRelatedPart(rel);
+        } catch (InvalidFormatException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public InputStream getImageData() {
+        try {
+            return getPart().getInputStream();
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public String getContentType() {
+        if (blip == null || !blip.isSetEmbed() || blip.getEmbed().isEmpty()) {
+            return null;
+        }
+        /* TOOD: map content-type */
+        return getPart().getContentType();
+    }
+
+    @Override
+    public int getAlpha() {
+        return (blip.sizeOfAlphaModFixArray() > 0)
+                ? blip.getAlphaModFixArray(0).getAmt()
+                : 100000;
+    }
+
+    @Override
+    public boolean isRotatedWithShape() {
+        return blipFill.isSetRotWithShape() && blipFill.getRotWithShape();
+    }
+
+    @Override
+    public Dimension2D getScale() {
+        CTTileInfoProperties tile = blipFill.getTile();
+        return (tile == null) ? null : new Dimension2DDouble(
+                tile.isSetSx() ? tile.getSx()/100_000. : 1,
+                tile.isSetSy() ? tile.getSy()/100_000. : 1);
+    }
+
+    @Override
+    public Point2D getOffset() {
+        CTTileInfoProperties tile = blipFill.getTile();
+        return (tile == null) ? null : new Point2D.Double(
+                tile.isSetTx() ? Units.toPoints(tile.getTx()) : 0,
+                tile.isSetTy() ? Units.toPoints(tile.getTy()) : 0);
+    }
+
+    @Override
+    public PaintStyle.FlipMode getFlipMode() {
+        CTTileInfoProperties tile = blipFill.getTile();
+        switch (tile == null || tile.getFlip() == null ? STTileFlipMode.INT_NONE : tile.getFlip().intValue()) {
+            default:
+            case STTileFlipMode.INT_NONE:
+                return PaintStyle.FlipMode.NONE;
+            case STTileFlipMode.INT_X:
+                return PaintStyle.FlipMode.X;
+            case STTileFlipMode.INT_Y:
+                return PaintStyle.FlipMode.Y;
+            case STTileFlipMode.INT_XY:
+                return PaintStyle.FlipMode.XY;
+        }
+    }
+
+    @Override
+    public PaintStyle.TextureAlignment getAlignment() {
+        CTTileInfoProperties tile = blipFill.getTile();
+        return (tile == null || !tile.isSetAlgn()) ? null
+                : PaintStyle.TextureAlignment.fromOoxmlId(tile.getAlgn().toString());
+    }
+
+    @Override
+    public Insets2D getInsets() {
+        return getRectVal(blipFill.getSrcRect());
+    }
+
+    @Override
+    public Insets2D getStretch() {
+        return getRectVal(blipFill.isSetStretch() ? blipFill.getStretch().getFillRect() : null);
+    }
+
+    private static Insets2D getRectVal(CTRelativeRect rect) {
+        return rect == null ? null : new Insets2D(
+            getRectVal(rect::isSetT, rect::getT),
+            getRectVal(rect::isSetL, rect::getL),
+            getRectVal(rect::isSetB, rect::getB),
+            getRectVal(rect::isSetR, rect::getR)
+        );
+    }
+
+    private static int getRectVal(Supplier<Boolean> isSet, Supplier<Integer> val) {
+        return isSet.get() ? val.get() : 0;
+    }
+}

Propchange: poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTexturePaint.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFill.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFill.java?rev=1869669&r1=1869668&r2=1869669&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFill.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFill.java Mon Nov 11 23:06:29 2019
@@ -228,45 +228,44 @@ public final class HSLFFill {
 
 
     public FillStyle getFillStyle() {
-        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),
-                // the direction, e.g. top right, and bounds (e.g. for rectangular boxes)
-                switch (fillType) {
-                    case FILL_SOLID:
-                        return DrawPaint.createSolidPaint(getForegroundColor());
-                    case FILL_SHADE_SHAPE:
-                        return getGradientPaint(GradientType.shape);
-                    case FILL_SHADE_CENTER:
-                    case FILL_SHADE_TITLE:
-                        return getGradientPaint(GradientType.circular);
-                    case FILL_SHADE:
-                    case FILL_SHADE_SCALE:
-                        return getGradientPaint(GradientType.linear);
-                    case FILL_PICTURE:
-                        return getTexturePaint();
-                    default:
-                        LOG.log(POILogger.WARN, "unsuported fill type: " + fillType);
-                        return null;
-                }
-            }
-        };
+        return this::getPaintStyle;
+    }
+
+    private PaintStyle getPaintStyle() {
+        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),
+        // the direction, e.g. top right, and bounds (e.g. for rectangular boxes)
+        switch (fillType) {
+            case FILL_SOLID:
+                return DrawPaint.createSolidPaint(getForegroundColor());
+            case FILL_SHADE_SHAPE:
+                return getGradientPaint(GradientType.shape);
+            case FILL_SHADE_CENTER:
+            case FILL_SHADE_TITLE:
+                return getGradientPaint(GradientType.circular);
+            case FILL_SHADE:
+            case FILL_SHADE_SCALE:
+                return getGradientPaint(GradientType.linear);
+            case FILL_PICTURE:
+                return getTexturePaint();
+            default:
+                LOG.log(POILogger.WARN, "unsuported fill type: " + fillType);
+                return null;
+        }
     }
     
     private boolean isRotatedWithShape() {



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