#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
This commit is contained in:
Andreas Beeker 2017-02-12 22:30:49 +00:00
parent ee56c07a74
commit 967a3b0e0c
6 changed files with 291 additions and 123 deletions

View File

@ -33,21 +33,21 @@ public class DrawMasterSheet extends DrawSheet {
} }
/** /**
* Checks if this <code>sheet</code> displays the specified shape. * Checks if this {@code sheet} displays the specified shape.
* *
* Subclasses can override it and skip certain shapes from drawings, * Subclasses can override it and skip certain shapes from drawings,
* for instance, slide masters and layouts don't display placeholders * for instance, slide masters and layouts don't display placeholders
*/ */
@Override @Override
protected boolean canDraw(Graphics2D graphics, Shape<?,?> shape) { protected boolean canDraw(Graphics2D graphics, Shape<?,?> shape) {
Slide<?,?> slide = (Slide<?,?>)graphics.getRenderingHint(Drawable.CURRENT_SLIDE);
if (shape instanceof SimpleShape) { if (shape instanceof SimpleShape) {
// in XSLF, slidenumber and date shapes aren't marked as placeholders opposed to HSLF // in XSLF, slidenumber and date shapes aren't marked as placeholders opposed to HSLF
Placeholder ph = ((SimpleShape<?,?>)shape).getPlaceholder(); Placeholder ph = ((SimpleShape<?,?>)shape).getPlaceholder();
if (ph != null) { if (ph != null) {
Slide<?,?> slide = (Slide<?,?>)graphics.getRenderingHint(Drawable.CURRENT_SLIDE);
return slide.getDisplayPlaceholder(ph); return slide.getDisplayPlaceholder(ph);
} }
} }
return true; return slide.getFollowMasterGraphics();
} }
} }

View File

@ -21,8 +21,6 @@ import java.awt.Color;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.LinearGradientPaint; import java.awt.LinearGradientPaint;
import java.awt.MultipleGradientPaint.ColorSpaceType;
import java.awt.MultipleGradientPaint.CycleMethod;
import java.awt.Paint; import java.awt.Paint;
import java.awt.RadialGradientPaint; import java.awt.RadialGradientPaint;
import java.awt.geom.AffineTransform; import java.awt.geom.AffineTransform;
@ -44,18 +42,18 @@ import org.apache.poi.util.POILogger;
/** /**
* This class handles color transformations. * This class handles color transformations.
* *
* @see <a href="https://tips4java.wordpress.com/2009/07/05/hsl-color/">HSL code taken from Java Tips Weblog</a> * @see <a href="https://tips4java.wordpress.com/2009/07/05/hsl-color/">HSL code taken from Java Tips Weblog</a>
*/ */
public class DrawPaint { public class DrawPaint {
// HSL code is public domain - see https://tips4java.wordpress.com/contact-us/ // HSL code is public domain - see https://tips4java.wordpress.com/contact-us/
private static final POILogger LOG = POILogFactory.getLogger(DrawPaint.class); private static final POILogger LOG = POILogFactory.getLogger(DrawPaint.class);
private static final Color TRANSPARENT = new Color(1f,1f,1f,0f); private static final Color TRANSPARENT = new Color(1f,1f,1f,0f);
protected PlaceableShape<?,?> shape; protected PlaceableShape<?,?> shape;
public DrawPaint(PlaceableShape<?,?> shape) { public DrawPaint(PlaceableShape<?,?> shape) {
this.shape = shape; this.shape = shape;
} }
@ -68,17 +66,27 @@ public class DrawPaint {
throw new NullPointerException("Color needs to be specified"); throw new NullPointerException("Color needs to be specified");
} }
this.solidColor = new ColorStyle(){ this.solidColor = new ColorStyle(){
@Override
public Color getColor() { public Color getColor() {
return new Color(color.getRed(), color.getGreen(), color.getBlue()); return new Color(color.getRed(), color.getGreen(), color.getBlue());
} }
@Override
public int getAlpha() { return (int)Math.round(color.getAlpha()*100000./255.); } public int getAlpha() { return (int)Math.round(color.getAlpha()*100000./255.); }
@Override
public int getHueOff() { return -1; } public int getHueOff() { return -1; }
@Override
public int getHueMod() { return -1; } public int getHueMod() { return -1; }
@Override
public int getSatOff() { return -1; } public int getSatOff() { return -1; }
@Override
public int getSatMod() { return -1; } public int getSatMod() { return -1; }
@Override
public int getLumOff() { return -1; } public int getLumOff() { return -1; }
@Override
public int getLumMod() { return -1; } public int getLumMod() { return -1; }
@Override
public int getShade() { return -1; } public int getShade() { return -1; }
@Override
public int getTint() { return -1; } public int getTint() { return -1; }
}; };
} }
@ -89,20 +97,21 @@ public class DrawPaint {
} }
this.solidColor = color; this.solidColor = color;
} }
@Override
public ColorStyle getSolidColor() { public ColorStyle getSolidColor() {
return solidColor; return solidColor;
} }
} }
public static SolidPaint createSolidPaint(final Color color) { public static SolidPaint createSolidPaint(final Color color) {
return (color == null) ? null : new SimpleSolidPaint(color); return (color == null) ? null : new SimpleSolidPaint(color);
} }
public static SolidPaint createSolidPaint(final ColorStyle color) { public static SolidPaint createSolidPaint(final ColorStyle color) {
return (color == null) ? null : new SimpleSolidPaint(color); return (color == null) ? null : new SimpleSolidPaint(color);
} }
public Paint getPaint(Graphics2D graphics, PaintStyle paint) { public Paint getPaint(Graphics2D graphics, PaintStyle paint) {
if (paint instanceof SolidPaint) { if (paint instanceof SolidPaint) {
return getSolidPaint((SolidPaint)paint, graphics); return getSolidPaint((SolidPaint)paint, graphics);
@ -113,7 +122,7 @@ public class DrawPaint {
} }
return null; return null;
} }
protected Paint getSolidPaint(SolidPaint fill, Graphics2D graphics) { protected Paint getSolidPaint(SolidPaint fill, Graphics2D graphics) {
return applyColorTransform(fill.getSolidColor()); return applyColorTransform(fill.getSolidColor());
} }
@ -133,9 +142,11 @@ public class DrawPaint {
protected Paint getTexturePaint(TexturePaint fill, Graphics2D graphics) { protected Paint getTexturePaint(TexturePaint fill, Graphics2D graphics) {
InputStream is = fill.getImageData(); InputStream is = fill.getImageData();
if (is == null) return null; if (is == null) {
return null;
}
assert(graphics != null); assert(graphics != null);
ImageRenderer renderer = DrawPictureShape.getImageRenderer(graphics, fill.getContentType()); ImageRenderer renderer = DrawPictureShape.getImageRenderer(graphics, fill.getContentType());
try { try {
@ -153,12 +164,12 @@ public class DrawPaint {
if (0 <= alpha && alpha < 100000) { if (0 <= alpha && alpha < 100000) {
renderer.setAlpha(alpha/100000.f); renderer.setAlpha(alpha/100000.f);
} }
Rectangle2D textAnchor = shape.getAnchor(); Rectangle2D textAnchor = shape.getAnchor();
BufferedImage image; BufferedImage image;
if ("image/x-wmf".equals(fill.getContentType())) { if ("image/x-wmf".equals(fill.getContentType())) {
// don't rely on wmf dimensions, use dimension of anchor // 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())); image = renderer.getImage(new Dimension((int)textAnchor.getWidth(), (int)textAnchor.getHeight()));
} else { } else {
image = renderer.getImage(); image = renderer.getImage();
@ -172,10 +183,10 @@ public class DrawPaint {
return paint; return paint;
} }
/** /**
* Convert color transformations in {@link ColorStyle} to a {@link Color} instance * Convert color transformations in {@link ColorStyle} to a {@link Color} instance
* *
* @see <a href="https://msdn.microsoft.com/en-us/library/dd560821%28v=office.12%29.aspx">Using Office Open XML to Customize Document Formatting in the 2007 Office System</a> * @see <a href="https://msdn.microsoft.com/en-us/library/dd560821%28v=office.12%29.aspx">Using Office Open XML to Customize Document Formatting in the 2007 Office System</a>
* @see <a href="https://social.msdn.microsoft.com/Forums/office/en-US/040e0a1f-dbfe-4ce5-826b-38b4b6f6d3f7/saturation-modulation-satmod">saturation modulation (satMod)</a> * @see <a href="https://social.msdn.microsoft.com/Forums/office/en-US/040e0a1f-dbfe-4ce5-826b-38b4b6f6d3f7/saturation-modulation-satmod">saturation modulation (satMod)</a>
* @see <a href="http://stackoverflow.com/questions/6754127/office-open-xml-satmod-results-in-more-than-100-saturation">Office Open XML satMod results in more than 100% saturation</a> * @see <a href="http://stackoverflow.com/questions/6754127/office-open-xml-satmod-results-in-more-than-100-saturation">Office Open XML satMod results in more than 100% saturation</a>
@ -186,7 +197,7 @@ public class DrawPaint {
if (color == null || color.getColor() == null) { if (color == null || color.getColor() == null) {
return TRANSPARENT; return TRANSPARENT;
} }
Color result = color.getColor(); Color result = color.getColor();
double alpha = getAlpha(result, color); double alpha = getAlpha(result, color);
@ -198,7 +209,7 @@ public class DrawPaint {
applyTint(hsl, color); applyTint(hsl, color);
result = HSL2RGB(hsl[0], hsl[1], hsl[2], alpha); result = HSL2RGB(hsl[0], hsl[1], hsl[2], alpha);
return result; return result;
} }
@ -210,10 +221,10 @@ public class DrawPaint {
} }
return Math.min(1, Math.max(0, alpha)); return Math.min(1, Math.max(0, alpha));
} }
/** /**
* Apply the modulation and offset adjustments to the given HSL part * Apply the modulation and offset adjustments to the given HSL part
* *
* Example for lumMod/lumOff: * Example for lumMod/lumOff:
* The lumMod value is the percent luminance. A lumMod value of "60000", * The lumMod value is the percent luminance. A lumMod value of "60000",
* is 60% of the luminance of the original color. * 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. * attribute is the only one of the tags shown here that appears.
* The <a:lumOff> tag appears after the <a:lumMod> tag when the color is a * The <a:lumOff> tag appears after the <a:lumMod> tag when the color is a
* tint of the original. The lumOff value always equals 1-lumMod, which is used in the tint calculation * 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, * Despite having different ways to display the tint and shade percentages,
* all of the programs use the same method to calculate the resulting color. * 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) * 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. * 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, * (The % tint in the following equations refers to the tint, themetint, themeshade,
* or lumMod values, as applicable.) * or lumMod values, as applicable.)
* *
* @param hsl the hsl values * @param hsl the hsl values
* @param hslPart the hsl part to modify [0..2] * @param hslPart the hsl part to modify [0..2]
* @param mod the modulation adjustment * @param mod the modulation adjustment
* @param off the offset adjustment * @param off the offset adjustment
* @return the modified hsl value * @return the modified hsl value
* *
*/ */
private static void applyHslModOff(double hsl[], int hslPart, int mod, int off) { private static void applyHslModOff(double hsl[], int hslPart, int mod, int off) {
if (mod == -1) mod = 100000; if (mod == -1) {
if (off == -1) off = 0; mod = 100000;
}
if (off == -1) {
off = 0;
}
if (!(mod == 100000 && off == 0)) { if (!(mod == 100000 && off == 0)) {
double fOff = off / 1000d; double fOff = off / 1000d;
double fMod = mod / 100000d; double fMod = mod / 100000d;
hsl[hslPart] = hsl[hslPart]*fMod+fOff; hsl[hslPart] = hsl[hslPart]*fMod+fOff;
} }
} }
/** /**
* Apply the shade * Apply the shade
* *
* For a shade, the equation is luminance * %tint. * For a shade, the equation is luminance * %tint.
*/ */
private static void applyShade(double hsl[], ColorStyle fc) { private static void applyShade(double hsl[], ColorStyle fc) {
int shade = fc.getShade(); int shade = fc.getShade();
if (shade == -1) return; if (shade == -1) {
return;
}
double fshade = shade / 100000.d; double fshade = shade / 100000.d;
hsl[2] *= fshade; hsl[2] *= fshade;
} }
/** /**
* Apply the tint * Apply the tint
* *
* For a tint, the equation is luminance * %tint + (1-%tint). * For a tint, the equation is luminance * %tint + (1-%tint).
* (Note that 1-%tint is equal to the lumOff value in DrawingML.) * (Note that 1-%tint is equal to the lumOff value in DrawingML.)
*/ */
private static void applyTint(double hsl[], ColorStyle fc) { private static void applyTint(double hsl[], ColorStyle fc) {
int tint = fc.getTint(); int tint = fc.getTint();
if (tint == -1) return; if (tint == -1) {
return;
}
double ftint = tint / 100000.f; double ftint = tint / 100000.f;
hsl[2] = hsl[2] * ftint + (100 - ftint*100.); hsl[2] = hsl[2] * ftint + (100 - ftint*100.);
} }
protected Paint createLinearGradientPaint(GradientPaint fill, Graphics2D graphics) { 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(); double angle = fill.getGradientAngle();
if (!fill.isRotatedWithShape()) {
angle -= shape.getRotation();
}
Rectangle2D anchor = DrawShape.getAnchor(graphics, shape); Rectangle2D anchor = DrawShape.getAnchor(graphics, shape);
final double h = anchor.getHeight(), w = anchor.getWidth(), x = anchor.getX(), y = anchor.getY();
AffineTransform at = AffineTransform.getRotateInstance( AffineTransform at = AffineTransform.getRotateInstance(Math.toRadians(angle), anchor.getCenterX(), anchor.getCenterY());
Math.toRadians(angle),
anchor.getX() + anchor.getWidth() / 2,
anchor.getY() + anchor.getHeight() / 2);
double diagonal = Math.sqrt(anchor.getHeight() * anchor.getHeight() + anchor.getWidth() * anchor.getWidth()); double diagonal = Math.sqrt(h * h + w * w);
Point2D p1 = new Point2D.Double(anchor.getX() + anchor.getWidth() / 2 - diagonal / 2, Point2D p1 = new Point2D.Double(x + w / 2 - diagonal / 2, y + h / 2);
anchor.getY() + anchor.getHeight() / 2);
p1 = at.transform(p1, null); 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); p2 = at.transform(p2, null);
snapToAnchor(p1, anchor); // snapToAnchor(p1, anchor);
snapToAnchor(p2, anchor); // snapToAnchor(p2, anchor);
if (p1.equals(p2)) { if (p1.equals(p2)) {
// gradient paint on the same point throws an exception ... and doesn't make sense // 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(); float[] fractions = fill.getGradientFractions();
Color[] colors = new Color[fractions.length]; Color[] colors = new Color[fractions.length];
int i = 0; int i = 0;
for (ColorStyle fc : fill.getGradientColors()) { for (ColorStyle fc : fill.getGradientColors()) {
// if fc is null, use transparent color to get color of background // if fc is null, use transparent color to get color of background
colors[i++] = (fc == null) ? TRANSPARENT : applyColorTransform(fc); colors[i++] = (fc == null) ? TRANSPARENT : applyColorTransform(fc);
} }
AffineTransform grAt = new AffineTransform(); return new LinearGradientPaint(p1, p2, fractions, colors);
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);
} }
protected Paint createRadialGradientPaint(GradientPaint fill, Graphics2D graphics) { protected Paint createRadialGradientPaint(GradientPaint fill, Graphics2D graphics) {
@ -348,7 +357,7 @@ public class DrawPaint {
protected Paint createPathGradientPaint(GradientPaint fill, Graphics2D graphics) { protected Paint createPathGradientPaint(GradientPaint fill, Graphics2D graphics) {
// currently we ignore an eventually center setting // currently we ignore an eventually center setting
float[] fractions = fill.getGradientFractions(); float[] fractions = fill.getGradientFractions();
Color[] colors = new Color[fractions.length]; Color[] colors = new Color[fractions.length];
@ -359,7 +368,7 @@ public class DrawPaint {
return new PathGradientPaint(colors, fractions); return new PathGradientPaint(colors, fractions);
} }
protected void snapToAnchor(Point2D p, Rectangle2D anchor) { protected void snapToAnchor(Point2D p, Rectangle2D anchor) {
if (p.getX() < anchor.getX()) { if (p.getX() < anchor.getX()) {
p.setLocation(anchor.getX(), p.getY()); p.setLocation(anchor.getX(), p.getY());
@ -420,9 +429,13 @@ public class DrawPaint {
} }
private static double HUE2RGB(double p, double q, double h) { 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) { if (6d * h < 1d) {
return p + ((q - p) * 6d * h); return p + ((q - p) * 6d * h);
@ -491,10 +504,10 @@ public class DrawPaint {
return new double[] {h, s * 100, l * 100}; return new double[] {h, s * 100, l * 100};
} }
/** /**
* Convert sRGB float component [0..1] from sRGB to linear RGB [0..100000] * Convert sRGB float component [0..1] from sRGB to linear RGB [0..100000]
* *
* @see Color#getRGBColorComponents(float[]) * @see Color#getRGBColorComponents(float[])
*/ */
public static int srgb2lin(float sRGB) { 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)); 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] * Convert linear RGB [0..100000] to sRGB float component [0..1]
* *
* @see Color#getRGBColorComponents(float[]) * @see Color#getRGBColorComponents(float[])
*/ */
public static float lin2srgb(int linRGB) { public static float lin2srgb(int linRGB) {

View File

@ -74,42 +74,52 @@ public class XSLFColor {
public ColorStyle getColorStyle() { public ColorStyle getColorStyle() {
return new ColorStyle() { return new ColorStyle() {
@Override
public Color getColor() { public Color getColor() {
return _color; return _color;
} }
@Override
public int getAlpha() { public int getAlpha() {
return getRawValue("alpha"); return getRawValue("alpha");
} }
@Override
public int getHueOff() { public int getHueOff() {
return getRawValue("hueOff"); return getRawValue("hueOff");
} }
@Override
public int getHueMod() { public int getHueMod() {
return getRawValue("hueMod"); return getRawValue("hueMod");
} }
@Override
public int getSatOff() { public int getSatOff() {
return getRawValue("satOff"); return getRawValue("satOff");
} }
@Override
public int getSatMod() { public int getSatMod() {
return getRawValue("satMod"); return getRawValue("satMod");
} }
@Override
public int getLumOff() { public int getLumOff() {
return getRawValue("lumOff"); return getRawValue("lumOff");
} }
@Override
public int getLumMod() { public int getLumMod() {
return getRawValue("lumMod"); return getRawValue("lumMod");
} }
@Override
public int getShade() { public int getShade() {
return getRawValue("shade"); return getRawValue("shade");
} }
@Override
public int getTint() { public int getTint() {
return getRawValue("tint"); 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 // find referenced CTColor in the theme and convert it to java.awt.Color via a recursive call
CTColor ctColor = theme.getCTColor(colorRef); CTColor ctColor = theme.getCTColor(colorRef);
if(ctColor != null) color = toColor(ctColor, null); if(ctColor != null) {
color = toColor(ctColor, null);
}
} else if (ch instanceof CTScRgbColor) { } else if (ch instanceof CTScRgbColor) {
// color in percentage is in linear RGB color space, i.e. needs to be gamma corrected for AWT color // color in percentage is in linear RGB color space, i.e. needs to be gamma corrected for AWT color
CTScRgbColor scrgb = (CTScRgbColor)ch; CTScRgbColor scrgb = (CTScRgbColor)ch;

View File

@ -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.CTStyleMatrix;
import org.openxmlformats.schemas.drawingml.x2006.main.CTStyleMatrixReference; import org.openxmlformats.schemas.drawingml.x2006.main.CTStyleMatrixReference;
import org.openxmlformats.schemas.drawingml.x2006.main.STPathShadeType; 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.CTApplicationNonVisualDrawingProps;
import org.openxmlformats.schemas.presentationml.x2006.main.CTBackgroundProperties; import org.openxmlformats.schemas.presentationml.x2006.main.CTBackgroundProperties;
import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder; import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder;
@ -395,9 +394,9 @@ public abstract class XSLFShape implements Shape<XSLFShape,XSLFTextParagraph> {
// if there's a reference to the placeholder color, // if there's a reference to the placeholder color,
// stop evaluating further and let the caller select // stop evaluating further and let the caller select
// the next style inheritance level // the next style inheritance level
if (STSchemeColorVal.PH_CLR.equals(solidFill.getSchemeClr().getVal())) { // if (STSchemeColorVal.PH_CLR.equals(solidFill.getSchemeClr().getVal())) {
return null; // return null;
} // }
if (phClr == null) { if (phClr == null) {
phClr = solidFill.getSchemeClr(); phClr = solidFill.getSchemeClr();
} }
@ -483,8 +482,7 @@ public abstract class XSLFShape implements Shape<XSLFShape,XSLFTextParagraph> {
} }
public boolean isRotatedWithShape() { public boolean isRotatedWithShape() {
// TODO: is this correct??? return gradFill.getRotWithShape();
return (gradFill.isSetRotWithShape() || !gradFill.getRotWithShape());
} }
public GradientType getGradientType() { public GradientType getGradientType() {

View File

@ -320,6 +320,12 @@ public abstract class XSLFSimpleShape extends XSLFShape
public boolean fetch(XSLFShape shape) { public boolean fetch(XSLFShape shape) {
CTLineProperties spPr = getLn(shape, false); CTLineProperties spPr = getLn(shape, false);
XSLFFillProperties fp = XSLFPropertiesDelegate.getFillDelegate(spPr); XSLFFillProperties fp = XSLFPropertiesDelegate.getFillDelegate(spPr);
if (fp != null && fp.isSetNoFill()) {
setValue(null);
return true;
}
PackagePart pp = shape.getSheet().getPackagePart(); PackagePart pp = shape.getSheet().getPackagePart();
PaintStyle paint = selectPaint(fp, null, pp, theme, hasPlaceholder); PaintStyle paint = selectPaint(fp, null, pp, theme, hasPlaceholder);
if (paint != null) { if (paint != null) {
@ -331,39 +337,41 @@ public abstract class XSLFSimpleShape extends XSLFShape
if (style != null) { if (style != null) {
fp = XSLFPropertiesDelegate.getFillDelegate(style.getLnRef()); fp = XSLFPropertiesDelegate.getFillDelegate(style.getLnRef());
paint = selectPaint(fp, null, pp, theme, hasPlaceholder); 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) { if (paint != null) {
setValue(paint); setValue(paint);
return true; return true;
} }
return false; 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); fetchShapeProperty(fetcher);
PaintStyle paint = fetcher.getValue(); return 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;
} }
/** /**

View File

@ -31,6 +31,7 @@ import org.apache.poi.ddf.EscherProperties;
import org.apache.poi.ddf.EscherRecord; import org.apache.poi.ddf.EscherRecord;
import org.apache.poi.ddf.EscherSimpleProperty; import org.apache.poi.ddf.EscherSimpleProperty;
import org.apache.poi.hslf.record.Document; 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.draw.DrawPaint;
import org.apache.poi.sl.usermodel.ColorStyle; import org.apache.poi.sl.usermodel.ColorStyle;
import org.apache.poi.sl.usermodel.FillStyle; 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;
import org.apache.poi.sl.usermodel.PaintStyle.GradientPaint.GradientType; import org.apache.poi.sl.usermodel.PaintStyle.GradientPaint.GradientType;
import org.apache.poi.sl.usermodel.PaintStyle.TexturePaint; 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.LittleEndian;
import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger; import org.apache.poi.util.POILogger;
@ -101,6 +104,108 @@ public final class HSLFFill {
*/ */
public static final int FILL_BACKGROUND = 9; 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() { public FillStyle getFillStyle() {
return new FillStyle() { return new FillStyle() {
@Override
public PaintStyle getPaint() { public PaintStyle getPaint() {
final int fillType = getFillType(); final int fillType = getFillType();
// TODO: fix gradient types, this mismatches with the MS-ODRAW definition ... // 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) { 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 EscherArrayProperty ep = HSLFShape.getEscherProperty(opt, EscherProperties.FILL__SHADECOLORS);
final int colorCnt = (ep == null) ? 0 : ep.getNumberOfElementsInArray(); 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() { return new GradientPaint() {
@Override
public double getGradientAngle() { public double getGradientAngle() {
// A value of type FixedPoint, as specified in [MS-OSHARED] section 2.2.1.6, // 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 // 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); int rot = shape.getEscherProperty(EscherProperties.FILL__ANGLE);
return 90-Units.fixedPointToDouble(rot); return 90-Units.fixedPointToDouble(rot);
} }
@Override
public ColorStyle[] getGradientColors() { public ColorStyle[] getGradientColors() {
ColorStyle cs[]; ColorStyle cs[];
if (colorCnt == 0) { if (colorCnt == 0) {
@ -179,9 +295,12 @@ public final class HSLFFill {
} }
return cs; return cs;
} }
private ColorStyle wrapColor(Color col) { private ColorStyle wrapColor(Color col) {
return (col == null) ? null : DrawPaint.createSolidPaint(col).getSolidColor(); return (col == null) ? null : DrawPaint.createSolidPaint(col).getSolidColor();
} }
@Override
public float[] getGradientFractions() { public float[] getGradientFractions() {
float frc[]; float frc[];
if (colorCnt == 0) { if (colorCnt == 0) {
@ -196,9 +315,13 @@ public final class HSLFFill {
} }
return frc; return frc;
} }
@Override
public boolean isRotatedWithShape() { public boolean isRotatedWithShape() {
return false; return rotateWithShape;
} }
@Override
public GradientType getGradientType() { public GradientType getGradientType() {
return gradientType; return gradientType;
} }
@ -212,14 +335,17 @@ public final class HSLFFill {
} }
return new TexturePaint() { return new TexturePaint() {
@Override
public InputStream getImageData() { public InputStream getImageData() {
return new ByteArrayInputStream(pd.getData()); return new ByteArrayInputStream(pd.getData());
} }
@Override
public String getContentType() { public String getContentType() {
return pd.getContentType(); return pd.getContentType();
} }
@Override
public int getAlpha() { public int getAlpha() {
return (int)(shape.getAlpha(EscherProperties.FILL__FILLOPACITY)*100000.0); return (int)(shape.getAlpha(EscherProperties.FILL__FILLOPACITY)*100000.0);
} }
@ -286,11 +412,11 @@ public final class HSLFFill {
public Color getForegroundColor(){ public Color getForegroundColor(){
AbstractEscherOptRecord opt = shape.getEscherOptRecord(); AbstractEscherOptRecord opt = shape.getEscherOptRecord();
EscherSimpleProperty p = HSLFShape.getEscherProperty(opt, EscherProperties.FILL__NOFILLHITTEST); 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 (FILL_USE_FILLED.isSet(propVal) && !FILL_FILLED.isSet(propVal))
return shape.getColor(EscherProperties.FILL__FILLCOLOR, EscherProperties.FILL__FILLOPACITY, -1); ? null
: shape.getColor(EscherProperties.FILL__FILLCOLOR, EscherProperties.FILL__FILLOPACITY, -1);
} }
/** /**
@ -298,22 +424,30 @@ public final class HSLFFill {
*/ */
public void setForegroundColor(Color color){ public void setForegroundColor(Color color){
AbstractEscherOptRecord opt = shape.getEscherOptRecord(); AbstractEscherOptRecord opt = shape.getEscherOptRecord();
if (color == null) { opt.removeEscherProperty(EscherProperties.FILL__FILLOPACITY);
opt.removeEscherProperty(EscherProperties.FILL__FILLCOLOR); opt.removeEscherProperty(EscherProperties.FILL__FILLCOLOR);
HSLFShape.setEscherProperty(opt, EscherProperties.FILL__NOFILLHITTEST, 0x150000);
} if (color != null) {
else {
int rgb = new Color(color.getBlue(), color.getGreen(), color.getRed(), 0).getRGB(); int rgb = new Color(color.getBlue(), color.getGreen(), color.getRed(), 0).getRGB();
HSLFShape.setEscherProperty(opt, EscherProperties.FILL__FILLCOLOR, rgb); HSLFShape.setEscherProperty(opt, EscherProperties.FILL__FILLCOLOR, rgb);
int alpha = color.getAlpha(); int alpha = color.getAlpha();
if (alpha == 255) { if (alpha < 255) {
opt.removeEscherProperty(EscherProperties.FILL__FILLOPACITY);
} else {
int alphaFP = Units.doubleToFixedPoint(alpha/255d); int alphaFP = Units.doubleToFixedPoint(alpha/255d);
HSLFShape.setEscherProperty(opt, EscherProperties.FILL__FILLOPACITY, alphaFP); 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(){ public Color getBackgroundColor(){
AbstractEscherOptRecord opt = shape.getEscherOptRecord(); AbstractEscherOptRecord opt = shape.getEscherOptRecord();
EscherSimpleProperty p = HSLFShape.getEscherProperty(opt, EscherProperties.FILL__NOFILLHITTEST); 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 (FILL_USE_FILLED.isSet(propVal) && !FILL_FILLED.isSet(propVal))
? null
return shape.getColor(EscherProperties.FILL__FILLBACKCOLOR, EscherProperties.FILL__FILLOPACITY, -1); : shape.getColor(EscherProperties.FILL__FILLBACKCOLOR, EscherProperties.FILL__FILLOPACITY, -1);
} }
/** /**
@ -349,7 +484,9 @@ public final class HSLFFill {
public HSLFPictureData getPictureData(){ public HSLFPictureData getPictureData(){
AbstractEscherOptRecord opt = shape.getEscherOptRecord(); AbstractEscherOptRecord opt = shape.getEscherOptRecord();
EscherSimpleProperty p = HSLFShape.getEscherProperty(opt, EscherProperties.FILL__PATTERNTEXTURE); EscherSimpleProperty p = HSLFShape.getEscherProperty(opt, EscherProperties.FILL__PATTERNTEXTURE);
if (p == null) return null; if (p == null) {
return null;
}
HSLFSlideShow ppt = shape.getSheet().getSlideShow(); HSLFSlideShow ppt = shape.getSheet().getSlideShow();
List<HSLFPictureData> pict = ppt.getPictureData(); List<HSLFPictureData> pict = ppt.getPictureData();