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

org.apache.poi.xslf.usermodel.XSLFFreeformShape 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 java.awt.geom.AffineTransform;
import java.awt.geom.Path2D;
import java.awt.geom.PathIterator;
import java.awt.geom.Rectangle2D;

import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;

import org.apache.poi.ooxml.POIXMLTypeLoader;
import org.apache.poi.ooxml.util.POIXMLUnits;
import org.apache.poi.sl.draw.geom.CustomGeometry;
import org.apache.poi.sl.draw.geom.PresetGeometries;
import org.apache.poi.sl.usermodel.FreeformShape;
import org.apache.poi.util.Beta;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
import org.apache.poi.util.Units;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlOptions;
import org.openxmlformats.schemas.drawingml.x2006.main.CTAdjPoint2D;
import org.openxmlformats.schemas.drawingml.x2006.main.CTCustomGeometry2D;
import org.openxmlformats.schemas.drawingml.x2006.main.CTGeomRect;
import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps;
import org.openxmlformats.schemas.drawingml.x2006.main.CTPath2D;
import org.openxmlformats.schemas.drawingml.x2006.main.CTPath2DClose;
import org.openxmlformats.schemas.drawingml.x2006.main.CTPath2DCubicBezierTo;
import org.openxmlformats.schemas.drawingml.x2006.main.CTPath2DLineTo;
import org.openxmlformats.schemas.drawingml.x2006.main.CTPath2DMoveTo;
import org.openxmlformats.schemas.drawingml.x2006.main.CTPath2DQuadBezierTo;
import org.openxmlformats.schemas.drawingml.x2006.main.CTShapeProperties;
import org.openxmlformats.schemas.drawingml.x2006.main.CTTransform2D;
import org.openxmlformats.schemas.presentationml.x2006.main.CTShape;
import org.openxmlformats.schemas.presentationml.x2006.main.CTShapeNonVisual;

/**
 * Represents a custom geometric shape.
 * This shape will consist of a series of lines and curves described within a creation path.
 */
@Beta
public class XSLFFreeformShape extends XSLFAutoShape
    implements FreeformShape {

    private static final POILogger LOG = POILogFactory.getLogger(XSLFFreeformShape.class);

    /*package*/ XSLFFreeformShape(CTShape shape, XSLFSheet sheet) {
        super(shape, sheet);
    }

    @Override
    public int setPath(final Path2D path) {
        final CTPath2D ctPath = CTPath2D.Factory.newInstance();

        final Rectangle2D bounds = path.getBounds2D();
        final int x0 = Units.toEMU(bounds.getX());
        final int y0 = Units.toEMU(bounds.getY());
        final PathIterator it = path.getPathIterator(new AffineTransform());
        int numPoints = 0;
        ctPath.setH(Units.toEMU(bounds.getHeight()));
        ctPath.setW(Units.toEMU(bounds.getWidth()));

        final double[] vals = new double[6];
        while (!it.isDone()) {
            final int type = it.currentSegment(vals);
            final CTAdjPoint2D[] points;
            switch (type) {
                case PathIterator.SEG_MOVETO:
                    points = addMoveTo(ctPath);
                    break;
                case PathIterator.SEG_LINETO:
                    points = addLineTo(ctPath);
                    break;
                case PathIterator.SEG_QUADTO:
                    points = addQuadBezierTo(ctPath);
                    break;
                case PathIterator.SEG_CUBICTO:
                    points = addCubicBezierTo(ctPath);
                    break;
                case PathIterator.SEG_CLOSE:
                    points = addClosePath(ctPath);
                    break;
                default: {
                    throw new IllegalStateException("Unrecognized path segment type: " + type);
                }
            }

            int i=0;
            for (final CTAdjPoint2D point : points) {
                point.setX(Units.toEMU(vals[i++])-x0);
                point.setY(Units.toEMU(vals[i++])-y0);
            }

            numPoints += Math.max(points.length, 1);
            it.next();
        }

        XmlObject xo = getShapeProperties();
        if (!(xo instanceof CTShapeProperties)) {
            return -1;
        }

        ((CTShapeProperties)xo).getCustGeom().getPathLst().setPathArray(new CTPath2D[]{ctPath});
        setAnchor(bounds);
        return numPoints;
    }

    /**
     * @return definition of the shape geometry
     */
    @Override
    public CustomGeometry getGeometry() {
        final XmlObject xo = getShapeProperties();
        if (!(xo instanceof CTShapeProperties)) {
            return null;
        }

        XmlOptions xop = new XmlOptions(POIXMLTypeLoader.DEFAULT_XML_OPTIONS);
        xop.setSaveOuter();

        XMLStreamReader staxReader = ((CTShapeProperties)xo).getCustGeom().newXMLStreamReader(xop);
        CustomGeometry custGeo = PresetGeometries.convertCustomGeometry(staxReader);
        try {
            staxReader.close();
        } catch (XMLStreamException e) {
            LOG.log(POILogger.WARN,
                    "An error occurred while closing a Custom Geometry XML Stream Reader: " + e.getMessage());
        }

        return custGeo;
    }

    @Override
    public Path2D.Double getPath() {
        final Path2D.Double path = new Path2D.Double();

        final XmlObject xo = getShapeProperties();
        if (!(xo instanceof CTShapeProperties)) {
            return null;
        }

        final CTCustomGeometry2D geom = ((CTShapeProperties)xo).getCustGeom();
        for(CTPath2D spPath : geom.getPathLst().getPathArray()){
            XmlCursor cursor = spPath.newCursor();
            try {
                if (cursor.toFirstChild()) {
                    do {
                        final XmlObject ch = cursor.getObject();
                        if (ch instanceof CTPath2DMoveTo) {
                            addMoveTo(path, (CTPath2DMoveTo)ch);
                        } else if (ch instanceof CTPath2DLineTo) {
                            addLineTo(path, (CTPath2DLineTo)ch);
                        } else if (ch instanceof CTPath2DQuadBezierTo) {
                            addQuadBezierTo(path, (CTPath2DQuadBezierTo)ch);
                        } else if (ch instanceof CTPath2DCubicBezierTo) {
                            addCubicBezierTo(path, (CTPath2DCubicBezierTo)ch);
                        } else if (ch instanceof CTPath2DClose) {
                            addClosePath(path);
                        } else {
                            LOG.log(POILogger.WARN, "can't handle path of type "+xo.getClass());
                        }
                    } while (cursor.toNextSibling());
                }
            } finally {
                cursor.dispose();
            }
        }

        // the created path starts at (x=0, y=0).
        // this used to scale each path element to the path bounding box,
        // but now the dimensions/relations are kept as-is
        final AffineTransform at = new AffineTransform();

        final CTTransform2D xfrm = getXfrm(false);
        final Rectangle2D xfrm2d = new Rectangle2D.Double
                (POIXMLUnits.parseLength(xfrm.getOff().xgetX()), POIXMLUnits.parseLength(xfrm.getOff().xgetY()), xfrm.getExt().getCx(), xfrm.getExt().getCy());

        final Rectangle2D bounds = getAnchor();
        at.translate(bounds.getX()+bounds.getCenterX(), bounds.getY()+bounds.getCenterY());
        at.scale(1./Units.EMU_PER_POINT, 1./Units.EMU_PER_POINT);
        at.translate(-xfrm2d.getCenterX(), -xfrm2d.getCenterY());
        return new Path2D.Double(at.createTransformedShape(path));
    }

    private static CTAdjPoint2D[] addMoveTo(final CTPath2D path) {
        return new CTAdjPoint2D[]{path.addNewMoveTo().addNewPt()};
    }

    private static void addMoveTo(final Path2D path, final CTPath2DMoveTo xo) {
        final CTAdjPoint2D pt = xo.getPt();
        path.moveTo((Long)pt.getX(), (Long)pt.getY());
    }

    private static CTAdjPoint2D[] addLineTo(final CTPath2D path) {
        return new CTAdjPoint2D[]{path.addNewLnTo().addNewPt()};
    }

    private static void addLineTo(final Path2D path, final CTPath2DLineTo xo) {
        final CTAdjPoint2D pt = xo.getPt();
        path.lineTo((Long)pt.getX(), (Long)pt.getY());
    }

    private static CTAdjPoint2D[] addQuadBezierTo(final CTPath2D path) {
        final CTPath2DQuadBezierTo bez = path.addNewQuadBezTo();
        return new CTAdjPoint2D[]{ bez.addNewPt(), bez.addNewPt() };
    }

    private static void addQuadBezierTo(final Path2D path, final CTPath2DQuadBezierTo xo) {
        final CTAdjPoint2D pt1 = xo.getPtArray(0);
        final CTAdjPoint2D pt2 = xo.getPtArray(1);
        path.quadTo((Long)pt1.getX(), (Long)pt1.getY(),
                (Long)pt2.getX(), (Long)pt2.getY());
    }

    private static CTAdjPoint2D[] addCubicBezierTo(final CTPath2D path) {
        final CTPath2DCubicBezierTo bez = path.addNewCubicBezTo();
        return new CTAdjPoint2D[]{ bez.addNewPt(), bez.addNewPt(), bez.addNewPt() };
    }

    private static void addCubicBezierTo(final Path2D path, final CTPath2DCubicBezierTo xo) {
        final CTAdjPoint2D pt1 = xo.getPtArray(0);
        final CTAdjPoint2D pt2 = xo.getPtArray(1);
        final CTAdjPoint2D pt3 = xo.getPtArray(2);
        path.curveTo((Long)pt1.getX(), (Long)pt1.getY(),
                (Long)pt2.getX(), (Long)pt2.getY(),
                (Long)pt3.getX(), (Long)pt3.getY());
    }

    private static CTAdjPoint2D[] addClosePath(final CTPath2D path) {
        path.addNewClose();
        return new CTAdjPoint2D[0];
    }

    private static void addClosePath(final Path2D path) {
        path.closePath();
    }

    /**
     * @param shapeId 1-based shapeId
     */
    static CTShape prototype(int shapeId) {
        CTShape ct = CTShape.Factory.newInstance();
        CTShapeNonVisual nvSpPr = ct.addNewNvSpPr();
        CTNonVisualDrawingProps cnv = nvSpPr.addNewCNvPr();
        cnv.setName("Freeform " + shapeId);
        cnv.setId(shapeId);
        nvSpPr.addNewCNvSpPr();
        nvSpPr.addNewNvPr();
        CTShapeProperties spPr = ct.addNewSpPr();
        CTCustomGeometry2D geom = spPr.addNewCustGeom();
        geom.addNewAvLst();
        geom.addNewGdLst();
        geom.addNewAhLst();
        geom.addNewCxnLst();
        CTGeomRect rect = geom.addNewRect();
        rect.setR("r");
        rect.setB("b");
        rect.setT("t");
        rect.setL("l");
        geom.addNewPathLst();
        return ct;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy