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.hssf.record;
019:
020: import org.apache.poi.ddf.*;
021: import org.apache.poi.hssf.usermodel.*;
022: import org.apache.poi.hssf.model.AbstractShape;
023: import org.apache.poi.hssf.model.TextboxShape;
024: import org.apache.poi.hssf.model.DrawingManager2;
025: import org.apache.poi.hssf.model.ConvertAnchor;
026: import org.apache.poi.hssf.model.CommentShape;
027:
028: import java.util.*;
029:
030: /**
031: * This class is used to aggregate the MSODRAWING and OBJ record
032: * combinations. This is necessary due to the bizare way in which
033: * these records are serialized. What happens is that you get a
034: * combination of MSODRAWING -> OBJ -> MSODRAWING -> OBJ records
035: * but the escher records are serialized _across_ the MSODRAWING
036: * records.
037: * <p>
038: * It gets even worse when you start looking at TXO records.
039: * <p>
040: * So what we do with this class is aggregate lazily. That is
041: * we don't aggregate the MSODRAWING -> OBJ records unless we
042: * need to modify them.
043: *
044: *
045: * @author Glen Stampoultzis (glens at apache.org)
046: */
047: public class EscherAggregate extends AbstractEscherHolderRecord {
048: public static final short sid = 9876;
049:
050: public static final short ST_MIN = (short) 0;
051: public static final short ST_NOT_PRIMATIVE = ST_MIN;
052: public static final short ST_RECTANGLE = (short) 1;
053: public static final short ST_ROUNDRECTANGLE = (short) 2;
054: public static final short ST_ELLIPSE = (short) 3;
055: public static final short ST_DIAMOND = (short) 4;
056: public static final short ST_ISOCELESTRIANGLE = (short) 5;
057: public static final short ST_RIGHTTRIANGLE = (short) 6;
058: public static final short ST_PARALLELOGRAM = (short) 7;
059: public static final short ST_TRAPEZOID = (short) 8;
060: public static final short ST_HEXAGON = (short) 9;
061: public static final short ST_OCTAGON = (short) 10;
062: public static final short ST_PLUS = (short) 11;
063: public static final short ST_STAR = (short) 12;
064: public static final short ST_ARROW = (short) 13;
065: public static final short ST_THICKARROW = (short) 14;
066: public static final short ST_HOMEPLATE = (short) 15;
067: public static final short ST_CUBE = (short) 16;
068: public static final short ST_BALLOON = (short) 17;
069: public static final short ST_SEAL = (short) 18;
070: public static final short ST_ARC = (short) 19;
071: public static final short ST_LINE = (short) 20;
072: public static final short ST_PLAQUE = (short) 21;
073: public static final short ST_CAN = (short) 22;
074: public static final short ST_DONUT = (short) 23;
075: public static final short ST_TEXTSIMPLE = (short) 24;
076: public static final short ST_TEXTOCTAGON = (short) 25;
077: public static final short ST_TEXTHEXAGON = (short) 26;
078: public static final short ST_TEXTCURVE = (short) 27;
079: public static final short ST_TEXTWAVE = (short) 28;
080: public static final short ST_TEXTRING = (short) 29;
081: public static final short ST_TEXTONCURVE = (short) 30;
082: public static final short ST_TEXTONRING = (short) 31;
083: public static final short ST_STRAIGHTCONNECTOR1 = (short) 32;
084: public static final short ST_BENTCONNECTOR2 = (short) 33;
085: public static final short ST_BENTCONNECTOR3 = (short) 34;
086: public static final short ST_BENTCONNECTOR4 = (short) 35;
087: public static final short ST_BENTCONNECTOR5 = (short) 36;
088: public static final short ST_CURVEDCONNECTOR2 = (short) 37;
089: public static final short ST_CURVEDCONNECTOR3 = (short) 38;
090: public static final short ST_CURVEDCONNECTOR4 = (short) 39;
091: public static final short ST_CURVEDCONNECTOR5 = (short) 40;
092: public static final short ST_CALLOUT1 = (short) 41;
093: public static final short ST_CALLOUT2 = (short) 42;
094: public static final short ST_CALLOUT3 = (short) 43;
095: public static final short ST_ACCENTCALLOUT1 = (short) 44;
096: public static final short ST_ACCENTCALLOUT2 = (short) 45;
097: public static final short ST_ACCENTCALLOUT3 = (short) 46;
098: public static final short ST_BORDERCALLOUT1 = (short) 47;
099: public static final short ST_BORDERCALLOUT2 = (short) 48;
100: public static final short ST_BORDERCALLOUT3 = (short) 49;
101: public static final short ST_ACCENTBORDERCALLOUT1 = (short) 50;
102: public static final short ST_ACCENTBORDERCALLOUT2 = (short) 51;
103: public static final short ST_ACCENTBORDERCALLOUT3 = (short) 52;
104: public static final short ST_RIBBON = (short) 53;
105: public static final short ST_RIBBON2 = (short) 54;
106: public static final short ST_CHEVRON = (short) 55;
107: public static final short ST_PENTAGON = (short) 56;
108: public static final short ST_NOSMOKING = (short) 57;
109: public static final short ST_SEAL8 = (short) 58;
110: public static final short ST_SEAL16 = (short) 59;
111: public static final short ST_SEAL32 = (short) 60;
112: public static final short ST_WEDGERECTCALLOUT = (short) 61;
113: public static final short ST_WEDGERRECTCALLOUT = (short) 62;
114: public static final short ST_WEDGEELLIPSECALLOUT = (short) 63;
115: public static final short ST_WAVE = (short) 64;
116: public static final short ST_FOLDEDCORNER = (short) 65;
117: public static final short ST_LEFTARROW = (short) 66;
118: public static final short ST_DOWNARROW = (short) 67;
119: public static final short ST_UPARROW = (short) 68;
120: public static final short ST_LEFTRIGHTARROW = (short) 69;
121: public static final short ST_UPDOWNARROW = (short) 70;
122: public static final short ST_IRREGULARSEAL1 = (short) 71;
123: public static final short ST_IRREGULARSEAL2 = (short) 72;
124: public static final short ST_LIGHTNINGBOLT = (short) 73;
125: public static final short ST_HEART = (short) 74;
126: public static final short ST_PICTUREFRAME = (short) 75;
127: public static final short ST_QUADARROW = (short) 76;
128: public static final short ST_LEFTARROWCALLOUT = (short) 77;
129: public static final short ST_RIGHTARROWCALLOUT = (short) 78;
130: public static final short ST_UPARROWCALLOUT = (short) 79;
131: public static final short ST_DOWNARROWCALLOUT = (short) 80;
132: public static final short ST_LEFTRIGHTARROWCALLOUT = (short) 81;
133: public static final short ST_UPDOWNARROWCALLOUT = (short) 82;
134: public static final short ST_QUADARROWCALLOUT = (short) 83;
135: public static final short ST_BEVEL = (short) 84;
136: public static final short ST_LEFTBRACKET = (short) 85;
137: public static final short ST_RIGHTBRACKET = (short) 86;
138: public static final short ST_LEFTBRACE = (short) 87;
139: public static final short ST_RIGHTBRACE = (short) 88;
140: public static final short ST_LEFTUPARROW = (short) 89;
141: public static final short ST_BENTUPARROW = (short) 90;
142: public static final short ST_BENTARROW = (short) 91;
143: public static final short ST_SEAL24 = (short) 92;
144: public static final short ST_STRIPEDRIGHTARROW = (short) 93;
145: public static final short ST_NOTCHEDRIGHTARROW = (short) 94;
146: public static final short ST_BLOCKARC = (short) 95;
147: public static final short ST_SMILEYFACE = (short) 96;
148: public static final short ST_VERTICALSCROLL = (short) 97;
149: public static final short ST_HORIZONTALSCROLL = (short) 98;
150: public static final short ST_CIRCULARARROW = (short) 99;
151: public static final short ST_NOTCHEDCIRCULARARROW = (short) 100;
152: public static final short ST_UTURNARROW = (short) 101;
153: public static final short ST_CURVEDRIGHTARROW = (short) 102;
154: public static final short ST_CURVEDLEFTARROW = (short) 103;
155: public static final short ST_CURVEDUPARROW = (short) 104;
156: public static final short ST_CURVEDDOWNARROW = (short) 105;
157: public static final short ST_CLOUDCALLOUT = (short) 106;
158: public static final short ST_ELLIPSERIBBON = (short) 107;
159: public static final short ST_ELLIPSERIBBON2 = (short) 108;
160: public static final short ST_FLOWCHARTPROCESS = (short) 109;
161: public static final short ST_FLOWCHARTDECISION = (short) 110;
162: public static final short ST_FLOWCHARTINPUTOUTPUT = (short) 111;
163: public static final short ST_FLOWCHARTPREDEFINEDPROCESS = (short) 112;
164: public static final short ST_FLOWCHARTINTERNALSTORAGE = (short) 113;
165: public static final short ST_FLOWCHARTDOCUMENT = (short) 114;
166: public static final short ST_FLOWCHARTMULTIDOCUMENT = (short) 115;
167: public static final short ST_FLOWCHARTTERMINATOR = (short) 116;
168: public static final short ST_FLOWCHARTPREPARATION = (short) 117;
169: public static final short ST_FLOWCHARTMANUALINPUT = (short) 118;
170: public static final short ST_FLOWCHARTMANUALOPERATION = (short) 119;
171: public static final short ST_FLOWCHARTCONNECTOR = (short) 120;
172: public static final short ST_FLOWCHARTPUNCHEDCARD = (short) 121;
173: public static final short ST_FLOWCHARTPUNCHEDTAPE = (short) 122;
174: public static final short ST_FLOWCHARTSUMMINGJUNCTION = (short) 123;
175: public static final short ST_FLOWCHARTOR = (short) 124;
176: public static final short ST_FLOWCHARTCOLLATE = (short) 125;
177: public static final short ST_FLOWCHARTSORT = (short) 126;
178: public static final short ST_FLOWCHARTEXTRACT = (short) 127;
179: public static final short ST_FLOWCHARTMERGE = (short) 128;
180: public static final short ST_FLOWCHARTOFFLINESTORAGE = (short) 129;
181: public static final short ST_FLOWCHARTONLINESTORAGE = (short) 130;
182: public static final short ST_FLOWCHARTMAGNETICTAPE = (short) 131;
183: public static final short ST_FLOWCHARTMAGNETICDISK = (short) 132;
184: public static final short ST_FLOWCHARTMAGNETICDRUM = (short) 133;
185: public static final short ST_FLOWCHARTDISPLAY = (short) 134;
186: public static final short ST_FLOWCHARTDELAY = (short) 135;
187: public static final short ST_TEXTPLAINTEXT = (short) 136;
188: public static final short ST_TEXTSTOP = (short) 137;
189: public static final short ST_TEXTTRIANGLE = (short) 138;
190: public static final short ST_TEXTTRIANGLEINVERTED = (short) 139;
191: public static final short ST_TEXTCHEVRON = (short) 140;
192: public static final short ST_TEXTCHEVRONINVERTED = (short) 141;
193: public static final short ST_TEXTRINGINSIDE = (short) 142;
194: public static final short ST_TEXTRINGOUTSIDE = (short) 143;
195: public static final short ST_TEXTARCHUPCURVE = (short) 144;
196: public static final short ST_TEXTARCHDOWNCURVE = (short) 145;
197: public static final short ST_TEXTCIRCLECURVE = (short) 146;
198: public static final short ST_TEXTBUTTONCURVE = (short) 147;
199: public static final short ST_TEXTARCHUPPOUR = (short) 148;
200: public static final short ST_TEXTARCHDOWNPOUR = (short) 149;
201: public static final short ST_TEXTCIRCLEPOUR = (short) 150;
202: public static final short ST_TEXTBUTTONPOUR = (short) 151;
203: public static final short ST_TEXTCURVEUP = (short) 152;
204: public static final short ST_TEXTCURVEDOWN = (short) 153;
205: public static final short ST_TEXTCASCADEUP = (short) 154;
206: public static final short ST_TEXTCASCADEDOWN = (short) 155;
207: public static final short ST_TEXTWAVE1 = (short) 156;
208: public static final short ST_TEXTWAVE2 = (short) 157;
209: public static final short ST_TEXTWAVE3 = (short) 158;
210: public static final short ST_TEXTWAVE4 = (short) 159;
211: public static final short ST_TEXTINFLATE = (short) 160;
212: public static final short ST_TEXTDEFLATE = (short) 161;
213: public static final short ST_TEXTINFLATEBOTTOM = (short) 162;
214: public static final short ST_TEXTDEFLATEBOTTOM = (short) 163;
215: public static final short ST_TEXTINFLATETOP = (short) 164;
216: public static final short ST_TEXTDEFLATETOP = (short) 165;
217: public static final short ST_TEXTDEFLATEINFLATE = (short) 166;
218: public static final short ST_TEXTDEFLATEINFLATEDEFLATE = (short) 167;
219: public static final short ST_TEXTFADERIGHT = (short) 168;
220: public static final short ST_TEXTFADELEFT = (short) 169;
221: public static final short ST_TEXTFADEUP = (short) 170;
222: public static final short ST_TEXTFADEDOWN = (short) 171;
223: public static final short ST_TEXTSLANTUP = (short) 172;
224: public static final short ST_TEXTSLANTDOWN = (short) 173;
225: public static final short ST_TEXTCANUP = (short) 174;
226: public static final short ST_TEXTCANDOWN = (short) 175;
227: public static final short ST_FLOWCHARTALTERNATEPROCESS = (short) 176;
228: public static final short ST_FLOWCHARTOFFPAGECONNECTOR = (short) 177;
229: public static final short ST_CALLOUT90 = (short) 178;
230: public static final short ST_ACCENTCALLOUT90 = (short) 179;
231: public static final short ST_BORDERCALLOUT90 = (short) 180;
232: public static final short ST_ACCENTBORDERCALLOUT90 = (short) 181;
233: public static final short ST_LEFTRIGHTUPARROW = (short) 182;
234: public static final short ST_SUN = (short) 183;
235: public static final short ST_MOON = (short) 184;
236: public static final short ST_BRACKETPAIR = (short) 185;
237: public static final short ST_BRACEPAIR = (short) 186;
238: public static final short ST_SEAL4 = (short) 187;
239: public static final short ST_DOUBLEWAVE = (short) 188;
240: public static final short ST_ACTIONBUTTONBLANK = (short) 189;
241: public static final short ST_ACTIONBUTTONHOME = (short) 190;
242: public static final short ST_ACTIONBUTTONHELP = (short) 191;
243: public static final short ST_ACTIONBUTTONINFORMATION = (short) 192;
244: public static final short ST_ACTIONBUTTONFORWARDNEXT = (short) 193;
245: public static final short ST_ACTIONBUTTONBACKPREVIOUS = (short) 194;
246: public static final short ST_ACTIONBUTTONEND = (short) 195;
247: public static final short ST_ACTIONBUTTONBEGINNING = (short) 196;
248: public static final short ST_ACTIONBUTTONRETURN = (short) 197;
249: public static final short ST_ACTIONBUTTONDOCUMENT = (short) 198;
250: public static final short ST_ACTIONBUTTONSOUND = (short) 199;
251: public static final short ST_ACTIONBUTTONMOVIE = (short) 200;
252: public static final short ST_HOSTCONTROL = (short) 201;
253: public static final short ST_TEXTBOX = (short) 202;
254: public static final short ST_NIL = (short) 0x0FFF;
255:
256: protected HSSFPatriarch patriarch;
257:
258: /** Maps shape container objects to their OBJ records */
259: private Map shapeToObj = new HashMap();
260: private DrawingManager2 drawingManager;
261: private short drawingGroupId;
262:
263: /**
264: * list of "tail" records that need to be serialized after all drawing group records
265: */
266: private List tailRec = new ArrayList();
267:
268: public EscherAggregate(DrawingManager2 drawingManager) {
269: this .drawingManager = drawingManager;
270: }
271:
272: /**
273: * @return Returns the current sid.
274: */
275: public short getSid() {
276: return sid;
277: }
278:
279: /**
280: * Unused since this is an aggregate record. Use createAggregate().
281: *
282: * @see #createAggregate
283: */
284: protected void fillFields(byte[] data, short size, int offset) {
285: throw new IllegalStateException("Should not reach here");
286: }
287:
288: /**
289: * Calculates the string representation of this record. This is
290: * simply a dump of all the records.
291: */
292: public String toString() {
293: String nl = System.getProperty("line.separtor");
294:
295: StringBuffer result = new StringBuffer();
296: result.append('[').append(getRecordName()).append(']' + nl);
297: for (Iterator iterator = getEscherRecords().iterator(); iterator
298: .hasNext();) {
299: EscherRecord escherRecord = (EscherRecord) iterator.next();
300: result.append(escherRecord.toString());
301: }
302: result.append("[/").append(getRecordName()).append(']' + nl);
303:
304: return result.toString();
305: }
306:
307: /**
308: * Collapses the drawing records into an aggregate.
309: */
310: public static EscherAggregate createAggregate(List records,
311: int locFirstDrawingRecord, DrawingManager2 drawingManager) {
312: // Keep track of any shape records created so we can match them back to the object id's.
313: // Textbox objects are also treated as shape objects.
314: final List shapeRecords = new ArrayList();
315: EscherRecordFactory recordFactory = new DefaultEscherRecordFactory() {
316: public EscherRecord createRecord(byte[] data, int offset) {
317: EscherRecord r = super .createRecord(data, offset);
318: if (r.getRecordId() == EscherClientDataRecord.RECORD_ID
319: || r.getRecordId() == EscherTextboxRecord.RECORD_ID) {
320: shapeRecords.add(r);
321: }
322: return r;
323: }
324: };
325:
326: // Calculate the size of the buffer
327: EscherAggregate agg = new EscherAggregate(drawingManager);
328: int loc = locFirstDrawingRecord;
329: int dataSize = 0;
330: while (loc + 1 < records.size()
331: && sid(records, loc) == DrawingRecord.sid
332: && isObjectRecord(records, loc + 1)) {
333: dataSize += ((DrawingRecord) records.get(loc)).getData().length;
334: loc += 2;
335: }
336:
337: // Create one big buffer
338: byte buffer[] = new byte[dataSize];
339: int offset = 0;
340: loc = locFirstDrawingRecord;
341: while (loc + 1 < records.size()
342: && sid(records, loc) == DrawingRecord.sid
343: && isObjectRecord(records, loc + 1)) {
344: DrawingRecord drawingRecord = (DrawingRecord) records
345: .get(loc);
346: System.arraycopy(drawingRecord.getData(), 0, buffer,
347: offset, drawingRecord.getData().length);
348: offset += drawingRecord.getData().length;
349: loc += 2;
350: }
351:
352: // Decode the shapes
353: // agg.escherRecords = new ArrayList();
354: int pos = 0;
355: while (pos < dataSize) {
356: EscherRecord r = recordFactory.createRecord(buffer, pos);
357: int bytesRead = r.fillFields(buffer, pos, recordFactory);
358: agg.addEscherRecord(r);
359: pos += bytesRead;
360: }
361:
362: // Associate the object records with the shapes
363: loc = locFirstDrawingRecord;
364: int shapeIndex = 0;
365: agg.shapeToObj = new HashMap();
366: while (loc + 1 < records.size()
367: && sid(records, loc) == DrawingRecord.sid
368: && isObjectRecord(records, loc + 1)) {
369: Record objRecord = (Record) records.get(loc + 1);
370: agg.shapeToObj.put(shapeRecords.get(shapeIndex++),
371: objRecord);
372: loc += 2;
373: }
374:
375: return agg;
376:
377: }
378:
379: /**
380: * Serializes this aggregate to a byte array. Since this is an aggregate
381: * record it will effectively serialize the aggregated records.
382: *
383: * @param offset The offset into the start of the array.
384: * @param data The byte array to serialize to.
385: * @return The number of bytes serialized.
386: */
387: public int serialize(int offset, byte[] data) {
388: convertUserModelToRecords();
389:
390: // Determine buffer size
391: List records = getEscherRecords();
392: int size = getEscherRecordSize(records);
393: byte[] buffer = new byte[size];
394:
395: // Serialize escher records into one big data structure and keep note of ending offsets.
396: final List spEndingOffsets = new ArrayList();
397: final List shapes = new ArrayList();
398: int pos = 0;
399: for (Iterator iterator = records.iterator(); iterator.hasNext();) {
400: EscherRecord e = (EscherRecord) iterator.next();
401: pos += e.serialize(pos, buffer,
402: new EscherSerializationListener() {
403: public void beforeRecordSerialize(int offset,
404: short recordId, EscherRecord record) {
405: }
406:
407: public void afterRecordSerialize(int offset,
408: short recordId, int size,
409: EscherRecord record) {
410: if (recordId == EscherClientDataRecord.RECORD_ID
411: || recordId == EscherTextboxRecord.RECORD_ID) {
412: spEndingOffsets
413: .add(new Integer(offset));
414: shapes.add(record);
415: }
416: }
417: });
418: }
419: // todo: fix this
420: shapes.add(0, null);
421: spEndingOffsets.add(0, null);
422:
423: // Split escher records into separate MSODRAWING and OBJ, TXO records. (We don't break on
424: // the first one because it's the patriach).
425: pos = offset;
426: for (int i = 1; i < shapes.size(); i++) {
427: int endOffset = ((Integer) spEndingOffsets.get(i))
428: .intValue() - 1;
429: int startOffset;
430: if (i == 1)
431: startOffset = 0;
432: else
433: startOffset = ((Integer) spEndingOffsets.get(i - 1))
434: .intValue();
435:
436: // Create and write a new MSODRAWING record
437: DrawingRecord drawing = new DrawingRecord();
438: byte[] drawingData = new byte[endOffset - startOffset + 1];
439: System.arraycopy(buffer, startOffset, drawingData, 0,
440: drawingData.length);
441: drawing.setData(drawingData);
442: int temp = drawing.serialize(pos, data);
443: pos += temp;
444:
445: // Write the matching OBJ record
446: Record obj = (Record) shapeToObj.get(shapes.get(i));
447: temp = obj.serialize(pos, data);
448: pos += temp;
449:
450: }
451:
452: // write records that need to be serialized after all drawing group records
453: for (int i = 0; i < tailRec.size(); i++) {
454: Record rec = (Record) tailRec.get(i);
455: pos += rec.serialize(pos, data);
456: }
457:
458: int bytesWritten = pos - offset;
459: if (bytesWritten != getRecordSize())
460: throw new RecordFormatException(bytesWritten
461: + " bytes written but getRecordSize() reports "
462: + getRecordSize());
463: return bytesWritten;
464: }
465:
466: /**
467: * How many bytes do the raw escher records contain.
468: * @param records List of escher records
469: * @return the number of bytes
470: */
471: private int getEscherRecordSize(List records) {
472: int size = 0;
473: for (Iterator iterator = records.iterator(); iterator.hasNext();)
474: size += ((EscherRecord) iterator.next()).getRecordSize();
475: return size;
476: }
477:
478: /**
479: * The number of bytes required to serialize this record.
480: */
481: public int getRecordSize() {
482: convertUserModelToRecords();
483: List records = getEscherRecords();
484: int rawEscherSize = getEscherRecordSize(records);
485: int drawingRecordSize = rawEscherSize + (shapeToObj.size()) * 4;
486: int objRecordSize = 0;
487: for (Iterator iterator = shapeToObj.values().iterator(); iterator
488: .hasNext();) {
489: Record r = (Record) iterator.next();
490: objRecordSize += r.getRecordSize();
491: }
492: int tailRecordSize = 0;
493: for (Iterator iterator = tailRec.iterator(); iterator.hasNext();) {
494: Record r = (Record) iterator.next();
495: tailRecordSize += r.getRecordSize();
496: }
497: return drawingRecordSize + objRecordSize + tailRecordSize;
498: }
499:
500: /**
501: * Associates an escher record to an OBJ record or a TXO record.
502: */
503: public Object assoicateShapeToObjRecord(EscherRecord r,
504: Record objRecord) {
505: return shapeToObj.put(r, objRecord);
506: }
507:
508: public HSSFPatriarch getPatriarch() {
509: return patriarch;
510: }
511:
512: public void setPatriarch(HSSFPatriarch patriarch) {
513: this .patriarch = patriarch;
514: }
515:
516: public void clear() {
517: clearEscherRecords();
518: shapeToObj.clear();
519: // lastShapeId = 1024;
520: }
521:
522: protected String getRecordName() {
523: return "ESCHERAGGREGATE";
524: }
525:
526: // =============== Private methods ========================
527:
528: private static boolean isObjectRecord(List records, int loc) {
529: return sid(records, loc) == ObjRecord.sid
530: || sid(records, loc) == TextObjectRecord.sid;
531: }
532:
533: private void convertUserModelToRecords() {
534: if (patriarch != null) {
535: shapeToObj.clear();
536: tailRec.clear();
537: clearEscherRecords();
538: if (patriarch.getChildren().size() != 0) {
539: convertPatriarch(patriarch);
540: EscherContainerRecord dgContainer = (EscherContainerRecord) getEscherRecord(0);
541: EscherContainerRecord spgrContainer = null;
542: for (int i = 0; i < dgContainer.getChildRecords()
543: .size(); i++)
544: if (dgContainer.getChild(i).getRecordId() == EscherContainerRecord.SPGR_CONTAINER)
545: spgrContainer = (EscherContainerRecord) dgContainer
546: .getChild(i);
547: convertShapes(patriarch, spgrContainer, shapeToObj);
548:
549: patriarch = null;
550: }
551: }
552: }
553:
554: private void convertShapes(HSSFShapeContainer parent,
555: EscherContainerRecord escherParent, Map shapeToObj) {
556: if (escherParent == null)
557: throw new IllegalArgumentException("Parent record required");
558:
559: List shapes = parent.getChildren();
560: for (Iterator iterator = shapes.iterator(); iterator.hasNext();) {
561: HSSFShape shape = (HSSFShape) iterator.next();
562: if (shape instanceof HSSFShapeGroup) {
563: convertGroup((HSSFShapeGroup) shape, escherParent,
564: shapeToObj);
565: } else {
566: AbstractShape shapeModel = AbstractShape.createShape(
567: shape, drawingManager
568: .allocateShapeId(drawingGroupId));
569: shapeToObj.put(findClientData(shapeModel
570: .getSpContainer()), shapeModel.getObjRecord());
571: if (shapeModel instanceof TextboxShape) {
572: EscherRecord escherTextbox = ((TextboxShape) shapeModel)
573: .getEscherTextbox();
574: shapeToObj.put(escherTextbox,
575: ((TextboxShape) shapeModel)
576: .getTextObjectRecord());
577: // escherParent.addChildRecord(escherTextbox);
578:
579: if (shapeModel instanceof CommentShape) {
580: CommentShape comment = (CommentShape) shapeModel;
581: tailRec.add(comment.getNoteRecord());
582: }
583:
584: }
585: escherParent
586: .addChildRecord(shapeModel.getSpContainer());
587: }
588: }
589: // drawingManager.newCluster( (short)1 );
590: // drawingManager.newCluster( (short)2 );
591:
592: }
593:
594: private void convertGroup(HSSFShapeGroup shape,
595: EscherContainerRecord escherParent, Map shapeToObj) {
596: EscherContainerRecord spgrContainer = new EscherContainerRecord();
597: EscherContainerRecord spContainer = new EscherContainerRecord();
598: EscherSpgrRecord spgr = new EscherSpgrRecord();
599: EscherSpRecord sp = new EscherSpRecord();
600: EscherOptRecord opt = new EscherOptRecord();
601: EscherRecord anchor;
602: EscherClientDataRecord clientData = new EscherClientDataRecord();
603:
604: spgrContainer.setRecordId(EscherContainerRecord.SPGR_CONTAINER);
605: spgrContainer.setOptions((short) 0x000F);
606: spContainer.setRecordId(EscherContainerRecord.SP_CONTAINER);
607: spContainer.setOptions((short) 0x000F);
608: spgr.setRecordId(EscherSpgrRecord.RECORD_ID);
609: spgr.setOptions((short) 0x0001);
610: spgr.setRectX1(shape.getX1());
611: spgr.setRectY1(shape.getY1());
612: spgr.setRectX2(shape.getX2());
613: spgr.setRectY2(shape.getY2());
614: sp.setRecordId(EscherSpRecord.RECORD_ID);
615: sp.setOptions((short) 0x0002);
616: int shapeId = drawingManager.allocateShapeId(drawingGroupId);
617: sp.setShapeId(shapeId);
618: if (shape.getAnchor() instanceof HSSFClientAnchor)
619: sp.setFlags(EscherSpRecord.FLAG_GROUP
620: | EscherSpRecord.FLAG_HAVEANCHOR);
621: else
622: sp.setFlags(EscherSpRecord.FLAG_GROUP
623: | EscherSpRecord.FLAG_HAVEANCHOR
624: | EscherSpRecord.FLAG_CHILD);
625: opt.setRecordId(EscherOptRecord.RECORD_ID);
626: opt.setOptions((short) 0x0023);
627: opt.addEscherProperty(new EscherBoolProperty(
628: EscherProperties.PROTECTION__LOCKAGAINSTGROUPING,
629: 0x00040004));
630: opt.addEscherProperty(new EscherBoolProperty(
631: EscherProperties.GROUPSHAPE__PRINT, 0x00080000));
632:
633: anchor = ConvertAnchor.createAnchor(shape.getAnchor());
634: // clientAnchor.setCol1( ( (HSSFClientAnchor) shape.getAnchor() ).getCol1() );
635: // clientAnchor.setRow1( (short) ( (HSSFClientAnchor) shape.getAnchor() ).getRow1() );
636: // clientAnchor.setDx1( (short) shape.getAnchor().getDx1() );
637: // clientAnchor.setDy1( (short) shape.getAnchor().getDy1() );
638: // clientAnchor.setCol2( ( (HSSFClientAnchor) shape.getAnchor() ).getCol2() );
639: // clientAnchor.setRow2( (short) ( (HSSFClientAnchor) shape.getAnchor() ).getRow2() );
640: // clientAnchor.setDx2( (short) shape.getAnchor().getDx2() );
641: // clientAnchor.setDy2( (short) shape.getAnchor().getDy2() );
642: clientData.setRecordId(EscherClientDataRecord.RECORD_ID);
643: clientData.setOptions((short) 0x0000);
644:
645: spgrContainer.addChildRecord(spContainer);
646: spContainer.addChildRecord(spgr);
647: spContainer.addChildRecord(sp);
648: spContainer.addChildRecord(opt);
649: spContainer.addChildRecord(anchor);
650: spContainer.addChildRecord(clientData);
651:
652: ObjRecord obj = new ObjRecord();
653: CommonObjectDataSubRecord cmo = new CommonObjectDataSubRecord();
654: cmo.setObjectType(CommonObjectDataSubRecord.OBJECT_TYPE_GROUP);
655: cmo.setObjectId((short) (shapeId));
656: cmo.setLocked(true);
657: cmo.setPrintable(true);
658: cmo.setAutofill(true);
659: cmo.setAutoline(true);
660: GroupMarkerSubRecord gmo = new GroupMarkerSubRecord();
661: EndSubRecord end = new EndSubRecord();
662: obj.addSubRecord(cmo);
663: obj.addSubRecord(gmo);
664: obj.addSubRecord(end);
665: shapeToObj.put(clientData, obj);
666:
667: escherParent.addChildRecord(spgrContainer);
668:
669: convertShapes(shape, spgrContainer, shapeToObj);
670:
671: }
672:
673: private EscherRecord findClientData(
674: EscherContainerRecord spContainer) {
675: for (Iterator iterator = spContainer.getChildRecords()
676: .iterator(); iterator.hasNext();) {
677: EscherRecord r = (EscherRecord) iterator.next();
678: if (r.getRecordId() == EscherClientDataRecord.RECORD_ID)
679: return r;
680: }
681: throw new IllegalArgumentException(
682: "Can not find client data record");
683: }
684:
685: private void convertPatriarch(HSSFPatriarch patriarch) {
686: EscherContainerRecord dgContainer = new EscherContainerRecord();
687: EscherDgRecord dg;
688: EscherContainerRecord spgrContainer = new EscherContainerRecord();
689: EscherContainerRecord spContainer1 = new EscherContainerRecord();
690: EscherSpgrRecord spgr = new EscherSpgrRecord();
691: EscherSpRecord sp1 = new EscherSpRecord();
692:
693: dgContainer.setRecordId(EscherContainerRecord.DG_CONTAINER);
694: dgContainer.setOptions((short) 0x000F);
695: dg = drawingManager.createDgRecord();
696: drawingGroupId = dg.getDrawingGroupId();
697: // dg.setOptions( (short) ( drawingId << 4 ) );
698: // dg.setNumShapes( getNumberOfShapes( patriarch ) );
699: // dg.setLastMSOSPID( 0 ); // populated after all shape id's are assigned.
700: spgrContainer.setRecordId(EscherContainerRecord.SPGR_CONTAINER);
701: spgrContainer.setOptions((short) 0x000F);
702: spContainer1.setRecordId(EscherContainerRecord.SP_CONTAINER);
703: spContainer1.setOptions((short) 0x000F);
704: spgr.setRecordId(EscherSpgrRecord.RECORD_ID);
705: spgr.setOptions((short) 0x0001); // version
706: spgr.setRectX1(patriarch.getX1());
707: spgr.setRectY1(patriarch.getY1());
708: spgr.setRectX2(patriarch.getX2());
709: spgr.setRectY2(patriarch.getY2());
710: sp1.setRecordId(EscherSpRecord.RECORD_ID);
711: sp1.setOptions((short) 0x0002);
712: sp1.setShapeId(drawingManager.allocateShapeId(dg
713: .getDrawingGroupId()));
714: sp1.setFlags(EscherSpRecord.FLAG_GROUP
715: | EscherSpRecord.FLAG_PATRIARCH);
716:
717: dgContainer.addChildRecord(dg);
718: dgContainer.addChildRecord(spgrContainer);
719: spgrContainer.addChildRecord(spContainer1);
720: spContainer1.addChildRecord(spgr);
721: spContainer1.addChildRecord(sp1);
722:
723: addEscherRecord(dgContainer);
724: }
725:
726: /** Retrieve the number of shapes (including the patriarch). */
727: // private int getNumberOfShapes( HSSFPatriarch patriarch )
728: // {
729: // return patriarch.countOfAllChildren();
730: // }
731: private static short sid(List records, int loc) {
732: return ((Record) records.get(loc)).getSid();
733: }
734:
735: }
|