git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1869669 13f79535-47bb-0310-9956-ffa450edef68tags/REL_4_1_2
@@ -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()); | |||
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(); | |||
xform.translate(newBounds.getX(), newBounds.getY()); | |||
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() { |