From 967a3b0e0c8c185f4c780200671e2ff6eddc8e0a Mon Sep 17 00:00:00 2001 From: Andreas Beeker Date: Sun, 12 Feb 2017 22:30:49 +0000 Subject: [PATCH] #60625 - Rendering issue with background and shape overlayed by image git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1782706 13f79535-47bb-0310-9956-ffa450edef68 --- .../apache/poi/sl/draw/DrawMasterSheet.java | 6 +- .../org/apache/poi/sl/draw/DrawPaint.java | 153 ++++++++------- .../apache/poi/xslf/usermodel/XSLFColor.java | 14 +- .../apache/poi/xslf/usermodel/XSLFShape.java | 10 +- .../poi/xslf/usermodel/XSLFSimpleShape.java | 54 +++--- .../apache/poi/hslf/usermodel/HSLFFill.java | 177 ++++++++++++++++-- 6 files changed, 291 insertions(+), 123 deletions(-) diff --git a/src/java/org/apache/poi/sl/draw/DrawMasterSheet.java b/src/java/org/apache/poi/sl/draw/DrawMasterSheet.java index 9dbbe251c..2dbbfef69 100644 --- a/src/java/org/apache/poi/sl/draw/DrawMasterSheet.java +++ b/src/java/org/apache/poi/sl/draw/DrawMasterSheet.java @@ -33,21 +33,21 @@ public class DrawMasterSheet extends DrawSheet { } /** - * Checks if this sheet displays the specified shape. + * Checks if this {@code sheet} displays the specified shape. * * Subclasses can override it and skip certain shapes from drawings, * for instance, slide masters and layouts don't display placeholders */ @Override protected boolean canDraw(Graphics2D graphics, Shape shape) { + Slide slide = (Slide)graphics.getRenderingHint(Drawable.CURRENT_SLIDE); if (shape instanceof SimpleShape) { // in XSLF, slidenumber and date shapes aren't marked as placeholders opposed to HSLF Placeholder ph = ((SimpleShape)shape).getPlaceholder(); if (ph != null) { - Slide slide = (Slide)graphics.getRenderingHint(Drawable.CURRENT_SLIDE); return slide.getDisplayPlaceholder(ph); } } - return true; + return slide.getFollowMasterGraphics(); } } diff --git a/src/java/org/apache/poi/sl/draw/DrawPaint.java b/src/java/org/apache/poi/sl/draw/DrawPaint.java index d42e38338..3b1bc1ab8 100644 --- a/src/java/org/apache/poi/sl/draw/DrawPaint.java +++ b/src/java/org/apache/poi/sl/draw/DrawPaint.java @@ -21,8 +21,6 @@ import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics2D; import java.awt.LinearGradientPaint; -import java.awt.MultipleGradientPaint.ColorSpaceType; -import java.awt.MultipleGradientPaint.CycleMethod; import java.awt.Paint; import java.awt.RadialGradientPaint; import java.awt.geom.AffineTransform; @@ -44,18 +42,18 @@ import org.apache.poi.util.POILogger; /** * This class handles color transformations. - * + * * @see HSL code taken from Java Tips Weblog */ public class DrawPaint { // HSL code is public domain - see https://tips4java.wordpress.com/contact-us/ - + private static final POILogger LOG = POILogFactory.getLogger(DrawPaint.class); private static final Color TRANSPARENT = new Color(1f,1f,1f,0f); - + protected PlaceableShape shape; - + public DrawPaint(PlaceableShape shape) { this.shape = shape; } @@ -68,17 +66,27 @@ public class DrawPaint { throw new NullPointerException("Color needs to be specified"); } this.solidColor = new ColorStyle(){ + @Override public Color getColor() { return new Color(color.getRed(), color.getGreen(), color.getBlue()); } + @Override public int getAlpha() { return (int)Math.round(color.getAlpha()*100000./255.); } + @Override public int getHueOff() { return -1; } + @Override public int getHueMod() { return -1; } + @Override public int getSatOff() { return -1; } + @Override public int getSatMod() { return -1; } + @Override public int getLumOff() { return -1; } + @Override public int getLumMod() { return -1; } + @Override public int getShade() { return -1; } + @Override public int getTint() { return -1; } }; } @@ -89,20 +97,21 @@ public class DrawPaint { } this.solidColor = color; } - + + @Override public ColorStyle getSolidColor() { return solidColor; } } - + public static SolidPaint createSolidPaint(final Color color) { return (color == null) ? null : new SimpleSolidPaint(color); } - + public static SolidPaint createSolidPaint(final ColorStyle color) { return (color == null) ? null : new SimpleSolidPaint(color); } - + public Paint getPaint(Graphics2D graphics, PaintStyle paint) { if (paint instanceof SolidPaint) { return getSolidPaint((SolidPaint)paint, graphics); @@ -113,7 +122,7 @@ public class DrawPaint { } return null; } - + protected Paint getSolidPaint(SolidPaint fill, Graphics2D graphics) { return applyColorTransform(fill.getSolidColor()); } @@ -133,9 +142,11 @@ public class DrawPaint { protected Paint getTexturePaint(TexturePaint fill, Graphics2D graphics) { InputStream is = fill.getImageData(); - if (is == null) return null; + if (is == null) { + return null; + } assert(graphics != null); - + ImageRenderer renderer = DrawPictureShape.getImageRenderer(graphics, fill.getContentType()); try { @@ -153,12 +164,12 @@ public class DrawPaint { if (0 <= alpha && alpha < 100000) { renderer.setAlpha(alpha/100000.f); } - + Rectangle2D textAnchor = shape.getAnchor(); BufferedImage image; if ("image/x-wmf".equals(fill.getContentType())) { // don't rely on wmf dimensions, use dimension of anchor - // TODO: check pixels vs. points for image dimension + // TODO: check pixels vs. points for image dimension image = renderer.getImage(new Dimension((int)textAnchor.getWidth(), (int)textAnchor.getHeight())); } else { image = renderer.getImage(); @@ -172,10 +183,10 @@ public class DrawPaint { return paint; } - + /** * Convert color transformations in {@link ColorStyle} to a {@link Color} instance - * + * * @see Using Office Open XML to Customize Document Formatting in the 2007 Office System * @see saturation modulation (satMod) * @see Office Open XML satMod results in more than 100% saturation @@ -186,7 +197,7 @@ public class DrawPaint { if (color == null || color.getColor() == null) { return TRANSPARENT; } - + Color result = color.getColor(); double alpha = getAlpha(result, color); @@ -198,7 +209,7 @@ public class DrawPaint { applyTint(hsl, color); result = HSL2RGB(hsl[0], hsl[1], hsl[2], alpha); - + return result; } @@ -210,10 +221,10 @@ public class DrawPaint { } return Math.min(1, Math.max(0, alpha)); } - + /** * Apply the modulation and offset adjustments to the given HSL part - * + * * Example for lumMod/lumOff: * The lumMod value is the percent luminance. A lumMod value of "60000", * is 60% of the luminance of the original color. @@ -221,80 +232,92 @@ public class DrawPaint { * attribute is the only one of the tags shown here that appears. * The tag appears after the tag when the color is a * tint of the original. The lumOff value always equals 1-lumMod, which is used in the tint calculation - * + * * Despite having different ways to display the tint and shade percentages, * all of the programs use the same method to calculate the resulting color. * Convert the original RGB value to HSL ... and then adjust the luminance (L) * with one of the following equations before converting the HSL value back to RGB. * (The % tint in the following equations refers to the tint, themetint, themeshade, * or lumMod values, as applicable.) - * + * * @param hsl the hsl values * @param hslPart the hsl part to modify [0..2] * @param mod the modulation adjustment * @param off the offset adjustment * @return the modified hsl value - * + * */ private static void applyHslModOff(double hsl[], int hslPart, int mod, int off) { - if (mod == -1) mod = 100000; - if (off == -1) off = 0; + if (mod == -1) { + mod = 100000; + } + if (off == -1) { + off = 0; + } if (!(mod == 100000 && off == 0)) { double fOff = off / 1000d; double fMod = mod / 100000d; hsl[hslPart] = hsl[hslPart]*fMod+fOff; } } - + /** * Apply the shade - * + * * For a shade, the equation is luminance * %tint. */ private static void applyShade(double hsl[], ColorStyle fc) { int shade = fc.getShade(); - if (shade == -1) return; - + if (shade == -1) { + return; + } + double fshade = shade / 100000.d; - + hsl[2] *= fshade; } /** * Apply the tint - * + * * For a tint, the equation is luminance * %tint + (1-%tint). * (Note that 1-%tint is equal to the lumOff value in DrawingML.) */ private static void applyTint(double hsl[], ColorStyle fc) { int tint = fc.getTint(); - if (tint == -1) return; - + if (tint == -1) { + return; + } + double ftint = tint / 100000.f; hsl[2] = hsl[2] * ftint + (100 - ftint*100.); } - protected Paint createLinearGradientPaint(GradientPaint fill, Graphics2D graphics) { + // TODO: we need to find the two points for gradient - the problem is, which point at the outline + // do you take? My solution would be to apply the gradient rotation to the shape in reverse + // and then scan the shape for the largest possible horizontal distance + double angle = fill.getGradientAngle(); + if (!fill.isRotatedWithShape()) { + angle -= shape.getRotation(); + } + Rectangle2D anchor = DrawShape.getAnchor(graphics, shape); + final double h = anchor.getHeight(), w = anchor.getWidth(), x = anchor.getX(), y = anchor.getY(); - AffineTransform at = AffineTransform.getRotateInstance( - Math.toRadians(angle), - anchor.getX() + anchor.getWidth() / 2, - anchor.getY() + anchor.getHeight() / 2); + AffineTransform at = AffineTransform.getRotateInstance(Math.toRadians(angle), anchor.getCenterX(), anchor.getCenterY()); - double diagonal = Math.sqrt(anchor.getHeight() * anchor.getHeight() + anchor.getWidth() * anchor.getWidth()); - Point2D p1 = new Point2D.Double(anchor.getX() + anchor.getWidth() / 2 - diagonal / 2, - anchor.getY() + anchor.getHeight() / 2); + double diagonal = Math.sqrt(h * h + w * w); + Point2D p1 = new Point2D.Double(x + w / 2 - diagonal / 2, y + h / 2); p1 = at.transform(p1, null); - Point2D p2 = new Point2D.Double(anchor.getX() + anchor.getWidth(), anchor.getY() + anchor.getHeight() / 2); + Point2D p2 = new Point2D.Double(x + w, y + h / 2); p2 = at.transform(p2, null); - - snapToAnchor(p1, anchor); - snapToAnchor(p2, anchor); + +// snapToAnchor(p1, anchor); +// snapToAnchor(p2, anchor); if (p1.equals(p2)) { // gradient paint on the same point throws an exception ... and doesn't make sense @@ -303,28 +326,14 @@ public class DrawPaint { float[] fractions = fill.getGradientFractions(); Color[] colors = new Color[fractions.length]; - + int i = 0; for (ColorStyle fc : fill.getGradientColors()) { // if fc is null, use transparent color to get color of background colors[i++] = (fc == null) ? TRANSPARENT : applyColorTransform(fc); } - AffineTransform grAt = new AffineTransform(); - if(fill.isRotatedWithShape()) { - double rotation = shape.getRotation(); - if (rotation != 0.) { - double centerX = anchor.getX() + anchor.getWidth() / 2; - double centerY = anchor.getY() + anchor.getHeight() / 2; - - grAt.translate(centerX, centerY); - grAt.rotate(Math.toRadians(-rotation)); - grAt.translate(-centerX, -centerY); - } - } - - return new LinearGradientPaint - (p1, p2, fractions, colors, CycleMethod.NO_CYCLE, ColorSpaceType.SRGB, grAt); + return new LinearGradientPaint(p1, p2, fractions, colors); } protected Paint createRadialGradientPaint(GradientPaint fill, Graphics2D graphics) { @@ -348,7 +357,7 @@ public class DrawPaint { protected Paint createPathGradientPaint(GradientPaint fill, Graphics2D graphics) { // currently we ignore an eventually center setting - + float[] fractions = fill.getGradientFractions(); Color[] colors = new Color[fractions.length]; @@ -359,7 +368,7 @@ public class DrawPaint { return new PathGradientPaint(colors, fractions); } - + protected void snapToAnchor(Point2D p, Rectangle2D anchor) { if (p.getX() < anchor.getX()) { p.setLocation(anchor.getX(), p.getY()); @@ -420,9 +429,13 @@ public class DrawPaint { } private static double HUE2RGB(double p, double q, double h) { - if (h < 0d) h += 1d; + if (h < 0d) { + h += 1d; + } - if (h > 1d) h -= 1d; + if (h > 1d) { + h -= 1d; + } if (6d * h < 1d) { return p + ((q - p) * 6d * h); @@ -491,10 +504,10 @@ public class DrawPaint { return new double[] {h, s * 100, l * 100}; } - + /** * Convert sRGB float component [0..1] from sRGB to linear RGB [0..100000] - * + * * @see Color#getRGBColorComponents(float[]) */ public static int srgb2lin(float sRGB) { @@ -506,10 +519,10 @@ public class DrawPaint { return (int)Math.rint(100000d * Math.pow((sRGB + 0.055d) / 1.055d, 2.4d)); } } - + /** * Convert linear RGB [0..100000] to sRGB float component [0..1] - * + * * @see Color#getRGBColorComponents(float[]) */ public static float lin2srgb(int linRGB) { diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFColor.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFColor.java index 30f353811..d5c40a7cc 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFColor.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFColor.java @@ -74,42 +74,52 @@ public class XSLFColor { public ColorStyle getColorStyle() { return new ColorStyle() { + @Override public Color getColor() { return _color; } + @Override public int getAlpha() { return getRawValue("alpha"); } + @Override public int getHueOff() { return getRawValue("hueOff"); } + @Override public int getHueMod() { return getRawValue("hueMod"); } + @Override public int getSatOff() { return getRawValue("satOff"); } + @Override public int getSatMod() { return getRawValue("satMod"); } + @Override public int getLumOff() { return getRawValue("lumOff"); } + @Override public int getLumMod() { return getRawValue("lumMod"); } + @Override public int getShade() { return getRawValue("shade"); } + @Override public int getTint() { return getRawValue("tint"); } @@ -141,7 +151,9 @@ public class XSLFColor { } // find referenced CTColor in the theme and convert it to java.awt.Color via a recursive call CTColor ctColor = theme.getCTColor(colorRef); - if(ctColor != null) color = toColor(ctColor, null); + if(ctColor != null) { + color = toColor(ctColor, null); + } } else if (ch instanceof CTScRgbColor) { // color in percentage is in linear RGB color space, i.e. needs to be gamma corrected for AWT color CTScRgbColor scrgb = (CTScRgbColor)ch; diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShape.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShape.java index d3011604c..91b5abd63 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShape.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShape.java @@ -58,7 +58,6 @@ import org.openxmlformats.schemas.drawingml.x2006.main.CTSolidColorFillPropertie import org.openxmlformats.schemas.drawingml.x2006.main.CTStyleMatrix; import org.openxmlformats.schemas.drawingml.x2006.main.CTStyleMatrixReference; import org.openxmlformats.schemas.drawingml.x2006.main.STPathShadeType; -import org.openxmlformats.schemas.drawingml.x2006.main.STSchemeColorVal; import org.openxmlformats.schemas.presentationml.x2006.main.CTApplicationNonVisualDrawingProps; import org.openxmlformats.schemas.presentationml.x2006.main.CTBackgroundProperties; import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder; @@ -395,9 +394,9 @@ public abstract class XSLFShape implements Shape { // if there's a reference to the placeholder color, // stop evaluating further and let the caller select // the next style inheritance level - if (STSchemeColorVal.PH_CLR.equals(solidFill.getSchemeClr().getVal())) { - return null; - } +// if (STSchemeColorVal.PH_CLR.equals(solidFill.getSchemeClr().getVal())) { +// return null; +// } if (phClr == null) { phClr = solidFill.getSchemeClr(); } @@ -483,8 +482,7 @@ public abstract class XSLFShape implements Shape { } public boolean isRotatedWithShape() { - // TODO: is this correct??? - return (gradFill.isSetRotWithShape() || !gradFill.getRotWithShape()); + return gradFill.getRotWithShape(); } public GradientType getGradientType() { diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSimpleShape.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSimpleShape.java index 5d1d23191..19eba48ed 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSimpleShape.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSimpleShape.java @@ -320,6 +320,12 @@ public abstract class XSLFSimpleShape extends XSLFShape public boolean fetch(XSLFShape shape) { CTLineProperties spPr = getLn(shape, false); XSLFFillProperties fp = XSLFPropertiesDelegate.getFillDelegate(spPr); + + if (fp != null && fp.isSetNoFill()) { + setValue(null); + return true; + } + PackagePart pp = shape.getSheet().getPackagePart(); PaintStyle paint = selectPaint(fp, null, pp, theme, hasPlaceholder); if (paint != null) { @@ -331,39 +337,41 @@ public abstract class XSLFSimpleShape extends XSLFShape if (style != null) { fp = XSLFPropertiesDelegate.getFillDelegate(style.getLnRef()); paint = selectPaint(fp, null, pp, theme, hasPlaceholder); + + // line color was not found, check if it is defined in the theme + if (paint == null) { + paint = getThemePaint(style, pp); + } } + if (paint != null) { setValue(paint); return true; } + return false; } + + PaintStyle getThemePaint(CTShapeStyle style, PackagePart pp) { + // get a reference to a line style within the style matrix. + CTStyleMatrixReference lnRef = style.getLnRef(); + if (lnRef == null) { + return null; + } + int idx = (int)lnRef.getIdx(); + CTSchemeColor phClr = lnRef.getSchemeClr(); + if(idx <= 0){ + return null; + } + + CTLineProperties props = theme.getXmlObject().getThemeElements().getFmtScheme().getLnStyleLst().getLnArray(idx - 1); + XSLFFillProperties fp = XSLFPropertiesDelegate.getFillDelegate(props); + return selectPaint(fp, phClr, pp, theme, hasPlaceholder); + } }; fetchShapeProperty(fetcher); - PaintStyle paint = fetcher.getValue(); - if (paint != null) { - return paint; - } - - // line color was not found, check if it is defined in the theme - CTShapeStyle style = getSpStyle(); - if (style == null) { - return null; - } - - // get a reference to a line style within the style matrix. - CTStyleMatrixReference lnRef = style.getLnRef(); - int idx = (int)lnRef.getIdx(); - CTSchemeColor phClr = lnRef.getSchemeClr(); - if(idx > 0){ - CTLineProperties props = theme.getXmlObject().getThemeElements().getFmtScheme().getLnStyleLst().getLnArray(idx - 1); - XSLFFillProperties fp = XSLFPropertiesDelegate.getFillDelegate(props); - PackagePart pp = sheet.getPackagePart(); - paint = selectPaint(fp, phClr, pp, theme, hasPlaceholder); - } - - return paint; + return fetcher.getValue(); } /** 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 8eb18ee69..b7d23871e 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFill.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFill.java @@ -31,6 +31,7 @@ import org.apache.poi.ddf.EscherProperties; import org.apache.poi.ddf.EscherRecord; import org.apache.poi.ddf.EscherSimpleProperty; import org.apache.poi.hslf.record.Document; +import org.apache.poi.hslf.record.RecordTypes; import org.apache.poi.sl.draw.DrawPaint; import org.apache.poi.sl.usermodel.ColorStyle; import org.apache.poi.sl.usermodel.FillStyle; @@ -38,6 +39,8 @@ import org.apache.poi.sl.usermodel.PaintStyle; import org.apache.poi.sl.usermodel.PaintStyle.GradientPaint; import org.apache.poi.sl.usermodel.PaintStyle.GradientPaint.GradientType; import org.apache.poi.sl.usermodel.PaintStyle.TexturePaint; +import org.apache.poi.util.BitField; +import org.apache.poi.util.BitFieldFactory; import org.apache.poi.util.LittleEndian; import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogger; @@ -101,6 +104,108 @@ public final class HSLFFill { */ public static final int FILL_BACKGROUND = 9; + /** + * A bit that specifies whether the RecolorFillAsPicture bit is set. + * A value of 0x0 specifies that the fRecolorFillAsPicture MUST be ignored. + * The default value for this property is 0x0. + */ + private static final BitField FILL_USE_RECOLOR_FILL_AS_PICTURE = BitFieldFactory.getInstance(0x00400000); + + /** + * A bit that specifies whether the UseShapeAnchor bit is set. + * A value of 0x0 specifies that the fUseShapeAnchor MUST be ignored. + * The default value for this property is 0x0. + */ + private static final BitField FILL_USE_USE_SHAPE_ANCHOR = BitFieldFactory.getInstance(0x00200000); + + /** + * A bit that specifies whether the Filled bit is set. + * A value of 0x0 specifies that the Filled MUST be ignored. + * The default value for this property is 0x0. + */ + private static final BitField FILL_USE_FILLED = BitFieldFactory.getInstance(0x00100000); + + /** + * A bit that specifies whether the HitTestFill bit is set. + * A value of 0x0 specifies that the HitTestFill MUST be ignored. + * The default value for this property is 0x0. + */ + private static final BitField FILL_USE_HIT_TEST_FILL = BitFieldFactory.getInstance(0x00080000); + + /** + * A bit that specifies whether the fillShape bit is set. + * A value of 0x0 specifies that the fillShape MUST be ignored. + * The default value for this property is 0x0. + */ + private static final BitField FILL_USE_FILL_SHAPE = BitFieldFactory.getInstance(0x00040000); + + /** + * A bit that specifies whether the fillUseRect bit is set. + * A value of 0x0 specifies that the fillUseRect MUST be ignored. + * The default value for this property is 0x0. + */ + private static final BitField FILL_USE_FILL_USE_RECT = BitFieldFactory.getInstance(0x00020000); + + /** + * A bit that specifies whether the fNoFillHitTest bit is set. + * A value of 0x0 specifies that the fNoFillHitTest MUST be ignored. + * The default value for this property is 0x0. + */ + private static final BitField FILL_USE_NO_FILL_HIT_TEST = BitFieldFactory.getInstance(0x00010000); + + /** + * A bit that specifies how to recolor a picture fill. If this bit is set to 0x1, the pictureFillCrMod + * property of the picture fill is used for recoloring. If this bit is set to 0x0, the fillCrMod property, + * as defined in section 2.3.7.6, is used for recoloring. + * If UsefRecolorFillAsPicture equals 0x0, this value MUST be ignored. + * The default value for this property is 0x0. + */ + private static final BitField FILL_RECOLOR_FILL_AS_PICTURE = BitFieldFactory.getInstance(0x00000040); + + /** + * A bit that specifies whether the fill is rotated with the shape. + * If UseUseShapeAnchor equals 0x0, this value MUST be ignored. + * The default value for this property is 0x0. + */ + private static final BitField FILL_USE_SHAPE_ANCHOR = BitFieldFactory.getInstance(0x00000020); + + /** + * A bit that specifies whether the fill is rendered if the shape is a 2-D shape. + * If this bit is set to 0x1, the fill of this shape is rendered based on the properties of the Fill Style + * property set. If this bit is set to 0x0, the fill of this shape is not rendered. + * If UseFilled is 0x0, this value MUST be ignored. The default value for this property is 0x1. + */ + private static final BitField FILL_FILLED = BitFieldFactory.getInstance(0x00000010); + + /** + * A bit that specifies whether this fill will be hit tested. + * If UsefHitTestFill equals 0x0, this value MUST be ignored. + * The default value for this property is 0x1. + */ + private static final BitField FILL_HIT_TEST_FILL = BitFieldFactory.getInstance(0x00000008); + + /** + * A bit that specifies how the fill is aligned. If this bit is set to 0x1, the fill is + * aligned relative to the shape so that it moves with the shape. If this bit is set to 0x0, + * the fill is aligned with the origin of the view. If fUsefillShape equals 0x0, this value MUST be ignored. + * The default value for this property is 0x1. + */ + private static final BitField FILL_FILL_SHAPE = BitFieldFactory.getInstance(0x00000004); + + /** + * A bit that specifies whether to use the rectangle specified by the fillRectLeft, fillRectRight, + * fillRectTop, and fillRectBottom properties, rather than the bounding rectangle of the shape, + * as the filled area. If fUsefillUseRect equals 0x0, this value MUST be ignored. + * The default value for this property is 0x0. + */ + private static final BitField FILL_FILL_USE_RECT = BitFieldFactory.getInstance(0x00000002); + + /** + * A bit that specifies whether this shape will be hit tested as though it were filled. + * If UsefNoFillHitTest equals 0x0, this value MUST be ignored. + * The default value for this property is 0x0. + */ + private static final BitField FILL_NO_FILL_HIT_TEST = BitFieldFactory.getInstance(0x00000001); /** @@ -121,6 +226,7 @@ public final class HSLFFill { public FillStyle getFillStyle() { return new FillStyle() { + @Override public PaintStyle getPaint() { final int fillType = getFillType(); // TODO: fix gradient types, this mismatches with the MS-ODRAW definition ... @@ -150,11 +256,19 @@ public final class HSLFFill { private GradientPaint getGradientPaint(final GradientType gradientType) { - final AbstractEscherOptRecord opt = shape.getEscherOptRecord(); + AbstractEscherOptRecord opt = shape.getEscherOptRecord(); final EscherArrayProperty ep = HSLFShape.getEscherProperty(opt, EscherProperties.FILL__SHADECOLORS); final int colorCnt = (ep == null) ? 0 : ep.getNumberOfElementsInArray(); + // NOFILLHITTEST can be in the normal escher opt record but also in the tertiary record + // the extended bit fields seem to be in the second + opt = (AbstractEscherOptRecord)shape.getEscherChild(RecordTypes.EscherUserDefined); + EscherSimpleProperty p = HSLFShape.getEscherProperty(opt, EscherProperties.FILL__NOFILLHITTEST); + int propVal = (p == null) ? 0 : p.getPropertyValue(); + final boolean rotateWithShape = FILL_USE_USE_SHAPE_ANCHOR.isSet(propVal) && FILL_USE_SHAPE_ANCHOR.isSet(propVal); + return new GradientPaint() { + @Override public double getGradientAngle() { // A value of type FixedPoint, as specified in [MS-OSHARED] section 2.2.1.6, // that specifies the angle of the gradient fill. Zero degrees represents a vertical vector from @@ -162,6 +276,8 @@ public final class HSLFFill { int rot = shape.getEscherProperty(EscherProperties.FILL__ANGLE); return 90-Units.fixedPointToDouble(rot); } + + @Override public ColorStyle[] getGradientColors() { ColorStyle cs[]; if (colorCnt == 0) { @@ -179,9 +295,12 @@ public final class HSLFFill { } return cs; } + private ColorStyle wrapColor(Color col) { return (col == null) ? null : DrawPaint.createSolidPaint(col).getSolidColor(); } + + @Override public float[] getGradientFractions() { float frc[]; if (colorCnt == 0) { @@ -196,9 +315,13 @@ public final class HSLFFill { } return frc; } + + @Override public boolean isRotatedWithShape() { - return false; + return rotateWithShape; } + + @Override public GradientType getGradientType() { return gradientType; } @@ -212,14 +335,17 @@ public final class HSLFFill { } return new TexturePaint() { + @Override public InputStream getImageData() { return new ByteArrayInputStream(pd.getData()); } + @Override public String getContentType() { return pd.getContentType(); } + @Override public int getAlpha() { return (int)(shape.getAlpha(EscherProperties.FILL__FILLOPACITY)*100000.0); } @@ -286,11 +412,11 @@ public final class HSLFFill { public Color getForegroundColor(){ AbstractEscherOptRecord opt = shape.getEscherOptRecord(); EscherSimpleProperty p = HSLFShape.getEscherProperty(opt, EscherProperties.FILL__NOFILLHITTEST); - - if(p != null && (p.getPropertyValue() & 0x10) == 0) return null; - - return shape.getColor(EscherProperties.FILL__FILLCOLOR, EscherProperties.FILL__FILLOPACITY, -1); - + int propVal = (p == null) ? 0 : p.getPropertyValue(); + + return (FILL_USE_FILLED.isSet(propVal) && !FILL_FILLED.isSet(propVal)) + ? null + : shape.getColor(EscherProperties.FILL__FILLCOLOR, EscherProperties.FILL__FILLOPACITY, -1); } /** @@ -298,22 +424,30 @@ public final class HSLFFill { */ public void setForegroundColor(Color color){ AbstractEscherOptRecord opt = shape.getEscherOptRecord(); - if (color == null) { - opt.removeEscherProperty(EscherProperties.FILL__FILLCOLOR); - HSLFShape.setEscherProperty(opt, EscherProperties.FILL__NOFILLHITTEST, 0x150000); - } - else { + opt.removeEscherProperty(EscherProperties.FILL__FILLOPACITY); + opt.removeEscherProperty(EscherProperties.FILL__FILLCOLOR); + + if (color != null) { int rgb = new Color(color.getBlue(), color.getGreen(), color.getRed(), 0).getRGB(); HSLFShape.setEscherProperty(opt, EscherProperties.FILL__FILLCOLOR, rgb); int alpha = color.getAlpha(); - if (alpha == 255) { - opt.removeEscherProperty(EscherProperties.FILL__FILLOPACITY); - } else { + if (alpha < 255) { int alphaFP = Units.doubleToFixedPoint(alpha/255d); HSLFShape.setEscherProperty(opt, EscherProperties.FILL__FILLOPACITY, alphaFP); } - HSLFShape.setEscherProperty(opt, EscherProperties.FILL__NOFILLHITTEST, 0x150011); } + + EscherSimpleProperty p = HSLFShape.getEscherProperty(opt, EscherProperties.FILL__NOFILLHITTEST); + int propVal = (p == null) ? 0 : p.getPropertyValue(); + propVal = FILL_FILLED.setBoolean(propVal, color != null); + propVal = FILL_NO_FILL_HIT_TEST.setBoolean(propVal, color != null); + propVal = FILL_USE_FILLED.set(propVal); + propVal = FILL_USE_FILL_SHAPE.set(propVal); + propVal = FILL_USE_NO_FILL_HIT_TEST.set(propVal); + // TODO: check why we always clear this ... + propVal = FILL_FILL_SHAPE.clear(propVal); + + HSLFShape.setEscherProperty(opt, EscherProperties.FILL__NOFILLHITTEST, propVal); } /** @@ -322,10 +456,11 @@ public final class HSLFFill { public Color getBackgroundColor(){ AbstractEscherOptRecord opt = shape.getEscherOptRecord(); EscherSimpleProperty p = HSLFShape.getEscherProperty(opt, EscherProperties.FILL__NOFILLHITTEST); + int propVal = (p == null) ? 0 : p.getPropertyValue(); - if(p != null && (p.getPropertyValue() & 0x10) == 0) return null; - - return shape.getColor(EscherProperties.FILL__FILLBACKCOLOR, EscherProperties.FILL__FILLOPACITY, -1); + return (FILL_USE_FILLED.isSet(propVal) && !FILL_FILLED.isSet(propVal)) + ? null + : shape.getColor(EscherProperties.FILL__FILLBACKCOLOR, EscherProperties.FILL__FILLOPACITY, -1); } /** @@ -349,7 +484,9 @@ public final class HSLFFill { public HSLFPictureData getPictureData(){ AbstractEscherOptRecord opt = shape.getEscherOptRecord(); EscherSimpleProperty p = HSLFShape.getEscherProperty(opt, EscherProperties.FILL__PATTERNTEXTURE); - if (p == null) return null; + if (p == null) { + return null; + } HSLFSlideShow ppt = shape.getSheet().getSlideShow(); List pict = ppt.getPictureData();