/* ====================================================================
|
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.
|
==================================================================== */
|
|
namespace HH.WMS.Utils.NPOI.HSSF.Record
|
{
|
using System;
|
using System.Text;
|
using System.Collections;
|
using HH.WMS.Utils.NPOI.DDF;
|
using HH.WMS.Utils.NPOI.HSSF.UserModel;
|
using HH.WMS.Utils.NPOI.HSSF.Model;
|
using HH.WMS.Utils.NPOI.Util;
|
using System.Collections.Generic;
|
|
internal class SerializationListener : EscherSerializationListener
|
{
|
IList spEndingOffsets;
|
IList shapes;
|
|
public SerializationListener(ref IList spEndingOffsets, ref IList shapes)
|
{
|
this.spEndingOffsets = spEndingOffsets;
|
this.shapes = shapes;
|
}
|
|
#region EscherSerializationListener Members
|
|
void EscherSerializationListener.BeforeRecordSerialize(int Offset, short recordId, EscherRecord record)
|
{
|
|
}
|
|
void EscherSerializationListener.AfterRecordSerialize(int Offset, short recordId, int size, EscherRecord record)
|
{
|
if (recordId == EscherClientDataRecord.RECORD_ID || recordId == EscherTextboxRecord.RECORD_ID)
|
{
|
spEndingOffsets.Add(Offset);
|
shapes.Add(record);
|
}
|
}
|
|
#endregion
|
}
|
|
/**
|
* This class Is used to aggregate the MSODRAWING and OBJ record
|
* combinations. This Is necessary due to the bizare way in which
|
* these records are Serialized. What happens Is that you Get a
|
* combination of MSODRAWING -> OBJ -> MSODRAWING -> OBJ records
|
* but the escher records are Serialized _across_ the MSODRAWING
|
* records.
|
*
|
* It Gets even worse when you start looking at TXO records.
|
*
|
* So what we do with this class Is aggregate lazily. That Is
|
* we don't aggregate the MSODRAWING -> OBJ records Unless we
|
* need to modify them.
|
*
|
*
|
* @author Glen Stampoultzis (glens at apache.org)
|
*/
|
public class EscherAggregate : AbstractEscherHolderRecord
|
{
|
public const short sid = 9876;
|
private static POILogger log = POILogFactory.GetLogger(typeof(EscherAggregate));
|
|
public static short ST_MIN = (short)0;
|
public static short ST_NOT_PRIMATIVE = ST_MIN;
|
public static short ST_RECTANGLE = (short)1;
|
public static short ST_ROUNDRECTANGLE = (short)2;
|
public static short ST_ELLIPSE = (short)3;
|
public static short ST_DIAMOND = (short)4;
|
public static short ST_ISOCELESTRIANGLE = (short)5;
|
public static short ST_RIGHTTRIANGLE = (short)6;
|
public static short ST_PARALLELOGRAM = (short)7;
|
public static short ST_TRAPEZOID = (short)8;
|
public static short ST_HEXAGON = (short)9;
|
public static short ST_OCTAGON = (short)10;
|
public static short ST_PLUS = (short)11;
|
public static short ST_STAR = (short)12;
|
public static short ST_ARROW = (short)13;
|
public static short ST_THICKARROW = (short)14;
|
public static short ST_HOMEPLATE = (short)15;
|
public static short ST_CUBE = (short)16;
|
public static short ST_BALLOON = (short)17;
|
public static short ST_SEAL = (short)18;
|
public static short ST_ARC = (short)19;
|
public static short ST_LINE = (short)20;
|
public static short ST_PLAQUE = (short)21;
|
public static short ST_CAN = (short)22;
|
public static short ST_DONUT = (short)23;
|
public static short ST_TEXTSIMPLE = (short)24;
|
public static short ST_TEXTOCTAGON = (short)25;
|
public static short ST_TEXTHEXAGON = (short)26;
|
public static short ST_TEXTCURVE = (short)27;
|
public static short ST_TEXTWAVE = (short)28;
|
public static short ST_TEXTRING = (short)29;
|
public static short ST_TEXTONCURVE = (short)30;
|
public static short ST_TEXTONRING = (short)31;
|
public static short ST_STRAIGHTCONNECTOR1 = (short)32;
|
public static short ST_BENTCONNECTOR2 = (short)33;
|
public static short ST_BENTCONNECTOR3 = (short)34;
|
public static short ST_BENTCONNECTOR4 = (short)35;
|
public static short ST_BENTCONNECTOR5 = (short)36;
|
public static short ST_CURVEDCONNECTOR2 = (short)37;
|
public static short ST_CURVEDCONNECTOR3 = (short)38;
|
public static short ST_CURVEDCONNECTOR4 = (short)39;
|
public static short ST_CURVEDCONNECTOR5 = (short)40;
|
public static short ST_CALLOUT1 = (short)41;
|
public static short ST_CALLOUT2 = (short)42;
|
public static short ST_CALLOUT3 = (short)43;
|
public static short ST_ACCENTCALLOUT1 = (short)44;
|
public static short ST_ACCENTCALLOUT2 = (short)45;
|
public static short ST_ACCENTCALLOUT3 = (short)46;
|
public static short ST_BORDERCALLOUT1 = (short)47;
|
public static short ST_BORDERCALLOUT2 = (short)48;
|
public static short ST_BORDERCALLOUT3 = (short)49;
|
public static short ST_ACCENTBORDERCALLOUT1 = (short)50;
|
public static short ST_ACCENTBORDERCALLOUT2 = (short)51;
|
public static short ST_ACCENTBORDERCALLOUT3 = (short)52;
|
public static short ST_RIBBON = (short)53;
|
public static short ST_RIBBON2 = (short)54;
|
public static short ST_CHEVRON = (short)55;
|
public static short ST_PENTAGON = (short)56;
|
public static short ST_NOSMOKING = (short)57;
|
public static short ST_SEAL8 = (short)58;
|
public static short ST_SEAL16 = (short)59;
|
public static short ST_SEAL32 = (short)60;
|
public static short ST_WEDGERECTCALLOUT = (short)61;
|
public static short ST_WEDGERRECTCALLOUT = (short)62;
|
public static short ST_WEDGEELLIPSECALLOUT = (short)63;
|
public static short ST_WAVE = (short)64;
|
public static short ST_FOLDEDCORNER = (short)65;
|
public static short ST_LEFTARROW = (short)66;
|
public static short ST_DOWNARROW = (short)67;
|
public static short ST_UPARROW = (short)68;
|
public static short ST_LEFTRIGHTARROW = (short)69;
|
public static short ST_UPDOWNARROW = (short)70;
|
public static short ST_IRREGULARSEAL1 = (short)71;
|
public static short ST_IRREGULARSEAL2 = (short)72;
|
public static short ST_LIGHTNINGBOLT = (short)73;
|
public static short ST_HEART = (short)74;
|
public const short ST_PICTUREFRAME = (short)75;
|
public static short ST_QUADARROW = (short)76;
|
public static short ST_LEFTARROWCALLOUT = (short)77;
|
public static short ST_RIGHTARROWCALLOUT = (short)78;
|
public static short ST_UPARROWCALLOUT = (short)79;
|
public static short ST_DOWNARROWCALLOUT = (short)80;
|
public static short ST_LEFTRIGHTARROWCALLOUT = (short)81;
|
public static short ST_UPDOWNARROWCALLOUT = (short)82;
|
public static short ST_QUADARROWCALLOUT = (short)83;
|
public static short ST_BEVEL = (short)84;
|
public static short ST_LEFTBRACKET = (short)85;
|
public static short ST_RIGHTBRACKET = (short)86;
|
public static short ST_LEFTBRACE = (short)87;
|
public static short ST_RIGHTBRACE = (short)88;
|
public static short ST_LEFTUPARROW = (short)89;
|
public static short ST_BENTUPARROW = (short)90;
|
public static short ST_BENTARROW = (short)91;
|
public static short ST_SEAL24 = (short)92;
|
public static short ST_STRIPEDRIGHTARROW = (short)93;
|
public static short ST_NOTCHEDRIGHTARROW = (short)94;
|
public static short ST_BLOCKARC = (short)95;
|
public static short ST_SMILEYFACE = (short)96;
|
public static short ST_VERTICALSCROLL = (short)97;
|
public static short ST_HORIZONTALSCROLL = (short)98;
|
public static short ST_CIRCULARARROW = (short)99;
|
public static short ST_NOTCHEDCIRCULARARROW = (short)100;
|
public static short ST_UTURNARROW = (short)101;
|
public static short ST_CURVEDRIGHTARROW = (short)102;
|
public static short ST_CURVEDLEFTARROW = (short)103;
|
public static short ST_CURVEDUPARROW = (short)104;
|
public static short ST_CURVEDDOWNARROW = (short)105;
|
public static short ST_CLOUDCALLOUT = (short)106;
|
public static short ST_ELLIPSERIBBON = (short)107;
|
public static short ST_ELLIPSERIBBON2 = (short)108;
|
public static short ST_FLOWCHARTProcess = (short)109;
|
public static short ST_FLOWCHARTDECISION = (short)110;
|
public static short ST_FLOWCHARTINPUTOUTPUT = (short)111;
|
public static short ST_FLOWCHARTPREDEFINEDProcess = (short)112;
|
public static short ST_FLOWCHARTINTERNALSTORAGE = (short)113;
|
public static short ST_FLOWCHARTDOCUMENT = (short)114;
|
public static short ST_FLOWCHARTMULTIDOCUMENT = (short)115;
|
public static short ST_FLOWCHARTTERMINATOR = (short)116;
|
public static short ST_FLOWCHARTPREPARATION = (short)117;
|
public static short ST_FLOWCHARTMANUALINPUT = (short)118;
|
public static short ST_FLOWCHARTMANUALOPERATION = (short)119;
|
public static short ST_FLOWCHARTCONNECTOR = (short)120;
|
public static short ST_FLOWCHARTPUNCHEDCARD = (short)121;
|
public static short ST_FLOWCHARTPUNCHEDTAPE = (short)122;
|
public static short ST_FLOWCHARTSUMMINGJUNCTION = (short)123;
|
public static short ST_FLOWCHARTOR = (short)124;
|
public static short ST_FLOWCHARTCOLLATE = (short)125;
|
public static short ST_FLOWCHARTSORT = (short)126;
|
public static short ST_FLOWCHARTEXTRACT = (short)127;
|
public static short ST_FLOWCHARTMERGE = (short)128;
|
public static short ST_FLOWCHARTOFFLINESTORAGE = (short)129;
|
public static short ST_FLOWCHARTONLINESTORAGE = (short)130;
|
public static short ST_FLOWCHARTMAGNETICTAPE = (short)131;
|
public static short ST_FLOWCHARTMAGNETICDISK = (short)132;
|
public static short ST_FLOWCHARTMAGNETICDRUM = (short)133;
|
public static short ST_FLOWCHARTDISPLAY = (short)134;
|
public static short ST_FLOWCHARTDELAY = (short)135;
|
public static short ST_TEXTPLAINTEXT = (short)136;
|
public static short ST_TEXTSTOP = (short)137;
|
public static short ST_TEXTTRIANGLE = (short)138;
|
public static short ST_TEXTTRIANGLEINVERTED = (short)139;
|
public static short ST_TEXTCHEVRON = (short)140;
|
public static short ST_TEXTCHEVRONINVERTED = (short)141;
|
public static short ST_TEXTRINGINSIDE = (short)142;
|
public static short ST_TEXTRINGOUTSIDE = (short)143;
|
public static short ST_TEXTARCHUPCURVE = (short)144;
|
public static short ST_TEXTARCHDOWNCURVE = (short)145;
|
public static short ST_TEXTCIRCLECURVE = (short)146;
|
public static short ST_TEXTBUTTONCURVE = (short)147;
|
public static short ST_TEXTARCHUPPOUR = (short)148;
|
public static short ST_TEXTARCHDOWNPOUR = (short)149;
|
public static short ST_TEXTCIRCLEPOUR = (short)150;
|
public static short ST_TEXTBUTTONPOUR = (short)151;
|
public static short ST_TEXTCURVEUP = (short)152;
|
public static short ST_TEXTCURVEDOWN = (short)153;
|
public static short ST_TEXTCASCADEUP = (short)154;
|
public static short ST_TEXTCASCADEDOWN = (short)155;
|
public static short ST_TEXTWAVE1 = (short)156;
|
public static short ST_TEXTWAVE2 = (short)157;
|
public static short ST_TEXTWAVE3 = (short)158;
|
public static short ST_TEXTWAVE4 = (short)159;
|
public static short ST_TEXTINFLATE = (short)160;
|
public static short ST_TEXTDEFLATE = (short)161;
|
public static short ST_TEXTINFLATEBOTTOM = (short)162;
|
public static short ST_TEXTDEFLATEBOTTOM = (short)163;
|
public static short ST_TEXTINFLATETOP = (short)164;
|
public static short ST_TEXTDEFLATETOP = (short)165;
|
public static short ST_TEXTDEFLATEINFLATE = (short)166;
|
public static short ST_TEXTDEFLATEINFLATEDEFLATE = (short)167;
|
public static short ST_TEXTFADERIGHT = (short)168;
|
public static short ST_TEXTFADELEFT = (short)169;
|
public static short ST_TEXTFADEUP = (short)170;
|
public static short ST_TEXTFADEDOWN = (short)171;
|
public static short ST_TEXTSLANTUP = (short)172;
|
public static short ST_TEXTSLANTDOWN = (short)173;
|
public static short ST_TEXTCANUP = (short)174;
|
public static short ST_TEXTCANDOWN = (short)175;
|
public static short ST_FLOWCHARTALTERNATEProcess = (short)176;
|
public static short ST_FLOWCHARTOFFPAGECONNECTOR = (short)177;
|
public static short ST_CALLOUT90 = (short)178;
|
public static short ST_ACCENTCALLOUT90 = (short)179;
|
public static short ST_BORDERCALLOUT90 = (short)180;
|
public static short ST_ACCENTBORDERCALLOUT90 = (short)181;
|
public static short ST_LEFTRIGHTUPARROW = (short)182;
|
public static short ST_SUN = (short)183;
|
public static short ST_MOON = (short)184;
|
public static short ST_BRACKETPAIR = (short)185;
|
public static short ST_BRACEPAIR = (short)186;
|
public static short ST_SEAL4 = (short)187;
|
public static short ST_DOUBLEWAVE = (short)188;
|
public static short ST_ACTIONBUTTONBLANK = (short)189;
|
public static short ST_ACTIONBUTTONHOME = (short)190;
|
public static short ST_ACTIONBUTTONHELP = (short)191;
|
public static short ST_ACTIONBUTTONINFORMATION = (short)192;
|
public static short ST_ACTIONBUTTONFORWARDNEXT = (short)193;
|
public static short ST_ACTIONBUTTONBACKPREVIOUS = (short)194;
|
public static short ST_ACTIONBUTTONEND = (short)195;
|
public static short ST_ACTIONBUTTONBEGINNING = (short)196;
|
public static short ST_ACTIONBUTTONRETURN = (short)197;
|
public static short ST_ACTIONBUTTONDOCUMENT = (short)198;
|
public static short ST_ACTIONBUTTONSOUND = (short)199;
|
public static short ST_ACTIONBUTTONMOVIE = (short)200;
|
public static short ST_HOSTCONTROL = (short)201;
|
public const short ST_TEXTBOX = (short)202;
|
public const short ST_NIL = (short)0x0FFF;
|
|
protected HSSFPatriarch patriarch;
|
|
/** Maps shape container objects to their OBJ records */
|
private Hashtable shapeToObj = new Hashtable();
|
private DrawingManager2 drawingManager;
|
private short drawingGroupId;
|
|
/**
|
* list of "tail" records that need to be Serialized after all drawing Group records
|
*/
|
private IList tailRec = new ArrayList();
|
|
public EscherAggregate(DrawingManager2 drawingManager)
|
{
|
this.drawingManager = drawingManager;
|
}
|
|
/**
|
* @return Returns the current sid.
|
*/
|
public override short Sid
|
{
|
get { return sid; }
|
}
|
|
/**
|
* Unused since this Is an aggregate record. Use CreateAggregate().
|
*
|
* @see #CreateAggregate
|
*/
|
public System.Collections.IList Children(byte[] data, short size, int offset)
|
{
|
throw new InvalidOperationException("Should not reach here");
|
}
|
|
/**
|
* Calculates the string representation of this record. This Is
|
* simply a dump of all the records.
|
*/
|
public override String ToString()
|
{
|
String nl = Environment.NewLine;
|
|
StringBuilder result = new StringBuilder();
|
result.Append('[').Append(RecordName).Append(']' + nl);
|
for (IEnumerator iterator = EscherRecords.GetEnumerator(); iterator.MoveNext(); )
|
{
|
EscherRecord escherRecord = (EscherRecord)iterator.Current;
|
result.Append(escherRecord.ToString());
|
}
|
result.Append("[/").Append(RecordName).Append(']' + nl);
|
|
return result.ToString();
|
}
|
|
internal class CustomEscherRecordFactory : DefaultEscherRecordFactory
|
{
|
IList shapeRecords;
|
public CustomEscherRecordFactory(ref IList shapeRecords)
|
{
|
this.shapeRecords = shapeRecords;
|
}
|
|
public override EscherRecord CreateRecord(byte[] data, int offset)
|
{
|
EscherRecord r = base.CreateRecord(data, offset);
|
if (r.RecordId == EscherClientDataRecord.RECORD_ID || r.RecordId == EscherTextboxRecord.RECORD_ID)
|
{
|
shapeRecords.Add(r);
|
}
|
return r;
|
}
|
}
|
|
/**
|
* Collapses the drawing records into an aggregate.
|
*/
|
public static EscherAggregate CreateAggregate(IList records, int locFirstDrawingRecord, DrawingManager2 drawingManager)
|
{
|
// Keep track of any shape records Created so we can match them back to the object id's.
|
// Textbox objects are also treated as shape objects.
|
IList shapeRecords = new ArrayList();
|
EscherRecordFactory recordFactory = new CustomEscherRecordFactory(ref shapeRecords);
|
|
// Calculate the size of the buffer
|
EscherAggregate agg = new EscherAggregate(drawingManager);
|
int loc = locFirstDrawingRecord;
|
int dataSize = 0;
|
while (loc + 1 < records.Count
|
&& GetSid(records, loc) == DrawingRecord.sid
|
&& IsObjectRecord(records, loc + 1))
|
{
|
dataSize += ((DrawingRecord)records[loc]).Data.Length;
|
loc += 2;
|
}
|
|
// Create one big buffer
|
byte[] buffer = new byte[dataSize];
|
int offset = 0;
|
loc = locFirstDrawingRecord;
|
while (loc + 1 < records.Count
|
&& GetSid(records, loc) == DrawingRecord.sid
|
&& IsObjectRecord(records, loc + 1))
|
{
|
DrawingRecord drawingRecord = (DrawingRecord)records[loc];
|
Array.Copy(drawingRecord.Data, 0, buffer, offset, drawingRecord.Data.Length);
|
offset += drawingRecord.Data.Length;
|
loc += 2;
|
}
|
|
// Decode the shapes
|
// agg.escherRecords = new ArrayList();
|
int pos = 0;
|
while (pos < dataSize)
|
{
|
EscherRecord r = recordFactory.CreateRecord(buffer, pos);
|
int bytesRead = r.FillFields(buffer, pos, recordFactory);
|
agg.AddEscherRecord(r);
|
pos += bytesRead;
|
}
|
|
// Associate the object records with the shapes
|
loc = locFirstDrawingRecord;
|
int shapeIndex = 0;
|
agg.shapeToObj = new Hashtable();
|
while (loc + 1 < records.Count
|
&& GetSid(records, loc) == DrawingRecord.sid
|
&& IsObjectRecord(records, loc + 1))
|
{
|
Record objRecord = (Record)records[loc + 1];
|
agg.shapeToObj[shapeRecords[shapeIndex++]]= objRecord;
|
loc += 2;
|
}
|
|
return agg;
|
|
}
|
|
IList spEndingOffsets;
|
IList shapes;
|
/**
|
* Serializes this aggregate to a byte array. Since this Is an aggregate
|
* record it will effectively Serialize the aggregated records.
|
*
|
* @param offset The offset into the start of the array.
|
* @param data The byte array to Serialize to.
|
* @return The number of bytes Serialized.
|
*/
|
public override int Serialize(int offset, byte [] data)
|
{
|
ConvertUserModelToRecords();
|
|
// Determine buffer size
|
IList records = EscherRecords;
|
int size = GetEscherRecordSize(records);
|
byte[] buffer = new byte[size];
|
|
|
// Serialize escher records into one big data structure and keep note of ending offsets.
|
spEndingOffsets = new ArrayList();
|
shapes = new ArrayList();
|
int pos = 0;
|
for (IEnumerator iterator = records.GetEnumerator(); iterator.MoveNext(); )
|
{
|
EscherRecord e = (EscherRecord)iterator.Current;
|
pos += e.Serialize(pos, buffer, new SerializationListener(ref spEndingOffsets,ref shapes));
|
}
|
// todo: fix this
|
shapes.Insert(0, null);
|
spEndingOffsets.Insert(0, null);
|
|
// Split escher records into Separate MSODRAWING and OBJ, TXO records. (We don't break on
|
// the first one because it's the patriach).
|
pos = offset;
|
for (int i = 1; i < shapes.Count; i++)
|
{
|
int endOffset = (int)spEndingOffsets[i] - 1;
|
int startOffset;
|
if (i == 1)
|
startOffset = 0;
|
else
|
startOffset = (int)spEndingOffsets[i - 1];
|
|
// Create and Write a new MSODRAWING record
|
DrawingRecord drawing = new DrawingRecord();
|
byte[] drawingData = new byte[endOffset - startOffset + 1];
|
Array.Copy(buffer, startOffset, drawingData, 0, drawingData.Length);
|
drawing.Data=drawingData;
|
int temp = drawing.Serialize(pos, data);
|
pos += temp;
|
|
// Write the matching OBJ record
|
Record obj = (Record)shapeToObj[shapes[i]];
|
temp = obj.Serialize(pos, data);
|
pos += temp;
|
|
}
|
|
// Write records that need to be Serialized after all drawing Group records
|
for (int i = 0; i < tailRec.Count; i++)
|
{
|
Record rec = (Record)tailRec[i];
|
pos += rec.Serialize(pos, data);
|
}
|
|
int bytesWritten = pos - offset;
|
if (bytesWritten != RecordSize)
|
throw new RecordFormatException(bytesWritten + " bytes written but RecordSize reports " + RecordSize);
|
return bytesWritten;
|
}
|
|
/**
|
* How many bytes do the raw escher records contain.
|
* @param records List of escher records
|
* @return the number of bytes
|
*/
|
private int GetEscherRecordSize(IList records)
|
{
|
int size = 0;
|
for (IEnumerator iterator = records.GetEnumerator(); iterator.MoveNext(); )
|
size += ((EscherRecord)iterator.Current).RecordSize;
|
return size;
|
}
|
|
/**
|
* The number of bytes required to Serialize this record.
|
*/
|
public override int RecordSize
|
{
|
get
|
{
|
ConvertUserModelToRecords();
|
IList records = EscherRecords;
|
int rawEscherSize = GetEscherRecordSize(records);
|
int drawingRecordSize = rawEscherSize + (shapeToObj.Count) * 4;
|
int objRecordSize = 0;
|
for (IEnumerator iterator = shapeToObj.Values.GetEnumerator(); iterator.MoveNext(); )
|
{
|
Record r = (Record)iterator.Current;
|
objRecordSize += r.RecordSize;
|
}
|
int tailRecordSize = 0;
|
for (IEnumerator iterator = tailRec.GetEnumerator(); iterator.MoveNext(); )
|
{
|
Record r = (Record)iterator.Current;
|
tailRecordSize += r.RecordSize;
|
}
|
return drawingRecordSize + objRecordSize + tailRecordSize;
|
}
|
}
|
|
/**
|
* Associates an escher record to an OBJ record or a TXO record.
|
*/
|
public Object AssoicateShapeToObjRecord(EscherRecord r, Record objRecord)
|
{
|
return shapeToObj[r]= objRecord;
|
}
|
|
public HSSFPatriarch Patriarch
|
{
|
get { return patriarch; }
|
set { this.patriarch = value; }
|
}
|
|
/**
|
* Converts the Records into UserModel
|
* objects on the bound HSSFPatriarch
|
*/
|
public void ConvertRecordsToUserModel()
|
{
|
if (patriarch == null)
|
{
|
throw new InvalidOperationException("Must call SetPatriarch() first");
|
}
|
|
// The top level container ought to have
|
// the DgRecord and the container of one container
|
// per shape Group (patriach overall first)
|
EscherContainerRecord topContainer =
|
(EscherContainerRecord)GetEscherContainer();
|
if (topContainer == null)
|
{
|
return;
|
}
|
topContainer = (EscherContainerRecord)
|
topContainer.ChildContainers[0];
|
|
IList<EscherContainerRecord> tcc = topContainer.ChildContainers;
|
if (tcc.Count == 0)
|
{
|
throw new InvalidOperationException("No child escher containers at the point that should hold the patriach data, and one container per top level shape!");
|
}
|
|
// First up, Get the patriach position
|
// This Is in the first EscherSpgrRecord, in
|
// the first container, with a EscherSRecord too
|
EscherContainerRecord patriachContainer =
|
(EscherContainerRecord)tcc[0];
|
EscherSpgrRecord spgr = null;
|
for (IEnumerator it = patriachContainer.ChildRecords.GetEnumerator(); it.MoveNext(); )
|
{
|
EscherRecord r = (EscherRecord)it.Current;
|
if (r is EscherSpgrRecord)
|
{
|
spgr = (EscherSpgrRecord)r;
|
break;
|
}
|
}
|
if (spgr != null)
|
{
|
patriarch.SetCoordinates(
|
spgr.RectX1, spgr.RectY1,
|
spgr.RectX2, spgr.RectY2
|
);
|
}
|
|
// Now Process the containers for each Group
|
// and objects
|
for (int i = 1; i < tcc.Count; i++)
|
{
|
EscherContainerRecord shapeContainer =
|
(EscherContainerRecord)tcc[i];
|
//Console.Error.WriteLine("\n\n*****\n\n");
|
//Console.Error.WriteLine(shapeContainer);
|
|
// Could be a Group, or a base object
|
if (shapeContainer.RecordId == EscherContainerRecord.SPGR_CONTAINER)
|
{
|
if(shapeContainer.ChildRecords.Count>0)
|
{
|
// Group
|
HSSFShapeGroup group =
|
new HSSFShapeGroup(null, new HSSFClientAnchor());
|
patriarch.Children.Add(group);
|
|
EscherContainerRecord groupContainer =
|
(EscherContainerRecord)shapeContainer.GetChild(0);
|
ConvertRecordsToUserModel(groupContainer, group);
|
}
|
}
|
else if (shapeContainer.RecordId == EscherContainerRecord.SP_CONTAINER)
|
{
|
EscherSpRecord spRecord = shapeContainer.GetChildById(EscherSpRecord.RECORD_ID);
|
int type = spRecord.Options >> 4;
|
|
switch (type)
|
{
|
case ST_TEXTBOX:
|
// TextBox
|
HSSFTextbox box =
|
new HSSFTextbox(null, new HSSFClientAnchor());
|
patriarch.Children.Add(box);
|
|
ConvertRecordsToUserModel(shapeContainer, box);
|
break;
|
case ST_PICTUREFRAME:
|
// Duplicated from
|
// org.apache.poi.hslf.model.Picture.getPictureIndex()
|
EscherOptRecord opt = (EscherOptRecord)GetEscherChild(shapeContainer, EscherOptRecord.RECORD_ID);
|
EscherSimpleProperty prop = (EscherSimpleProperty)opt.Lookup(EscherProperties.BLIP__BLIPTODISPLAY);
|
if (prop != null)
|
{
|
int pictureIndex = prop.PropertyValue;
|
EscherClientAnchorRecord anchorRecord = (EscherClientAnchorRecord)GetEscherChild(shapeContainer, EscherClientAnchorRecord.RECORD_ID);
|
HSSFClientAnchor anchor = new HSSFClientAnchor();
|
anchor.Col1 = anchorRecord.Col1;
|
anchor.Col2 = anchorRecord.Col2;
|
anchor.Dx1 = anchorRecord.Dx1;
|
anchor.Dx2 = anchorRecord.Dx2;
|
anchor.Dy1 = anchorRecord.Dy1;
|
anchor.Dy2 = anchorRecord.Dy2;
|
anchor.Row1 = anchorRecord.Row1;
|
anchor.Row2 = anchorRecord.Row2;
|
HSSFPicture picture = new HSSFPicture(null, anchor);
|
picture.PictureIndex = pictureIndex;
|
patriarch.AddShape(picture);
|
|
}
|
break;
|
}
|
|
|
}
|
else
|
{
|
// Base level
|
ConvertRecordsToUserModel(shapeContainer, patriarch);
|
}
|
}
|
|
// Now, clear any trace of what records make up
|
// the patriarch
|
// Otherwise, everything will go horribly wrong
|
// when we try to Write out again....
|
// clearEscherRecords();
|
drawingManager.GetDgg().FileIdClusters=new EscherDggRecord.FileIdCluster[0];
|
|
// TODO: Support Converting our records
|
// back into shapes
|
log.Log(POILogger.WARN, "Not Processing objects into Patriarch!");
|
}
|
|
private EscherRecord GetEscherChild(EscherContainerRecord owner, int recordId)
|
{
|
for (IEnumerator iterator = owner.ChildRecords.GetEnumerator(); iterator.MoveNext(); )
|
{
|
EscherRecord escherRecord = (EscherRecord)iterator.Current;
|
if (escherRecord.RecordId == recordId)
|
return escherRecord;
|
}
|
return null;
|
}
|
|
|
private void ConvertRecordsToUserModel(EscherContainerRecord shapeContainer, Object model)
|
{
|
for (IEnumerator it = shapeContainer.ChildRecords.GetEnumerator(); it.MoveNext(); )
|
{
|
EscherRecord r = (EscherRecord)it.Current;
|
if (r is EscherSpgrRecord)
|
{
|
// This may be overriden by a later EscherClientAnchorRecord
|
EscherSpgrRecord spgr = (EscherSpgrRecord)r;
|
|
if (model is HSSFShapeGroup)
|
{
|
HSSFShapeGroup g = (HSSFShapeGroup)model;
|
g.SetCoordinates(
|
spgr.RectX1, spgr.RectY1,
|
spgr.RectX2, spgr.RectY2
|
);
|
}
|
else
|
{
|
throw new InvalidOperationException("Got top level anchor but not Processing a Group");
|
}
|
}
|
else if (r is EscherClientAnchorRecord)
|
{
|
EscherClientAnchorRecord car = (EscherClientAnchorRecord)r;
|
|
if (model is HSSFShape)
|
{
|
HSSFShape g = (HSSFShape)model;
|
g.Anchor.Dx1=car.Dx1;
|
g.Anchor.Dx2=car.Dx2;
|
g.Anchor.Dy1=car.Dy1;
|
g.Anchor.Dy2=car.Dy2;
|
}
|
else
|
{
|
throw new InvalidOperationException("Got top level anchor but not Processing a Group or shape");
|
}
|
}
|
else if (r is EscherTextboxRecord)
|
{
|
EscherTextboxRecord tbr = (EscherTextboxRecord)r;
|
|
// Also need to Find the TextObjectRecord too
|
// TODO
|
}
|
else if (r is EscherSpRecord)
|
{
|
// Use flags if needed
|
}
|
else if (r is EscherOptRecord)
|
{
|
// Use properties if needed
|
}
|
else
|
{
|
//Console.Error.WriteLine(r);
|
}
|
}
|
}
|
|
public void Clear()
|
{
|
ClearEscherRecords();
|
shapeToObj.Clear();
|
// lastShapeId = 1024;
|
}
|
|
protected override String RecordName
|
{
|
get { return "ESCHERAGGREGATE"; }
|
}
|
|
// =============== Private methods ========================
|
|
private static bool IsObjectRecord(IList records, int loc)
|
{
|
return GetSid(records, loc) == ObjRecord.sid || GetSid(records, loc) == TextObjectRecord.sid;
|
}
|
|
private void ConvertUserModelToRecords()
|
{
|
if (patriarch != null)
|
{
|
shapeToObj.Clear();
|
tailRec.Clear();
|
ClearEscherRecords();
|
if (patriarch.Children.Count != 0)
|
{
|
ConvertPatriarch(patriarch);
|
EscherContainerRecord dgContainer = (EscherContainerRecord)GetEscherRecord(0);
|
EscherContainerRecord spgrContainer = null;
|
for (int i = 0; i < dgContainer.ChildRecords.Count; i++)
|
if (dgContainer.GetChild(i).RecordId == EscherContainerRecord.SPGR_CONTAINER)
|
spgrContainer = (EscherContainerRecord)dgContainer.GetChild(i);
|
ConvertShapes(patriarch, spgrContainer, shapeToObj);
|
|
patriarch = null;
|
}
|
}
|
}
|
|
private void ConvertShapes(HSSFShapeContainer parent, EscherContainerRecord escherParent, Hashtable shapeToObj)
|
{
|
if (escherParent == null) throw new ArgumentException("Parent record required");
|
|
IList shapes = parent.Children;
|
for (IEnumerator iterator = shapes.GetEnumerator(); iterator.MoveNext(); )
|
{
|
HSSFShape shape = (HSSFShape)iterator.Current;
|
if (shape is HSSFShapeGroup)
|
{
|
ConvertGroup((HSSFShapeGroup)shape, escherParent, shapeToObj);
|
}
|
else
|
{
|
AbstractShape shapeModel = AbstractShape.CreateShape(
|
shape,
|
drawingManager.AllocateShapeId(drawingGroupId));
|
shapeToObj[FindClientData(shapeModel.SpContainer)]=shapeModel.ObjRecord;
|
if (shapeModel is TextboxShape)
|
{
|
EscherRecord escherTextbox = ((TextboxShape)shapeModel).EscherTextbox;
|
shapeToObj[escherTextbox]=((TextboxShape)shapeModel).TextObjectRecord;
|
// escherParent.AddChildRecord(escherTextbox);
|
|
if (shapeModel is CommentShape)
|
{
|
CommentShape comment = (CommentShape)shapeModel;
|
tailRec.Add(comment.NoteRecord);
|
}
|
|
}
|
escherParent.AddChildRecord(shapeModel.SpContainer);
|
}
|
}
|
// drawingManager.newCluster( (short)1 );
|
// drawingManager.newCluster( (short)2 );
|
|
}
|
|
private void ConvertGroup(HSSFShapeGroup shape, EscherContainerRecord escherParent, Hashtable shapeToObj)
|
{
|
EscherContainerRecord spgrContainer = new EscherContainerRecord();
|
EscherContainerRecord spContainer = new EscherContainerRecord();
|
EscherSpgrRecord spgr = new EscherSpgrRecord();
|
EscherSpRecord sp = new EscherSpRecord();
|
EscherOptRecord opt = new EscherOptRecord();
|
EscherRecord anchor;
|
EscherClientDataRecord clientData = new EscherClientDataRecord();
|
|
spgrContainer.RecordId = EscherContainerRecord.SPGR_CONTAINER;
|
spgrContainer.Options = (short)0x000F;
|
spContainer.RecordId = EscherContainerRecord.SP_CONTAINER;
|
spContainer.Options = (short)0x000F;
|
spgr.RecordId = EscherSpgrRecord.RECORD_ID;
|
spgr.Options = (short)0x0001;
|
spgr.RectX1 = shape.X1;
|
spgr.RectY1 = shape.Y1;
|
spgr.RectX2 = shape.X2;
|
spgr.RectY2 = shape.Y2;
|
sp.RecordId = EscherSpRecord.RECORD_ID;
|
sp.Options = (short)0x0002;
|
int shapeId = drawingManager.AllocateShapeId(drawingGroupId);
|
sp.ShapeId = shapeId;
|
if (shape.Anchor is HSSFClientAnchor)
|
sp.Flags = EscherSpRecord.FLAG_GROUP | EscherSpRecord.FLAG_HAVEANCHOR;
|
else
|
sp.Flags = EscherSpRecord.FLAG_GROUP | EscherSpRecord.FLAG_HAVEANCHOR | EscherSpRecord.FLAG_CHILD;
|
opt.RecordId = EscherOptRecord.RECORD_ID;
|
opt.Options = (short)0x0023;
|
opt.AddEscherProperty(new EscherBoolProperty(EscherProperties.PROTECTION__LOCKAGAINSTGROUPING, 0x00040004));
|
opt.AddEscherProperty(new EscherBoolProperty(EscherProperties.GROUPSHAPE__PRINT, 0x00080000));
|
|
anchor = ConvertAnchor.CreateAnchor(shape.Anchor);
|
// clientAnchor.Col1( ( (HSSFClientAnchor) shape.Anchor ).Col1 );
|
// clientAnchor.Row1( (short) ( (HSSFClientAnchor) shape.Anchor ).Row1 );
|
// clientAnchor.Dx1( (short) shape.Anchor.Dx1 );
|
// clientAnchor.Dy1( (short) shape.Anchor.Dy1 );
|
// clientAnchor.Col2( ( (HSSFClientAnchor) shape.Anchor ).Col2 );
|
// clientAnchor.Row2( (short) ( (HSSFClientAnchor) shape.Anchor ).Row2 );
|
// clientAnchor.Dx2( (short) shape.Anchor.Dx2 );
|
// clientAnchor.Dy2( (short) shape.Anchor.Dy2 );
|
clientData.RecordId = (EscherClientDataRecord.RECORD_ID);
|
clientData.Options = ((short)0x0000);
|
|
spgrContainer.AddChildRecord(spContainer);
|
spContainer.AddChildRecord(spgr);
|
spContainer.AddChildRecord(sp);
|
spContainer.AddChildRecord(opt);
|
spContainer.AddChildRecord(anchor);
|
spContainer.AddChildRecord(clientData);
|
|
ObjRecord obj = new ObjRecord();
|
CommonObjectDataSubRecord cmo = new CommonObjectDataSubRecord();
|
cmo.ObjectType = CommonObjectType.GROUP;
|
cmo.ObjectId = shapeId;
|
cmo.IsLocked = true;
|
cmo.IsPrintable = true;
|
cmo.IsAutoFill = true;
|
cmo.IsAutoline = true;
|
GroupMarkerSubRecord gmo = new GroupMarkerSubRecord();
|
EndSubRecord end = new EndSubRecord();
|
obj.AddSubRecord(cmo);
|
obj.AddSubRecord(gmo);
|
obj.AddSubRecord(end);
|
shapeToObj[clientData] = obj;
|
|
escherParent.AddChildRecord(spgrContainer);
|
|
ConvertShapes(shape, spgrContainer, shapeToObj);
|
|
}
|
|
private EscherRecord FindClientData(EscherContainerRecord spContainer)
|
{
|
for (IEnumerator iterator = spContainer.ChildRecords.GetEnumerator(); iterator.MoveNext(); )
|
{
|
EscherRecord r = (EscherRecord)iterator.Current;
|
if (r.RecordId == EscherClientDataRecord.RECORD_ID)
|
return r;
|
}
|
throw new ArgumentException("Can not Find client data record");
|
}
|
|
private void ConvertPatriarch(HSSFPatriarch patriarch)
|
{
|
EscherContainerRecord dgContainer = new EscherContainerRecord();
|
EscherDgRecord dg;
|
EscherContainerRecord spgrContainer = new EscherContainerRecord();
|
EscherContainerRecord spContainer1 = new EscherContainerRecord();
|
EscherSpgrRecord spgr = new EscherSpgrRecord();
|
EscherSpRecord sp1 = new EscherSpRecord();
|
|
dgContainer.RecordId=EscherContainerRecord.DG_CONTAINER;
|
dgContainer.Options=(short)0x000F;
|
dg = drawingManager.CreateDgRecord();
|
drawingGroupId = dg.DrawingGroupId;
|
// dg.Options( (short) ( drawingId << 4 ) );
|
// dg.NumShapes( GetNumberOfShapes( patriarch ) );
|
// dg.LastMSOSPID( 0 ); // populated after all shape id's are assigned.
|
spgrContainer.RecordId=EscherContainerRecord.SPGR_CONTAINER;
|
spgrContainer.Options=(short)0x000F;
|
spContainer1.RecordId=EscherContainerRecord.SP_CONTAINER;
|
spContainer1.Options=(short)0x000F;
|
spgr.RecordId=EscherSpgrRecord.RECORD_ID;
|
spgr.Options=(short)0x0001; // version
|
spgr.RectX1=patriarch.X1;
|
spgr.RectY1=patriarch.Y1;
|
spgr.RectX2=patriarch.X2;
|
spgr.RectY2=patriarch.Y2;
|
sp1.RecordId=EscherSpRecord.RECORD_ID;
|
sp1.Options=(short)0x0002;
|
sp1.ShapeId=drawingManager.AllocateShapeId(dg.DrawingGroupId);
|
sp1.Flags=EscherSpRecord.FLAG_GROUP | EscherSpRecord.FLAG_PATRIARCH;
|
|
dgContainer.AddChildRecord(dg);
|
dgContainer.AddChildRecord(spgrContainer);
|
spgrContainer.AddChildRecord(spContainer1);
|
spContainer1.AddChildRecord(spgr);
|
spContainer1.AddChildRecord(sp1);
|
|
AddEscherRecord(dgContainer);
|
}
|
|
private static short GetSid(IList records, int loc)
|
{
|
return ((Record)records[loc]).Sid;
|
}
|
|
|
}
|
}
|