org.apache.poi.xslf.usermodel.XSLFShape Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of com.liferay.dynamic.data.lists.service
Show all versions of com.liferay.dynamic.data.lists.service
Liferay Dynamic Data Lists Service
/*
* ====================================================================
* 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.Graphics2D;
import java.awt.geom.Rectangle2D;
import java.util.Locale;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLStreamReader;
import com.microsoft.schemas.compatibility.AlternateContentDocument;
import com.microsoft.schemas.compatibility.AlternateContentDocument.AlternateContent;
import org.apache.poi.openxml4j.opc.PackagePart;
import org.apache.poi.sl.draw.DrawFactory;
import org.apache.poi.sl.draw.DrawPaint;
import org.apache.poi.sl.usermodel.MasterSheet;
import org.apache.poi.sl.usermodel.PaintStyle;
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.Internal;
import org.apache.poi.xslf.model.PropertyFetcher;
import org.apache.poi.xslf.usermodel.XSLFPropertiesDelegate.XSLFFillProperties;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.impl.values.XmlAnyTypeImpl;
import org.openxmlformats.schemas.drawingml.x2006.main.CTBlipFillProperties;
import org.openxmlformats.schemas.drawingml.x2006.main.CTGradientFillProperties;
import org.openxmlformats.schemas.drawingml.x2006.main.CTGroupShapeProperties;
import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps;
import org.openxmlformats.schemas.drawingml.x2006.main.CTSchemeColor;
import org.openxmlformats.schemas.drawingml.x2006.main.CTShapeProperties;
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.presentationml.x2006.main.CTBackgroundProperties;
import org.openxmlformats.schemas.presentationml.x2006.main.CTPicture;
import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder;
import org.openxmlformats.schemas.presentationml.x2006.main.CTShape;
import org.openxmlformats.schemas.presentationml.x2006.main.STPlaceholderType;
/**
* Base super-class class for all shapes in PresentationML
*/
@Beta
public abstract class XSLFShape implements Shape {
@Internal
public interface ReparseFactory {
T parse(XMLStreamReader reader) throws XmlException;
}
static final String DML_NS = "http://schemas.openxmlformats.org/drawingml/2006/main";
static final String PML_NS = "http://schemas.openxmlformats.org/presentationml/2006/main";
private static final String MC_NS = "http://schemas.openxmlformats.org/markup-compatibility/2006";
private static final String MAC_DML_NS = "http://schemas.microsoft.com/office/mac/drawingml/2008/main";
private static final QName ALTERNATE_CONTENT_TAG = new QName(MC_NS, "AlternateContent");
private static final QName[] NV_CONTAINER = {
new QName(PML_NS, "nvSpPr"),
new QName(PML_NS, "nvCxnSpPr"),
new QName(PML_NS, "nvGrpSpPr"),
new QName(PML_NS, "nvPicPr"),
new QName(PML_NS, "nvGraphicFramePr")
};
private static final QName[] CNV_PROPS = {
new QName(PML_NS, "cNvPr")
};
private static final String OSGI_ERROR =
"Schemas (*.xsb) for can't be loaded - usually this happens when OSGI " +
"loading is used and the thread context classloader has no reference to " +
"the xmlbeans classes - please either verify if the .xsb is on the " +
"classpath or alternatively try to use the full ooxml-schemas-x.x.jar";
private final XmlObject _shape;
private final XSLFSheet _sheet;
private XSLFShapeContainer _parent;
private CTShapeStyle _spStyle;
private CTNonVisualDrawingProps _nvPr;
protected XSLFShape(XmlObject shape, XSLFSheet sheet) {
_shape = shape;
_sheet = sheet;
}
/**
* @return the xml bean holding this shape's data
*/
public final XmlObject getXmlObject() {
// it's final because the xslf inheritance hierarchy is not necessary the same as
// the (not existing) xmlbeans hierarchy and subclasses shouldn't narrow it's return value
return _shape;
}
@Override
public XSLFSheet getSheet() {
return _sheet;
}
@Override
public String getShapeName() {
CTNonVisualDrawingProps nonVisualDrawingProps = getCNvPr();
return nonVisualDrawingProps == null ? null : nonVisualDrawingProps.getName();
}
@Override
public int getShapeId() {
CTNonVisualDrawingProps nonVisualDrawingProps = getCNvPr();
if (nonVisualDrawingProps == null) {
throw new IllegalStateException("no underlying shape exists");
}
return Math.toIntExact(nonVisualDrawingProps.getId());
}
/**
* Set the contents of this shape to be a copy of the source shape.
* This method is called recursively for each shape when merging slides
*
* @param sh the source shape
* @see org.apache.poi.xslf.usermodel.XSLFSlide#importContent(XSLFSheet)
*/
@Internal
void copy(XSLFShape sh) {
if (!getClass().isInstance(sh)) {
throw new IllegalArgumentException(
"Can't copy " + sh.getClass().getSimpleName() + " into " + getClass().getSimpleName());
}
if (this instanceof PlaceableShape) {
PlaceableShape,?> ps = (PlaceableShape,?>)this;
ps.setAnchor(sh.getAnchor());
}
}
public void setParent(XSLFShapeContainer parent) {
this._parent = parent;
}
@Override
public XSLFShapeContainer getParent() {
return this._parent;
}
protected PaintStyle getFillPaint() {
final XSLFTheme theme = getSheet().getTheme();
final boolean hasPlaceholder = getPlaceholder() != null;
PropertyFetcher fetcher = new PropertyFetcher() {
@Override
public boolean fetch(XSLFShape shape) {
PackagePart pp = shape.getSheet().getPackagePart();
if (shape instanceof XSLFPictureShape) {
CTPicture pic = (CTPicture)shape.getXmlObject();
if (pic.getBlipFill() != null) {
setValue(selectPaint(pic.getBlipFill(), pp, null, theme));
return true;
}
}
XSLFFillProperties fp = XSLFPropertiesDelegate.getFillDelegate(shape.getShapeProperties());
if (fp == null) {
return false;
}
if (fp.isSetNoFill()) {
setValue(null);
return true;
}
PaintStyle paint = selectPaint(fp, null, pp, theme, hasPlaceholder);
if (paint != null) {
setValue(paint);
return true;
}
CTShapeStyle style = shape.getSpStyle();
if (style != null) {
fp = XSLFPropertiesDelegate.getFillDelegate(style.getFillRef());
paint = selectPaint(fp, null, pp, theme, hasPlaceholder);
}
if (paint != null) {
setValue(paint);
return true;
}
return false;
}
};
fetchShapeProperty(fetcher);
return fetcher.getValue();
}
@SuppressWarnings("unused")
protected CTBackgroundProperties getBgPr() {
return getChild(CTBackgroundProperties.class, PML_NS, "bgPr");
}
@SuppressWarnings("unused")
protected CTStyleMatrixReference getBgRef() {
return getChild(CTStyleMatrixReference.class, PML_NS, "bgRef");
}
protected CTGroupShapeProperties getGrpSpPr() {
return getChild(CTGroupShapeProperties.class, PML_NS, "grpSpPr");
}
protected CTNonVisualDrawingProps getCNvPr() {
try {
if (_nvPr == null) {
_nvPr = selectProperty(CTNonVisualDrawingProps.class, null, NV_CONTAINER, CNV_PROPS);
}
return _nvPr;
} catch (XmlException e) {
return null;
}
}
@SuppressWarnings("WeakerAccess")
protected CTShapeStyle getSpStyle() {
if (_spStyle == null) {
_spStyle = getChild(CTShapeStyle.class, PML_NS, "style");
}
return _spStyle;
}
/**
* Return direct child objects of this shape
*
* @param childClass the class to cast the properties to
* @param namespace the namespace - usually it is {@code "http://schemas.openxmlformats.org/presentationml/2006/main"}
* @param nodename the node name, without prefix
* @return the properties object or null if it can't be found
*/
@SuppressWarnings({"unchecked", "WeakerAccess", "unused", "SameParameterValue"})
protected T getChild(Class childClass, String namespace, String nodename) {
XmlCursor cur = getXmlObject().newCursor();
T child = null;
if (cur.toChild(namespace, nodename)) {
child = (T)cur.getObject();
}
if (cur.toChild(XSLFRelation.NS_DRAWINGML, nodename)) {
child = (T)cur.getObject();
}
cur.dispose();
return child;
}
public boolean isPlaceholder() {
return getPlaceholderDetails().getCTPlaceholder(false) != null;
}
/**
* @see PlaceholderDetails#getPlaceholder()
*/
public Placeholder getPlaceholder() {
return getPlaceholderDetails().getPlaceholder();
}
/**
* @see PlaceholderDetails#setPlaceholder(Placeholder)
*/
public void setPlaceholder(final Placeholder placeholder) {
getPlaceholderDetails().setPlaceholder(placeholder);
}
/**
* @see SimpleShape#getPlaceholderDetails()
*/
@SuppressWarnings("WeakerAccess")
public XSLFPlaceholderDetails getPlaceholderDetails() {
return new XSLFPlaceholderDetails(this);
}
/**
* As there's no xmlbeans hierarchy, but XSLF works with subclassing, not all
* child classes work with a {@link CTShape} object, but often contain the same
* properties. This method is the generalized form of selecting and casting those
* properties.
*
* @param resultClass the requested result class
* @param xquery the simple (xmlbean) xpath expression to the property
* @return the xml object at the xpath location, or null if not found
*/
@SuppressWarnings({"unchecked", "WeakerAccess"})
protected T selectProperty(Class resultClass, String xquery) {
XmlObject[] rs = getXmlObject().selectPath(xquery);
if (rs.length == 0) {
return null;
}
return (resultClass.isInstance(rs[0])) ? (T)rs[0] : null;
}
/**
* Internal code - API may change any time!
*
* The {@link #selectProperty(Class, String)} xquery method has some performance penalties,
* which can be workaround by using {@link XmlCursor}. This method also takes into account
* that {@code AlternateContent} tags can occur anywhere on the given path.
*
* It returns the first element found - the search order is:
*
* - searching for a direct child
* - searching for a AlternateContent.Choice child
* - searching for a AlternateContent.Fallback child
*
* Currently POI OOXML is based on the first edition of the ECMA 376 schema, which doesn't
* allow AlternateContent tags to show up everywhere. The factory flag is
* a workaround to process files based on a later edition. But it comes with the drawback:
* any change on the returned XmlObject aren't saved back to the underlying document -
* so it's a non updatable clone. If factory is null, a XmlException is
* thrown if the AlternateContent is not allowed by the surrounding element or if the
* extracted object is of the generic type XmlAnyTypeImpl.
*
* @param resultClass the requested result class
* @param factory a factory parse method reference to allow reparsing of elements
* extracted from AlternateContent elements. Usually the enclosing XmlBeans type needs to be used
* to parse the stream
* @param path the elements path, each array must contain at least 1 QName,
* but can contain additional alternative tags
* @return the xml object at the path location, or null if not found
*
* @throws XmlException If factory is null, a XmlException is
* thrown if the AlternateContent is not allowed by the surrounding element or if the
* extracted object is of the generic type XmlAnyTypeImpl.
*
* @since POI 4.1.2
*/
@SuppressWarnings("unchecked")
@Internal
public T selectProperty(Class resultClass, ReparseFactory factory, QName[]... path)
throws XmlException {
XmlObject xo = getXmlObject();
XmlCursor cur = xo.newCursor();
XmlCursor innerCur = null;
try {
innerCur = selectProperty(cur, path, 0, factory != null, false);
if (innerCur == null) {
return null;
}
// Pesky XmlBeans bug - see Bugzilla #49934
// it never happens when using the full ooxml-schemas jar but may happen with the abridged poi-ooxml-schemas
xo = innerCur.getObject();
if (xo instanceof XmlAnyTypeImpl) {
String errorTxt = OSGI_ERROR
.replace("", resultClass.getSimpleName())
.replace("", resultClass.getSimpleName().toLowerCase(Locale.ROOT)+"*");
if (factory == null) {
throw new XmlException(errorTxt);
} else {
xo = factory.parse(innerCur.newXMLStreamReader());
}
}
return (T)xo;
} finally {
cur.dispose();
if (innerCur != null) {
innerCur.dispose();
}
}
}
private XmlCursor selectProperty(final XmlCursor cur, final QName[][] path, final int offset, final boolean reparseAlternate, final boolean isAlternate)
throws XmlException {
// first try the direct children
for (QName qn : path[offset]) {
if (cur.toChild(qn)) {
if (offset == path.length-1) {
return cur;
}
cur.push();
XmlCursor innerCur = selectProperty(cur, path, offset+1, reparseAlternate, false);
if (innerCur != null) {
return innerCur;
}
cur.pop();
}
}
// if we were called inside an alternate content handling don't look for alternates again
if (isAlternate || !cur.toChild(ALTERNATE_CONTENT_TAG)) {
return null;
}
// otherwise check first the choice then the fallback content
XmlObject xo = cur.getObject();
AlternateContent alterCont;
if (xo instanceof AlternateContent) {
alterCont = (AlternateContent)xo;
} else {
// Pesky XmlBeans bug - see Bugzilla #49934
// it never happens when using the full ooxml-schemas jar but may happen with the abridged poi-ooxml-schemas
if (!reparseAlternate) {
throw new XmlException(OSGI_ERROR
.replace("", "AlternateContent")
.replace("", "alternatecontentelement")
);
}
try {
AlternateContentDocument acd = AlternateContentDocument.Factory.parse(cur.newXMLStreamReader());
alterCont = acd.getAlternateContent();
} catch (XmlException e) {
throw new XmlException("unable to parse AlternateContent element", e);
}
}
final int choices = alterCont.sizeOfChoiceArray();
for (int i=0; i
*
* The following order of inheritance is assumed:
*
* - slide
*
- slideLayout
*
- slideMaster
*
*
* Currently themes and their defaults aren't correctly handled
*
* @param visitor the object that collects the desired property
* @return true if the property was fetched
*/
@SuppressWarnings("WeakerAccess")
protected boolean fetchShapeProperty(PropertyFetcher> visitor) {
// try shape properties in slide
if (visitor.fetch(this)) {
return true;
}
final CTPlaceholder ph = getPlaceholderDetails().getCTPlaceholder(false);
if (ph == null) {
return false;
}
MasterSheet sm = getSheet().getMasterSheet();
// try slide layout
if (sm instanceof XSLFSlideLayout) {
XSLFSlideLayout slideLayout = (XSLFSlideLayout)sm;
XSLFSimpleShape placeholderShape = slideLayout.getPlaceholder(ph);
if (placeholderShape != null && visitor.fetch(placeholderShape)) {
return true;
}
sm = slideLayout.getMasterSheet();
}
// try slide master
if (sm instanceof XSLFSlideMaster) {
XSLFSlideMaster master = (XSLFSlideMaster)sm;
int textType = getPlaceholderType(ph);
XSLFSimpleShape masterShape = master.getPlaceholderByType(textType);
return masterShape != null && visitor.fetch(masterShape);
}
return false;
}
private static int getPlaceholderType(CTPlaceholder ph) {
if ( !ph.isSetType()) {
return STPlaceholderType.INT_BODY;
}
switch (ph.getType().intValue()) {
case STPlaceholderType.INT_TITLE:
case STPlaceholderType.INT_CTR_TITLE:
return STPlaceholderType.INT_TITLE;
case STPlaceholderType.INT_FTR:
case STPlaceholderType.INT_SLD_NUM:
case STPlaceholderType.INT_DT:
return ph.getType().intValue();
default:
return STPlaceholderType.INT_BODY;
}
}
/**
* Convert shape fill into java.awt.Paint. The result is either Color or
* TexturePaint or GradientPaint or null
*
* @param fp a properties handler specific to the underlying shape properties
* @param phClr context color
* @param parentPart the parent package part. Any external references (images, etc.) are resolved relative to it.
* @param theme the theme for the shape/sheet
*
* @return the applied Paint or null if none was applied
*/
@SuppressWarnings("WeakerAccess")
protected PaintStyle selectPaint(XSLFFillProperties fp, final CTSchemeColor phClr, final PackagePart parentPart, final XSLFTheme theme, boolean hasPlaceholder) {
if (fp == null || fp.isSetNoFill()) {
return null;
} else if (fp.isSetSolidFill()) {
return selectPaint(fp.getSolidFill(), phClr, theme);
} else if (fp.isSetBlipFill()) {
return selectPaint(fp.getBlipFill(), parentPart, phClr, theme);
} else if (fp.isSetGradFill()) {
return selectPaint(fp.getGradFill(), phClr, theme);
} else if (fp.isSetMatrixStyle()) {
return selectPaint(fp.getMatrixStyle(), theme, fp.isLineStyle(), hasPlaceholder);
} else {
return null;
}
}
@SuppressWarnings("WeakerAccess")
protected PaintStyle selectPaint(CTSolidColorFillProperties solidFill, CTSchemeColor phClr, final XSLFTheme theme) {
if (solidFill.isSetSchemeClr()) {
// if there's a reference to the placeholder color,
// stop evaluating further and let the caller select
// the next style inheritance level
// if (STSchemeColorVal.PH_CLR.equals(solidFill.getSchemeClr().getVal())) {
// return null;
// }
if (phClr == null) {
phClr = solidFill.getSchemeClr();
}
}
final XSLFColor c = new XSLFColor(solidFill, theme, phClr, _sheet);
return DrawPaint.createSolidPaint(c.getColorStyle());
}
@SuppressWarnings("WeakerAccess")
protected PaintStyle selectPaint(final CTBlipFillProperties blipFill, final PackagePart parentPart, CTSchemeColor phClr, final XSLFTheme theme) {
return new XSLFTexturePaint(blipFill, parentPart, phClr, theme, _sheet);
}
@SuppressWarnings("WeakerAccess")
protected PaintStyle selectPaint(final CTGradientFillProperties gradFill, CTSchemeColor phClr, final XSLFTheme theme) {
return new XSLFGradientPaint(gradFill, phClr, theme, _sheet);
}
@SuppressWarnings("WeakerAccess")
protected PaintStyle selectPaint(CTStyleMatrixReference fillRef, final XSLFTheme theme, boolean isLineStyle, boolean hasPlaceholder) {
if (fillRef == null) {
return null;
}
// The idx attribute refers to the index of a fill style or
// background fill style within the presentation's style matrix, defined by the fmtScheme element.
// value of 0 or 1000 indicates no background,
// values 1-999 refer to the index of a fill style within the fillStyleLst element
// values 1001 and above refer to the index of a background fill style within the bgFillStyleLst element.
long idx = fillRef.getIdx();
CTStyleMatrix matrix = theme.getXmlObject().getThemeElements().getFmtScheme();
final XmlObject styleLst;
long childIdx;
if (idx >= 1 && idx <= 999) {
childIdx = idx-1;
styleLst = (isLineStyle) ? matrix.getLnStyleLst() : matrix.getFillStyleLst();
} else if (idx >= 1001 ){
childIdx = idx - 1001;
styleLst = matrix.getBgFillStyleLst();
} else {
return null;
}
XmlCursor cur = styleLst.newCursor();
XSLFFillProperties fp = null;
if (cur.toChild(Math.toIntExact(childIdx))) {
fp = XSLFPropertiesDelegate.getFillDelegate(cur.getObject());
}
cur.dispose();
CTSchemeColor phClr = fillRef.getSchemeClr();
PaintStyle res = selectPaint(fp, phClr, theme.getPackagePart(), theme, hasPlaceholder);
// check for empty placeholder value
// see http://officeopenxml.com/prSlide-color.php - "Color Placeholders within Themes"
if (res != null || hasPlaceholder) {
return res;
}
XSLFColor col = new XSLFColor(fillRef, theme, phClr, _sheet);
return DrawPaint.createSolidPaint(col.getColorStyle());
}
@Override
public void draw(Graphics2D graphics, Rectangle2D bounds) {
DrawFactory.getInstance(graphics).drawShape(graphics, this, bounds);
}
/**
* Return the shape specific (visual) properties
*
* @return the shape specific properties
*/
protected XmlObject getShapeProperties() {
return getChild(CTShapeProperties.class, PML_NS, "spPr");
}
}