Save This Page
Home » poi-src-3.2-FINAL-20081019 » org.apache » poi » hssf » record » [javadoc | source]
    1   /* ====================================================================
    2      Licensed to the Apache Software Foundation (ASF) under one or more
    3      contributor license agreements.  See the NOTICE file distributed with
    4      this work for additional information regarding copyright ownership.
    5      The ASF licenses this file to You under the Apache License, Version 2.0
    6      (the "License"); you may not use this file except in compliance with
    7      the License.  You may obtain a copy of the License at
    8   
    9          http://www.apache.org/licenses/LICENSE-2.0
   10   
   11      Unless required by applicable law or agreed to in writing, software
   12      distributed under the License is distributed on an "AS IS" BASIS,
   13      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   14      See the License for the specific language governing permissions and
   15      limitations under the License.
   16   ==================================================================== */
   17   
   18   package org.apache.poi.hssf.record;
   19   
   20   import org.apache.poi.ddf;
   21   import org.apache.poi.hssf.usermodel;
   22   import org.apache.poi.hssf.model.AbstractShape;
   23   import org.apache.poi.hssf.model.TextboxShape;
   24   import org.apache.poi.hssf.model.DrawingManager2;
   25   import org.apache.poi.hssf.model.ConvertAnchor;
   26   import org.apache.poi.hssf.model.CommentShape;
   27   import org.apache.poi.util.POILogFactory;
   28   import org.apache.poi.util.POILogger;
   29   
   30   import java.util;
   31   
   32   /**
   33    * This class is used to aggregate the MSODRAWING and OBJ record
   34    * combinations.  This is necessary due to the bizare way in which
   35    * these records are serialized.  What happens is that you get a
   36    * combination of MSODRAWING -> OBJ -> MSODRAWING -> OBJ records
   37    * but the escher records are serialized _across_ the MSODRAWING
   38    * records.
   39    * <p>
   40    * It gets even worse when you start looking at TXO records.
   41    * <p>
   42    * So what we do with this class is aggregate lazily.  That is
   43    * we don't aggregate the MSODRAWING -> OBJ records unless we
   44    * need to modify them.
   45    *
   46    *
   47    * @author Glen Stampoultzis (glens at apache.org)
   48    */
   49   public class EscherAggregate extends AbstractEscherHolderRecord
   50   {
   51       public static final short sid = 9876;
   52       private static POILogger log = POILogFactory.getLogger(EscherAggregate.class);
   53   
   54       public static final short ST_MIN = (short) 0;
   55       public static final short ST_NOT_PRIMATIVE = ST_MIN;
   56       public static final short ST_RECTANGLE = (short) 1;
   57       public static final short ST_ROUNDRECTANGLE = (short) 2;
   58       public static final short ST_ELLIPSE = (short) 3;
   59       public static final short ST_DIAMOND = (short) 4;
   60       public static final short ST_ISOCELESTRIANGLE = (short) 5;
   61       public static final short ST_RIGHTTRIANGLE = (short) 6;
   62       public static final short ST_PARALLELOGRAM = (short) 7;
   63       public static final short ST_TRAPEZOID = (short) 8;
   64       public static final short ST_HEXAGON = (short) 9;
   65       public static final short ST_OCTAGON = (short) 10;
   66       public static final short ST_PLUS = (short) 11;
   67       public static final short ST_STAR = (short) 12;
   68       public static final short ST_ARROW = (short) 13;
   69       public static final short ST_THICKARROW = (short) 14;
   70       public static final short ST_HOMEPLATE = (short) 15;
   71       public static final short ST_CUBE = (short) 16;
   72       public static final short ST_BALLOON = (short) 17;
   73       public static final short ST_SEAL = (short) 18;
   74       public static final short ST_ARC = (short) 19;
   75       public static final short ST_LINE = (short) 20;
   76       public static final short ST_PLAQUE = (short) 21;
   77       public static final short ST_CAN = (short) 22;
   78       public static final short ST_DONUT = (short) 23;
   79       public static final short ST_TEXTSIMPLE = (short) 24;
   80       public static final short ST_TEXTOCTAGON = (short) 25;
   81       public static final short ST_TEXTHEXAGON = (short) 26;
   82       public static final short ST_TEXTCURVE = (short) 27;
   83       public static final short ST_TEXTWAVE = (short) 28;
   84       public static final short ST_TEXTRING = (short) 29;
   85       public static final short ST_TEXTONCURVE = (short) 30;
   86       public static final short ST_TEXTONRING = (short) 31;
   87       public static final short ST_STRAIGHTCONNECTOR1 = (short) 32;
   88       public static final short ST_BENTCONNECTOR2 = (short) 33;
   89       public static final short ST_BENTCONNECTOR3 = (short) 34;
   90       public static final short ST_BENTCONNECTOR4 = (short) 35;
   91       public static final short ST_BENTCONNECTOR5 = (short) 36;
   92       public static final short ST_CURVEDCONNECTOR2 = (short) 37;
   93       public static final short ST_CURVEDCONNECTOR3 = (short) 38;
   94       public static final short ST_CURVEDCONNECTOR4 = (short) 39;
   95       public static final short ST_CURVEDCONNECTOR5 = (short) 40;
   96       public static final short ST_CALLOUT1 = (short) 41;
   97       public static final short ST_CALLOUT2 = (short) 42;
   98       public static final short ST_CALLOUT3 = (short) 43;
   99       public static final short ST_ACCENTCALLOUT1 = (short) 44;
  100       public static final short ST_ACCENTCALLOUT2 = (short) 45;
  101       public static final short ST_ACCENTCALLOUT3 = (short) 46;
  102       public static final short ST_BORDERCALLOUT1 = (short) 47;
  103       public static final short ST_BORDERCALLOUT2 = (short) 48;
  104       public static final short ST_BORDERCALLOUT3 = (short) 49;
  105       public static final short ST_ACCENTBORDERCALLOUT1 = (short) 50;
  106       public static final short ST_ACCENTBORDERCALLOUT2 = (short) 51;
  107       public static final short ST_ACCENTBORDERCALLOUT3 = (short) 52;
  108       public static final short ST_RIBBON = (short) 53;
  109       public static final short ST_RIBBON2 = (short) 54;
  110       public static final short ST_CHEVRON = (short) 55;
  111       public static final short ST_PENTAGON = (short) 56;
  112       public static final short ST_NOSMOKING = (short) 57;
  113       public static final short ST_SEAL8 = (short) 58;
  114       public static final short ST_SEAL16 = (short) 59;
  115       public static final short ST_SEAL32 = (short) 60;
  116       public static final short ST_WEDGERECTCALLOUT = (short) 61;
  117       public static final short ST_WEDGERRECTCALLOUT = (short) 62;
  118       public static final short ST_WEDGEELLIPSECALLOUT = (short) 63;
  119       public static final short ST_WAVE = (short) 64;
  120       public static final short ST_FOLDEDCORNER = (short) 65;
  121       public static final short ST_LEFTARROW = (short) 66;
  122       public static final short ST_DOWNARROW = (short) 67;
  123       public static final short ST_UPARROW = (short) 68;
  124       public static final short ST_LEFTRIGHTARROW = (short) 69;
  125       public static final short ST_UPDOWNARROW = (short) 70;
  126       public static final short ST_IRREGULARSEAL1 = (short) 71;
  127       public static final short ST_IRREGULARSEAL2 = (short) 72;
  128       public static final short ST_LIGHTNINGBOLT = (short) 73;
  129       public static final short ST_HEART = (short) 74;
  130       public static final short ST_PICTUREFRAME = (short) 75;
  131       public static final short ST_QUADARROW = (short) 76;
  132       public static final short ST_LEFTARROWCALLOUT = (short) 77;
  133       public static final short ST_RIGHTARROWCALLOUT = (short) 78;
  134       public static final short ST_UPARROWCALLOUT = (short) 79;
  135       public static final short ST_DOWNARROWCALLOUT = (short) 80;
  136       public static final short ST_LEFTRIGHTARROWCALLOUT = (short) 81;
  137       public static final short ST_UPDOWNARROWCALLOUT = (short) 82;
  138       public static final short ST_QUADARROWCALLOUT = (short) 83;
  139       public static final short ST_BEVEL = (short) 84;
  140       public static final short ST_LEFTBRACKET = (short) 85;
  141       public static final short ST_RIGHTBRACKET = (short) 86;
  142       public static final short ST_LEFTBRACE = (short) 87;
  143       public static final short ST_RIGHTBRACE = (short) 88;
  144       public static final short ST_LEFTUPARROW = (short) 89;
  145       public static final short ST_BENTUPARROW = (short) 90;
  146       public static final short ST_BENTARROW = (short) 91;
  147       public static final short ST_SEAL24 = (short) 92;
  148       public static final short ST_STRIPEDRIGHTARROW = (short) 93;
  149       public static final short ST_NOTCHEDRIGHTARROW = (short) 94;
  150       public static final short ST_BLOCKARC = (short) 95;
  151       public static final short ST_SMILEYFACE = (short) 96;
  152       public static final short ST_VERTICALSCROLL = (short) 97;
  153       public static final short ST_HORIZONTALSCROLL = (short) 98;
  154       public static final short ST_CIRCULARARROW = (short) 99;
  155       public static final short ST_NOTCHEDCIRCULARARROW = (short) 100;
  156       public static final short ST_UTURNARROW = (short) 101;
  157       public static final short ST_CURVEDRIGHTARROW = (short) 102;
  158       public static final short ST_CURVEDLEFTARROW = (short) 103;
  159       public static final short ST_CURVEDUPARROW = (short) 104;
  160       public static final short ST_CURVEDDOWNARROW = (short) 105;
  161       public static final short ST_CLOUDCALLOUT = (short) 106;
  162       public static final short ST_ELLIPSERIBBON = (short) 107;
  163       public static final short ST_ELLIPSERIBBON2 = (short) 108;
  164       public static final short ST_FLOWCHARTPROCESS = (short) 109;
  165       public static final short ST_FLOWCHARTDECISION = (short) 110;
  166       public static final short ST_FLOWCHARTINPUTOUTPUT = (short) 111;
  167       public static final short ST_FLOWCHARTPREDEFINEDPROCESS = (short) 112;
  168       public static final short ST_FLOWCHARTINTERNALSTORAGE = (short) 113;
  169       public static final short ST_FLOWCHARTDOCUMENT = (short) 114;
  170       public static final short ST_FLOWCHARTMULTIDOCUMENT = (short) 115;
  171       public static final short ST_FLOWCHARTTERMINATOR = (short) 116;
  172       public static final short ST_FLOWCHARTPREPARATION = (short) 117;
  173       public static final short ST_FLOWCHARTMANUALINPUT = (short) 118;
  174       public static final short ST_FLOWCHARTMANUALOPERATION = (short) 119;
  175       public static final short ST_FLOWCHARTCONNECTOR = (short) 120;
  176       public static final short ST_FLOWCHARTPUNCHEDCARD = (short) 121;
  177       public static final short ST_FLOWCHARTPUNCHEDTAPE = (short) 122;
  178       public static final short ST_FLOWCHARTSUMMINGJUNCTION = (short) 123;
  179       public static final short ST_FLOWCHARTOR = (short) 124;
  180       public static final short ST_FLOWCHARTCOLLATE = (short) 125;
  181       public static final short ST_FLOWCHARTSORT = (short) 126;
  182       public static final short ST_FLOWCHARTEXTRACT = (short) 127;
  183       public static final short ST_FLOWCHARTMERGE = (short) 128;
  184       public static final short ST_FLOWCHARTOFFLINESTORAGE = (short) 129;
  185       public static final short ST_FLOWCHARTONLINESTORAGE = (short) 130;
  186       public static final short ST_FLOWCHARTMAGNETICTAPE = (short) 131;
  187       public static final short ST_FLOWCHARTMAGNETICDISK = (short) 132;
  188       public static final short ST_FLOWCHARTMAGNETICDRUM = (short) 133;
  189       public static final short ST_FLOWCHARTDISPLAY = (short) 134;
  190       public static final short ST_FLOWCHARTDELAY = (short) 135;
  191       public static final short ST_TEXTPLAINTEXT = (short) 136;
  192       public static final short ST_TEXTSTOP = (short) 137;
  193       public static final short ST_TEXTTRIANGLE = (short) 138;
  194       public static final short ST_TEXTTRIANGLEINVERTED = (short) 139;
  195       public static final short ST_TEXTCHEVRON = (short) 140;
  196       public static final short ST_TEXTCHEVRONINVERTED = (short) 141;
  197       public static final short ST_TEXTRINGINSIDE = (short) 142;
  198       public static final short ST_TEXTRINGOUTSIDE = (short) 143;
  199       public static final short ST_TEXTARCHUPCURVE = (short) 144;
  200       public static final short ST_TEXTARCHDOWNCURVE = (short) 145;
  201       public static final short ST_TEXTCIRCLECURVE = (short) 146;
  202       public static final short ST_TEXTBUTTONCURVE = (short) 147;
  203       public static final short ST_TEXTARCHUPPOUR = (short) 148;
  204       public static final short ST_TEXTARCHDOWNPOUR = (short) 149;
  205       public static final short ST_TEXTCIRCLEPOUR = (short) 150;
  206       public static final short ST_TEXTBUTTONPOUR = (short) 151;
  207       public static final short ST_TEXTCURVEUP = (short) 152;
  208       public static final short ST_TEXTCURVEDOWN = (short) 153;
  209       public static final short ST_TEXTCASCADEUP = (short) 154;
  210       public static final short ST_TEXTCASCADEDOWN = (short) 155;
  211       public static final short ST_TEXTWAVE1 = (short) 156;
  212       public static final short ST_TEXTWAVE2 = (short) 157;
  213       public static final short ST_TEXTWAVE3 = (short) 158;
  214       public static final short ST_TEXTWAVE4 = (short) 159;
  215       public static final short ST_TEXTINFLATE = (short) 160;
  216       public static final short ST_TEXTDEFLATE = (short) 161;
  217       public static final short ST_TEXTINFLATEBOTTOM = (short) 162;
  218       public static final short ST_TEXTDEFLATEBOTTOM = (short) 163;
  219       public static final short ST_TEXTINFLATETOP = (short) 164;
  220       public static final short ST_TEXTDEFLATETOP = (short) 165;
  221       public static final short ST_TEXTDEFLATEINFLATE = (short) 166;
  222       public static final short ST_TEXTDEFLATEINFLATEDEFLATE = (short) 167;
  223       public static final short ST_TEXTFADERIGHT = (short) 168;
  224       public static final short ST_TEXTFADELEFT = (short) 169;
  225       public static final short ST_TEXTFADEUP = (short) 170;
  226       public static final short ST_TEXTFADEDOWN = (short) 171;
  227       public static final short ST_TEXTSLANTUP = (short) 172;
  228       public static final short ST_TEXTSLANTDOWN = (short) 173;
  229       public static final short ST_TEXTCANUP = (short) 174;
  230       public static final short ST_TEXTCANDOWN = (short) 175;
  231       public static final short ST_FLOWCHARTALTERNATEPROCESS = (short) 176;
  232       public static final short ST_FLOWCHARTOFFPAGECONNECTOR = (short) 177;
  233       public static final short ST_CALLOUT90 = (short) 178;
  234       public static final short ST_ACCENTCALLOUT90 = (short) 179;
  235       public static final short ST_BORDERCALLOUT90 = (short) 180;
  236       public static final short ST_ACCENTBORDERCALLOUT90 = (short) 181;
  237       public static final short ST_LEFTRIGHTUPARROW = (short) 182;
  238       public static final short ST_SUN = (short) 183;
  239       public static final short ST_MOON = (short) 184;
  240       public static final short ST_BRACKETPAIR = (short) 185;
  241       public static final short ST_BRACEPAIR = (short) 186;
  242       public static final short ST_SEAL4 = (short) 187;
  243       public static final short ST_DOUBLEWAVE = (short) 188;
  244       public static final short ST_ACTIONBUTTONBLANK = (short) 189;
  245       public static final short ST_ACTIONBUTTONHOME = (short) 190;
  246       public static final short ST_ACTIONBUTTONHELP = (short) 191;
  247       public static final short ST_ACTIONBUTTONINFORMATION = (short) 192;
  248       public static final short ST_ACTIONBUTTONFORWARDNEXT = (short) 193;
  249       public static final short ST_ACTIONBUTTONBACKPREVIOUS = (short) 194;
  250       public static final short ST_ACTIONBUTTONEND = (short) 195;
  251       public static final short ST_ACTIONBUTTONBEGINNING = (short) 196;
  252       public static final short ST_ACTIONBUTTONRETURN = (short) 197;
  253       public static final short ST_ACTIONBUTTONDOCUMENT = (short) 198;
  254       public static final short ST_ACTIONBUTTONSOUND = (short) 199;
  255       public static final short ST_ACTIONBUTTONMOVIE = (short) 200;
  256       public static final short ST_HOSTCONTROL = (short) 201;
  257       public static final short ST_TEXTBOX = (short) 202;
  258       public static final short ST_NIL = (short) 0x0FFF;
  259   
  260       protected HSSFPatriarch patriarch;
  261   
  262       /** Maps shape container objects to their OBJ records */
  263       private Map shapeToObj = new HashMap();
  264       private DrawingManager2 drawingManager;
  265       private short drawingGroupId;
  266   
  267       /**
  268        * list of "tail" records that need to be serialized after all drawing group records
  269        */
  270       private List tailRec = new ArrayList();
  271   
  272       public EscherAggregate( DrawingManager2 drawingManager )
  273       {
  274           this.drawingManager = drawingManager;
  275       }
  276   
  277       /**
  278        * @return  Returns the current sid.
  279        */
  280       public short getSid()
  281       {
  282           return sid;
  283       }
  284   
  285       /**
  286        * Unused since this is an aggregate record.  Use createAggregate().
  287        *
  288        * @see #createAggregate
  289        */
  290       protected void fillFields( byte[] data, short size, int offset )
  291       {
  292           throw new IllegalStateException( "Should not reach here" );
  293       }
  294   
  295       /**
  296        * Calculates the string representation of this record.  This is
  297        * simply a dump of all the records.
  298        */
  299       public String toString()
  300       {
  301           String nl = System.getProperty( "line.separtor" );
  302   
  303           StringBuffer result = new StringBuffer();
  304           result.append( '[' ).append( getRecordName() ).append( ']' + nl );
  305           for ( Iterator iterator = getEscherRecords().iterator(); iterator.hasNext(); )
  306           {
  307               EscherRecord escherRecord = (EscherRecord) iterator.next();
  308               result.append( escherRecord.toString() );
  309           }
  310           result.append( "[/" ).append( getRecordName() ).append( ']' + nl );
  311   
  312           return result.toString();
  313       }
  314   
  315       /**
  316        * Collapses the drawing records into an aggregate.
  317        */
  318       public static EscherAggregate createAggregate( List records, int locFirstDrawingRecord, DrawingManager2 drawingManager )
  319       {
  320           // Keep track of any shape records created so we can match them back to the object id's.
  321           // Textbox objects are also treated as shape objects.
  322           final List shapeRecords = new ArrayList();
  323           EscherRecordFactory recordFactory = new DefaultEscherRecordFactory()
  324           {
  325               public EscherRecord createRecord( byte[] data, int offset )
  326               {
  327                   EscherRecord r = super.createRecord( data, offset );
  328                   if ( r.getRecordId() == EscherClientDataRecord.RECORD_ID || r.getRecordId() == EscherTextboxRecord.RECORD_ID )
  329                   {
  330                       shapeRecords.add( r );
  331                   }
  332                   return r;
  333               }
  334           };
  335   
  336           // Calculate the size of the buffer
  337           EscherAggregate agg = new EscherAggregate(drawingManager);
  338           int loc = locFirstDrawingRecord;
  339           int dataSize = 0;
  340           while ( loc + 1 < records.size()
  341                   && sid( records, loc ) == DrawingRecord.sid
  342                   && isObjectRecord( records, loc + 1 ) )
  343           {
  344               dataSize += ( (DrawingRecord) records.get( loc ) ).getData().length;
  345               loc += 2;
  346           }
  347   
  348           // Create one big buffer
  349           byte buffer[] = new byte[dataSize];
  350           int offset = 0;
  351           loc = locFirstDrawingRecord;
  352           while ( loc + 1 < records.size()
  353                   && sid( records, loc ) == DrawingRecord.sid
  354                   && isObjectRecord( records, loc + 1 ) )
  355           {
  356               DrawingRecord drawingRecord = (DrawingRecord) records.get( loc );
  357               System.arraycopy( drawingRecord.getData(), 0, buffer, offset, drawingRecord.getData().length );
  358               offset += drawingRecord.getData().length;
  359               loc += 2;
  360           }
  361   
  362           // Decode the shapes
  363           //        agg.escherRecords = new ArrayList();
  364           int pos = 0;
  365           while ( pos < dataSize )
  366           {
  367               EscherRecord r = recordFactory.createRecord( buffer, pos );
  368               int bytesRead = r.fillFields( buffer, pos, recordFactory );
  369               agg.addEscherRecord( r );
  370               pos += bytesRead;
  371           }
  372   
  373           // Associate the object records with the shapes
  374           loc = locFirstDrawingRecord;
  375           int shapeIndex = 0;
  376           agg.shapeToObj = new HashMap();
  377           while ( loc + 1 < records.size()
  378                   && sid( records, loc ) == DrawingRecord.sid
  379                   && isObjectRecord( records, loc + 1 ) )
  380           {
  381               Record objRecord = (Record) records.get( loc + 1 );
  382               agg.shapeToObj.put( shapeRecords.get( shapeIndex++ ), objRecord );
  383               loc += 2;
  384           }
  385   
  386           return agg;
  387   
  388       }
  389   
  390       /**
  391        * Serializes this aggregate to a byte array.  Since this is an aggregate
  392        * record it will effectively serialize the aggregated records.
  393        *
  394        * @param offset    The offset into the start of the array.
  395        * @param data      The byte array to serialize to.
  396        * @return          The number of bytes serialized.
  397        */
  398       public int serialize( int offset, byte[] data )
  399       {
  400           convertUserModelToRecords();
  401   
  402           // Determine buffer size
  403           List records = getEscherRecords();
  404           int size = getEscherRecordSize( records );
  405           byte[] buffer = new byte[size];
  406   
  407   
  408           // Serialize escher records into one big data structure and keep note of ending offsets.
  409           final List spEndingOffsets = new ArrayList();
  410           final List shapes = new ArrayList();
  411           int pos = 0;
  412           for ( Iterator iterator = records.iterator(); iterator.hasNext(); )
  413           {
  414               EscherRecord e = (EscherRecord) iterator.next();
  415               pos += e.serialize( pos, buffer, new EscherSerializationListener()
  416               {
  417                   public void beforeRecordSerialize( int offset, short recordId, EscherRecord record )
  418                   {
  419                   }
  420   
  421                   public void afterRecordSerialize( int offset, short recordId, int size, EscherRecord record )
  422                   {
  423                       if ( recordId == EscherClientDataRecord.RECORD_ID || recordId == EscherTextboxRecord.RECORD_ID )
  424                       {
  425                           spEndingOffsets.add( new Integer( offset ) );
  426                           shapes.add( record );
  427                       }
  428                   }
  429               } );
  430           }
  431           // todo: fix this
  432           shapes.add( 0, null );
  433           spEndingOffsets.add( 0, null );
  434   
  435           // Split escher records into separate MSODRAWING and OBJ, TXO records.  (We don't break on
  436           // the first one because it's the patriach).
  437           pos = offset;
  438           for ( int i = 1; i < shapes.size(); i++ )
  439           {
  440               int endOffset = ( (Integer) spEndingOffsets.get( i ) ).intValue() - 1;
  441               int startOffset;
  442               if ( i == 1 )
  443                   startOffset = 0;
  444               else
  445                   startOffset = ( (Integer) spEndingOffsets.get( i - 1 ) ).intValue();
  446   
  447               // Create and write a new MSODRAWING record
  448               DrawingRecord drawing = new DrawingRecord();
  449               byte[] drawingData = new byte[endOffset - startOffset + 1];
  450               System.arraycopy( buffer, startOffset, drawingData, 0, drawingData.length );
  451               drawing.setData( drawingData );
  452               int temp = drawing.serialize( pos, data );
  453               pos += temp;
  454   
  455               // Write the matching OBJ record
  456               Record obj = (Record) shapeToObj.get( shapes.get( i ) );
  457               temp = obj.serialize( pos, data );
  458               pos += temp;
  459   
  460           }
  461   
  462           // write records that need to be serialized after all drawing group records
  463           for ( int i = 0; i < tailRec.size(); i++ )
  464           {
  465               Record rec = (Record)tailRec.get(i);
  466               pos += rec.serialize( pos, data );
  467           }
  468   
  469           int bytesWritten = pos - offset;
  470           if ( bytesWritten != getRecordSize() )
  471               throw new RecordFormatException( bytesWritten + " bytes written but getRecordSize() reports " + getRecordSize() );
  472           return bytesWritten;
  473       }
  474   
  475       /**
  476        * How many bytes do the raw escher records contain.
  477        * @param records   List of escher records
  478        * @return  the number of bytes
  479        */
  480       private int getEscherRecordSize( List records )
  481       {
  482           int size = 0;
  483           for ( Iterator iterator = records.iterator(); iterator.hasNext(); )
  484               size += ( (EscherRecord) iterator.next() ).getRecordSize();
  485           return size;
  486       }
  487   
  488       /**
  489        * The number of bytes required to serialize this record.
  490        */
  491       public int getRecordSize()
  492       {
  493           convertUserModelToRecords();
  494           List records = getEscherRecords();
  495           int rawEscherSize = getEscherRecordSize( records );
  496           int drawingRecordSize = rawEscherSize + ( shapeToObj.size() ) * 4;
  497           int objRecordSize = 0;
  498           for ( Iterator iterator = shapeToObj.values().iterator(); iterator.hasNext(); )
  499           {
  500               Record r = (Record) iterator.next();
  501               objRecordSize += r.getRecordSize();
  502           }
  503           int tailRecordSize = 0;
  504           for ( Iterator iterator = tailRec.iterator(); iterator.hasNext(); )
  505           {
  506               Record r = (Record) iterator.next();
  507               tailRecordSize += r.getRecordSize();
  508           }
  509           return drawingRecordSize + objRecordSize + tailRecordSize;
  510       }
  511   
  512       /**
  513        * Associates an escher record to an OBJ record or a TXO record.
  514        */
  515       public Object assoicateShapeToObjRecord( EscherRecord r, Record objRecord )
  516       {
  517           return shapeToObj.put( r, objRecord );
  518       }
  519   
  520       public HSSFPatriarch getPatriarch()
  521       {
  522           return patriarch;
  523       }
  524   
  525       public void setPatriarch( HSSFPatriarch patriarch )
  526       {
  527           this.patriarch = patriarch;
  528       }
  529       
  530       /**
  531        * Converts the Records into UserModel
  532        *  objects on the bound HSSFPatriarch
  533        */
  534       public void convertRecordsToUserModel() {
  535       	if(patriarch == null) {
  536       		throw new IllegalStateException("Must call setPatriarch() first");
  537       	}
  538       	
  539       	// The top level container ought to have
  540       	//  the DgRecord and the container of one container
  541       	//  per shape group (patriach overall first)
  542       	EscherContainerRecord topContainer =
  543       		(EscherContainerRecord)getEscherContainer();
  544       	if(topContainer == null) {
  545       		return;
  546       	}
  547       	topContainer = (EscherContainerRecord) 
  548       		topContainer.getChildContainers().get(0);
  549       	
  550       	List tcc = topContainer.getChildContainers();
  551       	if(tcc.size() == 0) {
  552       		throw new IllegalStateException("No child escher containers at the point that should hold the patriach data, and one container per top level shape!");
  553       	}
  554       	
  555       	// First up, get the patriach position
  556       	// This is in the first EscherSpgrRecord, in
  557       	//  the first container, with a EscherSRecord too
  558       	EscherContainerRecord patriachContainer =
  559       		(EscherContainerRecord)tcc.get(0);
  560       	EscherSpgrRecord spgr = null;
  561       	for(Iterator it = patriachContainer.getChildRecords().iterator(); it.hasNext();) {
  562       		EscherRecord r = (EscherRecord)it.next();
  563       		if(r instanceof EscherSpgrRecord) {
  564       			spgr = (EscherSpgrRecord)r;
  565       			break;
  566       		}
  567       	}
  568       	if(spgr != null) {
  569       		patriarch.setCoordinates(
  570       				spgr.getRectX1(), spgr.getRectY1(),
  571       				spgr.getRectX2(), spgr.getRectY2()
  572       		);
  573       	}
  574       	
  575       	// Now process the containers for each group
  576       	//  and objects
  577       	for(int i=1; i<tcc.size(); i++) {
  578       		EscherContainerRecord shapeContainer =
  579       			(EscherContainerRecord)tcc.get(i);
  580       		//System.err.println("\n\n*****\n\n");
  581       		//System.err.println(shapeContainer);
  582       		
  583       		// Could be a group, or a base object
  584       		if(shapeContainer.getChildRecords().size() == 1 &&
  585       				shapeContainer.getChildContainers().size() == 1) {
  586       			// Group
  587       			HSSFShapeGroup group =
  588       				new HSSFShapeGroup(null, new HSSFClientAnchor());
  589       			patriarch.getChildren().add(group);
  590       			
  591       			EscherContainerRecord groupContainer =
  592       				(EscherContainerRecord)shapeContainer.getChild(0);
  593       			convertRecordsToUserModel(groupContainer, group);
  594       		} else if(shapeContainer.hasChildOfType((short)0xF00D)) {
  595       			// TextBox
  596       			HSSFTextbox box =
  597       				new HSSFTextbox(null, new HSSFClientAnchor());
  598       			patriarch.getChildren().add(box);
  599       			
  600       			convertRecordsToUserModel(shapeContainer, box);
  601       		} else if(shapeContainer.hasChildOfType((short)0xF011)) {
  602       			// Not yet supporting EscherClientDataRecord stuff
  603       		} else {
  604       			// Base level
  605       			convertRecordsToUserModel(shapeContainer, patriarch);
  606       		}
  607       	}
  608       	
  609       	// Now, clear any trace of what records make up
  610       	//  the patriarch
  611       	// Otherwise, everything will go horribly wrong
  612       	//  when we try to write out again....
  613   //    	clearEscherRecords();
  614       	drawingManager.getDgg().setFileIdClusters(new EscherDggRecord.FileIdCluster[0]);
  615   
  616       	// TODO: Support converting our records
  617       	//  back into shapes
  618       	log.log(POILogger.WARN, "Not processing objects into Patriarch!");
  619       }
  620       
  621       private void convertRecordsToUserModel(EscherContainerRecord shapeContainer, Object model) {
  622       	for(Iterator it = shapeContainer.getChildRecords().iterator(); it.hasNext();) {
  623       		EscherRecord r = (EscherRecord)it.next();
  624       		if(r instanceof EscherSpgrRecord) {
  625       			// This may be overriden by a later EscherClientAnchorRecord
  626       			EscherSpgrRecord spgr = (EscherSpgrRecord)r;
  627       			
  628       			if(model instanceof HSSFShapeGroup) {
  629       				HSSFShapeGroup g = (HSSFShapeGroup)model;
  630       				g.setCoordinates(
  631       						spgr.getRectX1(), spgr.getRectY1(),
  632       						spgr.getRectX2(), spgr.getRectY2()
  633       				);
  634       			} else {
  635       				throw new IllegalStateException("Got top level anchor but not processing a group");
  636       			}
  637       		} 
  638       		else if(r instanceof EscherClientAnchorRecord) {
  639       			EscherClientAnchorRecord car = (EscherClientAnchorRecord)r;
  640       			
  641       			if(model instanceof HSSFShape) {
  642       				HSSFShape g = (HSSFShape)model;
  643       				g.getAnchor().setDx1(car.getDx1());
  644       				g.getAnchor().setDx2(car.getDx2());
  645       				g.getAnchor().setDy1(car.getDy1());
  646       				g.getAnchor().setDy2(car.getDy2());
  647       			} else {
  648       				throw new IllegalStateException("Got top level anchor but not processing a group or shape");
  649       			}
  650       		}
  651       		else if(r instanceof EscherTextboxRecord) {
  652       			EscherTextboxRecord tbr = (EscherTextboxRecord)r;
  653       			
  654       			// Also need to find the TextObjectRecord too
  655       			// TODO
  656       		}
  657       		else if(r instanceof EscherSpRecord) {
  658       			// Use flags if needed
  659       		}
  660       		else if(r instanceof EscherOptRecord) {
  661       			// Use properties if needed
  662       		}
  663       		else {
  664       			//System.err.println(r);
  665       		}
  666       	}
  667       }
  668       
  669       public void clear()
  670       {
  671           clearEscherRecords();
  672           shapeToObj.clear();
  673   //        lastShapeId = 1024;
  674       }
  675   
  676       protected String getRecordName()
  677       {
  678           return "ESCHERAGGREGATE";
  679       }
  680   
  681       // =============== Private methods ========================
  682   
  683       private static boolean isObjectRecord( List records, int loc )
  684       {
  685           return sid( records, loc ) == ObjRecord.sid || sid( records, loc ) == TextObjectRecord.sid;
  686       }
  687   
  688       private void convertUserModelToRecords()
  689       {
  690           if ( patriarch != null )
  691           {
  692               shapeToObj.clear();
  693               tailRec.clear();
  694               clearEscherRecords();
  695               if ( patriarch.getChildren().size() != 0 )
  696               {
  697                   convertPatriarch( patriarch );
  698                   EscherContainerRecord dgContainer = (EscherContainerRecord) getEscherRecord( 0 );
  699                   EscherContainerRecord spgrContainer = null;
  700                   for ( int i = 0; i < dgContainer.getChildRecords().size(); i++ )
  701                       if ( dgContainer.getChild( i ).getRecordId() == EscherContainerRecord.SPGR_CONTAINER )
  702                           spgrContainer = (EscherContainerRecord) dgContainer.getChild( i );
  703                   convertShapes( patriarch, spgrContainer, shapeToObj );
  704   
  705                   patriarch = null;
  706               }
  707           }
  708       }
  709   
  710       private void convertShapes( HSSFShapeContainer parent, EscherContainerRecord escherParent, Map shapeToObj )
  711       {
  712           if ( escherParent == null ) throw new IllegalArgumentException( "Parent record required" );
  713   
  714           List shapes = parent.getChildren();
  715           for ( Iterator iterator = shapes.iterator(); iterator.hasNext(); )
  716           {
  717               HSSFShape shape = (HSSFShape) iterator.next();
  718               if ( shape instanceof HSSFShapeGroup )
  719               {
  720                   convertGroup( (HSSFShapeGroup) shape, escherParent, shapeToObj );
  721               }
  722               else
  723               {
  724                   AbstractShape shapeModel = AbstractShape.createShape(
  725                           shape,
  726                           drawingManager.allocateShapeId(drawingGroupId) );
  727                   shapeToObj.put( findClientData( shapeModel.getSpContainer() ), shapeModel.getObjRecord() );
  728                   if ( shapeModel instanceof TextboxShape )
  729                   {
  730                       EscherRecord escherTextbox = ( (TextboxShape) shapeModel ).getEscherTextbox();
  731                       shapeToObj.put( escherTextbox, ( (TextboxShape) shapeModel ).getTextObjectRecord() );
  732                       //                    escherParent.addChildRecord(escherTextbox);
  733                       
  734                       if ( shapeModel instanceof CommentShape ){
  735                           CommentShape comment = (CommentShape)shapeModel;
  736                           tailRec.add(comment.getNoteRecord());
  737                       }
  738   
  739                   }
  740                   escherParent.addChildRecord( shapeModel.getSpContainer() );
  741               }
  742           }
  743   //        drawingManager.newCluster( (short)1 );
  744   //        drawingManager.newCluster( (short)2 );
  745   
  746       }
  747   
  748       private void convertGroup( HSSFShapeGroup shape, EscherContainerRecord escherParent, Map shapeToObj )
  749       {
  750           EscherContainerRecord spgrContainer = new EscherContainerRecord();
  751           EscherContainerRecord spContainer = new EscherContainerRecord();
  752           EscherSpgrRecord spgr = new EscherSpgrRecord();
  753           EscherSpRecord sp = new EscherSpRecord();
  754           EscherOptRecord opt = new EscherOptRecord();
  755           EscherRecord anchor;
  756           EscherClientDataRecord clientData = new EscherClientDataRecord();
  757   
  758           spgrContainer.setRecordId( EscherContainerRecord.SPGR_CONTAINER );
  759           spgrContainer.setOptions( (short) 0x000F );
  760           spContainer.setRecordId( EscherContainerRecord.SP_CONTAINER );
  761           spContainer.setOptions( (short) 0x000F );
  762           spgr.setRecordId( EscherSpgrRecord.RECORD_ID );
  763           spgr.setOptions( (short) 0x0001 );
  764           spgr.setRectX1( shape.getX1() );
  765           spgr.setRectY1( shape.getY1() );
  766           spgr.setRectX2( shape.getX2() );
  767           spgr.setRectY2( shape.getY2() );
  768           sp.setRecordId( EscherSpRecord.RECORD_ID );
  769           sp.setOptions( (short) 0x0002 );
  770           int shapeId = drawingManager.allocateShapeId(drawingGroupId);
  771           sp.setShapeId( shapeId );
  772           if (shape.getAnchor() instanceof HSSFClientAnchor)
  773               sp.setFlags( EscherSpRecord.FLAG_GROUP | EscherSpRecord.FLAG_HAVEANCHOR );
  774           else
  775               sp.setFlags( EscherSpRecord.FLAG_GROUP | EscherSpRecord.FLAG_HAVEANCHOR | EscherSpRecord.FLAG_CHILD );
  776           opt.setRecordId( EscherOptRecord.RECORD_ID );
  777           opt.setOptions( (short) 0x0023 );
  778           opt.addEscherProperty( new EscherBoolProperty( EscherProperties.PROTECTION__LOCKAGAINSTGROUPING, 0x00040004 ) );
  779           opt.addEscherProperty( new EscherBoolProperty( EscherProperties.GROUPSHAPE__PRINT, 0x00080000 ) );
  780   
  781           anchor = ConvertAnchor.createAnchor( shape.getAnchor() );
  782   //        clientAnchor.setCol1( ( (HSSFClientAnchor) shape.getAnchor() ).getCol1() );
  783   //        clientAnchor.setRow1( (short) ( (HSSFClientAnchor) shape.getAnchor() ).getRow1() );
  784   //        clientAnchor.setDx1( (short) shape.getAnchor().getDx1() );
  785   //        clientAnchor.setDy1( (short) shape.getAnchor().getDy1() );
  786   //        clientAnchor.setCol2( ( (HSSFClientAnchor) shape.getAnchor() ).getCol2() );
  787   //        clientAnchor.setRow2( (short) ( (HSSFClientAnchor) shape.getAnchor() ).getRow2() );
  788   //        clientAnchor.setDx2( (short) shape.getAnchor().getDx2() );
  789   //        clientAnchor.setDy2( (short) shape.getAnchor().getDy2() );
  790           clientData.setRecordId( EscherClientDataRecord.RECORD_ID );
  791           clientData.setOptions( (short) 0x0000 );
  792   
  793           spgrContainer.addChildRecord( spContainer );
  794           spContainer.addChildRecord( spgr );
  795           spContainer.addChildRecord( sp );
  796           spContainer.addChildRecord( opt );
  797           spContainer.addChildRecord( anchor );
  798           spContainer.addChildRecord( clientData );
  799   
  800           ObjRecord obj = new ObjRecord();
  801           CommonObjectDataSubRecord cmo = new CommonObjectDataSubRecord();
  802           cmo.setObjectType( CommonObjectDataSubRecord.OBJECT_TYPE_GROUP );
  803           cmo.setObjectId( (short) ( shapeId ) );
  804           cmo.setLocked( true );
  805           cmo.setPrintable( true );
  806           cmo.setAutofill( true );
  807           cmo.setAutoline( true );
  808           GroupMarkerSubRecord gmo = new GroupMarkerSubRecord();
  809           EndSubRecord end = new EndSubRecord();
  810           obj.addSubRecord( cmo );
  811           obj.addSubRecord( gmo );
  812           obj.addSubRecord( end );
  813           shapeToObj.put( clientData, obj );
  814   
  815           escherParent.addChildRecord( spgrContainer );
  816   
  817           convertShapes( shape, spgrContainer, shapeToObj );
  818   
  819       }
  820   
  821       private EscherRecord findClientData( EscherContainerRecord spContainer )
  822       {
  823           for ( Iterator iterator = spContainer.getChildRecords().iterator(); iterator.hasNext(); )
  824           {
  825               EscherRecord r = (EscherRecord) iterator.next();
  826               if ( r.getRecordId() == EscherClientDataRecord.RECORD_ID )
  827                   return r;
  828           }
  829           throw new IllegalArgumentException( "Can not find client data record" );
  830       }
  831   
  832       private void convertPatriarch( HSSFPatriarch patriarch )
  833       {
  834           EscherContainerRecord dgContainer = new EscherContainerRecord();
  835           EscherDgRecord dg;
  836           EscherContainerRecord spgrContainer = new EscherContainerRecord();
  837           EscherContainerRecord spContainer1 = new EscherContainerRecord();
  838           EscherSpgrRecord spgr = new EscherSpgrRecord();
  839           EscherSpRecord sp1 = new EscherSpRecord();
  840   
  841           dgContainer.setRecordId( EscherContainerRecord.DG_CONTAINER );
  842           dgContainer.setOptions( (short) 0x000F );
  843           dg = drawingManager.createDgRecord();
  844           drawingGroupId = dg.getDrawingGroupId();
  845   //        dg.setOptions( (short) ( drawingId << 4 ) );
  846   //        dg.setNumShapes( getNumberOfShapes( patriarch ) );
  847   //        dg.setLastMSOSPID( 0 );  // populated after all shape id's are assigned.
  848           spgrContainer.setRecordId( EscherContainerRecord.SPGR_CONTAINER );
  849           spgrContainer.setOptions( (short) 0x000F );
  850           spContainer1.setRecordId( EscherContainerRecord.SP_CONTAINER );
  851           spContainer1.setOptions( (short) 0x000F );
  852           spgr.setRecordId( EscherSpgrRecord.RECORD_ID );
  853           spgr.setOptions( (short) 0x0001 );    // version
  854           spgr.setRectX1( patriarch.getX1() );
  855           spgr.setRectY1( patriarch.getY1() );
  856           spgr.setRectX2( patriarch.getX2() );
  857           spgr.setRectY2( patriarch.getY2() );
  858           sp1.setRecordId( EscherSpRecord.RECORD_ID );
  859           sp1.setOptions( (short) 0x0002 );
  860           sp1.setShapeId( drawingManager.allocateShapeId(dg.getDrawingGroupId()) );
  861           sp1.setFlags( EscherSpRecord.FLAG_GROUP | EscherSpRecord.FLAG_PATRIARCH );
  862   
  863           dgContainer.addChildRecord( dg );
  864           dgContainer.addChildRecord( spgrContainer );
  865           spgrContainer.addChildRecord( spContainer1 );
  866           spContainer1.addChildRecord( spgr );
  867           spContainer1.addChildRecord( sp1 );
  868   
  869           addEscherRecord( dgContainer );
  870       }
  871   
  872       /** Retrieve the number of shapes (including the patriarch). */
  873   //    private int getNumberOfShapes( HSSFPatriarch patriarch )
  874   //    {
  875   //        return patriarch.countOfAllChildren();
  876   //    }
  877   
  878       private static short sid( List records, int loc )
  879       {
  880           return ( (Record) records.get( loc ) ).getSid();
  881       }
  882   
  883   
  884   }

Save This Page
Home » poi-src-3.2-FINAL-20081019 » org.apache » poi » hssf » record » [javadoc | source]