]> source.dussan.org Git - poi.git/commitdiff
Make a start on processing shapes on a sheet out of a record. For now, doesn't actual...
authorNick Burch <nick@apache.org>
Wed, 9 Jan 2008 23:21:35 +0000 (23:21 +0000)
committerNick Burch <nick@apache.org>
Wed, 9 Jan 2008 23:21:35 +0000 (23:21 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@610608 13f79535-47bb-0310-9956-ffa450edef68

src/java/org/apache/poi/ddf/EscherOptRecord.java
src/java/org/apache/poi/hssf/model/Sheet.java
src/java/org/apache/poi/hssf/model/Workbook.java
src/java/org/apache/poi/hssf/record/AbstractEscherHolderRecord.java
src/java/org/apache/poi/hssf/record/DrawingGroupRecord.java
src/java/org/apache/poi/hssf/record/EscherAggregate.java
src/java/org/apache/poi/hssf/usermodel/HSSFPatriarch.java
src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java
src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheet.java
src/testcases/org/apache/poi/hssf/usermodel/TestHSSFWorkbook.java

index f18e38f03c3bf34f02238c3d502cf2da67517d5b..d7de48edafc4db8f4772a5b023421f68d083200a 100644 (file)
         
 package org.apache.poi.ddf;
 
-import org.apache.poi.util.LittleEndian;
-import org.apache.poi.util.HexDump;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
 
-import java.util.*;
-import java.io.IOException;
+import org.apache.poi.util.HexDump;
+import org.apache.poi.util.LittleEndian;
 
 /**
  * The opt record is used to store property values for a shape.  It is the key to determining
index c3f93b0a0149f18643bf48a48648212c7f5c38d5..59e5de3246a7a248505f7006fd4184e6acef2faf 100644 (file)
@@ -2663,12 +2663,26 @@ public class Sheet implements Model
        return margins;
     }
 
-    public int aggregateDrawingRecords(DrawingManager2 drawingManager)
+    /**
+     * Finds the DrawingRecord for our sheet, and
+     *  attaches it to the DrawingManager (which knows about
+     *  the overall DrawingGroup for our workbook).
+     * If requested, will create a new DrawRecord
+     *  if none currently exist
+     * @param drawingManager The DrawingManager2 for our workbook
+     * @param createIfMissing Should one be created if missing?
+     */
+    public int aggregateDrawingRecords(DrawingManager2 drawingManager, boolean createIfMissing)
     {
         int loc = findFirstRecordLocBySid(DrawingRecord.sid);
-        boolean noDrawingRecordsFound = loc == -1;
+        boolean noDrawingRecordsFound = (loc == -1);
         if (noDrawingRecordsFound)
         {
+               if(!createIfMissing) {
+                       // None found, and not allowed to add in
+                       return -1;
+               }
+               
             EscherAggregate aggregate = new EscherAggregate( drawingManager );
             loc = findFirstRecordLocBySid(EscherAggregate.sid);
             if (loc == -1)
index 2af77a7814dc23205c9861c84dabef21f22dde72..08c236cda116ac9fcaf131f758c04abea98bf357 100644 (file)
@@ -2165,13 +2165,68 @@ public class Workbook implements Model
       }
       return palette;
     }
+    
+    /**
+     * Finds the primary drawing group, if one already exists
+     */
+    public void findDrawingGroup() {
+       // Need to find a DrawingGroupRecord that
+       //  contains a EscherDggRecord
+       for(Iterator rit = records.iterator(); rit.hasNext();) {
+               Record r = (Record)rit.next();
+               
+               if(r instanceof DrawingGroupRecord) {
+               DrawingGroupRecord dg = (DrawingGroupRecord)r;
+               dg.processChildRecords();
+               
+               EscherContainerRecord cr =
+                       dg.getEscherContainer();
+               if(cr == null) {
+                       continue;
+               }
+               
+               EscherDggRecord dgg = null;
+               for(Iterator it = cr.getChildRecords().iterator(); it.hasNext();) {
+                       Object er = it.next();
+                       if(er instanceof EscherDggRecord) {
+                               dgg = (EscherDggRecord)er;
+                       }
+               }
+               
+               if(dgg != null) {
+                       drawingManager = new DrawingManager2(dgg);
+                       return;
+               }
+               }
+       }
+
+       // Look for the DrawingGroup record
+        int dgLoc = findFirstRecordLocBySid(DrawingGroupRecord.sid);
+        
+       // If there is one, does it have a EscherDggRecord?
+        if(dgLoc != -1) {
+               DrawingGroupRecord dg =
+                       (DrawingGroupRecord)records.get(dgLoc);
+               EscherDggRecord dgg = null;
+               for(Iterator it = dg.getEscherRecords().iterator(); it.hasNext();) {
+                       Object er = it.next();
+                       if(er instanceof EscherDggRecord) {
+                               dgg = (EscherDggRecord)er;
+                       }
+               }
+               
+               if(dgg != null) {
+                       drawingManager = new DrawingManager2(dgg);
+               }
+        }
+    }
 
     /**
-     * Creates a drawing group record.  If it already exists then it's modified.
+     * Creates a primary drawing group record.  If it already 
+     *  exists then it's modified.
      */
     public void createDrawingGroup()
     {
-
         if (drawingManager == null)
         {
             EscherContainerRecord dggContainer = new EscherContainerRecord();
@@ -2235,7 +2290,6 @@ public class Workbook implements Model
             }
 
         }
-
     }
     
     public WindowOneRecord getWindowOne() {
index c9417a9c3ab36b2a7429ebf8d3018022c986e913..0260fc915bd69a4f188669091b8947c24a7660f6 100644 (file)
 
 package org.apache.poi.hssf.record;
 
+import java.io.ByteArrayInputStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
 import org.apache.poi.ddf.DefaultEscherRecordFactory;
+import org.apache.poi.ddf.EscherContainerRecord;
 import org.apache.poi.ddf.EscherRecord;
 import org.apache.poi.ddf.EscherRecordFactory;
 import org.apache.poi.ddf.NullEscherSerializationListener;
 import org.apache.poi.util.LittleEndian;
 
-import java.io.ByteArrayInputStream;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-
 /**
  * The escher container record is used to hold escher records.  It is abstract and
  * must be subclassed for maximum benefit.
@@ -97,6 +96,9 @@ public abstract class AbstractEscherHolderRecord
         }
     }
 
+    protected void convertRawBytesToEscherRecords() {
+       convertToEscherRecords(0, rawData.length, rawData);
+    }
     private void convertToEscherRecords( int offset, int size, byte[] data )
     {
         EscherRecordFactory recordFactory = new DefaultEscherRecordFactory();
@@ -264,6 +266,54 @@ public abstract class AbstractEscherHolderRecord
     {
         escherRecords.clear();
     }
+    
+    /**
+     * If we have a EscherContainerRecord as one of our
+     *  children (and most top level escher holders do),
+     *  then return that.
+     */
+    public EscherContainerRecord getEscherContainer() {
+       for(Iterator it = escherRecords.iterator(); it.hasNext();) {
+               Object er = it.next();
+               if(er instanceof EscherContainerRecord) {
+                       return (EscherContainerRecord)er;
+               }
+       }
+       return null;
+    }
+
+    /**
+     * Descends into all our children, returning the
+     *  first EscherRecord with the given id, or null
+     *  if none found
+     */
+    public EscherRecord findFirstWithId(short id) {
+       return findFirstWithId(id, getEscherRecords());
+    }
+    private EscherRecord findFirstWithId(short id, List records) {
+       // Check at our level
+       for(Iterator it = records.iterator(); it.hasNext();) {
+               EscherRecord r = (EscherRecord)it.next();
+               if(r.getRecordId() == id) {
+                       return r;
+               }
+       }
+       
+       // Then check our children in turn
+       for(Iterator it = records.iterator(); it.hasNext();) {
+               EscherRecord r = (EscherRecord)it.next();
+               if(r.isContainerRecord()) {
+                       EscherRecord found =
+                               findFirstWithId(id, r.getChildRecords());
+                       if(found != null) {
+                               return found;
+                       }
+               }
+       }
+       
+       // Not found in this lot
+       return null;
+    }
 
 
     public EscherRecord getEscherRecord(int index)
index 3782273a5dafd1097bd8dca281b7d07d62889c2a..ea083ee1e3bc21fbe8555808308acb3174889231 100644 (file)
@@ -72,6 +72,16 @@ public class DrawingGroupRecord extends AbstractEscherHolderRecord
             return writeData( offset, data, buffer );
         }
     }
+    
+    /**
+     * Process the bytes into escher records.
+     * (Not done by default in case we break things,
+     *  unless you set the "poi.deserialize.escher" 
+     *  system property)
+     */
+    public void processChildRecords() {
+       convertRawBytesToEscherRecords();
+    }
 
     /**
      * Size of record (including 4 byte headers for all sections)
index 28a717682a994d20271bf51b23b4aedc69aa7369..4f5d18c239d12c2111005cf22543766dd7813793 100644 (file)
@@ -523,7 +523,20 @@ public class EscherAggregate extends AbstractEscherHolderRecord
     {
         this.patriarch = patriarch;
     }
-
+    
+    /**
+     * Converts the Records into UserModel
+     *  objects on the bound HSSFPatriarch
+     */
+    public void convertRecordsToUserModel() {
+       if(patriarch == null) {
+               throw new IllegalStateException("Must call setPatriarch() first");
+       }
+       
+       // TODO: Support converting our records
+       //  back into shapes
+    }
+    
     public void clear()
     {
         clearEscherRecords();
index e383ed558686a1c55a2b6814c7a927b0e933ed0d..3df804da76844e9d2557078b9fab44d5c10d7aef 100644 (file)
@@ -21,6 +21,13 @@ import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
 
+import org.apache.poi.ddf.EscherComplexProperty;
+import org.apache.poi.ddf.EscherOptRecord;
+import org.apache.poi.ddf.EscherProperty;
+import org.apache.poi.hssf.record.EscherAggregate;
+import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.StringUtil;
+
 /**
  * The patriarch is the toplevel container for shapes in a sheet.  It does
  * little other than act as a container for other shapes and groups.
@@ -37,13 +44,21 @@ public class HSSFPatriarch
     int x2 = 1023;
     int y2 = 255;
 
+    /**
+     * The EscherAggregate we have been bound to.
+     * (This will handle writing us out into records,
+     *  and building up our shapes from the records)
+     */
+    private EscherAggregate boundAggregate;
+
     /**
      * Creates the patriarch.
      *
-     * @param sheet     the sheet this patriarch is stored in.
+     * @param sheet the sheet this patriarch is stored in.
      */
-    HSSFPatriarch(HSSFSheet sheet)
+    HSSFPatriarch(HSSFSheet sheet, EscherAggregate boundAggregate)
     {
+       this.boundAggregate = boundAggregate;
         this.sheet = sheet;
     }
 
@@ -173,6 +188,39 @@ public class HSSFPatriarch
         this.x2 = x2;
         this.y2 = y2;
     }
+    
+    /**
+     * Does this HSSFPatriarch contain a chart?
+     * (Technically a reference to a chart, since they
+     *  get stored in a different block of records)
+     * FIXME - detect chart in all cases (only seems
+     *  to work on some charts so far)
+     */
+    public boolean containsChart() {
+       // TODO - support charts properly in usermodel
+       
+       // We're looking for a EscherOptRecord
+       EscherOptRecord optRecord = (EscherOptRecord)
+               boundAggregate.findFirstWithId(EscherOptRecord.RECORD_ID);
+       if(optRecord == null) {
+               // No opt record, can't have chart
+               return false;
+       }
+       
+       for(Iterator it = optRecord.getEscherProperties().iterator(); it.hasNext();) {
+               EscherProperty prop = (EscherProperty)it.next();
+               if(prop.getPropertyNumber() == 896 && prop.isComplex()) {
+                       EscherComplexProperty cp = (EscherComplexProperty)prop;
+                       String str = StringUtil.getFromUnicodeLE(cp.getComplexData());
+                       System.err.println(str);
+                       if(str.equals("Chart 1\0")) {
+                               return true;
+                       }
+               }
+       }
+
+       return false;
+    }
 
     /**
      * The top left x coordinate of this group.
index 6fc113c085760f50b991c666824377fa3a15ce51..3ad8a94777b2386a77192f74606b8e3fd0f3539e 100644 (file)
@@ -1488,7 +1488,7 @@ public class HSSFSheet
      */
     public void dumpDrawingRecords(boolean fat)
     {
-        sheet.aggregateDrawingRecords(book.getDrawingManager());
+        sheet.aggregateDrawingRecords(book.getDrawingManager(), false);
 
         EscherAggregate r = (EscherAggregate) getSheet().findFirstRecordBySid(EscherAggregate.sid);
         List escherRecords = r.getEscherRecords();
@@ -1505,9 +1505,10 @@ public class HSSFSheet
     }
 
     /**
-     * Creates the toplevel drawing patriarch.  This will have the effect of
-     * removing any existing drawings on this sheet.
-     *
+     * Creates the top-level drawing patriarch.  This will have
+     *  the effect of removing any existing drawings on this
+     *  sheet.
+     * This may then be used to add graphics or charts
      * @return  The new patriarch.
      */
     public HSSFPatriarch createDrawingPatriarch()
@@ -1515,14 +1516,43 @@ public class HSSFSheet
         // Create the drawing group if it doesn't already exist.
         book.createDrawingGroup();
 
-        sheet.aggregateDrawingRecords(book.getDrawingManager());
+        sheet.aggregateDrawingRecords(book.getDrawingManager(), true);
         EscherAggregate agg = (EscherAggregate) sheet.findFirstRecordBySid(EscherAggregate.sid);
-        HSSFPatriarch patriarch = new HSSFPatriarch(this);
+        HSSFPatriarch patriarch = new HSSFPatriarch(this, agg);
         agg.clear();     // Initially the behaviour will be to clear out any existing shapes in the sheet when
                          // creating a new patriarch.
         agg.setPatriarch(patriarch);
         return patriarch;
     }
+    
+    /**
+     * Returns the top-level drawing patriach, if there is
+     *  one.
+     * This will hold any graphics or charts for the sheet
+     */
+    public HSSFPatriarch getDrawingPatriarch() {
+       book.findDrawingGroup();
+       
+       // If there's now no drawing manager, then there's
+       //  no drawing escher records on the workbook
+       if(book.getDrawingManager() == null) {
+               return null;
+       }
+       
+       int found = sheet.aggregateDrawingRecords(
+                       book.getDrawingManager(), false
+       );
+       if(found == -1) {
+               // Workbook has drawing stuff, but this sheet doesn't
+               return null;
+       }
+       
+        EscherAggregate agg = (EscherAggregate) sheet.findFirstRecordBySid(EscherAggregate.sid);
+        HSSFPatriarch patriarch = new HSSFPatriarch(this, agg);
+        agg.setPatriarch(patriarch);
+        agg.convertRecordsToUserModel();
+        return patriarch;
+    }
 
     /**
      * Expands or collapses a column group.
index 5f9ef53c89200d6849d91e2f97672d5b31b1f1bb..f0deb68e0c04479a57cb24fac2235f62daf3c158 100644 (file)
@@ -407,6 +407,30 @@ public class TestHSSFSheet
         assertEquals(0, r6.getOutlineLevel());
     }
     
+    public void testGetDrawings() throws Exception {
+        String filename = System.getProperty("HSSF.testdata.path");
+       HSSFWorkbook wb1c = new HSSFWorkbook(
+                       new FileInputStream(new File(filename,"WithChart.xls"))
+       );
+       HSSFWorkbook wb2c = new HSSFWorkbook(
+                       new FileInputStream(new File(filename,"WithTwoCharts.xls"))
+       );
+       
+       // 1 chart sheet -> data on 1st, chart on 2nd
+       assertNotNull(wb1c.getSheetAt(0).getDrawingPatriarch());
+       assertNotNull(wb1c.getSheetAt(1).getDrawingPatriarch());
+       assertFalse(wb1c.getSheetAt(0).getDrawingPatriarch().containsChart());
+       assertTrue(wb1c.getSheetAt(1).getDrawingPatriarch().containsChart());
+       
+       // 2 chart sheet -> data on 1st, chart on 2nd+3rd
+       assertNotNull(wb2c.getSheetAt(0).getDrawingPatriarch());
+       assertNotNull(wb2c.getSheetAt(1).getDrawingPatriarch());
+       assertNotNull(wb2c.getSheetAt(2).getDrawingPatriarch());
+       assertFalse(wb2c.getSheetAt(0).getDrawingPatriarch().containsChart());
+       assertTrue(wb2c.getSheetAt(1).getDrawingPatriarch().containsChart());
+       assertTrue(wb2c.getSheetAt(2).getDrawingPatriarch().containsChart());
+    }
+    
        /**
         * Test that the ProtectRecord is included when creating or cloning a sheet
         */
index 6d5afca114c0414234a543146845763617caccde..40a577240e2c9739af712d2ff47e81c22c24904b 100644 (file)
@@ -16,6 +16,8 @@
 */
 package org.apache.poi.hssf.usermodel;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileInputStream;
 
@@ -129,4 +131,69 @@ public class TestHSSFWorkbook extends TestCase
         b.cloneSheet(0);
         assertEquals(2, b.getNumberOfSheets());
     }
+    
+    public void testReadWriteWithCharts() throws Exception {
+        HSSFWorkbook b;
+        HSSFSheet s;
+        
+        // Single chart, two sheets
+        b = new HSSFWorkbook(
+                       new FileInputStream(new File(filename,"44010-SingleChart.xls"))
+        );
+        assertEquals(2, b.getNumberOfSheets());
+        s = b.getSheetAt(1);
+        assertEquals(0, s.getFirstRowNum());
+        assertEquals(0, s.getLastRowNum());
+        
+        // Has chart on 1st sheet??
+        // FIXME
+        assertNotNull(b.getSheetAt(0).getDrawingPatriarch());
+        assertNull(b.getSheetAt(1).getDrawingPatriarch());
+        assertFalse(b.getSheetAt(0).getDrawingPatriarch().containsChart());
+        
+        b = writeRead(b);
+        assertEquals(2, b.getNumberOfSheets());
+        s = b.getSheetAt(1);
+        assertEquals(0, s.getFirstRowNum());
+        assertEquals(0, s.getLastRowNum());
+
+        
+        // Two charts, three sheets
+        b = new HSSFWorkbook(
+                       new FileInputStream(new File(filename,"44010-TwoCharts.xls"))
+        );
+        assertEquals(3, b.getNumberOfSheets());
+        
+        s = b.getSheetAt(1);
+        assertEquals(0, s.getFirstRowNum());
+        assertEquals(0, s.getLastRowNum());
+        s = b.getSheetAt(2);
+        assertEquals(0, s.getFirstRowNum());
+        assertEquals(0, s.getLastRowNum());
+        
+        // Has chart on 1st sheet??
+        // FIXME
+        assertNotNull(b.getSheetAt(0).getDrawingPatriarch());
+        assertNull(b.getSheetAt(1).getDrawingPatriarch());
+        assertNull(b.getSheetAt(2).getDrawingPatriarch());
+        assertFalse(b.getSheetAt(0).getDrawingPatriarch().containsChart());
+        
+        b = writeRead(b);
+        assertEquals(3, b.getNumberOfSheets());
+        
+        s = b.getSheetAt(1);
+        assertEquals(0, s.getFirstRowNum());
+        assertEquals(0, s.getLastRowNum());
+        s = b.getSheetAt(2);
+        assertEquals(0, s.getFirstRowNum());
+        assertEquals(0, s.getLastRowNum());
+    }
+    
+    private HSSFWorkbook writeRead(HSSFWorkbook b) throws Exception {
+       ByteArrayOutputStream baos = new ByteArrayOutputStream();
+       b.write(baos);
+       return new HSSFWorkbook(
+                       new ByteArrayInputStream(baos.toByteArray())
+       );
+    }
 }
\ No newline at end of file