All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.poi.xslf.usermodel.XSLFPictureShape Maven / Gradle / Ivy

Go to download

The Apache Commons Codec package contains simple encoder and decoders for various formats such as Base64 and Hexadecimal. In addition to these widely used encoders and decoders, the codec package also maintains a collection of phonetic encoding utilities.

There is a newer version: 62
Show newest version
/*
 *  ====================================================================
 *    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.Insets;
import java.awt.geom.Dimension2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;

import javax.imageio.ImageIO;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLStreamReader;

import org.apache.commons.io.output.UnsynchronizedByteArrayOutputStream;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.poi.ooxml.util.POIXMLUnits;
import org.apache.poi.ooxml.util.XPathHelper;
import org.apache.poi.openxml4j.opc.PackagePart;
import org.apache.poi.openxml4j.opc.PackageRelationship;
import org.apache.poi.sl.usermodel.PictureData;
import org.apache.poi.sl.usermodel.PictureData.PictureType;
import org.apache.poi.sl.usermodel.PictureShape;
import org.apache.poi.sl.usermodel.Placeholder;
import org.apache.poi.util.Beta;
import org.apache.poi.util.Units;
import org.apache.poi.xslf.draw.SVGImageRenderer;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlException;
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.CTNonVisualDrawingProps;
import org.openxmlformats.schemas.drawingml.x2006.main.CTOfficeArtExtension;
import org.openxmlformats.schemas.drawingml.x2006.main.CTOfficeArtExtensionList;
import org.openxmlformats.schemas.drawingml.x2006.main.CTPresetGeometry2D;
import org.openxmlformats.schemas.drawingml.x2006.main.CTRelativeRect;
import org.openxmlformats.schemas.drawingml.x2006.main.CTShapeProperties;
import org.openxmlformats.schemas.drawingml.x2006.main.STShapeType;
import org.openxmlformats.schemas.presentationml.x2006.main.CTApplicationNonVisualDrawingProps;
import org.openxmlformats.schemas.presentationml.x2006.main.CTPicture;
import org.openxmlformats.schemas.presentationml.x2006.main.CTPictureNonVisual;

/**
 * Represents a picture shape
 */
@Beta
public class XSLFPictureShape extends XSLFSimpleShape
    implements PictureShape {
    private static final Logger LOG = LogManager.getLogger(XSLFPictureShape.class);

    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) {
        super(shape, sheet);
    }


    /**
     * @param shapeId 1-based shapeId
     * @param rel     relationship to the picture data in the ooxml package
     */
    static CTPicture prototype(int shapeId, String rel) {
        CTPicture ct = CTPicture.Factory.newInstance();
        CTPictureNonVisual nvSpPr = ct.addNewNvPicPr();
        CTNonVisualDrawingProps cnv = nvSpPr.addNewCNvPr();
        cnv.setName("Picture " + shapeId);
        cnv.setId(shapeId);
        nvSpPr.addNewCNvPicPr().addNewPicLocks().setNoChangeAspect(true);
        nvSpPr.addNewNvPr();

        CTBlipFillProperties blipFill = ct.addNewBlipFill();
        CTBlip blip = blipFill.addNewBlip();
        blip.setEmbed(rel);
        blipFill.addNewStretch().addNewFillRect();

        CTShapeProperties spPr = ct.addNewSpPr();
        CTPresetGeometry2D prst = spPr.addNewPrstGeom();
        prst.setPrst(STShapeType.RECT);
        prst.addNewAvLst();
        return ct;
    }


    /**
     * Is this an internal picture (image data included within
     *  the PowerPoint file), or an external linked picture
     *  (image lives outside)?
     */
    public boolean isExternalLinkedPicture() {
        return getBlipId() == null && getBlipLink() != null;
    }

    /**
     * Return the data on the (internal) picture.
     * For an external linked picture, will return null
     */
    @Override
    public XSLFPictureData getPictureData() {
        if(_data == null){
            String blipId = getBlipId();
            if (blipId == null) {
                return null;
            }
            _data = (XSLFPictureData)getSheet().getRelationById(blipId);
        }
        return _data;
    }

    @Override
    public void setPlaceholder(Placeholder placeholder) {
        super.setPlaceholder(placeholder);
    }


    /**
     * For an external linked picture, return the last-seen
     *  path to the picture.
     * For an internal picture, returns null.
     */
    public URI getPictureLink() {
        if (getBlipId() != null) {
            // 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) {
            return rel.getTargetURI();
        }
        return null;
    }

    protected CTBlipFillProperties getBlipFill() {
        CTPicture ct = (CTPicture)getXmlObject();
        CTBlipFillProperties bfp = ct.getBlipFill();
        if (bfp != null) {
            return bfp;
        }

        try {
            return XPathHelper.selectProperty(getXmlObject(), CTBlipFillProperties.class, XSLFPictureShape::parse, BLIP_FILL);
        } catch (XmlException xe) {
            return null;
        }
    }

    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();
        if (blip != null) {
            String link = blip.getLink();
            return (link.isEmpty()) ? null : link;
        } else {
            return null;
        }
    }

    @SuppressWarnings("WeakerAccess")
    protected String getBlipId(){
        CTBlip blip = getBlip();
        if (blip != null) {
            String id = blip.getEmbed();
            return (id.isEmpty()) ? null : id;
        } else {
            return null;
        }
    }

    @Override
    public Insets getClipping(){
        CTRelativeRect r = getBlipFill().getSrcRect();

        return (r == null) ? null : new Insets(
            POIXMLUnits.parsePercent(r.xgetT()),
            POIXMLUnits.parsePercent(r.xgetL()),
            POIXMLUnits.parsePercent(r.xgetB()),
            POIXMLUnits.parsePercent(r.xgetR()));
    }

    /**
     * Add a SVG image reference
     * @param svgPic a previously imported svg image
     *
     * @since POI 4.1.0
     */
    public void setSvgImage(XSLFPictureData svgPic) {
        CTBlip blip = getBlip();
        CTOfficeArtExtensionList extLst = blip.isSetExtLst() ? blip.getExtLst() : blip.addNewExtLst();

        final int bitmapId = getExt(extLst, BITMAP_URI);
        CTOfficeArtExtension extBitmap;
        if (bitmapId == -1) {
            extBitmap = extLst.addNewExt();
            extBitmap.setUri(BITMAP_URI);
            XmlCursor cur = extBitmap.newCursor();
            try {
                cur.toEndToken();
                cur.beginElement(new QName(MS_DML_NS, "useLocalDpi", "a14"));
                cur.insertNamespace("a14", MS_DML_NS);
                cur.insertAttributeWithValue("val", "0");
            } finally {
                cur.dispose();
            }
        }

        final int svgId = getExt(extLst, SVG_URI);
        if (svgId != -1) {
            extLst.removeExt(svgId);
        }

        String svgRelId = getSheet().getRelationId(svgPic);
        if (svgRelId == null) {
            svgRelId = getSheet().addRelation(null, XSLFRelation.IMAGE_SVG, svgPic).getRelationship().getId();
        }

        CTOfficeArtExtension svgBitmap = extLst.addNewExt();
        svgBitmap.setUri(SVG_URI);
        XmlCursor cur = svgBitmap.newCursor();
        try {
            cur.toEndToken();
            cur.beginElement(new QName(MS_SVG_NS, "svgBlip", "asvg"));
            cur.insertNamespace("asvg", MS_SVG_NS);
            cur.insertAttributeWithValue(EMBED_TAG, svgRelId);
        } finally {
            cur.dispose();
        }
    }

    @Override
    public PictureData getAlternativePictureData() {
        return getSvgImage();
    }

    /**
     * @return picture name, can be null
     * @since POI 5.1.0
     */
    public String getName() {
        String name = null;
        CTPictureNonVisual nvPicPr = getCTPictureNonVisual();
        if (nvPicPr != null) {
            CTNonVisualDrawingProps cnvdProps = nvPicPr.getCNvPr();
            if (cnvdProps != null) {
                name = cnvdProps.getName();
            }
        }
        return name;
    }

    /**
     * @param name picture name
     * @return returns true if the name was set
     * @since POI 5.1.0
     */
    public boolean setName(String name) {
        XmlObject xmlObject = getXmlObject();
        if (xmlObject instanceof CTPicture) {
            CTPicture ctPicture = (CTPicture)xmlObject;
            CTPictureNonVisual nvPicPr = ctPicture.getNvPicPr();
            if (nvPicPr == null) {
                nvPicPr = ctPicture.addNewNvPicPr();
            }
            if (nvPicPr != null) {
                CTNonVisualDrawingProps cnvdProps = nvPicPr.getCNvPr();
                if (cnvdProps == null) {
                    cnvdProps = nvPicPr.addNewCNvPr();
                }
                if (cnvdProps != null) {
                    cnvdProps.setName(name);
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * @return SVG image data -- can return null if no SVG image is found
     */
    public XSLFPictureData getSvgImage() {
        CTBlip blip = getBlip();
        if (blip == null) {
            return null;
        }
        CTOfficeArtExtensionList extLst = blip.getExtLst();
        if (extLst == null) {
            return null;
        }

        int size = extLst.sizeOfExtArray();
        for (int i = 0; i < size; i++) {
            XmlCursor cur = extLst.getExtArray(i).newCursor();
            try {
                if (cur.toChild(MS_SVG_NS, "svgBlip")) {
                    String svgRelId = cur.getAttributeText(EMBED_TAG);
                    return (svgRelId != null) ? (XSLFPictureData) getSheet().getRelationById(svgRelId) : null;
                }
            } finally {
                cur.dispose();
            }
        }
        return null;
    }

    /**
     * Convenience method for adding SVG images, which generates the preview image
     * @param sheet the sheet to add
     * @param svgPic the svg picture to add
     * @param previewType the preview picture type or null (defaults to PNG) - currently only JPEG,GIF,PNG are allowed
     * @param anchor the image anchor (for calculating the preview image size) or
     *               null (the preview size is taken from the svg picture bounds)
     *
     * @since POI 4.1.0
     */
    public static XSLFPictureShape addSvgImage(XSLFSheet sheet, XSLFPictureData svgPic, PictureType previewType, Rectangle2D anchor) throws IOException {

        SVGImageRenderer renderer = new SVGImageRenderer();
        try (InputStream is = svgPic.getInputStream()) {
            renderer.loadImage(is, svgPic.getType().contentType);
        }

        Dimension2D dim = renderer.getDimension();
        Rectangle2D anc = (anchor != null) ? anchor
            : new Rectangle2D.Double(0,0, Units.pixelToPoints((int)dim.getWidth()), Units.pixelToPoints((int)dim.getHeight()));

        PictureType pt = (previewType != null) ? previewType : PictureType.PNG;
        if (pt != PictureType.JPEG && pt != PictureType.GIF && pt != PictureType.PNG) {
            pt = PictureType.PNG;
        }

        BufferedImage thmBI = renderer.getImage(dim);
        UnsynchronizedByteArrayOutputStream bos = new UnsynchronizedByteArrayOutputStream(100000);
        // use extension instead of enum name, because of "jpeg"
        ImageIO.write(thmBI, pt.extension.substring(1), bos);

        XSLFPictureData pngPic = sheet.getSlideShow().addPicture(bos.toInputStream(), pt);

        XSLFPictureShape shape = sheet.createPicture(pngPic);
        shape.setAnchor(anc);
        shape.setSvgImage(svgPic);
        return shape;
    }


    private int getExt(CTOfficeArtExtensionList extLst, String uri) {
        final int size = extLst.sizeOfExtArray();
        for (int i=0; i




© 2015 - 2024 Weber Informatics LLC | Privacy Policy