From 60e200d78a3ba89c8cfc06a0776cdcf2bc6ec3e0 Mon Sep 17 00:00:00 2001 From: Mark Olesen Date: Wed, 29 Jul 2015 12:19:26 +0200 Subject: [PATCH] coding concept of XSLF picture references (bug 58190) - the goal is to avoid integer references as being too fragile when adding/creating pictures BUG: picture checksum not reset in setData() --- .../apache/poi/xslf/usermodel/XMLSlideShow.java | 80 ++++++- .../apache/poi/xslf/usermodel/XSLFGroupShape.java | 44 ++++- .../org/apache/poi/xslf/usermodel/XSLFPicture.java | 215 ++++++++++++++++++++ .../apache/poi/xslf/usermodel/XSLFPictureData.java | 146 ++++++-------- .../poi/xslf/usermodel/XSLFShapeContainer.java | 7 +- .../org/apache/poi/xslf/usermodel/XSLFSheet.java | 39 ++++- 6 files changed, 425 insertions(+), 106 deletions(-) create mode 100644 src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPicture.java diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XMLSlideShow.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XMLSlideShow.java index ae2ed3a..89a59c0 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XMLSlideShow.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XMLSlideShow.java @@ -429,7 +429,7 @@ public class XMLSlideShow extends POIXMLDocument implements SlideShow { } /** - * Adds a picture to the workbook. + * Adds a picture to the slideshow. * * @param pictureData The bytes of the picture * @param format The format of the picture. @@ -442,28 +442,84 @@ public class XMLSlideShow extends POIXMLDocument implements SlideShow { * @see XSLFPictureData#PICTURE_TYPE_PNG * @see XSLFPictureData#PICTURE_TYPE_DIB */ - public int addPicture(byte[] pictureData, int format) { + @Deprecated + public int addPicture(byte[] pictureData, int format) + { + return addPicture + ( + pictureData, + XSLFPicture.Type.findByDeprecatedPictureTypeIndex(format) + ).getIndex(); + } + + + /** + * Adds a picture to the slideshow. + * + * @param pictureData The bytes of the picture + * @param type The format of the picture + * + * @return the reference to this picture + */ + public XSLFPicture.Reference addPicture + ( + byte[] pictureData, + XSLFPicture.Type format + ) + { XSLFPictureData img = findPictureData(pictureData); - // POIXMLRelation relDesc = XSLFPictureData.RELATIONS[format]; - if(img == null) { - int imageNumber = _pictures.size(); - img = (XSLFPictureData) createRelationship( - XSLFPictureData.RELATIONS[format], XSLFFactory.getInstance(), imageNumber + 1, true); + int imageNumber; + if (img == null) + { + imageNumber = _pictures.size(); + img = (XSLFPictureData) createRelationship + ( + format.getDescriptor(), + XSLFFactory.getInstance(), + imageNumber + 1, + true + ); _pictures.add(img); - try { + + try + { OutputStream out = img.getPackagePart().getOutputStream(); out.write(pictureData); out.close(); - } catch (IOException e) { + } + catch (IOException e) + { throw new POIXMLException(e); } - return _pictures.size() - 1; - } else { - return _pictures.indexOf(img); } + else + { + imageNumber = _pictures.indexOf(img); + } + + return new XSLFPicture.Reference(format, imageNumber); } + + /** + * Adds a picture to the slideshow. + * + * @param is The stream to read image from + * @param type The format of the picture + * + * @return the reference to this picture + */ + public XSLFPicture.Reference addPicture + ( + InputStream is, + XSLFPicture.Type format + ) throws IOException + { + return addPicture(IOUtils.toByteArray(is), format); + } + + /** * check if a picture with this picture data already exists in this presentation */ diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFGroupShape.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFGroupShape.java index 4fc9e0f..93ee78d 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFGroupShape.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFGroupShape.java @@ -239,9 +239,10 @@ public class XSLFGroupShape extends XSLFShape implements XSLFShapeContainer, Gro return sh; } - public XSLFPictureShape createPicture(int pictureIndex){ - - List pics = getSheet().getPackagePart().getPackage() + @Deprecated + public XSLFPictureShape createPicture(int pictureIndex) + { + List pics = getSheet().getPackagePart().getPackage() .getPartsByName(Pattern.compile("/ppt/media/image" + (pictureIndex + 1) + ".*?")); if(pics.size() == 0) { @@ -260,6 +261,34 @@ public class XSLFGroupShape extends XSLFShape implements XSLFShapeContainer, Gro return sh; } + + @Override + public XSLFPictureShape createPicture(XSLFPicture.Reference picref) + { + int pictureIndex = picref.getIndex1(); + List pics = getSheet().getPackagePart().getPackage() + .getPartsByName(Pattern.compile("/ppt/media/image" + pictureIndex + "\\..+")); + + if (pics.isEmpty()) + { + throw new IllegalArgumentException("Picture with index=" + pictureIndex + " was not found"); + } + + PackagePart pic = pics.get(0); + + PackageRelationship rel = getSheet().getPackagePart().addRelationship + ( + pic.getPartName(), TargetMode.INTERNAL, XSLFRelation.IMAGES.getRelation() + ); + + XSLFPictureShape sh = getDrawing().createPicture(rel.getId()); + sh.resize(); + _shapes.add(sh); + sh.setParent(this); + return sh; + } + + public XSLFTable createTable(){ XSLFTable sh = getDrawing().createTable(); _shapes.add(sh); @@ -321,8 +350,13 @@ public class XSLFGroupShape extends XSLFShape implements XSLFShapeContainer, Gro } else if (shape instanceof XSLFPictureShape) { XSLFPictureShape p = (XSLFPictureShape)shape; XSLFPictureData pd = p.getPictureData(); - int picId = getSheet().getSlideShow().addPicture(pd.getData(), pd.getPictureType()); - newShape = createPicture(picId); + + // OLD // int picId = getSheet().getSlideShow().addPicture(pd.getData(), pd.getPictureType()); + // OLD // newShape = createPicture(picId); + newShape = createPicture + ( + getSheet().getSlideShow().addPicture(pd.getData(), pd.getType()) + ); } else if (shape instanceof XSLFGroupShape) { newShape = createGroup(); } else if (shape instanceof XSLFTable) { diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPicture.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPicture.java new file mode 100644 index 0000000..be490d3 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPicture.java @@ -0,0 +1,215 @@ +/* + * ==================================================================== + * 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 org.apache.poi.POIXMLRelation; +import org.apache.poi.util.Beta; + +/** + * Manages picture types and references for adding images to a slideshow + */ +@Beta +public class XSLFPicture +{ + /** Utility class */ + private XSLFPicture() {} + + + /** + * Supported picture types for a slideshow + */ + public enum Type + { + /** Extended windows meta file (.emf) */ + EMF("emf", XSLFRelation.IMAGE_EMF, 2), + + /** Windows Meta File (.wmf) */ + WMF("wmf", XSLFRelation.IMAGE_WMF, 3), + + /** Mac PICT format (.pict) */ + PICT("pict", XSLFRelation.IMAGE_PICT, 4), + + /** JPEG format (.jpeg) */ + JPEG("jpeg", XSLFRelation.IMAGE_JPEG, 5), + + /** PNG format (.png) */ + PNG("png", XSLFRelation.IMAGE_PNG, 6), + + /** Device independent bitmap (.dib) */ + DIB("dib", XSLFRelation.IMAGE_DIB, 7), + + /** GIF image format (.gif) */ + GIF("gif", XSLFRelation.IMAGE_GIF, 8), + + /** Tag Image File (.tiff) */ + TIFF("tiff", XSLFRelation.IMAGE_TIFF, 9), + + /** Encapsulated Postscript (.eps) */ + EPS("eps", XSLFRelation.IMAGE_EPS, 10), + + /** Windows Bitmap (.bmp) */ + BMG("bmp", XSLFRelation.IMAGE_BMP, 11), + + /** WordPerfect graphics (.wpg) */ + WPG("wpg", XSLFRelation.IMAGE_WPG, 12), + + /** Microsoft Windows Media Photo image (.wdp) */ + WDP("wdp", XSLFRelation.IMAGE_WDP, 13); + + private String formatName; + private POIXMLRelation descriptor; + + private Type(String formatName, XSLFRelation descriptor) + { + this.formatName = formatName; + this.descriptor = descriptor; + } + + /** + * For transition purposes only - do not use for new code. + * This value matches the PICTURE_TYPE_* constants from XSLFPictudeData + */ + @Deprecated private int compat; + + @Deprecated private Type(String formatName, XSLFRelation descriptor, int compatibility) + { + this(formatName, descriptor); + compat = compatibility; + } + + + /** The informal format name */ + public String getFormatName() + { + return formatName; + } + + + /** The part descriptor (XSLFRelation) */ + public POIXMLRelation getDescriptor() + { + return descriptor; + } + + /** + * For transition purposes only - do not use for new code. + * This value matches the PICTURE_TYPE_* constants from XSLFPictudeData + */ + @Deprecated + public int getDeprecatedPictureTypeIndex() + { + return compat; + } + + + /** The (mime) content-type */ + public String getContentType() + { + return descriptor.getContentType(); + } + + + /** String representation = format name (for now) */ + @Override + public String toString() + { + return formatName; + } + + + /** + * Find the enumeration corresponding to specified content-type, + * null on failure. + */ + public static Type findByContentType(String contentType) + { + for (Type type : Type.values()) + { + if (type.getContentType().equals(contentType)) + { + return type; + } + } + + return null; + } + + /** + * Find the enumeration corresponding to specified content-type, + * null on failure. + */ + @Deprecated + /*package*/ static Type findByDeprecatedPictureTypeIndex(int typeIndex) + { + for (Type type : Type.values()) + { + if (type.compat == typeIndex) + { + return type; + } + } + + return null; + } + } + + + /** + * An indexed reference to an image within the slideshow + */ + public static class Reference + { + /* Picture type */ + private Type type_; + + /** The 0-based index */ + private int index_; + + /* package */ Reference(Type type, int index) + { + type_ = type; + index_ = index; + } + + + /** Return the type */ + public Type getType() + { + return type_; + } + + + /** Return the 0-based picture index */ + public int getIndex() + { + return index_; + } + + + /** Return the 1-based picture index */ + public int getIndex1() + { + return index_ + 1; + } + } + +} + +// ************************************************************************* // diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureData.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureData.java index 98c3b7c..630c36b 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureData.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureData.java @@ -24,7 +24,6 @@ import java.io.OutputStream; import org.apache.poi.POIXMLDocumentPart; import org.apache.poi.POIXMLException; -import org.apache.poi.POIXMLRelation; import org.apache.poi.openxml4j.opc.PackagePart; import org.apache.poi.openxml4j.opc.PackageRelationship; import org.apache.poi.sl.usermodel.PictureData; @@ -38,87 +37,43 @@ import org.apache.poi.util.IOUtils; */ @Beta public final class XSLFPictureData extends POIXMLDocumentPart implements PictureData { - /** - * Extended windows meta file - */ - public static final int PICTURE_TYPE_EMF = 2; - /** - * Windows Meta File - */ - public static final int PICTURE_TYPE_WMF = 3; + /** Extended windows meta file */ + @Deprecated public static final int PICTURE_TYPE_EMF = 2; - /** - * Mac PICT format - */ - public static final int PICTURE_TYPE_PICT = 4; + /** Windows Meta File */ + @Deprecated public static final int PICTURE_TYPE_WMF = 3; - /** - * JPEG format - */ - public static final int PICTURE_TYPE_JPEG = 5; + /** Mac PICT format */ + @Deprecated public static final int PICTURE_TYPE_PICT = 4; - /** - * PNG format - */ - public static final int PICTURE_TYPE_PNG = 6; + /** JPEG format */ + @Deprecated public static final int PICTURE_TYPE_JPEG = 5; - /** - * Device independent bitmap - */ - public static final int PICTURE_TYPE_DIB = 7; + /** PNG format */ + @Deprecated public static final int PICTURE_TYPE_PNG = 6; - /** - * GIF image format - */ - public static final int PICTURE_TYPE_GIF = 8; + /** Device independent bitmap */ + @Deprecated public static final int PICTURE_TYPE_DIB = 7; - /** - * Tag Image File (.tiff) - */ - public static final int PICTURE_TYPE_TIFF = 9; + /** GIF image format */ + @Deprecated public static final int PICTURE_TYPE_GIF = 8; - /** - * Encapsulated Postscript (.eps) - */ - public static final int PICTURE_TYPE_EPS = 10; + /** Tag Image File (.tiff) */ + @Deprecated public static final int PICTURE_TYPE_TIFF = 9; + /** Encapsulated Postscript (.eps) */ + @Deprecated public static final int PICTURE_TYPE_EPS = 10; - /** - * Windows Bitmap (.bmp) - */ - public static final int PICTURE_TYPE_BMP = 11; + /** Windows Bitmap (.bmp) */ + @Deprecated public static final int PICTURE_TYPE_BMP = 11; - /** - * WordPerfect graphics (.wpg) - */ - public static final int PICTURE_TYPE_WPG = 12; + /** WordPerfect graphics (.wpg) */ + @Deprecated public static final int PICTURE_TYPE_WPG = 12; - /** - * Microsoft Windows Media Photo image (.wdp) - */ - public static final int PICTURE_TYPE_WDP = 13; + /** Microsoft Windows Media Photo image (.wdp) */ + @Deprecated public static final int PICTURE_TYPE_WDP = 13; - /** - * Relationships for each known picture type - */ - protected static final POIXMLRelation[] RELATIONS; - - static { - RELATIONS = new POIXMLRelation[14]; - RELATIONS[PICTURE_TYPE_EMF] = XSLFRelation.IMAGE_EMF; - RELATIONS[PICTURE_TYPE_WMF] = XSLFRelation.IMAGE_WMF; - RELATIONS[PICTURE_TYPE_PICT] = XSLFRelation.IMAGE_PICT; - RELATIONS[PICTURE_TYPE_JPEG] = XSLFRelation.IMAGE_JPEG; - RELATIONS[PICTURE_TYPE_PNG] = XSLFRelation.IMAGE_PNG; - RELATIONS[PICTURE_TYPE_DIB] = XSLFRelation.IMAGE_DIB; - RELATIONS[PICTURE_TYPE_GIF] = XSLFRelation.IMAGE_GIF; - RELATIONS[PICTURE_TYPE_TIFF] = XSLFRelation.IMAGE_TIFF; - RELATIONS[PICTURE_TYPE_EPS] = XSLFRelation.IMAGE_EPS; - RELATIONS[PICTURE_TYPE_BMP] = XSLFRelation.IMAGE_BMP; - RELATIONS[PICTURE_TYPE_WPG] = XSLFRelation.IMAGE_WPG; - RELATIONS[PICTURE_TYPE_WDP] = XSLFRelation.IMAGE_WDP; - } private Long checksum = null; @@ -187,18 +142,22 @@ public final class XSLFPictureData extends POIXMLDocumentPart implements Picture * * @return an integer constant that specifies type of this picture */ + @Deprecated public int getPictureType() { - String contentType = getPackagePart().getContentType(); - for (int i = 0; i < RELATIONS.length; i++) { - if (RELATIONS[i] == null) { - continue; - } - - if (RELATIONS[i].getContentType().equals(contentType)) { - return i; - } - } - return 0; + // OLD // String contentType = getPackagePart().getContentType(); + // OLD // for (int i = 0; i < RELATIONS.length; i++) { + // OLD // if (RELATIONS[i] == null) { + // OLD // continue; + // OLD // } + // OLD // + // OLD // if (RELATIONS[i].getContentType().equals(contentType)) { + // OLD // return i; + // OLD // } + // OLD // } + // OLD // return 0; + + XSLFPicture.Type type = getType(); + return type == null ? 0 : type.getDeprecatedPictureTypeIndex(); } long getChecksum(){ @@ -217,16 +176,35 @@ public final class XSLFPictureData extends POIXMLDocumentPart implements Picture protected void prepareForCommit() { // do not clear the part here } - - public String getContentType() { - POIXMLRelation rel = RELATIONS[getPictureType()]; - return (rel == null) ? null : rel.getContentType(); + + + /** + * Return the enum corresponding to the type of this picture content, + * null on failure + */ + public XSLFPicture.Type getType() + { + return XSLFPicture.Type.findByContentType(getPackagePart().getContentType()); } + + public String getContentType() + { + // OLD // POIXMLRelation rel = RELATIONS[getPictureType()]; + // OLD // return (rel == null) ? null : rel.getContentType(); + + XSLFPicture.Type type = getType(); + return (type == null) ? null : type.getContentType(); + } + + @Override public void setData(byte[] data) throws IOException { OutputStream os = getPackagePart().getOutputStream(); os.write(data); os.close(); + + // checksum is invalid + checksum = null; } diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShapeContainer.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShapeContainer.java index e1d00c5..1b94189 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShapeContainer.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShapeContainer.java @@ -42,7 +42,6 @@ public interface XSLFShapeContainer extends ShapeContainer { XSLFTextBox createTextBox(); /** - * * create a connector */ XSLFConnectorShape createConnector(); @@ -53,9 +52,11 @@ public interface XSLFShapeContainer extends ShapeContainer { XSLFGroupShape createGroup(); /** - * create a picture belonging to this container + * create a picture belonging to this container. + * The picture will have been previously added via the + * XMLSlideShow.addPicture() method */ - XSLFPictureShape createPicture(int pictureIndex); + XSLFPictureShape createPicture(XSLFPicture.Reference picref); /** * Removes all of the elements from this container (optional operation). diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSheet.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSheet.java index 9549baf..4599cd0 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSheet.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSheet.java @@ -182,6 +182,7 @@ public abstract class XSLFSheet extends POIXMLDocumentPart implements XSLFShapeC return sh; } + @Deprecated public XSLFPictureShape createPicture(int pictureIndex){ List pics = getPackagePart().getPackage() .getPartsByName(Pattern.compile("/ppt/media/image" + (pictureIndex + 1) + ".*?")); @@ -204,6 +205,35 @@ public abstract class XSLFSheet extends POIXMLDocumentPart implements XSLFShapeC return sh; } + + @Override + public XSLFPictureShape createPicture(XSLFPicture.Reference picref) + { + int pictureIndex = picref.getIndex1(); + List pics = getPackagePart().getPackage() + .getPartsByName(Pattern.compile("/ppt/media/image" + pictureIndex + "\\..*")); + + if (pics.isEmpty()) + { + throw new IllegalArgumentException("Picture with index=" + pictureIndex + " was not found"); + } + + PackagePart pic = pics.get(0); + PackageRelationship rel = getPackagePart().addRelationship + ( + pic.getPartName(), TargetMode.INTERNAL, XSLFRelation.IMAGES.getRelation() + ); + addRelation(rel.getId(), new XSLFPictureData(pic, rel)); + + XSLFPictureShape sh = getDrawing().createPicture(rel.getId()); + sh.resize(); + + getShapeList().add(sh); + sh.setParent(this); + return sh; + } + + public XSLFTable createTable(){ List shapes = getShapeList(); XSLFTable sh = getDrawing().createTable(); @@ -525,8 +555,13 @@ public abstract class XSLFSheet extends POIXMLDocumentPart implements XSLFShapeC XSLFPictureData data = new XSLFPictureData(blipPart, null); XMLSlideShow ppt = getSlideShow(); - int pictureIdx = ppt.addPicture(data.getData(), data.getPictureType()); - PackagePart pic = ppt.getAllPictures().get(pictureIdx).getPackagePart(); + // OLD // int pictureIdx = ppt.addPicture(data.getData(), data.getPictureType()); + // OLD // PackagePart pic = ppt.getAllPictures().get(pictureIdx).getPackagePart(); + + PackagePart pic = ppt.getAllPictures().get + ( + ppt.addPicture(data.getData(), data.getType()).getIndex() + ).getPackagePart(); PackageRelationship rel = getPackagePart().addRelationship( pic.getPartName(), TargetMode.INTERNAL, blipRel.getRelationshipType()); -- 1.7.1