123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358 |
- /* ====================================================================
- 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.hslf.usermodel;
-
- import java.awt.geom.Rectangle2D;
- import java.util.ArrayList;
- import java.util.Iterator;
- import java.util.List;
-
- import org.apache.poi.ddf.EscherChildAnchorRecord;
- import org.apache.poi.ddf.EscherClientAnchorRecord;
- import org.apache.poi.ddf.EscherContainerRecord;
- import org.apache.poi.ddf.EscherRecord;
- import org.apache.poi.ddf.EscherSpRecord;
- import org.apache.poi.ddf.EscherSpgrRecord;
- import org.apache.poi.sl.usermodel.GroupShape;
- import org.apache.poi.sl.usermodel.PictureData;
- import org.apache.poi.sl.usermodel.ShapeContainer;
- import org.apache.poi.sl.usermodel.ShapeType;
- import org.apache.poi.util.LittleEndian;
- import org.apache.poi.util.POILogger;
- import org.apache.poi.util.Units;
-
- /**
- * Represents a group of shapes.
- *
- * @author Yegor Kozlov
- */
- public class HSLFGroupShape extends HSLFShape
- implements HSLFShapeContainer, GroupShape<HSLFShape,HSLFTextParagraph> {
- /**
- * Create a new ShapeGroup. This constructor is used when a new shape is created.
- *
- */
- public HSLFGroupShape(){
- this(null, null);
- _escherContainer = createSpContainer(false);
- }
-
- /**
- * Create a new ShapeGroup. This constructor is used when a new shape is created.
- *
- * @param parent the parent of the shape
- */
- public HSLFGroupShape(ShapeContainer<HSLFShape,HSLFTextParagraph> parent){
- this(null, parent);
- _escherContainer = createSpContainer(parent instanceof HSLFGroupShape);
- }
-
- /**
- * Create a ShapeGroup object and initialize it from the supplied Record container.
- *
- * @param escherRecord <code>EscherSpContainer</code> container which holds information about this shape
- * @param parent the parent of the shape
- */
- protected HSLFGroupShape(EscherContainerRecord escherRecord, ShapeContainer<HSLFShape,HSLFTextParagraph> parent){
- super(escherRecord, parent);
- }
-
- @Override
- public void setAnchor(Rectangle2D anchor) {
- EscherClientAnchorRecord clientAnchor = getEscherChild(EscherClientAnchorRecord.RECORD_ID);
- boolean isInitialized = !(clientAnchor.getDx1() == 0 && clientAnchor.getRow1() == 0);
-
- if (isInitialized) {
- moveAndScale(anchor);
- } else {
- setExteriorAnchor(anchor);
- }
- }
-
- @Override
- public void setInteriorAnchor(Rectangle2D anchor){
- EscherSpgrRecord spgr = getEscherChild(EscherSpgrRecord.RECORD_ID);
-
- int x1 = Units.pointsToMaster(anchor.getX());
- int y1 = Units.pointsToMaster(anchor.getY());
- int x2 = Units.pointsToMaster(anchor.getX() + anchor.getWidth());
- int y2 = Units.pointsToMaster(anchor.getY() + anchor.getHeight());
-
- spgr.setRectX1(x1);
- spgr.setRectY1(y1);
- spgr.setRectX2(x2);
- spgr.setRectY2(y2);
- }
-
- @Override
- public Rectangle2D getInteriorAnchor(){
- EscherSpgrRecord rec = getEscherChild(EscherSpgrRecord.RECORD_ID);
- double x1 = Units.masterToPoints(rec.getRectX1());
- double y1 = Units.masterToPoints(rec.getRectY1());
- double x2 = Units.masterToPoints(rec.getRectX2());
- double y2 = Units.masterToPoints(rec.getRectY2());
- return new Rectangle2D.Double(x1,y1,x2-x1,y2-y1);
- }
-
- protected void setExteriorAnchor(Rectangle2D anchor) {
- EscherClientAnchorRecord clientAnchor = getEscherChild(EscherClientAnchorRecord.RECORD_ID);
-
- //hack. internal variable EscherClientAnchorRecord.shortRecord can be
- //initialized only in fillFields(). We need to set shortRecord=false;
- byte[] header = new byte[16];
- LittleEndian.putUShort(header, 0, 0);
- LittleEndian.putUShort(header, 2, 0);
- LittleEndian.putInt(header, 4, 8);
- clientAnchor.fillFields(header, 0, null);
-
- // All coordinates need to be converted to Master units (576 dpi)
- clientAnchor.setFlag((short)Units.pointsToMaster(anchor.getY()));
- clientAnchor.setCol1((short)Units.pointsToMaster(anchor.getX()));
- clientAnchor.setDx1((short)Units.pointsToMaster(anchor.getWidth() + anchor.getX()));
- clientAnchor.setRow1((short)Units.pointsToMaster(anchor.getHeight() + anchor.getY()));
-
- // TODO: does this make sense?
- setInteriorAnchor(anchor);
- }
-
- /**
- * Create a new ShapeGroup and create an instance of <code>EscherSpgrContainer</code> which represents a group of shapes
- */
- protected EscherContainerRecord createSpContainer(boolean isChild) {
- EscherContainerRecord spgr = new EscherContainerRecord();
- spgr.setRecordId(EscherContainerRecord.SPGR_CONTAINER);
- spgr.setOptions((short)15);
-
- //The group itself is a shape, and always appears as the first EscherSpContainer in the group container.
- EscherContainerRecord spcont = new EscherContainerRecord();
- spcont.setRecordId(EscherContainerRecord.SP_CONTAINER);
- spcont.setOptions((short)15);
-
- EscherSpgrRecord spg = new EscherSpgrRecord();
- spg.setOptions((short)1);
- spcont.addChildRecord(spg);
-
- EscherSpRecord sp = new EscherSpRecord();
- short type = (short)((ShapeType.NOT_PRIMITIVE.nativeId << 4) + 2);
- sp.setOptions(type);
- sp.setFlags(EscherSpRecord.FLAG_HAVEANCHOR | EscherSpRecord.FLAG_GROUP);
- spcont.addChildRecord(sp);
-
- EscherClientAnchorRecord anchor = new EscherClientAnchorRecord();
- spcont.addChildRecord(anchor);
-
- spgr.addChildRecord(spcont);
- return spgr;
- }
-
- /**
- * Add a shape to this group.
- *
- * @param shape - the Shape to add
- */
- public void addShape(HSLFShape shape){
- _escherContainer.addChildRecord(shape.getSpContainer());
-
- HSLFSheet sheet = getSheet();
- shape.setSheet(sheet);
- shape.setShapeId(sheet.allocateShapeId());
- shape.afterInsert(sheet);
- }
-
- /**
- * Moves and scales this <code>ShapeGroup</code> to the specified anchor.
- */
- protected void moveAndScale(Rectangle2D anchorDest){
- Rectangle2D anchorSrc = getAnchor();
- double scaleX = (anchorSrc.getWidth() == 0) ? 0 : anchorDest.getWidth() / anchorSrc.getWidth();
- double scaleY = (anchorSrc.getHeight() == 0) ? 0 : anchorDest.getHeight() / anchorSrc.getHeight();
-
- setExteriorAnchor(anchorDest);
-
- for (HSLFShape shape : getShapes()) {
- Rectangle2D chanchor = shape.getAnchor();
- double x = anchorDest.getX()+(chanchor.getX()-anchorSrc.getX())*scaleX;
- double y = anchorDest.getY()+(chanchor.getY()-anchorSrc.getY())*scaleY;
- double width = chanchor.getWidth()*scaleX;
- double height = chanchor.getHeight()*scaleY;
- shape.setAnchor(new Rectangle2D.Double(x, y, width, height));
- }
- }
-
- /**
- * Returns the anchor (the bounding box rectangle) of this shape group.
- * All coordinates are expressed in points (72 dpi).
- *
- * @return the anchor of this shape group
- */
- public Rectangle2D getAnchor(){
- EscherClientAnchorRecord clientAnchor = getEscherChild(EscherClientAnchorRecord.RECORD_ID);
- int x1,y1,x2,y2;
- if(clientAnchor == null){
- logger.log(POILogger.INFO, "EscherClientAnchorRecord was not found for shape group. Searching for EscherChildAnchorRecord.");
- EscherChildAnchorRecord rec = getEscherChild(EscherChildAnchorRecord.RECORD_ID);
- x1 = rec.getDx1();
- y1 = rec.getDy1();
- x2 = rec.getDx2();
- y2 = rec.getDy2();
- } else {
- x1 = clientAnchor.getCol1();
- y1 = clientAnchor.getFlag();
- x2 = clientAnchor.getDx1();
- y2 = clientAnchor.getRow1();
- }
- Rectangle2D anchor= new Rectangle2D.Double(
- (x1 == -1 ? -1 : Units.masterToPoints(x1)),
- (y1 == -1 ? -1 : Units.masterToPoints(y1)),
- (x2 == -1 ? -1 : Units.masterToPoints(x2-x1)),
- (y2 == -1 ? -1 : Units.masterToPoints(y2-y1))
- );
-
- return anchor;
- }
-
- /**
- * Return type of the shape.
- * In most cases shape group type is {@link org.apache.poi.sl.usermodel.ShapeType#NOT_PRIMITIVE}
- *
- * @return type of the shape.
- */
- public ShapeType getShapeType(){
- EscherSpRecord spRecord = getEscherChild(EscherSpRecord.RECORD_ID);
- int nativeId = spRecord.getOptions() >> 4;
- return ShapeType.forId(nativeId, false);
- }
-
- /**
- * Returns <code>null</code> - shape groups can't have hyperlinks
- *
- * @return <code>null</code>.
- */
- public HSLFHyperlink getHyperlink(){
- return null;
- }
-
- @Override
- public <T extends EscherRecord> T getEscherChild(int recordId){
- EscherContainerRecord groupInfoContainer = (EscherContainerRecord)_escherContainer.getChild(0);
- return groupInfoContainer.getChildById((short)recordId);
- }
-
- public Iterator<HSLFShape> iterator() {
- return getShapes().iterator();
- }
-
- public boolean removeShape(HSLFShape shape) {
- // TODO: implement!
- throw new UnsupportedOperationException();
- }
-
- @Override
- public List<HSLFShape> getShapes() {
- // Out escher container record should contain several
- // SpContainers, the first of which is the group shape itself
- Iterator<EscherRecord> iter = _escherContainer.getChildIterator();
-
- // Don't include the first SpContainer, it is always NotPrimitive
- if (iter.hasNext()) {
- iter.next();
- }
- List<HSLFShape> shapeList = new ArrayList<HSLFShape>();
- while (iter.hasNext()) {
- EscherRecord r = iter.next();
- if(r instanceof EscherContainerRecord) {
- // Create the Shape for it
- EscherContainerRecord container = (EscherContainerRecord)r;
- HSLFShape shape = HSLFShapeFactory.createShape(container, this);
- shape.setSheet(getSheet());
- shapeList.add( shape );
- } else {
- // Should we do anything special with these non
- // Container records?
- logger.log(POILogger.ERROR, "Shape contained non container escher record, was " + r.getClass().getName());
- }
- }
-
- return shapeList;
- }
-
- @Override
- public HSLFTextBox createTextBox() {
- HSLFTextBox s = new HSLFTextBox(this);
- s.setHorizontalCentered(true);
- s.setAnchor(new Rectangle2D.Double(0, 0, 100, 100));
- addShape(s);
- return s;
- }
-
- @Override
- public HSLFAutoShape createAutoShape() {
- HSLFAutoShape s = new HSLFAutoShape(ShapeType.RECT, this);
- s.setHorizontalCentered(true);
- s.setAnchor(new Rectangle2D.Double(0, 0, 100, 100));
- addShape(s);
- return s;
- }
-
- @Override
- public HSLFFreeformShape createFreeform() {
- HSLFFreeformShape s = new HSLFFreeformShape(this);
- s.setHorizontalCentered(true);
- s.setAnchor(new Rectangle2D.Double(0, 0, 100, 100));
- addShape(s);
- return s;
- }
-
- @Override
- public HSLFConnectorShape createConnector() {
- HSLFConnectorShape s = new HSLFConnectorShape(this);
- s.setAnchor(new Rectangle2D.Double(0, 0, 100, 100));
- addShape(s);
- return s;
- }
-
- @Override
- public HSLFGroupShape createGroup() {
- HSLFGroupShape s = new HSLFGroupShape(this);
- s.setAnchor(new Rectangle2D.Double(0, 0, 100, 100));
- addShape(s);
- return s;
- }
-
- @Override
- public HSLFPictureShape createPicture(PictureData pictureData) {
- if (!(pictureData instanceof HSLFPictureData)) {
- throw new IllegalArgumentException("pictureData needs to be of type HSLFPictureData");
- }
- HSLFPictureShape s = new HSLFPictureShape((HSLFPictureData)pictureData, this);
- s.setAnchor(new Rectangle2D.Double(0, 0, 100, 100));
- addShape(s);
- return s;
- }
-
- @Override
- public HSLFTable createTable(int numRows, int numCols) {
- if (numRows < 1 || numCols < 1) {
- throw new IllegalArgumentException("numRows and numCols must be greater than 0");
- }
- HSLFTable s = new HSLFTable(numRows,numCols,this);
- s.setAnchor(new Rectangle2D.Double(0, 0, 100, 100));
- addShape(s);
- return s;
- }
- }
|