From d20e85387e8c48d70249f5349f796a7cc4db8199 Mon Sep 17 00:00:00 2001 From: Andreas Beeker Date: Sun, 2 Feb 2020 22:13:16 +0000 Subject: [PATCH] XSLF Performance - use XmlCursor instead of XQuery expression - handle AlternateContent elements uniformly git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1873514 13f79535-47bb-0310-9956-ffa450edef68 --- .../xslf/model/ParagraphPropertyFetcher.java | 39 ++-- .../xslf/model/TextBodyPropertyFetcher.java | 39 ++-- .../poi/xslf/usermodel/XSLFObjectShape.java | 57 ++--- .../poi/xslf/usermodel/XSLFPictureShape.java | 57 ++--- .../usermodel/XSLFPlaceholderDetails.java | 22 +- .../apache/poi/xslf/usermodel/XSLFShape.java | 202 +++++++++++++++++- .../org/apache/poi/xslf/TestXSLFBugs.java | 71 ++++-- 7 files changed, 364 insertions(+), 123 deletions(-) diff --git a/src/ooxml/java/org/apache/poi/xslf/model/ParagraphPropertyFetcher.java b/src/ooxml/java/org/apache/poi/xslf/model/ParagraphPropertyFetcher.java index 6085b9d80b..1d2c3422c8 100644 --- a/src/ooxml/java/org/apache/poi/xslf/model/ParagraphPropertyFetcher.java +++ b/src/ooxml/java/org/apache/poi/xslf/model/ParagraphPropertyFetcher.java @@ -19,15 +19,21 @@ package org.apache.poi.xslf.model; +import javax.xml.namespace.QName; +import javax.xml.stream.XMLStreamReader; + import org.apache.poi.xslf.usermodel.XSLFShape; -import org.apache.xmlbeans.XmlObject; +import org.apache.xmlbeans.XmlException; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraph; import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraphProperties; -/** - * - * @author Yegor Kozlov - */ public abstract class ParagraphPropertyFetcher extends PropertyFetcher { + static final String PML_NS = "http://schemas.openxmlformats.org/presentationml/2006/main"; + static final String DML_NS = "http://schemas.openxmlformats.org/drawingml/2006/main"; + + private static final QName[] TX_BODY = { new QName(PML_NS, "txBody") }; + private static final QName[] LST_STYLE = { new QName(DML_NS, "lstStyle") }; + int _level; public ParagraphPropertyFetcher(int level) { @@ -35,17 +41,20 @@ public abstract class ParagraphPropertyFetcher extends PropertyFetcher { } public boolean fetch(XSLFShape shape) { - - XmlObject[] o = shape.getXmlObject().selectPath( - "declare namespace p='http://schemas.openxmlformats.org/presentationml/2006/main' " + - "declare namespace a='http://schemas.openxmlformats.org/drawingml/2006/main' " + - ".//p:txBody/a:lstStyle/a:lvl" + (_level + 1) + "pPr" - ); - if (o.length == 1) { - CTTextParagraphProperties props = (CTTextParagraphProperties) o[0]; - return fetch(props); + QName[] lvlProp = { new QName(DML_NS, "lvl" + (_level + 1) + "pPr") }; + CTTextParagraphProperties props = null; + try { + props = shape.selectProperty( + CTTextParagraphProperties.class, ParagraphPropertyFetcher::parse, TX_BODY, LST_STYLE, lvlProp); + return (props != null) && fetch(props); + } catch (XmlException e) { + return false; } - return false; + } + + private static CTTextParagraphProperties parse(XMLStreamReader reader) throws XmlException { + CTTextParagraph para = CTTextParagraph.Factory.parse(reader); + return (para != null && para.isSetPPr()) ? para.getPPr() : null; } public abstract boolean fetch(CTTextParagraphProperties props); diff --git a/src/ooxml/java/org/apache/poi/xslf/model/TextBodyPropertyFetcher.java b/src/ooxml/java/org/apache/poi/xslf/model/TextBodyPropertyFetcher.java index 4b1d546e75..9bb02504f8 100644 --- a/src/ooxml/java/org/apache/poi/xslf/model/TextBodyPropertyFetcher.java +++ b/src/ooxml/java/org/apache/poi/xslf/model/TextBodyPropertyFetcher.java @@ -19,32 +19,35 @@ package org.apache.poi.xslf.model; +import static org.apache.poi.xslf.model.ParagraphPropertyFetcher.DML_NS; +import static org.apache.poi.xslf.model.ParagraphPropertyFetcher.PML_NS; + +import javax.xml.namespace.QName; +import javax.xml.stream.XMLStreamReader; + import org.apache.poi.xslf.usermodel.XSLFShape; -import org.apache.xmlbeans.XmlObject; +import org.apache.xmlbeans.XmlException; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBody; import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBodyProperties; -/** - * Created by IntelliJ IDEA. - * User: yegor - * Date: Oct 21, 2011 - * Time: 1:18:52 PM - * To change this template use File | Settings | File Templates. - */ public abstract class TextBodyPropertyFetcher extends PropertyFetcher { + private static final QName[] TX_BODY = { new QName(PML_NS, "txBody") }; + private static final QName[] BODY_PR = { new QName(DML_NS, "bodyPr") }; public boolean fetch(XSLFShape shape) { - - XmlObject[] o = shape.getXmlObject().selectPath( - "declare namespace p='http://schemas.openxmlformats.org/presentationml/2006/main' " + - "declare namespace a='http://schemas.openxmlformats.org/drawingml/2006/main' " + - ".//p:txBody/a:bodyPr" - ); - if (o.length == 1) { - CTTextBodyProperties props = (CTTextBodyProperties) o[0]; - return fetch(props); + CTTextBodyProperties props = null; + try { + props = shape.selectProperty( + CTTextBodyProperties.class, TextBodyPropertyFetcher::parse, TX_BODY, BODY_PR); + return (props != null) && fetch(props); + } catch (XmlException e) { + return false; } + } - return false; + private static CTTextBodyProperties parse(XMLStreamReader reader) throws XmlException { + CTTextBody body = CTTextBody.Factory.parse(reader); + return (body != null) ? body.getBodyPr() : null; } public abstract boolean fetch(CTTextBodyProperties props); diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFObjectShape.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFObjectShape.java index 34042273a0..eb71e7ed02 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFObjectShape.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFObjectShape.java @@ -25,10 +25,11 @@ import java.io.IOException; import java.io.OutputStream; import javax.xml.namespace.QName; +import javax.xml.stream.XMLStreamReader; +import org.apache.poi.hpsf.ClassID; import org.apache.poi.ooxml.POIXMLDocumentPart.RelationPart; import org.apache.poi.ooxml.POIXMLException; -import org.apache.poi.hpsf.ClassID; import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.apache.poi.openxml4j.opc.OPCPackage; import org.apache.poi.openxml4j.opc.PackagePart; @@ -42,8 +43,6 @@ import org.apache.poi.sl.usermodel.ObjectShape; import org.apache.poi.util.Internal; 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.CTBlip; import org.openxmlformats.schemas.drawingml.x2006.main.CTBlipFillProperties; import org.openxmlformats.schemas.drawingml.x2006.main.CTGraphicalObjectData; @@ -63,6 +62,10 @@ import org.openxmlformats.schemas.presentationml.x2006.main.CTPictureNonVisual; public class XSLFObjectShape extends XSLFGraphicFrame implements ObjectShape { /* package */ static final String OLE_URI = "http://schemas.openxmlformats.org/presentationml/2006/ole"; + private static final QName[] GRAPHIC = { new QName(DML_NS, "graphic") }; + private static final QName[] GRAPHIC_DATA = { new QName(DML_NS, "graphicData") }; + private static final QName[] OLE_OBJ = { new QName(PML_NS, "oleObj") }; + private static final QName[] CT_PICTURE = { new QName(PML_NS, "pic") }; private CTOleObject _oleObject; private XSLFPictureData _data; @@ -70,31 +73,13 @@ public class XSLFObjectShape extends XSLFGraphicFrame implements ObjectShape 0) ? gs.getPicArray(0) : null; + } @Override public OutputStream updateObjectData(final Application application, final ObjectMetaData metaData) throws IOException { diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureShape.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureShape.java index 1a0c96fa30..1408bedb5b 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureShape.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureShape.java @@ -33,6 +33,7 @@ import java.net.URI; import javax.imageio.ImageIO; import javax.xml.namespace.QName; +import javax.xml.stream.XMLStreamReader; import org.apache.poi.openxml4j.opc.PackagePart; import org.apache.poi.openxml4j.opc.PackageRelationship; @@ -69,11 +70,15 @@ public class XSLFPictureShape extends XSLFSimpleShape implements PictureShape { private static final POILogger LOG = POILogFactory.getLogger(XSLFPictureShape.class); - private static final String DML_NS = "http://schemas.microsoft.com/office/drawing/2010/main"; - private static final String SVG_NS = "http://schemas.microsoft.com/office/drawing/2016/SVG/main"; + private static final String MS_DML_NS = "http://schemas.microsoft.com/office/drawing/2010/main"; + private static final String MS_SVG_NS = "http://schemas.microsoft.com/office/drawing/2016/SVG/main"; private static final String BITMAP_URI = "{28A0092B-C50C-407E-A947-70E740481C1C}"; private static final String SVG_URI = "{96DAC541-7B7A-43D3-8B79-37D633B846F1}"; + private static final QName EMBED_TAG = new QName(CORE_PROPERTIES_ECMA376_NS, "embed", "rel"); + private static final QName[] BLIP_FILL = { new QName(PML_NS, "blipFill") }; + + private XSLFPictureData _data; /*package*/ XSLFPictureShape(CTPicture shape, XSLFSheet sheet) { @@ -135,8 +140,8 @@ public class XSLFPictureShape extends XSLFSimpleShape public void setPlaceholder(Placeholder placeholder) { super.setPlaceholder(placeholder); } - - + + /** * For an external linked picture, return the last-seen * path to the picture. @@ -147,13 +152,13 @@ public class XSLFPictureShape extends XSLFSimpleShape // Internal picture, nothing to return return null; } - + String rId = getBlipLink(); if (rId == null) { // No link recorded, nothing we can do return null; } - + PackagePart p = getSheet().getPackagePart(); PackageRelationship rel = p.getRelationship(rId); if (rel != null) { @@ -168,25 +173,23 @@ public class XSLFPictureShape extends XSLFSimpleShape if (bfp != null) { return bfp; } - - 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:blipFill" - ; - XmlObject xo = selectProperty(XmlObject.class, xquery); + try { - xo = CTPicture.Factory.parse(xo.getDomNode()); + return selectProperty(CTBlipFillProperties.class, XSLFPictureShape::parse, BLIP_FILL); } catch (XmlException xe) { return null; } - return ((CTPicture)xo).getBlipFill(); } - + + private static CTBlipFillProperties parse(XMLStreamReader reader) throws XmlException { + CTPicture pic = CTPicture.Factory.parse(reader); + return (pic != null) ? pic.getBlipFill() : null; + } + protected CTBlip getBlip(){ return getBlipFill().getBlip(); } - + @SuppressWarnings("WeakerAccess") protected String getBlipLink(){ CTBlip blip = getBlip(); @@ -232,8 +235,8 @@ public class XSLFPictureShape extends XSLFSimpleShape extBitmap.setUri(BITMAP_URI); XmlCursor cur = extBitmap.newCursor(); cur.toEndToken(); - cur.beginElement(new QName(DML_NS, "useLocalDpi", "a14")); - cur.insertNamespace("a14", DML_NS); + cur.beginElement(new QName(MS_DML_NS, "useLocalDpi", "a14")); + cur.insertNamespace("a14", MS_DML_NS); cur.insertAttributeWithValue("val", "0"); cur.dispose(); } @@ -252,9 +255,9 @@ public class XSLFPictureShape extends XSLFSimpleShape svgBitmap.setUri(SVG_URI); XmlCursor cur = svgBitmap.newCursor(); cur.toEndToken(); - cur.beginElement(new QName(SVG_NS, "svgBlip", "asvg")); - cur.insertNamespace("asvg", SVG_NS); - cur.insertAttributeWithValue(new QName(CORE_PROPERTIES_ECMA376_NS, "embed", "rel"), svgRelId); + cur.beginElement(new QName(MS_SVG_NS, "svgBlip", "asvg")); + cur.insertNamespace("asvg", MS_SVG_NS); + cur.insertAttributeWithValue(EMBED_TAG, svgRelId); cur.dispose(); } @@ -277,8 +280,8 @@ public class XSLFPictureShape extends XSLFSimpleShape for (int i = 0; i < size; i++) { XmlCursor cur = extLst.getExtArray(i).newCursor(); try { - if (cur.toChild(SVG_NS, "svgBlip")) { - String svgRelId = cur.getAttributeText(new QName(CORE_PROPERTIES_ECMA376_NS, "embed")); + if (cur.toChild(MS_SVG_NS, "svgBlip")) { + String svgRelId = cur.getAttributeText(EMBED_TAG); return (svgRelId != null) ? (XSLFPictureData) getSheet().getRelationById(svgRelId) : null; } } finally { @@ -367,13 +370,13 @@ public class XSLFPictureShape extends XSLFSimpleShape CTOfficeArtExtensionList extLst = blip.getExtLst(); //noinspection deprecation for(CTOfficeArtExtension ext : extLst.getExtArray()){ - String xpath = "declare namespace a14='"+ DML_NS +"' $this//a14:imgProps/a14:imgLayer"; + String xpath = "declare namespace a14='"+ MS_DML_NS +"' $this//a14:imgProps/a14:imgLayer"; XmlObject[] obj = ext.selectPath(xpath); if(obj != null && obj.length == 1){ XmlCursor c = obj[0].newCursor(); - String id = c.getAttributeText(new QName("http://schemas.openxmlformats.org/officeDocument/2006/relationships", "embed"));//selectPath("declare namespace r='http://schemas.openxmlformats.org/officeDocument/2006/relationships' $this//[@embed]"); + String id = c.getAttributeText(EMBED_TAG); String newId = getSheet().importBlip(id, p.getSheet()); - c.setAttributeText(new QName("http://schemas.openxmlformats.org/officeDocument/2006/relationships", "embed"), newId); + c.setAttributeText(EMBED_TAG, newId); c.dispose(); } } diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPlaceholderDetails.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPlaceholderDetails.java index e5d321bc0d..6cb179d964 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPlaceholderDetails.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPlaceholderDetails.java @@ -22,9 +22,12 @@ import static org.apache.poi.xslf.usermodel.XSLFShape.PML_NS; import java.util.function.Consumer; import java.util.function.Function; +import javax.xml.namespace.QName; + import org.apache.poi.sl.usermodel.MasterSheet; import org.apache.poi.sl.usermodel.Placeholder; import org.apache.poi.sl.usermodel.PlaceholderDetails; +import org.apache.xmlbeans.XmlException; import org.openxmlformats.schemas.presentationml.x2006.main.CTApplicationNonVisualDrawingProps; import org.openxmlformats.schemas.presentationml.x2006.main.CTHeaderFooter; import org.openxmlformats.schemas.presentationml.x2006.main.CTNotesMaster; @@ -190,9 +193,24 @@ public class XSLFPlaceholderDetails implements PlaceholderDetails { return _ph; } + 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[] NV_PROPS = { + new QName(PML_NS, "nvPr") + }; + private CTApplicationNonVisualDrawingProps getNvProps() { - final String xquery = "declare namespace p='" + PML_NS + "' .//*/p:nvPr"; - return shape.selectProperty(CTApplicationNonVisualDrawingProps.class, xquery); + try { + return shape.selectProperty(CTApplicationNonVisualDrawingProps.class, null, NV_CONTAINER, NV_PROPS); + } catch (XmlException e) { + return null; + } } private CTHeaderFooter getHeaderFooter(final boolean create) { diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShape.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShape.java index 7d80fbe3b9..efdbd7b12c 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShape.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShape.java @@ -21,7 +21,13 @@ 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; @@ -37,7 +43,9 @@ 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; @@ -59,7 +67,36 @@ import org.openxmlformats.schemas.presentationml.x2006.main.STPlaceholderType; */ @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; @@ -199,11 +236,14 @@ public abstract class XSLFShape implements Shape { } protected CTNonVisualDrawingProps getCNvPr() { - if (_nvPr == null) { - String xquery = "declare namespace p='http://schemas.openxmlformats.org/presentationml/2006/main' .//*/p:cNvPr"; - _nvPr = selectProperty(CTNonVisualDrawingProps.class, xquery); + try { + if (_nvPr == null) { + _nvPr = selectProperty(CTNonVisualDrawingProps.class, null, NV_CONTAINER, CNV_PROPS); + } + return _nvPr; + } catch (XmlException e) { + return null; } - return _nvPr; } @SuppressWarnings("WeakerAccess") @@ -281,6 +321,160 @@ public abstract class XSLFShape implements Shape { 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 * diff --git a/src/ooxml/testcases/org/apache/poi/xslf/TestXSLFBugs.java b/src/ooxml/testcases/org/apache/poi/xslf/TestXSLFBugs.java index a9a9e16b7c..d83de633cf 100644 --- a/src/ooxml/testcases/org/apache/poi/xslf/TestXSLFBugs.java +++ b/src/ooxml/testcases/org/apache/poi/xslf/TestXSLFBugs.java @@ -17,7 +17,14 @@ package org.apache.poi.xslf; import static org.apache.poi.POITestCase.assertContains; -import static org.junit.Assert.*; +import static org.apache.poi.xslf.XSLFTestDataSamples.openSampleDocument; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import java.awt.Color; import java.awt.Dimension; @@ -66,6 +73,7 @@ import org.apache.poi.xslf.usermodel.XSLFAutoShape; import org.apache.poi.xslf.usermodel.XSLFGroupShape; import org.apache.poi.xslf.usermodel.XSLFHyperlink; import org.apache.poi.xslf.usermodel.XSLFNotes; +import org.apache.poi.xslf.usermodel.XSLFObjectShape; import org.apache.poi.xslf.usermodel.XSLFPictureData; import org.apache.poi.xslf.usermodel.XSLFPictureShape; import org.apache.poi.xslf.usermodel.XSLFRelation; @@ -89,7 +97,7 @@ public class TestXSLFBugs { @Test public void bug62929() throws Exception { - try(XMLSlideShow ss1 = XSLFTestDataSamples.openSampleDocument("missing-blip-fill.pptx")) { + try(XMLSlideShow ss1 = openSampleDocument("missing-blip-fill.pptx")) { assertEquals(1, ss1.getSlides().size()); XSLFSlide slide = ss1.getSlides().get(0); @@ -108,7 +116,7 @@ public class TestXSLFBugs { @Test public void bug62736() throws Exception { - XMLSlideShow ss1 = XSLFTestDataSamples.openSampleDocument("bug62736.pptx"); + XMLSlideShow ss1 = openSampleDocument("bug62736.pptx"); assertEquals(1, ss1.getSlides().size()); @@ -332,7 +340,7 @@ public class TestXSLFBugs { @Test public void bug51187() throws Exception { - XMLSlideShow ss1 = XSLFTestDataSamples.openSampleDocument("51187.pptx"); + XMLSlideShow ss1 = openSampleDocument("51187.pptx"); assertEquals(1, ss1.getSlides().size()); @@ -373,7 +381,7 @@ public class TestXSLFBugs { */ @Test public void tika705() throws Exception { - XMLSlideShow ss = XSLFTestDataSamples.openSampleDocument("with_japanese.pptx"); + XMLSlideShow ss = openSampleDocument("with_japanese.pptx"); // Should have one slide assertEquals(1, ss.getSlides().size()); @@ -423,7 +431,7 @@ public class TestXSLFBugs { */ @Test public void bug54916() throws IOException { - try (XMLSlideShow ss = XSLFTestDataSamples.openSampleDocument("OverlappingRelations.pptx")) { + try (XMLSlideShow ss = openSampleDocument("OverlappingRelations.pptx")) { XSLFSlide slide; // Should find 4 slides @@ -452,7 +460,7 @@ public class TestXSLFBugs { */ @Test public void bug56812() throws Exception { - XMLSlideShow ppt = XSLFTestDataSamples.openSampleDocument("56812.pptx"); + XMLSlideShow ppt = openSampleDocument("56812.pptx"); int internalPictures = 0; int externalPictures = 0; @@ -485,7 +493,7 @@ public class TestXSLFBugs { @Test @Ignore("Similar to TestFontRendering it doesn't make sense to compare images because of tiny rendering differences in windows/unix") public void bug54542() throws Exception { - XMLSlideShow ss = XSLFTestDataSamples.openSampleDocument("54542_cropped_bitmap.pptx"); + XMLSlideShow ss = openSampleDocument("54542_cropped_bitmap.pptx"); Dimension pgsize = ss.getPageSize(); @@ -676,7 +684,7 @@ public class TestXSLFBugs { @Test public void bug58205() throws IOException { - XMLSlideShow ss = XSLFTestDataSamples.openSampleDocument("themes.pptx"); + XMLSlideShow ss = openSampleDocument("themes.pptx"); int i = 1; for (XSLFSlideMaster sm : ss.getSlideMasters()) { @@ -688,14 +696,14 @@ public class TestXSLFBugs { @Test public void bug55791a() throws IOException { - XMLSlideShow ppt = XSLFTestDataSamples.openSampleDocument("45541_Footer.pptx"); + XMLSlideShow ppt = openSampleDocument("45541_Footer.pptx"); removeAndCreateSlide(ppt); ppt.close(); } @Test public void bug55791b() throws IOException { - XMLSlideShow ppt = XSLFTestDataSamples.openSampleDocument("SampleShow.pptx"); + XMLSlideShow ppt = openSampleDocument("SampleShow.pptx"); removeAndCreateSlide(ppt); ppt.close(); } @@ -708,7 +716,7 @@ public class TestXSLFBugs { @Test public void blibFillAlternateContent() throws IOException { - XMLSlideShow ppt = XSLFTestDataSamples.openSampleDocument("2411-Performance_Up.pptx"); + XMLSlideShow ppt = openSampleDocument("2411-Performance_Up.pptx"); XSLFPictureShape ps = (XSLFPictureShape)ppt.getSlides().get(4).getShapes().get(0); assertNotNull(ps.getPictureData()); ppt.close(); @@ -781,7 +789,7 @@ public class TestXSLFBugs { @Test public void bug55714() throws IOException { - XMLSlideShow srcPptx = XSLFTestDataSamples.openSampleDocument("pptx2svg.pptx"); + XMLSlideShow srcPptx = openSampleDocument("pptx2svg.pptx"); XMLSlideShow newPptx = new XMLSlideShow(); XSLFSlide srcSlide = srcPptx.getSlides().get(0); XSLFSlide newSlide = newPptx.createSlide(); @@ -807,7 +815,7 @@ public class TestXSLFBugs { @Test public void bug59273() throws IOException { - XMLSlideShow ppt = XSLFTestDataSamples.openSampleDocument("bug59273.potx"); + XMLSlideShow ppt = openSampleDocument("bug59273.potx"); ppt.getPackage().replaceContentType( XSLFRelation.PRESENTATIONML_TEMPLATE.getContentType(), XSLFRelation.MAIN.getContentType() @@ -851,7 +859,7 @@ public class TestXSLFBugs { @Test public void bug60715() throws IOException { - XMLSlideShow ppt = XSLFTestDataSamples.openSampleDocument("bug60715.pptx"); + XMLSlideShow ppt = openSampleDocument("bug60715.pptx"); ppt.createSlide(); ppt.close(); } @@ -887,7 +895,7 @@ public class TestXSLFBugs { @Test public void test60810() throws IOException { - XMLSlideShow ppt = XSLFTestDataSamples.openSampleDocument("60810.pptx"); + XMLSlideShow ppt = openSampleDocument("60810.pptx"); for(XSLFSlide slide : ppt.getSlides()) { XSLFNotes notesSlide = ppt.getNotesSlide(slide); assertNotNull(notesSlide); @@ -898,7 +906,7 @@ public class TestXSLFBugs { @Test public void test60042() throws IOException { - XMLSlideShow ppt = XSLFTestDataSamples.openSampleDocument("60042.pptx"); + XMLSlideShow ppt = openSampleDocument("60042.pptx"); ppt.removeSlide(0); ppt.createSlide(); ppt.close(); @@ -906,7 +914,7 @@ public class TestXSLFBugs { @Test public void test61515() throws IOException { - XMLSlideShow ppt = XSLFTestDataSamples.openSampleDocument("61515.pptx"); + XMLSlideShow ppt = openSampleDocument("61515.pptx"); ppt.removeSlide(0); assertEquals(1, ppt.createSlide().getRelations().size()); try { @@ -923,7 +931,7 @@ public class TestXSLFBugs { @Test public void testAptia() throws IOException { - try (XMLSlideShow ppt = XSLFTestDataSamples.openSampleDocument("aptia.pptx"); + try (XMLSlideShow ppt = openSampleDocument("aptia.pptx"); XMLSlideShow saved = XSLFTestDataSamples.writeOutAndReadBack(ppt)) { assertEquals(ppt.getSlides().size(), saved.getSlides().size()); } @@ -932,7 +940,7 @@ public class TestXSLFBugs { @Ignore @Test public void testDivinoRevelado() throws IOException { - try (XMLSlideShow ppt = XSLFTestDataSamples.openSampleDocument("Divino_Revelado.pptx"); + try (XMLSlideShow ppt = openSampleDocument("Divino_Revelado.pptx"); XMLSlideShow saved = XSLFTestDataSamples.writeOutAndReadBack(ppt)){ assertEquals(ppt.getSlides().size(), saved.getSlides().size()); } @@ -968,7 +976,7 @@ public class TestXSLFBugs { @Test public void bug63200() throws Exception { - try (XMLSlideShow ss1 = XSLFTestDataSamples.openSampleDocument("63200.pptx")) { + try (XMLSlideShow ss1 = openSampleDocument("63200.pptx")) { assertEquals(1, ss1.getSlides().size()); XSLFSlide slide = ss1.getSlides().get(0); @@ -982,4 +990,25 @@ public class TestXSLFBugs { assertNull(arrow.getFillColor()); } } + + @Test + public void alternateContent() throws Exception { + try (XMLSlideShow ppt = openSampleDocument("alterman_security.pptx")) { + XSLFSlideMaster slide = ppt.getSlideMasters().get(0); + XSLFObjectShape os = (XSLFObjectShape)slide.getShapes().get(0); + // ctOleObject is nested in AlternateContent in this file + // if there are casting errors, we would fail early and wouldn't reach this point anyway + assertNotNull(os.getCTOleObject()); + // accessing the picture data of the AlternateContent fallback part + XSLFPictureData picData = os.getPictureData(); + assertNotNull(picData); + } + + try (XMLSlideShow ppt = openSampleDocument("2411-Performance_Up.pptx")) { + XSLFSlide slide = ppt.getSlides().get(4); + XSLFPictureShape ps = (XSLFPictureShape)slide.getShapes().get(0); + assertEquals("image4.png", ps.getPictureData().getFileName()); + assertEquals("Picture 5", ps.getShapeName()); + } + } }