mirror of https://github.com/apache/poi.git
#63918 - Fix texture fill - scale stretched images correctly
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1869669 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
d0daf51cba
commit
1794ae97c1
|
@ -17,10 +17,13 @@
|
|||
|
||||
package org.apache.poi.sl.draw;
|
||||
|
||||
import java.awt.AlphaComposite;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.PaintContext;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.RenderingHints;
|
||||
import java.awt.Shape;
|
||||
import java.awt.TexturePaint;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Dimension2D;
|
||||
import java.awt.geom.Point2D;
|
||||
|
@ -28,7 +31,9 @@ import java.awt.geom.Rectangle2D;
|
|||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.ColorModel;
|
||||
|
||||
import org.apache.poi.sl.usermodel.Insets2D;
|
||||
import org.apache.poi.sl.usermodel.PaintStyle;
|
||||
import org.apache.poi.util.Dimension2DDouble;
|
||||
|
||||
/* package */ class DrawTexturePaint extends java.awt.TexturePaint {
|
||||
private final PaintStyle.TexturePaint fill;
|
||||
|
@ -36,6 +41,9 @@ import org.apache.poi.sl.usermodel.PaintStyle;
|
|||
private final double flipX, flipY;
|
||||
private final boolean isBitmapSrc;
|
||||
|
||||
private static final Insets2D INSETS_EMPTY = new Insets2D(0,0,0,0);
|
||||
|
||||
|
||||
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()));
|
||||
|
@ -49,13 +57,10 @@ import org.apache.poi.sl.usermodel.PaintStyle;
|
|||
@Override
|
||||
public PaintContext createContext(ColorModel cm, Rectangle deviceBounds, Rectangle2D userBounds, AffineTransform xform, RenderingHints hints) {
|
||||
|
||||
final double usr_w, usr_h;
|
||||
|
||||
final Dimension2D userDim = new Dimension2DDouble();
|
||||
final Rectangle2D usedBounds;
|
||||
if (fill.isRotatedWithShape() || shape == null) {
|
||||
usr_w = userBounds.getWidth();
|
||||
usr_h = userBounds.getHeight();
|
||||
|
||||
xform.translate(userBounds.getX(), userBounds.getY());
|
||||
usedBounds = userBounds;
|
||||
} else {
|
||||
AffineTransform transform = new AffineTransform(xform);
|
||||
|
||||
|
@ -73,22 +78,111 @@ import org.apache.poi.sl.usermodel.PaintStyle;
|
|||
|
||||
// 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();
|
||||
usedBounds = transform.createTransformedShape(shape).getBounds2D();
|
||||
}
|
||||
userDim.setSize(usedBounds.getWidth(), usedBounds.getHeight());
|
||||
xform.translate(usedBounds.getX(), usedBounds.getY());
|
||||
|
||||
xform.translate(newBounds.getX(), newBounds.getY());
|
||||
BufferedImage bi = getImage(usedBounds);
|
||||
|
||||
if (fill.getStretch() != null) {
|
||||
TexturePaint tp = new TexturePaint(bi, new Rectangle2D.Double(0, 0, bi.getWidth(), bi.getHeight()));
|
||||
return tp.createContext(cm, deviceBounds, usedBounds, xform, hints);
|
||||
} else if (fill.getScale() != null) {
|
||||
AffineTransform newXform = getTiledInstance(usedBounds, (AffineTransform) xform.clone());
|
||||
TexturePaint tp = new TexturePaint(bi, new Rectangle2D.Double(0, 0, bi.getWidth(), bi.getHeight()));
|
||||
return tp.createContext(cm, deviceBounds, userBounds, newXform, hints);
|
||||
} else {
|
||||
return super.createContext(cm, deviceBounds, userBounds, xform, hints);
|
||||
}
|
||||
}
|
||||
|
||||
public BufferedImage getImage(Rectangle2D userBounds) {
|
||||
BufferedImage bi = super.getImage();
|
||||
final Insets2D insets = fill.getInsets();
|
||||
final Insets2D stretch = fill.getStretch();
|
||||
|
||||
if ((insets == null || INSETS_EMPTY.equals(insets)) && (stretch == null)) {
|
||||
return bi;
|
||||
}
|
||||
|
||||
final Dimension2D scale = fill.getScale();
|
||||
if (insets != null && !INSETS_EMPTY.equals(insets)) {
|
||||
final int width = bi.getWidth();
|
||||
final int height = bi.getHeight();
|
||||
|
||||
bi = bi.getSubimage(
|
||||
(int)(Math.max(insets.left,0)/100_000 * width),
|
||||
(int)(Math.max(insets.top,0)/100_000 * height),
|
||||
(int)((100_000-Math.max(insets.left,0)-Math.max(insets.right,0))/100_000 * width),
|
||||
(int)((100_000-Math.max(insets.top,0)-Math.max(insets.bottom,0))/100_000 * height)
|
||||
);
|
||||
|
||||
int addTop = (int)(Math.max(-insets.top, 0)/100_000 * height);
|
||||
int addLeft = (int)(Math.max(-insets.left, 0)/100_000 * width);
|
||||
int addBottom = (int)(Math.max(-insets.bottom, 0)/100_000 * height);
|
||||
int addRight = (int)(Math.max(-insets.right, 0)/100_000 * width);
|
||||
|
||||
// handle outsets
|
||||
if (addTop > 0 || addLeft > 0 || addBottom > 0 || addRight > 0) {
|
||||
int[] buf = new int[bi.getWidth()*bi.getHeight()];
|
||||
bi.getRGB(0, 0, bi.getWidth(), bi.getHeight(), buf, 0, bi.getWidth());
|
||||
BufferedImage borderBi = new BufferedImage(bi.getWidth()+addLeft+addRight, bi.getHeight()+addTop+addBottom, bi.getType());
|
||||
borderBi.setRGB(addLeft, addTop, bi.getWidth(), bi.getHeight(), buf, 0, bi.getWidth());
|
||||
bi = borderBi;
|
||||
}
|
||||
}
|
||||
|
||||
if (stretch != null) {
|
||||
Rectangle2D srcBounds = new Rectangle2D.Double(
|
||||
0, 0, bi.getWidth(), bi.getHeight()
|
||||
);
|
||||
|
||||
Rectangle2D dstBounds = new Rectangle2D.Double(
|
||||
stretch.left/100_000 * userBounds.getWidth(),
|
||||
stretch.top/100_000 * userBounds.getHeight(),
|
||||
(100_000-stretch.left-stretch.right)/100_000 * userBounds.getWidth(),
|
||||
(100_000-stretch.top-stretch.bottom)/100_000 * userBounds.getHeight()
|
||||
);
|
||||
|
||||
BufferedImage stretchBi = new BufferedImage((int)userBounds.getWidth(), (int)userBounds.getHeight(), BufferedImage.TYPE_INT_ARGB);
|
||||
Graphics2D g = stretchBi.createGraphics();
|
||||
|
||||
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
|
||||
g.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_SPEED);
|
||||
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
|
||||
g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
|
||||
|
||||
g.setComposite(AlphaComposite.Clear);
|
||||
g.fillRect(0, 0, stretchBi.getWidth(), stretchBi.getHeight());
|
||||
g.setComposite(AlphaComposite.SrcOver);
|
||||
|
||||
AffineTransform at = new AffineTransform();
|
||||
at.translate(dstBounds.getCenterX(), dstBounds.getCenterY());
|
||||
at.scale(dstBounds.getWidth()/srcBounds.getWidth(), dstBounds.getHeight()/srcBounds.getHeight());
|
||||
at.translate(-srcBounds.getCenterX(), -srcBounds.getCenterY());
|
||||
|
||||
g.drawRenderedImage(bi, at);
|
||||
|
||||
g.dispose();
|
||||
|
||||
bi = stretchBi;
|
||||
}
|
||||
|
||||
return bi;
|
||||
}
|
||||
|
||||
private AffineTransform getTiledInstance(final Rectangle2D usedBounds, final AffineTransform xform) {
|
||||
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;
|
||||
final Dimension2D scale = fill.getScale();
|
||||
assert(scale != null);
|
||||
final double img_w = bi.getWidth() * (scale.getWidth() == 0 ? 1 : scale.getWidth())/flipX;
|
||||
final double img_h = bi.getHeight() * (scale.getHeight() == 0 ? 1 : scale.getHeight())/flipY;
|
||||
|
||||
// Alignment happens after the scaling but before any offset.
|
||||
PaintStyle.TextureAlignment ta = fill.getAlignment();
|
||||
final double alg_x, alg_y;
|
||||
final double usr_w = usedBounds.getWidth(), usr_h = usedBounds.getHeight();
|
||||
switch (ta == null ? PaintStyle.TextureAlignment.TOP_LEFT : ta) {
|
||||
case BOTTOM:
|
||||
alg_x = (usr_w-img_w)/2;
|
||||
|
@ -140,10 +234,8 @@ import org.apache.poi.sl.usermodel.PaintStyle;
|
|||
xform.translate(offset.getX(),offset.getY());
|
||||
}
|
||||
|
||||
if (scale != null) {
|
||||
xform.scale(scale.getWidth()/(isBitmapSrc ? flipX : 1.),scale.getHeight()/(isBitmapSrc ? flipY : 1.));
|
||||
}
|
||||
xform.scale(scale.getWidth()/(isBitmapSrc ? flipX : 1.),scale.getHeight()/(isBitmapSrc ? flipY : 1.));
|
||||
|
||||
return super.createContext(cm, deviceBounds, userBounds, xform, hints);
|
||||
return xform;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -138,5 +138,37 @@ public interface PaintStyle {
|
|||
|
||||
|
||||
default TextureAlignment getAlignment() { return null; }
|
||||
|
||||
/**
|
||||
* Specifies the portion of the blip or image that is used for the fill.<p>
|
||||
*
|
||||
* Each edge of the image is defined by a percentage offset from the edge of the bounding box.
|
||||
* A positive percentage specifies an inset and a negative percentage specifies an outset.<p>
|
||||
*
|
||||
* The percentage are ints based on 100000, so 100% = 100000.<p>
|
||||
*
|
||||
* So, for example, a left offset of 25% specifies that the left edge of the image is located
|
||||
* to the right of the bounding box's left edge by 25% of the bounding box's width.
|
||||
*
|
||||
* @return the cropping insets of the source image
|
||||
*/
|
||||
default Insets2D getInsets() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The stretch specifies the edges of a fill rectangle.<p>
|
||||
*
|
||||
* Each edge of the fill rectangle is defined by a perentage offset from the corresponding edge
|
||||
* of the picture's bounding box. A positive percentage specifies an inset and a negative percentage
|
||||
* specifies an outset.<p>
|
||||
*
|
||||
* The percentage are ints based on 100000, so 100% = 100000.
|
||||
*
|
||||
* @return the stretching in the destination image
|
||||
*/
|
||||
default Insets2D getStretch() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
/* ====================================================================
|
||||
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.xslf.usermodel;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.apache.poi.sl.usermodel.ColorStyle;
|
||||
import org.apache.poi.sl.usermodel.PaintStyle;
|
||||
import org.apache.poi.util.Internal;
|
||||
import org.openxmlformats.schemas.drawingml.x2006.main.CTGradientFillProperties;
|
||||
import org.openxmlformats.schemas.drawingml.x2006.main.CTGradientStop;
|
||||
import org.openxmlformats.schemas.drawingml.x2006.main.CTSchemeColor;
|
||||
import org.openxmlformats.schemas.drawingml.x2006.main.STPathShadeType;
|
||||
|
||||
@Internal
|
||||
public class XSLFGradientPaint implements PaintStyle.GradientPaint {
|
||||
|
||||
private final CTGradientFillProperties gradFill;
|
||||
final ColorStyle[] cs;
|
||||
final float[] fractions;
|
||||
|
||||
public XSLFGradientPaint(final CTGradientFillProperties gradFill, CTSchemeColor phClr, final XSLFTheme theme) {
|
||||
this.gradFill = gradFill;
|
||||
|
||||
final CTGradientStop[] gs = gradFill.getGsLst() == null ?
|
||||
new CTGradientStop[0] : gradFill.getGsLst().getGsArray();
|
||||
|
||||
Arrays.sort(gs, (o1, o2) -> {
|
||||
int pos1 = o1.getPos();
|
||||
int pos2 = o2.getPos();
|
||||
return Integer.compare(pos1, pos2);
|
||||
});
|
||||
|
||||
cs = new ColorStyle[gs.length];
|
||||
fractions = new float[gs.length];
|
||||
|
||||
int i=0;
|
||||
for (CTGradientStop cgs : gs) {
|
||||
CTSchemeColor phClrCgs = phClr;
|
||||
if (phClrCgs == null && cgs.isSetSchemeClr()) {
|
||||
phClrCgs = cgs.getSchemeClr();
|
||||
}
|
||||
cs[i] = new XSLFColor(cgs, theme, phClrCgs).getColorStyle();
|
||||
fractions[i] = cgs.getPos() / 100000.f;
|
||||
i++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public double getGradientAngle() {
|
||||
return (gradFill.isSetLin())
|
||||
? gradFill.getLin().getAng() / 60000.d
|
||||
: 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ColorStyle[] getGradientColors() {
|
||||
return cs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float[] getGradientFractions() {
|
||||
return fractions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRotatedWithShape() {
|
||||
return gradFill.getRotWithShape();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PaintStyle.GradientPaint.GradientType getGradientType() {
|
||||
if (gradFill.isSetLin()) {
|
||||
return PaintStyle.GradientPaint.GradientType.linear;
|
||||
}
|
||||
|
||||
if (gradFill.isSetPath()) {
|
||||
/* TODO: handle rect path */
|
||||
STPathShadeType.Enum ps = gradFill.getPath().getPath();
|
||||
if (ps == STPathShadeType.CIRCLE) {
|
||||
return PaintStyle.GradientPaint.GradientType.circular;
|
||||
} else if (ps == STPathShadeType.SHAPE) {
|
||||
return PaintStyle.GradientPaint.GradientType.shape;
|
||||
}
|
||||
}
|
||||
|
||||
return PaintStyle.GradientPaint.GradientType.linear;
|
||||
}
|
||||
|
||||
}
|
|
@ -20,40 +20,26 @@
|
|||
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;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
|
||||
import org.apache.poi.openxml4j.opc.PackagePart;
|
||||
import org.apache.poi.openxml4j.opc.PackageRelationship;
|
||||
import org.apache.poi.sl.draw.DrawFactory;
|
||||
import org.apache.poi.sl.draw.DrawPaint;
|
||||
import org.apache.poi.sl.usermodel.ColorStyle;
|
||||
import org.apache.poi.sl.usermodel.MasterSheet;
|
||||
import org.apache.poi.sl.usermodel.PaintStyle;
|
||||
import org.apache.poi.sl.usermodel.PaintStyle.GradientPaint;
|
||||
import org.apache.poi.sl.usermodel.PaintStyle.TexturePaint;
|
||||
import org.apache.poi.sl.usermodel.PlaceableShape;
|
||||
import org.apache.poi.sl.usermodel.Placeholder;
|
||||
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;
|
||||
import org.apache.xmlbeans.XmlObject;
|
||||
import org.openxmlformats.schemas.drawingml.x2006.main.CTBlip;
|
||||
import org.openxmlformats.schemas.drawingml.x2006.main.CTBlipFillProperties;
|
||||
import org.openxmlformats.schemas.drawingml.x2006.main.CTGradientFillProperties;
|
||||
import org.openxmlformats.schemas.drawingml.x2006.main.CTGradientStop;
|
||||
import org.openxmlformats.schemas.drawingml.x2006.main.CTGroupShapeProperties;
|
||||
import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps;
|
||||
import org.openxmlformats.schemas.drawingml.x2006.main.CTSchemeColor;
|
||||
|
@ -62,9 +48,6 @@ 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;
|
||||
|
@ -153,6 +136,7 @@ public abstract class XSLFShape implements Shape<XSLFShape,XSLFTextParagraph> {
|
|||
protected PaintStyle getFillPaint() {
|
||||
final XSLFTheme theme = getSheet().getTheme();
|
||||
final boolean hasPlaceholder = getPlaceholder() != null;
|
||||
|
||||
PropertyFetcher<PaintStyle> fetcher = new PropertyFetcher<PaintStyle>() {
|
||||
@Override
|
||||
public boolean fetch(XSLFShape shape) {
|
||||
|
@ -411,159 +395,12 @@ public abstract class XSLFShape implements Shape<XSLFShape,XSLFTextParagraph> {
|
|||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
protected static PaintStyle selectPaint(final CTBlipFillProperties blipFill, final PackagePart parentPart) {
|
||||
final CTBlip blip = blipFill.getBlip();
|
||||
return new TexturePaint() {
|
||||
private PackagePart getPart() {
|
||||
try {
|
||||
String blipId = blip.getEmbed();
|
||||
PackageRelationship rel = parentPart.getRelationship(blipId);
|
||||
return parentPart.getRelatedPart(rel);
|
||||
} catch (InvalidFormatException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getImageData() {
|
||||
try {
|
||||
return getPart().getInputStream();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContentType() {
|
||||
if (blip == null || !blip.isSetEmbed() || blip.getEmbed().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
/* TOOD: map content-type */
|
||||
return getPart().getContentType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAlpha() {
|
||||
return (blip.sizeOfAlphaModFixArray() > 0)
|
||||
? 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 || tile.getFlip() == 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());
|
||||
}
|
||||
};
|
||||
return new XSLFTexturePaint(blipFill, parentPart);
|
||||
}
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
protected static PaintStyle selectPaint(final CTGradientFillProperties gradFill, CTSchemeColor phClr, final XSLFTheme theme) {
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
final CTGradientStop[] gs = gradFill.getGsLst() == null ?
|
||||
new CTGradientStop[0] : gradFill.getGsLst().getGsArray();
|
||||
|
||||
Arrays.sort(gs, (o1, o2) -> {
|
||||
int pos1 = o1.getPos();
|
||||
int pos2 = o2.getPos();
|
||||
return Integer.compare(pos1, pos2);
|
||||
});
|
||||
|
||||
final ColorStyle[] cs = new ColorStyle[gs.length];
|
||||
final float[] fractions = new float[gs.length];
|
||||
|
||||
int i=0;
|
||||
for (CTGradientStop cgs : gs) {
|
||||
CTSchemeColor phClrCgs = phClr;
|
||||
if (phClrCgs == null && cgs.isSetSchemeClr()) {
|
||||
phClrCgs = cgs.getSchemeClr();
|
||||
}
|
||||
cs[i] = new XSLFColor(cgs, theme, phClrCgs).getColorStyle();
|
||||
fractions[i] = cgs.getPos() / 100000.f;
|
||||
i++;
|
||||
}
|
||||
|
||||
return new GradientPaint() {
|
||||
|
||||
@Override
|
||||
public double getGradientAngle() {
|
||||
return (gradFill.isSetLin())
|
||||
? gradFill.getLin().getAng() / 60000.d
|
||||
: 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ColorStyle[] getGradientColors() {
|
||||
return cs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float[] getGradientFractions() {
|
||||
return fractions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRotatedWithShape() {
|
||||
return gradFill.getRotWithShape();
|
||||
}
|
||||
|
||||
@Override
|
||||
public GradientType getGradientType() {
|
||||
if (gradFill.isSetLin()) {
|
||||
return GradientType.linear;
|
||||
}
|
||||
|
||||
if (gradFill.isSetPath()) {
|
||||
/* TODO: handle rect path */
|
||||
STPathShadeType.Enum ps = gradFill.getPath().getPath();
|
||||
if (ps == STPathShadeType.CIRCLE) {
|
||||
return GradientType.circular;
|
||||
} else if (ps == STPathShadeType.SHAPE) {
|
||||
return GradientType.shape;
|
||||
}
|
||||
}
|
||||
|
||||
return GradientType.linear;
|
||||
}
|
||||
};
|
||||
return new XSLFGradientPaint(gradFill, phClr, theme);
|
||||
}
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
|
|
|
@ -0,0 +1,154 @@
|
|||
/* ====================================================================
|
||||
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.xslf.usermodel;
|
||||
|
||||
import java.awt.geom.Dimension2D;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
|
||||
import org.apache.poi.openxml4j.opc.PackagePart;
|
||||
import org.apache.poi.openxml4j.opc.PackageRelationship;
|
||||
import org.apache.poi.sl.usermodel.Insets2D;
|
||||
import org.apache.poi.sl.usermodel.PaintStyle;
|
||||
import org.apache.poi.util.Dimension2DDouble;
|
||||
import org.apache.poi.util.Internal;
|
||||
import org.apache.poi.util.Units;
|
||||
import org.openxmlformats.schemas.drawingml.x2006.main.CTBlip;
|
||||
import org.openxmlformats.schemas.drawingml.x2006.main.CTBlipFillProperties;
|
||||
import org.openxmlformats.schemas.drawingml.x2006.main.CTRelativeRect;
|
||||
import org.openxmlformats.schemas.drawingml.x2006.main.CTTileInfoProperties;
|
||||
import org.openxmlformats.schemas.drawingml.x2006.main.STTileFlipMode;
|
||||
|
||||
@Internal
|
||||
public class XSLFTexturePaint implements PaintStyle.TexturePaint {
|
||||
private final CTBlipFillProperties blipFill;
|
||||
private final PackagePart parentPart;
|
||||
private final CTBlip blip;
|
||||
|
||||
public XSLFTexturePaint(final CTBlipFillProperties blipFill, final PackagePart parentPart) {
|
||||
this.blipFill = blipFill;
|
||||
this.parentPart = parentPart;
|
||||
blip = blipFill.getBlip();
|
||||
}
|
||||
|
||||
|
||||
private PackagePart getPart() {
|
||||
try {
|
||||
String blipId = blip.getEmbed();
|
||||
PackageRelationship rel = parentPart.getRelationship(blipId);
|
||||
return parentPart.getRelatedPart(rel);
|
||||
} catch (InvalidFormatException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getImageData() {
|
||||
try {
|
||||
return getPart().getInputStream();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContentType() {
|
||||
if (blip == null || !blip.isSetEmbed() || blip.getEmbed().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
/* TOOD: map content-type */
|
||||
return getPart().getContentType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAlpha() {
|
||||
return (blip.sizeOfAlphaModFixArray() > 0)
|
||||
? 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 PaintStyle.FlipMode getFlipMode() {
|
||||
CTTileInfoProperties tile = blipFill.getTile();
|
||||
switch (tile == null || tile.getFlip() == null ? STTileFlipMode.INT_NONE : tile.getFlip().intValue()) {
|
||||
default:
|
||||
case STTileFlipMode.INT_NONE:
|
||||
return PaintStyle.FlipMode.NONE;
|
||||
case STTileFlipMode.INT_X:
|
||||
return PaintStyle.FlipMode.X;
|
||||
case STTileFlipMode.INT_Y:
|
||||
return PaintStyle.FlipMode.Y;
|
||||
case STTileFlipMode.INT_XY:
|
||||
return PaintStyle.FlipMode.XY;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PaintStyle.TextureAlignment getAlignment() {
|
||||
CTTileInfoProperties tile = blipFill.getTile();
|
||||
return (tile == null || !tile.isSetAlgn()) ? null
|
||||
: PaintStyle.TextureAlignment.fromOoxmlId(tile.getAlgn().toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Insets2D getInsets() {
|
||||
return getRectVal(blipFill.getSrcRect());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Insets2D getStretch() {
|
||||
return getRectVal(blipFill.isSetStretch() ? blipFill.getStretch().getFillRect() : null);
|
||||
}
|
||||
|
||||
private static Insets2D getRectVal(CTRelativeRect rect) {
|
||||
return rect == null ? null : new Insets2D(
|
||||
getRectVal(rect::isSetT, rect::getT),
|
||||
getRectVal(rect::isSetL, rect::getL),
|
||||
getRectVal(rect::isSetB, rect::getB),
|
||||
getRectVal(rect::isSetR, rect::getR)
|
||||
);
|
||||
}
|
||||
|
||||
private static int getRectVal(Supplier<Boolean> isSet, Supplier<Integer> val) {
|
||||
return isSet.get() ? val.get() : 0;
|
||||
}
|
||||
}
|
|
@ -228,45 +228,44 @@ public final class HSLFFill {
|
|||
|
||||
|
||||
public FillStyle getFillStyle() {
|
||||
return new FillStyle() {
|
||||
@Override
|
||||
public PaintStyle getPaint() {
|
||||
AbstractEscherOptRecord opt = shape.getEscherOptRecord();
|
||||
return this::getPaintStyle;
|
||||
}
|
||||
|
||||
EscherSimpleProperty hitProp = HSLFShape.getEscherProperty(opt, EscherPropertyTypes.FILL__NOFILLHITTEST);
|
||||
int propVal = (hitProp == null) ? 0 : hitProp.getPropertyValue();
|
||||
private PaintStyle getPaintStyle() {
|
||||
AbstractEscherOptRecord opt = shape.getEscherOptRecord();
|
||||
|
||||
EscherSimpleProperty masterProp = HSLFShape.getEscherProperty(opt, EscherPropertyTypes.SHAPE__MASTER);
|
||||
EscherSimpleProperty hitProp = HSLFShape.getEscherProperty(opt, EscherPropertyTypes.FILL__NOFILLHITTEST);
|
||||
int propVal = (hitProp == null) ? 0 : hitProp.getPropertyValue();
|
||||
|
||||
if (!FILL_USE_FILLED.isSet(propVal) && masterProp != null) {
|
||||
int masterId = masterProp.getPropertyValue();
|
||||
HSLFShape o = shape.getSheet().getMasterSheet().getShapes().stream().filter(s -> s.getShapeId() == masterId).findFirst().orElse(null);
|
||||
return o != null ? o.getFillStyle().getPaint() : null;
|
||||
}
|
||||
EscherSimpleProperty masterProp = HSLFShape.getEscherProperty(opt, EscherPropertyTypes.SHAPE__MASTER);
|
||||
|
||||
final int fillType = getFillType();
|
||||
// TODO: fix gradient types, this mismatches with the MS-ODRAW definition ...
|
||||
// need to handle (not only) the type (radial,rectangular,linear),
|
||||
// the direction, e.g. top right, and bounds (e.g. for rectangular boxes)
|
||||
switch (fillType) {
|
||||
case FILL_SOLID:
|
||||
return DrawPaint.createSolidPaint(getForegroundColor());
|
||||
case FILL_SHADE_SHAPE:
|
||||
return getGradientPaint(GradientType.shape);
|
||||
case FILL_SHADE_CENTER:
|
||||
case FILL_SHADE_TITLE:
|
||||
return getGradientPaint(GradientType.circular);
|
||||
case FILL_SHADE:
|
||||
case FILL_SHADE_SCALE:
|
||||
return getGradientPaint(GradientType.linear);
|
||||
case FILL_PICTURE:
|
||||
return getTexturePaint();
|
||||
default:
|
||||
LOG.log(POILogger.WARN, "unsuported fill type: " + fillType);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
if (!FILL_USE_FILLED.isSet(propVal) && masterProp != null) {
|
||||
int masterId = masterProp.getPropertyValue();
|
||||
HSLFShape o = shape.getSheet().getMasterSheet().getShapes().stream().filter(s -> s.getShapeId() == masterId).findFirst().orElse(null);
|
||||
return o != null ? o.getFillStyle().getPaint() : null;
|
||||
}
|
||||
|
||||
final int fillType = getFillType();
|
||||
// TODO: fix gradient types, this mismatches with the MS-ODRAW definition ...
|
||||
// need to handle (not only) the type (radial,rectangular,linear),
|
||||
// the direction, e.g. top right, and bounds (e.g. for rectangular boxes)
|
||||
switch (fillType) {
|
||||
case FILL_SOLID:
|
||||
return DrawPaint.createSolidPaint(getForegroundColor());
|
||||
case FILL_SHADE_SHAPE:
|
||||
return getGradientPaint(GradientType.shape);
|
||||
case FILL_SHADE_CENTER:
|
||||
case FILL_SHADE_TITLE:
|
||||
return getGradientPaint(GradientType.circular);
|
||||
case FILL_SHADE:
|
||||
case FILL_SHADE_SCALE:
|
||||
return getGradientPaint(GradientType.linear);
|
||||
case FILL_PICTURE:
|
||||
return getTexturePaint();
|
||||
default:
|
||||
LOG.log(POILogger.WARN, "unsuported fill type: " + fillType);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isRotatedWithShape() {
|
||||
|
|
Loading…
Reference in New Issue