From cf65519f616253bb674595a4a293246ca250cb09 Mon Sep 17 00:00:00 2001 From: Andreas Beeker Date: Sun, 13 Oct 2019 13:30:21 +0000 Subject: [PATCH] SL Common - Fix gradient fills git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1868407 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/poi/sl/draw/DrawPaint.java | 4 + .../apache/poi/sl/draw/PathGradientPaint.java | 29 ++++- .../apache/poi/sl/draw/geom/ArcToCommand.java | 10 +- .../org/apache/poi/xslf/util/PPTX2PNG.java | 4 +- .../apache/poi/hslf/usermodel/HSLFFill.java | 116 +++++++++++++----- .../apache/poi/hslf/usermodel/HSLFShape.java | 14 +-- 6 files changed, 130 insertions(+), 47 deletions(-) diff --git a/src/java/org/apache/poi/sl/draw/DrawPaint.java b/src/java/org/apache/poi/sl/draw/DrawPaint.java index 25affcedf8..f674aa1317 100644 --- a/src/java/org/apache/poi/sl/draw/DrawPaint.java +++ b/src/java/org/apache/poi/sl/draw/DrawPaint.java @@ -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)); diff --git a/src/java/org/apache/poi/sl/draw/PathGradientPaint.java b/src/java/org/apache/poi/sl/draw/PathGradientPaint.java index 2281bbff83..a26abadd2e 100644 --- a/src/java/org/apache/poi/sl/draw/PathGradientPaint.java +++ b/src/java/org/apache/poi/sl/draw/PathGradientPaint.java @@ -17,11 +17,27 @@ 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); } diff --git a/src/java/org/apache/poi/sl/draw/geom/ArcToCommand.java b/src/java/org/apache/poi/sl/draw/geom/ArcToCommand.java index 7d0feb366e..9ff57e6da9 100644 --- a/src/java/org/apache/poi/sl/draw/geom/ArcToCommand.java +++ b/src/java/org/apache/poi/sl/draw/geom/ArcToCommand.java @@ -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) * * - * @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 unskew angle **/ - 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; diff --git a/src/ooxml/java/org/apache/poi/xslf/util/PPTX2PNG.java b/src/ooxml/java/org/apache/poi/xslf/util/PPTX2PNG.java index 4c7e27d558..5a2b8dbe7e 100644 --- a/src/ooxml/java/org/apache/poi/xslf/util/PPTX2PNG.java +++ b/src/ooxml/java/org/apache/poi/xslf/util/PPTX2PNG.java @@ -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 getEmbeddings(); Dimension2D getSize(); diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFill.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFill.java index b87d2869be..3b505f703e 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFill.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFill.java @@ -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 colors = new ArrayList<>(); + final List 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 reflectedColors = new ArrayList<>(colors.subList(1,colors.size())); + Collections.reverse(reflectedColors); + colors.addAll(0, reflectedColors); + + final List 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 { // 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 { 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 { 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; }