mirror of https://github.com/apache/poi.git
Bug 63580 - Fix texture paint handling
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1863600 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
21ed5240e7
commit
58eb1a8070
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue