summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAndreas Beeker <kiwiwings@apache.org>2019-07-22 21:29:55 +0000
committerAndreas Beeker <kiwiwings@apache.org>2019-07-22 21:29:55 +0000
commit58eb1a8070ea045deb0999f8d82f120f7a3c3bc6 (patch)
treef97b7ea41d03a719a2a0d336b53a6c3e55160fa2 /src
parent21ed5240e7a5db6b2896987a4c172dcf4a1951c1 (diff)
downloadpoi-58eb1a8070ea045deb0999f8d82f120f7a3c3bc6.tar.gz
poi-58eb1a8070ea045deb0999f8d82f120f7a3c3bc6.zip
Bug 63580 - Fix texture paint handling
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1863600 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'src')
-rw-r--r--src/java/org/apache/poi/sl/draw/DrawPaint.java75
-rw-r--r--src/java/org/apache/poi/sl/draw/DrawPictureShape.java8
-rw-r--r--src/java/org/apache/poi/sl/draw/DrawSimpleShape.java59
-rw-r--r--src/java/org/apache/poi/sl/draw/DrawTexturePaint.java149
-rw-r--r--src/java/org/apache/poi/sl/draw/ImageRenderer.java2
-rw-r--r--src/java/org/apache/poi/sl/usermodel/PaintStyle.java65
-rw-r--r--src/java/org/apache/poi/util/Units.java8
-rw-r--r--src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShape.java50
-rw-r--r--src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestPPTX2PNG.java34
-rw-r--r--src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFill.java23
10 files changed, 409 insertions, 64 deletions
diff --git a/src/java/org/apache/poi/sl/draw/DrawPaint.java b/src/java/org/apache/poi/sl/draw/DrawPaint.java
index ea5e2ad39c..25affcedf8 100644
--- a/src/java/org/apache/poi/sl/draw/DrawPaint.java
+++ b/src/java/org/apache/poi/sl/draw/DrawPaint.java
@@ -18,13 +18,13 @@
package org.apache.poi.sl.draw;
import java.awt.Color;
-import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.LinearGradientPaint;
import java.awt.Paint;
import java.awt.RadialGradientPaint;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
+import java.awt.geom.Dimension2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
@@ -38,11 +38,13 @@ import java.util.function.BiFunction;
import org.apache.poi.sl.usermodel.AbstractColorStyle;
import org.apache.poi.sl.usermodel.ColorStyle;
import org.apache.poi.sl.usermodel.PaintStyle;
+import org.apache.poi.sl.usermodel.PaintStyle.FlipMode;
import org.apache.poi.sl.usermodel.PaintStyle.GradientPaint;
import org.apache.poi.sl.usermodel.PaintStyle.PaintModifier;
import org.apache.poi.sl.usermodel.PaintStyle.SolidPaint;
import org.apache.poi.sl.usermodel.PaintStyle.TexturePaint;
import org.apache.poi.sl.usermodel.PlaceableShape;
+import org.apache.poi.util.Dimension2DDouble;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
@@ -243,11 +245,6 @@ public class DrawPaint {
ImageRenderer renderer = DrawPictureShape.getImageRenderer(graphics, contentType);
- int alpha = fill.getAlpha();
- if (0 <= alpha && alpha < 100000) {
- renderer.setAlpha(alpha/100000.f);
- }
-
// TODO: handle tile settings, currently the pattern is always streched 100% in height/width
Rectangle2D textAnchor = shape.getAnchor();
@@ -258,25 +255,67 @@ public class DrawPaint {
renderer.loadImage(is, contentType);
- final BufferedImage image;
- switch (contentType) {
- case "image/x-wmf":
- case "image/x-emf":
- // don't rely on wmf dimensions, use dimension of anchor
- // TODO: check pixels vs. points for image dimension
- image = renderer.getImage(new Dimension((int)textAnchor.getWidth(), (int)textAnchor.getHeight()));
- break;
- default:
- image = renderer.getImage();
- break;
+ int alpha = fill.getAlpha();
+ if (0 <= alpha && alpha < 100000) {
+ renderer.setAlpha(alpha/100000.f);
+ }
+
+ Dimension2D imgDim = renderer.getDimension();
+ if ("image/x-wmf".contains(contentType)) {
+ // don't rely on wmf dimensions, use dimension of anchor
+ // TODO: check pixels vs. points for image dimension
+ imgDim = new Dimension2DDouble(textAnchor.getWidth(), textAnchor.getHeight());
}
+ BufferedImage image = renderer.getImage(imgDim);
if(image == null) {
LOG.log(POILogger.ERROR, "Can't load image data");
return TRANSPARENT;
}
- return new java.awt.TexturePaint(image, textAnchor);
+ double flipX = 1, flipY = 1;
+ final FlipMode flip = fill.getFlipMode();
+ if (flip != null && flip != FlipMode.NONE) {
+ final int width = image.getWidth(), height = image.getHeight();
+ switch (flip) {
+ case X:
+ flipX = 2;
+ break;
+ case Y:
+ flipY = 2;
+ break;
+ case XY:
+ flipX = 2;
+ flipY = 2;
+ break;
+ }
+
+ final BufferedImage img = new BufferedImage((int)(width*flipX), (int)(height*flipY), BufferedImage.TYPE_INT_ARGB);
+ Graphics2D g = img.createGraphics();
+ g.drawImage(image, 0, 0, null);
+
+ switch (flip) {
+ case X:
+ g.drawImage(image, 2*width, 0, -width, height, null);
+ break;
+ case Y:
+ g.drawImage(image, 0, 2*height, width, -height, null);
+ break;
+ case XY:
+ g.drawImage(image, 2*width, 0, -width, height, null);
+ g.drawImage(image, 0, 2*height, width, -height, null);
+ g.drawImage(image, 2*width, 2*height, -width, -height, null);
+ break;
+ }
+
+ g.dispose();
+ image = img;
+ }
+
+ Shape s = (Shape)graphics.getRenderingHint(Drawable.GRADIENT_SHAPE);
+
+ // TODO: check why original bitmaps scale/behave differently to vector based images
+ return new DrawTexturePaint(image, s, fill, flipX, flipY, renderer instanceof BitmapImageRenderer);
} catch (IOException e) {
LOG.log(POILogger.ERROR, "Can't load image data - using transparent color", e);
return TRANSPARENT;
diff --git a/src/java/org/apache/poi/sl/draw/DrawPictureShape.java b/src/java/org/apache/poi/sl/draw/DrawPictureShape.java
index 84d3ab785a..2a1d1effe9 100644
--- a/src/java/org/apache/poi/sl/draw/DrawPictureShape.java
+++ b/src/java/org/apache/poi/sl/draw/DrawPictureShape.java
@@ -20,6 +20,7 @@ package org.apache.poi.sl.draw;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Insets;
+import java.awt.Paint;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
@@ -114,7 +115,12 @@ public class DrawPictureShape extends DrawSimpleShape {
// falling back to BitmapImageRenderer, at least it gracefully handles invalid images
return bir;
}
-
+
+ @Override
+ protected Paint getFillPaint(Graphics2D graphics) {
+ return null;
+ }
+
@Override
protected PictureShape<?,?> getShape() {
return (PictureShape<?,?>)shape;
diff --git a/src/java/org/apache/poi/sl/draw/DrawSimpleShape.java b/src/java/org/apache/poi/sl/draw/DrawSimpleShape.java
index 3b32fb3152..c77cf32d08 100644
--- a/src/java/org/apache/poi/sl/draw/DrawSimpleShape.java
+++ b/src/java/org/apache/poi/sl/draw/DrawSimpleShape.java
@@ -24,12 +24,14 @@ import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.geom.AffineTransform;
+import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Path2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
+import java.util.function.Consumer;
import org.apache.poi.sl.draw.geom.Context;
import org.apache.poi.sl.draw.geom.CustomGeometry;
@@ -38,6 +40,8 @@ import org.apache.poi.sl.draw.geom.Path;
import org.apache.poi.sl.usermodel.LineDecoration;
import org.apache.poi.sl.usermodel.LineDecoration.DecorationShape;
import org.apache.poi.sl.usermodel.LineDecoration.DecorationSize;
+import org.apache.poi.sl.usermodel.PaintStyle;
+import org.apache.poi.sl.usermodel.PaintStyle.PaintModifier;
import org.apache.poi.sl.usermodel.PaintStyle.SolidPaint;
import org.apache.poi.sl.usermodel.Shadow;
import org.apache.poi.sl.usermodel.SimpleShape;
@@ -58,9 +62,8 @@ public class DrawSimpleShape extends DrawShape {
return;
}
- DrawPaint drawPaint = DrawFactory.getInstance(graphics).getPaint(getShape());
- Paint fill = drawPaint.getPaint(graphics, getShape().getFillStyle().getPaint());
- Paint line = drawPaint.getPaint(graphics, getShape().getStrokeStyle().getPaint());
+ Paint fill = getFillPaint(graphics);
+ Paint line = getLinePaint(graphics);
BasicStroke stroke = getStroke(); // the stroke applies both to the shadow and the shape
graphics.setStroke(stroke);
@@ -71,17 +74,29 @@ public class DrawSimpleShape extends DrawShape {
// then fill the shape interior
if (fill != null) {
+ final Path2D area = new Path2D.Double();
+ graphics.setRenderingHint(Drawable.GRADIENT_SHAPE, area);
+
+ Consumer<PaintModifier> fun = (pm) -> fillArea(graphics, pm, area);
+
+ PaintModifier pm = null;
for (Outline o : elems) {
- if (o.getPath().isFilled()){
- Paint fillMod = drawPaint.getPaint(graphics, getShape().getFillStyle().getPaint(), o.getPath().getFill());
- if (fillMod != null) {
- graphics.setPaint(fillMod);
- java.awt.Shape s = o.getOutline();
- graphics.setRenderingHint(Drawable.GRADIENT_SHAPE, s);
- fillPaintWorkaround(graphics, s);
+ Path path = o.getPath();
+ if (path.isFilled()) {
+ PaintModifier pmOld = pm;
+ pm = path.getFill();
+ if (pmOld != null && pmOld != pm) {
+ fun.accept(pmOld);
+ area.reset();
+ } else {
+ area.append(o.getOutline(), false);
}
}
}
+
+ if (area.getCurrentPoint() != null) {
+ fun.accept(pm);
+ }
}
// then draw any content within this shape (text, image, etc.)
@@ -104,6 +119,30 @@ public class DrawSimpleShape extends DrawShape {
drawDecoration(graphics, line, stroke);
}
+ private void fillArea(Graphics2D graphics, PaintModifier pm, Path2D area) {
+ final SimpleShape<?, ?> ss = getShape();
+ final PaintStyle ps = ss.getFillStyle().getPaint();
+ final DrawPaint drawPaint = DrawFactory.getInstance(graphics).getPaint(ss);
+ final Paint fillMod = drawPaint.getPaint(graphics, ps, pm);
+ if (fillMod != null) {
+ graphics.setPaint(fillMod);
+ fillPaintWorkaround(graphics, area);
+ }
+ }
+
+ protected Paint getFillPaint(Graphics2D graphics) {
+ final PaintStyle ps = getShape().getFillStyle().getPaint();
+ DrawPaint drawPaint = DrawFactory.getInstance(graphics).getPaint(getShape());
+ return drawPaint.getPaint(graphics, ps);
+ }
+
+ protected Paint getLinePaint(Graphics2D graphics) {
+ final PaintStyle ps = getShape().getFillStyle().getPaint();
+ DrawPaint drawPaint = DrawFactory.getInstance(graphics).getPaint(getShape());
+ return drawPaint.getPaint(graphics, getShape().getStrokeStyle().getPaint());
+ }
+
+
protected void drawDecoration(Graphics2D graphics, Paint line, BasicStroke stroke) {
if(line == null) {
return;
diff --git a/src/java/org/apache/poi/sl/draw/DrawTexturePaint.java b/src/java/org/apache/poi/sl/draw/DrawTexturePaint.java
new file mode 100644
index 0000000000..3864ec6cee
--- /dev/null
+++ b/src/java/org/apache/poi/sl/draw/DrawTexturePaint.java
@@ -0,0 +1,149 @@
+/* ====================================================================
+ 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.sl.draw;
+
+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.Dimension2D;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+import java.awt.image.BufferedImage;
+import java.awt.image.ColorModel;
+
+import org.apache.poi.sl.usermodel.PaintStyle;
+
+/* package */ class DrawTexturePaint extends java.awt.TexturePaint {
+ private final PaintStyle.TexturePaint fill;
+ private final Shape shape;
+ private final double flipX, flipY;
+ private final boolean isBitmapSrc;
+
+ 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()));
+ this.fill = fill;
+ this.shape = shape;
+ this.flipX = flipX;
+ this.flipY = flipY;
+ this.isBitmapSrc = isBitmapSrc;
+ }
+
+ @Override
+ public PaintContext createContext(ColorModel cm, Rectangle deviceBounds, Rectangle2D userBounds, AffineTransform xform, RenderingHints hints) {
+
+ final double usr_w, usr_h;
+
+ if (fill.isRotatedWithShape() || shape == null) {
+ usr_w = userBounds.getWidth();
+ usr_h = userBounds.getHeight();
+
+ xform.translate(userBounds.getX(), userBounds.getY());
+ } else {
+ AffineTransform transform = new AffineTransform(xform);
+
+ // Eliminate any post-translation
+ transform.preConcatenate(AffineTransform.getTranslateInstance(
+ -transform.getTranslateX(), -transform.getTranslateY()));
+ Point2D p1 = new Point2D.Double(1, 0);
+ p1 = transform.transform(p1,p1);
+
+ final double rad = Math.atan2(p1.getY(),p1.getX());
+
+ if (rad != 0) {
+ xform.rotate(-rad, userBounds.getCenterX(), userBounds.getCenterY());
+ }
+
+ // 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();
+
+ xform.translate(newBounds.getX(), newBounds.getY());
+ }
+
+ final Dimension2D scale = fill.getScale();
+
+ 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;
+
+ // Alignment happens after the scaling but before any offset.
+ PaintStyle.TextureAlignment ta = fill.getAlignment();
+ final double alg_x, alg_y;
+ switch (ta == null ? PaintStyle.TextureAlignment.TOP_LEFT : ta) {
+ case BOTTOM:
+ alg_x = (usr_w-img_w)/2;
+ alg_y = usr_h-img_h;
+ break;
+ case BOTTOM_LEFT:
+ alg_x = 0;
+ alg_y = usr_h-img_h;
+ break;
+ case BOTTOM_RIGHT:
+ alg_x = usr_w-img_w;
+ alg_y = usr_h-img_h;
+ break;
+ case CENTER:
+ alg_x = (usr_w-img_w)/2;
+ alg_y = (usr_h-img_h)/2;
+ break;
+ case LEFT:
+ alg_x = 0;
+ alg_y = (usr_h-img_h)/2;
+ break;
+ case RIGHT:
+ alg_x = usr_w-img_w;
+ alg_y = (usr_h-img_h)/2;
+ break;
+ case TOP:
+ alg_x = (usr_w-img_w)/2;
+ alg_y = 0;
+ break;
+ default:
+ case TOP_LEFT:
+ alg_x = 0;
+ alg_y = 0;
+ break;
+ case TOP_RIGHT:
+ alg_x = usr_w-img_w;
+ alg_y = 0;
+ break;
+ }
+ xform.translate(alg_x, alg_y);
+
+ // Apply additional horizontal/vertical offset after alignment.
+ // Values are as percentages.
+
+ // TODO: apply scaling of drawing context to offset
+ final Point2D offset = fill.getOffset();
+
+ if (offset != null) {
+ xform.translate(offset.getX(),offset.getY());
+ }
+
+ if (scale != null) {
+ xform.scale(scale.getWidth()/(isBitmapSrc ? flipX : 1.),scale.getHeight()/(isBitmapSrc ? flipY : 1.));
+ }
+
+ return super.createContext(cm, deviceBounds, userBounds, xform, hints);
+ }
+}
diff --git a/src/java/org/apache/poi/sl/draw/ImageRenderer.java b/src/java/org/apache/poi/sl/draw/ImageRenderer.java
index dd4e875b10..4703483b51 100644
--- a/src/java/org/apache/poi/sl/draw/ImageRenderer.java
+++ b/src/java/org/apache/poi/sl/draw/ImageRenderer.java
@@ -99,7 +99,7 @@ public interface ImageRenderer {
void loadImage(byte[] data, String contentType) throws IOException;
/**
- * @return the dimension of the buffered image
+ * @return the dimension of the buffered image in pixel
*/
Dimension2D getDimension();
diff --git a/src/java/org/apache/poi/sl/usermodel/PaintStyle.java b/src/java/org/apache/poi/sl/usermodel/PaintStyle.java
index 0ce32a1eb0..52bcec7146 100644
--- a/src/java/org/apache/poi/sl/usermodel/PaintStyle.java
+++ b/src/java/org/apache/poi/sl/usermodel/PaintStyle.java
@@ -17,6 +17,8 @@
package org.apache.poi.sl.usermodel;
+import java.awt.geom.Dimension2D;
+import java.awt.geom.Point2D;
import java.io.InputStream;
@@ -41,6 +43,45 @@ public interface PaintStyle {
DARKEN_LESS
}
+ enum FlipMode {
+ /** not flipped/mirrored */
+ NONE,
+ /** flipped/mirrored/duplicated along the x axis */
+ X,
+ /** flipped/mirrored/duplicated along the y axis */
+ Y,
+ /** flipped/mirrored/duplicated along the x and y axis */
+ XY
+ }
+
+ enum TextureAlignment {
+ BOTTOM("b"),
+ BOTTOM_LEFT("bl"),
+ BOTTOM_RIGHT("br"),
+ CENTER("ctr"),
+ LEFT("l"),
+ RIGHT("r"),
+ TOP("t"),
+ TOP_LEFT("tl"),
+ TOP_RIGHT("tr");
+
+ private final String ooxmlId;
+
+ TextureAlignment(String ooxmlId) {
+ this.ooxmlId = ooxmlId;
+ }
+
+ public static TextureAlignment fromOoxmlId(String ooxmlId) {
+ for (TextureAlignment ta : values()) {
+ if (ta.ooxmlId.equals(ooxmlId)) {
+ return ta;
+ }
+ }
+ return null;
+ }
+ }
+
+
interface SolidPaint extends PaintStyle {
ColorStyle getSolidColor();
}
@@ -73,5 +114,29 @@ public interface PaintStyle {
* @return the alpha mask in percents [0..100000]
*/
int getAlpha();
+
+ /**
+ * @return {@code true}, if the rotation of the shape is also applied to the texture paint
+ */
+ default boolean isRotatedWithShape() { return true; }
+
+ /**
+ * @return the dimensions of the tiles in percent of the shape dimensions
+ * or {@code null} if no scaling is applied
+ */
+ default Dimension2D getScale() { return null; }
+
+ /**
+ * @return the offset of the tiles in points or {@code null} if there's no offset
+ */
+ default Point2D getOffset() { return null; }
+
+ /**
+ * @return the flip/mirroring/duplication mode
+ */
+ default FlipMode getFlipMode() { return FlipMode.NONE; }
+
+
+ default TextureAlignment getAlignment() { return null; }
}
}
diff --git a/src/java/org/apache/poi/util/Units.java b/src/java/org/apache/poi/util/Units.java
index a2e85e0aa0..e645ca56b5 100644
--- a/src/java/org/apache/poi/util/Units.java
+++ b/src/java/org/apache/poi/util/Units.java
@@ -139,7 +139,7 @@ public class Units {
return (int)Math.rint(points);
}
- public static double pixelToPoints(int pixel) {
+ public static double pixelToPoints(double pixel) {
double points = pixel;
points *= POINT_DPI;
points /= PIXEL_DPI;
@@ -152,6 +152,12 @@ public class Units {
return new Dimension2DDouble(width, height);
}
+ public static Dimension2D pixelToPoints(Dimension2D pointsDim) {
+ double width = pointsDim.getWidth() * POINT_DPI / PIXEL_DPI;
+ double height = pointsDim.getHeight() * POINT_DPI / PIXEL_DPI;
+ return new Dimension2DDouble(width, height);
+ }
+
public static int charactersToEMU(double characters) {
return (int) characters * EMU_PER_CHARACTER;
}
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 f9d58b8e5b..d6c230d436 100644
--- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShape.java
+++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShape.java
@@ -20,6 +20,8 @@
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;
@@ -41,7 +43,9 @@ 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;
@@ -58,7 +62,9 @@ import org.openxmlformats.schemas.drawingml.x2006.main.CTShapeStyle;
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;
@@ -441,6 +447,50 @@ public abstract class XSLFShape implements Shape<XSLFShape,XSLFTextParagraph> {
? 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 ? 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());
+ }
};
}
diff --git a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestPPTX2PNG.java b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestPPTX2PNG.java
index da85aa14fb..c471ae1717 100644
--- a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestPPTX2PNG.java
+++ b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestPPTX2PNG.java
@@ -22,12 +22,11 @@ package org.apache.poi.xslf.usermodel;
import static org.junit.Assume.assumeFalse;
import java.io.File;
-import java.io.FileFilter;
import java.util.Collection;
import java.util.Locale;
-import java.util.Set;
-import java.util.TreeSet;
-import java.util.regex.Pattern;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
import org.apache.poi.POIDataSamples;
import org.apache.poi.xslf.util.PPTX2PNG;
@@ -66,29 +65,15 @@ public class TestPPTX2PNG {
@Parameter(value = 0)
public String pptFile;
+ @SuppressWarnings("ConstantConditions")
@Parameters(name="{0}")
public static Collection<String> data() {
- final Set<String> data = new TreeSet<>();
- for (String f : files.split(", ?")) {
- if (basedir == null) {
- data.add(f);
- } else {
- final Pattern p = Pattern.compile(f);
- basedir.listFiles(new FileFilter(){
- public boolean accept(File pathname) {
- String name = pathname.getName();
- if (p.matcher(name).matches()) {
- data.add(name);
- }
- return false;
- }
- });
- }
- }
-
- return data;
+ Function<String, Stream<String>> fun = (basedir == null) ? Stream::of :
+ (f) -> Stream.of(basedir.listFiles(p -> p.getName().matches(f))).map(File::getName);
+
+ return Stream.of(files.split(", ?")).flatMap(fun).collect(Collectors.toList());
}
-
+
@Test
public void render() throws Exception {
assumeFalse("ignore HSLF / .ppt files in no-scratchpad run", xslfOnly && pptFile.toLowerCase(Locale.ROOT).endsWith("ppt"));
@@ -98,6 +83,7 @@ public class TestPPTX2PNG {
"-slide", "-1", // -1 for all
"-outdir", new File("build/tmp/").getCanonicalPath(),
"-outpat", "${basename}-${slideno}-${ext}.${format}",
+ "-scale", "1.333333333",
"-quiet",
(basedir == null ? samples.getFile(pptFile) : new File(basedir, pptFile)).getAbsolutePath()
};
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 251c4172f0..806c206fb5 100644
--- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFill.java
+++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFill.java
@@ -254,20 +254,20 @@ public final class HSLFFill {
};
}
-
+ private boolean isRotatedWithShape() {
+ // 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
+ AbstractEscherOptRecord opt = shape.getEscherChild(RecordTypes.EscherUserDefined);
+ EscherSimpleProperty p = HSLFShape.getEscherProperty(opt, EscherProperties.FILL__NOFILLHITTEST);
+ int propVal = (p == null) ? 0 : p.getPropertyValue();
+ return FILL_USE_USE_SHAPE_ANCHOR.isSet(propVal) && FILL_USE_SHAPE_ANCHOR.isSet(propVal);
+ }
private GradientPaint getGradientPaint(final GradientType gradientType) {
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 = 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() {
@@ -319,7 +319,7 @@ public final class HSLFFill {
@Override
public boolean isRotatedWithShape() {
- return rotateWithShape;
+ return HSLFFill.this.isRotatedWithShape();
}
@Override
@@ -350,6 +350,11 @@ public final class HSLFFill {
public int getAlpha() {
return (int)(shape.getAlpha(EscherProperties.FILL__FILLOPACITY)*100000.0);
}
+
+ @Override
+ public boolean isRotatedWithShape() {
+ return HSLFFill.this.isRotatedWithShape();
+ }
};
}