001: /* ====================================================================
002: Licensed to the Apache Software Foundation (ASF) under one or more
003: contributor license agreements. See the NOTICE file distributed with
004: this work for additional information regarding copyright ownership.
005: The ASF licenses this file to You under the Apache License, Version 2.0
006: (the "License"); you may not use this file except in compliance with
007: the License. You may obtain a copy of the License at
008:
009: http://www.apache.org/licenses/LICENSE-2.0
010:
011: Unless required by applicable law or agreed to in writing, software
012: distributed under the License is distributed on an "AS IS" BASIS,
013: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: See the License for the specific language governing permissions and
015: limitations under the License.
016: ==================================================================== */
017: package org.apache.poi.hslf.model;
018:
019: import org.apache.poi.ddf.*;
020: import org.apache.poi.hslf.model.ShapeTypes;
021: import org.apache.poi.hslf.record.ColorSchemeAtom;
022: import org.apache.poi.util.POILogger;
023: import org.apache.poi.util.POILogFactory;
024:
025: import java.util.Iterator;
026: import java.awt.*;
027:
028: /**
029: * <p>
030: * Represents a Shape which is the elemental object that composes a drawing.
031: * This class is a wrapper around EscherSpContainer which holds all information
032: * about a shape in PowerPoint document.
033: * </p>
034: * <p>
035: * When you add a shape, you usually specify the dimensions of the shape and the position
036: * of the upper�left corner of the bounding box for the shape relative to the upper�left
037: * corner of the page, worksheet, or slide. Distances in the drawing layer are measured
038: * in points (72 points = 1 inch).
039: * </p>
040: * <p>
041: *
042: * @author Yegor Kozlov
043: */
044: public abstract class Shape {
045:
046: // For logging
047: protected POILogger logger = POILogFactory.getLogger(this
048: .getClass());
049:
050: /**
051: * In Escher absolute distances are specified in
052: * English Metric Units (EMUs), occasionally referred to as A units;
053: * there are 360000 EMUs per centimeter, 914400 EMUs per inch, 12700 EMUs per point.
054: */
055: public static final int EMU_PER_INCH = 914400;
056: public static final int EMU_PER_POINT = 12700;
057: public static final int EMU_PER_CENTIMETER = 360000;
058:
059: /**
060: * Master DPI (576 pixels per inch).
061: * Used by the reference coordinate system in PowerPoint.
062: */
063: public static final int MASTER_DPI = 576;
064:
065: /**
066: * Pixels DPI (96 pixels per inch)
067: */
068: public static final int PIXEL_DPI = 96;
069:
070: /**
071: * Points DPI (72 pixels per inch)
072: */
073: public static final int POINT_DPI = 72;
074:
075: /**
076: * Either EscherSpContainer or EscheSpgrContainer record
077: * which holds information about this shape.
078: */
079: protected EscherContainerRecord _escherContainer;
080:
081: /**
082: * Parent of this shape.
083: * <code>null</code> for the topmost shapes.
084: */
085: protected Shape _parent;
086:
087: /**
088: * The <code>Sheet</code> this shape belongs to
089: */
090: protected Sheet _sheet;
091:
092: /**
093: * Create a Shape object. This constructor is used when an existing Shape is read from from a PowerPoint document.
094: *
095: * @param escherRecord <code>EscherSpContainer</code> container which holds information about this shape
096: * @param parent the parent of this Shape
097: */
098: protected Shape(EscherContainerRecord escherRecord, Shape parent) {
099: _escherContainer = escherRecord;
100: _parent = parent;
101: }
102:
103: /**
104: * Creates the lowerlevel escher records for this shape.
105: */
106: protected abstract EscherContainerRecord createSpContainer(
107: boolean isChild);
108:
109: /**
110: * @return the parent of this shape
111: */
112: public Shape getParent() {
113: return _parent;
114: }
115:
116: /**
117: * @return name of the shape.
118: */
119: public String getShapeName() {
120: return ShapeTypes.typeName(getShapeType());
121: }
122:
123: /**
124: * @return type of the shape.
125: * @see org.apache.poi.hslf.record.RecordTypes
126: */
127: public int getShapeType() {
128: EscherSpRecord spRecord = _escherContainer
129: .getChildById(EscherSpRecord.RECORD_ID);
130: return spRecord.getOptions() >> 4;
131: }
132:
133: /**
134: * @param type type of the shape.
135: * @see org.apache.poi.hslf.record.RecordTypes
136: */
137: public void setShapeType(int type) {
138: EscherSpRecord spRecord = _escherContainer
139: .getChildById(EscherSpRecord.RECORD_ID);
140: spRecord.setOptions((short) (type << 4 | 0x2));
141: }
142:
143: /**
144: * Returns the anchor (the bounding box rectangle) of this shape.
145: * All coordinates are expressed in points (72 dpi).
146: *
147: * @return the anchor of this shape
148: */
149: public java.awt.Rectangle getAnchor() {
150: EscherSpRecord spRecord = _escherContainer
151: .getChildById(EscherSpRecord.RECORD_ID);
152: int flags = spRecord.getFlags();
153: java.awt.Rectangle anchor = null;
154: if ((flags & EscherSpRecord.FLAG_CHILD) != 0) {
155: EscherChildAnchorRecord rec = (EscherChildAnchorRecord) getEscherChild(
156: _escherContainer, EscherChildAnchorRecord.RECORD_ID);
157: anchor = new java.awt.Rectangle();
158: anchor.x = rec.getDx1() * POINT_DPI / MASTER_DPI;
159: anchor.y = rec.getDy1() * POINT_DPI / MASTER_DPI;
160: anchor.width = rec.getDx2() * POINT_DPI / MASTER_DPI
161: - anchor.x;
162: anchor.height = rec.getDy2() * POINT_DPI / MASTER_DPI
163: - anchor.y;
164: } else {
165: EscherClientAnchorRecord rec = (EscherClientAnchorRecord) getEscherChild(
166: _escherContainer,
167: EscherClientAnchorRecord.RECORD_ID);
168: anchor = new java.awt.Rectangle();
169: anchor.y = rec.getFlag() * POINT_DPI / MASTER_DPI;
170: anchor.x = rec.getCol1() * POINT_DPI / MASTER_DPI;
171: anchor.width = (rec.getDx1() - rec.getCol1()) * POINT_DPI
172: / MASTER_DPI;
173: anchor.height = (rec.getRow1() - rec.getFlag()) * POINT_DPI
174: / MASTER_DPI;
175: }
176: return anchor;
177: }
178:
179: /**
180: * Sets the anchor (the bounding box rectangle) of this shape.
181: * All coordinates should be expressed in points (72 dpi).
182: *
183: * @param anchor new anchor
184: */
185: public void setAnchor(java.awt.Rectangle anchor) {
186: EscherSpRecord spRecord = _escherContainer
187: .getChildById(EscherSpRecord.RECORD_ID);
188: int flags = spRecord.getFlags();
189: if ((flags & EscherSpRecord.FLAG_CHILD) != 0) {
190: EscherChildAnchorRecord rec = (EscherChildAnchorRecord) getEscherChild(
191: _escherContainer, EscherChildAnchorRecord.RECORD_ID);
192: rec.setDx1(anchor.x * MASTER_DPI / POINT_DPI);
193: rec.setDy1(anchor.y * MASTER_DPI / POINT_DPI);
194: rec.setDx2((anchor.width + anchor.x) * MASTER_DPI
195: / POINT_DPI);
196: rec.setDy2((anchor.height + anchor.y) * MASTER_DPI
197: / POINT_DPI);
198: } else {
199: EscherClientAnchorRecord rec = (EscherClientAnchorRecord) getEscherChild(
200: _escherContainer,
201: EscherClientAnchorRecord.RECORD_ID);
202: rec.setFlag((short) (anchor.y * MASTER_DPI / POINT_DPI));
203: rec.setCol1((short) (anchor.x * MASTER_DPI / POINT_DPI));
204: rec
205: .setDx1((short) ((anchor.width + anchor.x)
206: * MASTER_DPI / POINT_DPI));
207: rec.setRow1((short) ((anchor.height + anchor.y)
208: * MASTER_DPI / POINT_DPI));
209: }
210:
211: }
212:
213: /**
214: * Moves the top left corner of the shape to the specified point.
215: *
216: * @param x the x coordinate of the top left corner of the shape
217: * @param y the y coordinate of the top left corner of the shape
218: */
219: public void moveTo(int x, int y) {
220: java.awt.Rectangle anchor = getAnchor();
221: anchor.setLocation(x, y);
222: setAnchor(anchor);
223: }
224:
225: /**
226: * Helper method to return escher child by record ID
227: *
228: * @return escher record or <code>null</code> if not found.
229: */
230: public static EscherRecord getEscherChild(
231: EscherContainerRecord owner, int recordId) {
232: for (Iterator iterator = owner.getChildRecords().iterator(); iterator
233: .hasNext();) {
234: EscherRecord escherRecord = (EscherRecord) iterator.next();
235: if (escherRecord.getRecordId() == recordId)
236: return escherRecord;
237: }
238: return null;
239: }
240:
241: /**
242: * Returns escher property by id.
243: *
244: * @return escher property or <code>null</code> if not found.
245: */
246: public static EscherProperty getEscherProperty(EscherOptRecord opt,
247: int propId) {
248: for (Iterator iterator = opt.getEscherProperties().iterator(); iterator
249: .hasNext();) {
250: EscherProperty prop = (EscherProperty) iterator.next();
251: if (prop.getId() == propId)
252: return prop;
253: }
254: return null;
255: }
256:
257: /**
258: * Set an escher property for this shape.
259: *
260: * @param opt The opt record to set the properties to.
261: * @param propId The id of the property. One of the constants defined in EscherOptRecord.
262: * @param value value of the property. If value = -1 then the property is removed.
263: */
264: public static void setEscherProperty(EscherOptRecord opt,
265: short propId, int value) {
266: java.util.List props = opt.getEscherProperties();
267: for (Iterator iterator = props.iterator(); iterator.hasNext();) {
268: EscherProperty prop = (EscherProperty) iterator.next();
269: if (prop.getId() == propId) {
270: iterator.remove();
271: }
272: }
273: if (value != -1) {
274: opt.addEscherProperty(new EscherSimpleProperty(propId,
275: value));
276: opt.sortProperties();
277: }
278: }
279:
280: /**
281: * @return The shape container and it's children that can represent this
282: * shape.
283: */
284: public EscherContainerRecord getSpContainer() {
285: return _escherContainer;
286: }
287:
288: /**
289: * Event which fires when a shape is inserted in the sheet.
290: * In some cases we need to propagate changes to upper level containers.
291: * <br>
292: * Default implementation does nothing.
293: *
294: * @param sh - owning shape
295: */
296: protected void afterInsert(Sheet sh) {
297:
298: }
299:
300: /**
301: * @return the <code>SlideShow</code> this shape belongs to
302: */
303: public Sheet getSheet() {
304: return _sheet;
305: }
306:
307: /**
308: * Assign the <code>SlideShow</code> this shape belongs to
309: *
310: * @param sheet owner of this shape
311: */
312: public void setSheet(Sheet sheet) {
313: _sheet = sheet;
314: }
315:
316: protected Color getColor(int rgb) {
317: if (rgb >= 0x8000000) {
318: int idx = rgb - 0x8000000;
319: ColorSchemeAtom ca = getSheet().getColorScheme();
320: if (idx >= 0 && idx <= 7)
321: rgb = ca.getColor(idx);
322: }
323: Color tmp = new Color(rgb, true);
324: return new Color(tmp.getBlue(), tmp.getGreen(), tmp.getRed());
325: }
326:
327: /**
328: * Fill properties of this shape
329: *
330: * @return fill properties of this shape
331: */
332: public Fill getFill() {
333: return new Fill(this );
334: }
335:
336: /**
337: * Returns the hyperlink assigned to this shape
338: *
339: * @return the hyperlink assigned to this shape
340: * or <code>null</code> if not found.
341: */
342: public Hyperlink getHyperlink() {
343: return Hyperlink.find(this);
344: }
345:
346: }
|