blob: 59109b7e320595d8bfae42213b0ace07edce02bf [file] [log] [blame]
/*
* ====================================================================
* 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 static org.apache.poi.openxml4j.opc.PackageRelationshipTypes.CORE_PROPERTIES_ECMA376_NS;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
import javax.xml.namespace.QName;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.poi.ooxml.POIXMLException;
import org.apache.poi.ooxml.util.POIXMLUnits;
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.GraphicalFrame;
import org.apache.poi.sl.usermodel.ShapeType;
import org.apache.poi.util.Beta;
import org.apache.poi.util.Units;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlObject;
import org.openxmlformats.schemas.drawingml.x2006.main.CTGraphicalObject;
import org.openxmlformats.schemas.drawingml.x2006.main.CTGraphicalObjectData;
import org.openxmlformats.schemas.drawingml.x2006.main.CTPoint2D;
import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D;
import org.openxmlformats.schemas.drawingml.x2006.main.CTTransform2D;
import org.openxmlformats.schemas.presentationml.x2006.main.CTGraphicalObjectFrame;
import org.openxmlformats.schemas.presentationml.x2006.main.CTGroupShape;
@Beta
public class XSLFGraphicFrame extends XSLFShape implements GraphicalFrame<XSLFShape, XSLFTextParagraph> {
private static final String DRAWINGML_CHART_URI = "http://schemas.openxmlformats.org/drawingml/2006/chart";
private static final Logger LOG = LogManager.getLogger(XSLFGraphicFrame.class);
/*package*/ XSLFGraphicFrame(CTGraphicalObjectFrame shape, XSLFSheet sheet){
super(shape,sheet);
}
public ShapeType getShapeType(){
throw new UnsupportedOperationException();
}
@Override
public Rectangle2D getAnchor(){
CTTransform2D xfrm = ((CTGraphicalObjectFrame)getXmlObject()).getXfrm();
CTPoint2D off = xfrm.getOff();
double x = Units.toPoints(POIXMLUnits.parseLength(off.xgetX()));
double y = Units.toPoints(POIXMLUnits.parseLength(off.xgetY()));
CTPositiveSize2D ext = xfrm.getExt();
double cx = Units.toPoints(ext.getCx());
double cy = Units.toPoints(ext.getCy());
return new Rectangle2D.Double(x, y, cx, cy);
}
@Override
public void setAnchor(Rectangle2D anchor){
CTTransform2D xfrm = ((CTGraphicalObjectFrame)getXmlObject()).getXfrm();
CTPoint2D off = xfrm.isSetOff() ? xfrm.getOff() : xfrm.addNewOff();
long x = Units.toEMU(anchor.getX());
long y = Units.toEMU(anchor.getY());
off.setX(x);
off.setY(y);
CTPositiveSize2D ext = xfrm.isSetExt() ? xfrm.getExt() : xfrm
.addNewExt();
long cx = Units.toEMU(anchor.getWidth());
long cy = Units.toEMU(anchor.getHeight());
ext.setCx(cx);
ext.setCy(cy);
}
static XSLFGraphicFrame create(CTGraphicalObjectFrame shape, XSLFSheet sheet){
final String uri = getUri(shape);
switch (uri == null ? "" : uri) {
case XSLFTable.TABLE_URI:
return new XSLFTable(shape, sheet);
case XSLFObjectShape.OLE_URI:
return new XSLFObjectShape(shape, sheet);
default:
return new XSLFGraphicFrame(shape, sheet);
}
}
private static String getUri(CTGraphicalObjectFrame shape) {
final CTGraphicalObject g = shape.getGraphic();
if (g == null) {
return null;
}
CTGraphicalObjectData gd = g.getGraphicData();
return (gd == null) ? null : gd.getUri();
}
/**
* Rotate this shape.
* <p>
* Positive angles are clockwise (i.e., towards the positive y axis);
* negative angles are counter-clockwise (i.e., towards the negative y axis).
* </p>
*
* @param theta the rotation angle in degrees.
*/
@Override
public void setRotation(double theta){
throw new IllegalArgumentException("Operation not supported");
}
/**
* Rotation angle in degrees
* <p>
* Positive angles are clockwise (i.e., towards the positive y axis);
* negative angles are counter-clockwise (i.e., towards the negative y axis).
* </p>
*
* @return rotation angle in degrees
*/
@Override
public double getRotation(){
return 0;
}
@Override
public void setFlipHorizontal(boolean flip){
throw new IllegalArgumentException("Operation not supported");
}
@Override
public void setFlipVertical(boolean flip){
throw new IllegalArgumentException("Operation not supported");
}
/**
* Whether the shape is horizontally flipped
*
* @return whether the shape is horizontally flipped
*/
@Override
public boolean getFlipHorizontal(){
return false;
}
@Override
public boolean getFlipVertical(){
return false;
}
public boolean hasChart() {
String uri = getGraphicalData().getUri();
return uri.equals(DRAWINGML_CHART_URI);
}
private CTGraphicalObjectData getGraphicalData() {
return ((CTGraphicalObjectFrame)getXmlObject()).getGraphic().getGraphicData();
}
public XSLFChart getChart() {
if (hasChart()) {
String id = null;
String xpath = "declare namespace c='" + DRAWINGML_CHART_URI + "' c:chart";
XmlObject[] obj = getGraphicalData().selectPath(xpath);
if (obj != null && obj.length == 1) {
XmlCursor c = obj[0].newCursor();
QName idQualifiedName = new QName(CORE_PROPERTIES_ECMA376_NS, "id");
id = c.getAttributeText(idQualifiedName);
c.dispose();
}
if (id == null) {
return null;
} else {
return (XSLFChart) getSheet().getRelationById(id);
}
} else {
return null;
}
}
@Override
void copy(XSLFShape sh){
super.copy(sh);
CTGraphicalObjectData data = getGraphicalData();
String uri = data.getUri();
if(uri.equals("http://schemas.openxmlformats.org/drawingml/2006/diagram")){
copyDiagram(data, (XSLFGraphicFrame)sh);
} if(uri.equals(DRAWINGML_CHART_URI)){
copyChart(data, (XSLFGraphicFrame)sh);
} else {
// TODO support other types of objects
}
}
private void copyChart(CTGraphicalObjectData objData, XSLFGraphicFrame srcShape) {
XSLFSlide slide = (XSLFSlide) getSheet();
XSLFSheet src = srcShape.getSheet();
String xpath = "declare namespace c='" + DRAWINGML_CHART_URI + "' c:chart";
XmlObject[] obj = objData.selectPath(xpath);
if (obj != null && obj.length == 1) {
XmlCursor c = obj[0].newCursor();
try {
// duplicate chart with embedded workbook
QName idQualifiedName = new QName(CORE_PROPERTIES_ECMA376_NS, "id");
String id = c.getAttributeText(idQualifiedName);
XSLFChart srcChart = (XSLFChart) src.getRelationById(id);
XSLFChart chartCopy = slide.getSlideShow().createChart(slide);
chartCopy.importContent(srcChart);
chartCopy.setWorkbook(srcChart.getWorkbook());
c.setAttributeText(idQualifiedName, slide.getRelationId(chartCopy));
} catch (InvalidFormatException | IOException e) {
throw new POIXMLException(e);
}
c.dispose();
}
}
// TODO should be moved to a sub-class
private void copyDiagram(CTGraphicalObjectData objData, XSLFGraphicFrame srcShape){
String xpath = "declare namespace dgm='http://schemas.openxmlformats.org/drawingml/2006/diagram' $this//dgm:relIds";
XmlObject[] obj = objData.selectPath(xpath);
if(obj != null && obj.length == 1){
XmlCursor c = obj[0].newCursor();
XSLFSheet sheet = srcShape.getSheet();
try {
String dm = c.getAttributeText(new QName(CORE_PROPERTIES_ECMA376_NS, "dm"));
PackageRelationship dmRel = sheet.getPackagePart().getRelationship(dm);
PackagePart dmPart = sheet.getPackagePart().getRelatedPart(dmRel);
getSheet().importPart(dmRel, dmPart);
String lo = c.getAttributeText(new QName(CORE_PROPERTIES_ECMA376_NS, "lo"));
PackageRelationship loRel = sheet.getPackagePart().getRelationship(lo);
PackagePart loPart = sheet.getPackagePart().getRelatedPart(loRel);
getSheet().importPart(loRel, loPart);
String qs = c.getAttributeText(new QName(CORE_PROPERTIES_ECMA376_NS, "qs"));
PackageRelationship qsRel = sheet.getPackagePart().getRelationship(qs);
PackagePart qsPart = sheet.getPackagePart().getRelatedPart(qsRel);
getSheet().importPart(qsRel, qsPart);
String cs = c.getAttributeText(new QName(CORE_PROPERTIES_ECMA376_NS, "cs"));
PackageRelationship csRel = sheet.getPackagePart().getRelationship(cs);
PackagePart csPart = sheet.getPackagePart().getRelatedPart(csRel);
getSheet().importPart(csRel, csPart);
} catch (InvalidFormatException e){
throw new POIXMLException(e);
}
c.dispose();
}
}
@Override
public XSLFPictureShape getFallbackPicture() {
String xquery =
"declare namespace p='http://schemas.openxmlformats.org/presentationml/2006/main'; "
+ "declare namespace mc='http://schemas.openxmlformats.org/markup-compatibility/2006' "
+ ".//mc:Fallback/*/p:pic"
;
XmlObject xo = selectProperty(XmlObject.class, xquery);
if (xo == null) {
return null;
}
CTGroupShape gs;
try {
gs = CTGroupShape.Factory.parse(xo.newDomNode());
} catch (XmlException e) {
LOG.atWarn().withThrowable(e).log("Can't parse fallback picture stream of graphical frame");
return null;
}
if (gs.sizeOfPicArray() == 0) {
return null;
}
return new XSLFPictureShape(gs.getPicArray(0), getSheet());
}
}