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:
018: package org.apache.poi.hslf.record;
019:
020: import org.apache.poi.util.LittleEndian;
021: import org.apache.poi.util.POILogger;
022:
023: import org.apache.poi.ddf.*;
024: import org.apache.poi.hslf.model.ShapeTypes;
025:
026: import java.io.IOException;
027: import java.io.OutputStream;
028: import java.util.List;
029: import java.util.Vector;
030:
031: /**
032: * These are actually wrappers onto Escher drawings. Make use of
033: * the DDF classes to do useful things with them.
034: * For now, creates a tree of the Escher records, and then creates any
035: * PowerPoint (hslf) records found within the EscherTextboxRecord
036: * (msofbtClientTextbox) records.
037: * Also provides easy access to the EscherTextboxRecords, so that their
038: * text may be extracted and used in Sheets
039: *
040: * @author Nick Burch
041: */
042:
043: // For now, pretending to be an atom. Might not always be, but that
044: // would require a wrapping class
045: public class PPDrawing extends RecordAtom {
046: private byte[] _header;
047: private long _type;
048:
049: private EscherRecord[] childRecords;
050: private EscherTextboxWrapper[] textboxWrappers;
051:
052: /**
053: * Get access to the underlying Escher Records
054: */
055: public EscherRecord[] getEscherRecords() {
056: return childRecords;
057: }
058:
059: /**
060: * Get access to the atoms inside Textboxes
061: */
062: public EscherTextboxWrapper[] getTextboxWrappers() {
063: return textboxWrappers;
064: }
065:
066: /* ******************** record stuff follows ********************** */
067:
068: /**
069: * Sets everything up, groks the escher etc
070: */
071: protected PPDrawing(byte[] source, int start, int len) {
072: // Get the header
073: _header = new byte[8];
074: System.arraycopy(source, start, _header, 0, 8);
075:
076: // Get the type
077: _type = LittleEndian.getUShort(_header, 2);
078:
079: // Get the contents for now
080: byte[] contents = new byte[len];
081: System.arraycopy(source, start, contents, 0, len);
082:
083: // Build up a tree of Escher records contained within
084: DefaultEscherRecordFactory erf = new DefaultEscherRecordFactory();
085: Vector escherChildren = new Vector();
086: findEscherChildren(erf, contents, 8, len - 8, escherChildren);
087:
088: childRecords = new EscherRecord[escherChildren.size()];
089: for (int i = 0; i < childRecords.length; i++) {
090: childRecords[i] = (EscherRecord) escherChildren.get(i);
091: }
092:
093: // Find and EscherTextboxRecord's, and wrap them up
094: Vector textboxes = new Vector();
095: findEscherTextboxRecord(childRecords, textboxes);
096: textboxWrappers = new EscherTextboxWrapper[textboxes.size()];
097: for (int i = 0; i < textboxWrappers.length; i++) {
098: textboxWrappers[i] = (EscherTextboxWrapper) textboxes
099: .get(i);
100: }
101: }
102:
103: /**
104: * Creates a new, empty, PPDrawing (typically for use with a new Slide
105: * or Notes)
106: */
107: public PPDrawing() {
108: _header = new byte[8];
109: LittleEndian.putUShort(_header, 0, 15);
110: LittleEndian.putUShort(_header, 2,
111: (int) RecordTypes.PPDrawing.typeID);
112: LittleEndian.putInt(_header, 4, 0);
113:
114: textboxWrappers = new EscherTextboxWrapper[] {};
115: create();
116: }
117:
118: /**
119: * Tree walking way of finding Escher Child Records
120: */
121: private void findEscherChildren(DefaultEscherRecordFactory erf,
122: byte[] source, int startPos, int lenToGo, Vector found) {
123: // Find the record
124: EscherRecord r = erf.createRecord(source, startPos);
125: // Fill it in
126: r.fillFields(source, startPos, erf);
127: // Save it
128: found.add(r);
129:
130: // Wind on
131: int size = r.getRecordSize();
132: if (size < 8) {
133: logger.log(POILogger.WARN, "Hit short DDF record at "
134: + startPos + " - " + size);
135: }
136: startPos += size;
137: lenToGo -= size;
138: if (lenToGo >= 8) {
139: findEscherChildren(erf, source, startPos, lenToGo, found);
140: }
141: }
142:
143: /**
144: * Look for EscherTextboxRecords
145: */
146: private void findEscherTextboxRecord(EscherRecord[] toSearch,
147: Vector found) {
148: for (int i = 0; i < toSearch.length; i++) {
149: if (toSearch[i] instanceof EscherTextboxRecord) {
150: EscherTextboxRecord tbr = (EscherTextboxRecord) toSearch[i];
151: EscherTextboxWrapper w = new EscherTextboxWrapper(tbr);
152: found.add(w);
153: for (int j = i; j >= 0; j--) {
154: if (toSearch[j] instanceof EscherSpRecord) {
155: EscherSpRecord sp = (EscherSpRecord) toSearch[j];
156: w.setShapeId(sp.getShapeId());
157: break;
158: }
159: }
160: } else {
161: // If it has children, walk them
162: if (toSearch[i].isContainerRecord()) {
163: List childrenL = toSearch[i].getChildRecords();
164: EscherRecord[] children = new EscherRecord[childrenL
165: .size()];
166: for (int j = 0; j < children.length; j++) {
167: children[j] = (EscherRecord) childrenL.get(j);
168: }
169: findEscherTextboxRecord(children, found);
170: }
171: }
172: }
173: }
174:
175: /**
176: * We are type 1036
177: */
178: public long getRecordType() {
179: return _type;
180: }
181:
182: /**
183: * We're pretending to be an atom, so return null
184: */
185: public Record[] getChildRecords() {
186: return null;
187: }
188:
189: /**
190: * Write the contents of the record back, so it can be written
191: * to disk
192: * Walks the escher layer to get the contents
193: */
194: public void writeOut(OutputStream out) throws IOException {
195: // Ensure the escher layer reflects the text changes
196: for (int i = 0; i < textboxWrappers.length; i++) {
197: textboxWrappers[i].writeOut(null);
198: }
199:
200: // Find the new size of the escher children;
201: int newSize = 0;
202: for (int i = 0; i < childRecords.length; i++) {
203: newSize += childRecords[i].getRecordSize();
204: }
205:
206: // Update the size (header bytes 5-8)
207: LittleEndian.putInt(_header, 4, newSize);
208:
209: // Write out our header
210: out.write(_header);
211:
212: // Now grab the children's data
213: byte[] b = new byte[newSize];
214: int done = 0;
215: for (int i = 0; i < childRecords.length; i++) {
216: int written = childRecords[i].serialize(done, b);
217: done += written;
218: }
219:
220: // Finally, write out the children
221: out.write(b);
222: }
223:
224: /**
225: * Create the Escher records associated with a new PPDrawing
226: */
227: private void create() {
228: EscherContainerRecord dgContainer = new EscherContainerRecord();
229: dgContainer.setRecordId(EscherContainerRecord.DG_CONTAINER);
230: dgContainer.setOptions((short) 15);
231:
232: EscherDgRecord dg = new EscherDgRecord();
233: dg.setOptions((short) 16);
234: dg.setNumShapes(1);
235: dgContainer.addChildRecord(dg);
236:
237: EscherContainerRecord spgrContainer = new EscherContainerRecord();
238: spgrContainer.setOptions((short) 15);
239: spgrContainer.setRecordId(EscherContainerRecord.SPGR_CONTAINER);
240:
241: EscherContainerRecord spContainer = new EscherContainerRecord();
242: spContainer.setOptions((short) 15);
243: spContainer.setRecordId(EscherContainerRecord.SP_CONTAINER);
244:
245: EscherSpgrRecord spgr = new EscherSpgrRecord();
246: spgr.setOptions((short) 1);
247: spContainer.addChildRecord(spgr);
248:
249: EscherSpRecord sp = new EscherSpRecord();
250: sp.setOptions((short) ((ShapeTypes.NotPrimitive << 4) + 2));
251: sp.setFlags(EscherSpRecord.FLAG_PATRIARCH
252: | EscherSpRecord.FLAG_GROUP);
253: spContainer.addChildRecord(sp);
254: spgrContainer.addChildRecord(spContainer);
255: dgContainer.addChildRecord(spgrContainer);
256:
257: spContainer = new EscherContainerRecord();
258: spContainer.setOptions((short) 15);
259: spContainer.setRecordId(EscherContainerRecord.SP_CONTAINER);
260: sp = new EscherSpRecord();
261: sp.setOptions((short) ((ShapeTypes.Rectangle << 4) + 2));
262: sp.setFlags(EscherSpRecord.FLAG_BACKGROUND
263: | EscherSpRecord.FLAG_HASSHAPETYPE);
264: spContainer.addChildRecord(sp);
265:
266: EscherOptRecord opt = new EscherOptRecord();
267: opt.setRecordId(EscherOptRecord.RECORD_ID);
268: opt.addEscherProperty(new EscherRGBProperty(
269: EscherProperties.FILL__FILLCOLOR, 134217728));
270: opt.addEscherProperty(new EscherRGBProperty(
271: EscherProperties.FILL__FILLBACKCOLOR, 134217733));
272: opt.addEscherProperty(new EscherSimpleProperty(
273: EscherProperties.FILL__RECTRIGHT, 10064750));
274: opt.addEscherProperty(new EscherSimpleProperty(
275: EscherProperties.FILL__RECTBOTTOM, 7778750));
276: opt.addEscherProperty(new EscherBoolProperty(
277: EscherProperties.FILL__NOFILLHITTEST, 1179666));
278: opt.addEscherProperty(new EscherBoolProperty(
279: EscherProperties.LINESTYLE__NOLINEDRAWDASH, 524288));
280: opt.addEscherProperty(new EscherSimpleProperty(
281: EscherProperties.SHAPE__BLACKANDWHITESETTINGS, 9));
282: opt.addEscherProperty(new EscherSimpleProperty(
283: EscherProperties.SHAPE__BACKGROUNDSHAPE, 65537));
284: spContainer.addChildRecord(opt);
285:
286: dgContainer.addChildRecord(spContainer);
287:
288: childRecords = new EscherRecord[] { dgContainer };
289: }
290:
291: /**
292: * Add a new EscherTextboxWrapper to this <code>PPDrawing</code>.
293: */
294: public void addTextboxWrapper(EscherTextboxWrapper txtbox) {
295: EscherTextboxWrapper[] tw = new EscherTextboxWrapper[textboxWrappers.length + 1];
296: System.arraycopy(textboxWrappers, 0, tw, 0,
297: textboxWrappers.length);
298:
299: tw[textboxWrappers.length] = txtbox;
300: textboxWrappers = tw;
301: }
302: }
|