/*********************************************************************
*
* Copyright (C) 2004 Andrew Khan
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
***************************************************************************/
package jxl.biff.drawing;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import jxl.common.Logger;
import jxl.WorkbookSettings;
import jxl.biff.IntegerHelper;
import jxl.write.biff.File;
/**
* Handles the writing out of the different charts and images on a sheet.
* Called by the SheetWriter object
*/
public class SheetDrawingWriter
{
/**
* The logger
*/
private static Logger logger = Logger.getLogger(SheetDrawingWriter.class);
/**
* The drawings on the sheet
*/
private ArrayList drawings;
/**
* Flag indicating whether the drawings on the sheet were modified
*/
private boolean drawingsModified;
/**
* The charts on the sheet
*/
private Chart[] charts;
/**
* The workbook settings
*/
private WorkbookSettings workbookSettings;
/**
* Constructor
*
* @param ws the workbook settings
*/
public SheetDrawingWriter(WorkbookSettings ws)
{
charts = new Chart[0];
}
/**
* The drawings on the sheet
*
* @param dr the list of drawings
* @param mod flag indicating whether the drawings have been tampered with
*/
public void setDrawings(ArrayList dr, boolean mod)
{
drawings = dr;
drawingsModified = mod;
}
/**
* Writes out the MsoDrawing records and Obj records for each image
* and chart on the sheet
*
* @param outputFile the output file
* @exception IOException
*/
public void write(File outputFile) throws IOException
{
// If there are no drawings or charts on this sheet then exit
if (drawings.size() == 0 && charts.length == 0)
{
return;
}
// See if any drawing has been modified
boolean modified = drawingsModified;
int numImages = drawings.size();
for (Iterator i = drawings.iterator(); i.hasNext() && !modified;)
{
DrawingGroupObject d = (DrawingGroupObject) i.next();
if (d.getOrigin() != Origin.READ)
{
modified = true;
}
}
// If the drawing order has been muddled at all, then we'll need
// to regenerate the Escher drawing data
if (numImages > 0 && !modified)
{
DrawingGroupObject d2 = (DrawingGroupObject) drawings.get(0);
if (!d2.isFirst())
{
modified = true;
}
}
// Check to see if this sheet consists of just a single chart. If so
// there is no MsoDrawingRecord, so write out the data and exit
if (numImages == 0 &&
charts.length == 1 &&
charts[0].getMsoDrawingRecord() == null)
{
modified = false; // this sheet has not been modified
}
// If no drawing has been modified, then simply write them straight out
// again and exit
if (!modified)
{
writeUnmodified(outputFile);
return;
}
Object[] spContainerData = new Object[numImages + charts.length];
int length = 0;
EscherContainer firstSpContainer = null;
// Get all the spContainer byte data from the drawings
// and store in an array
for (int i = 0; i < numImages; i++)
{
DrawingGroupObject drawing = (DrawingGroupObject) drawings.get(i);
EscherContainer spc = drawing.getSpContainer();
if (spc != null)
{
byte[] data = spc.getData();
spContainerData[i] = data;
if (i == 0)
{
firstSpContainer = spc;
}
else
{
length += data.length;
}
}
}
// Get all the spContainer bytes from the charts and add to the array
for (int i = 0; i < charts.length; i++)
{
EscherContainer spContainer = charts[i].getSpContainer();
byte[] data = spContainer.getBytes(); //use getBytes instead of getData
data = spContainer.setHeaderData(data);
spContainerData[i + numImages] = data;
if (i == 0 && numImages == 0)
{
firstSpContainer = spContainer;
}
else
{
length += data.length;
}
}
// Put the generalised stuff around the first item
DgContainer dgContainer = new DgContainer();
Dg dg = new Dg(numImages + charts.length);
dgContainer.add(dg);
SpgrContainer spgrContainer = new SpgrContainer();
SpContainer spContainer = new SpContainer();
Spgr spgr = new Spgr();
spContainer.add(spgr);
Sp sp = new Sp(ShapeType.MIN, 1024, 5);
spContainer.add(sp);
spgrContainer.add(spContainer);
spgrContainer.add(firstSpContainer);
dgContainer.add(spgrContainer);
byte[] firstMsoData = dgContainer.getData();
// Adjust the length of the DgContainer
int len = IntegerHelper.getInt(firstMsoData[4],
firstMsoData[5],
firstMsoData[6],
firstMsoData[7]);
IntegerHelper.getFourBytes(len + length, firstMsoData, 4);
// Adjust the length of the SpgrContainer
len = IntegerHelper.getInt(firstMsoData[28],
firstMsoData[29],
firstMsoData[30],
firstMsoData[31]);
IntegerHelper.getFourBytes(len + length, firstMsoData, 28);
// Now write out each MsoDrawing record
// First MsoRecord
// test hack for form objects, to remove the ClientTextBox record
// from the end of the SpContainer
if (numImages > 0 &&
((DrawingGroupObject) drawings.get(0)).isFormObject())
{
byte[] msodata2 = new byte[firstMsoData.length - 8];
System.arraycopy(firstMsoData, 0, msodata2, 0, msodata2.length);
firstMsoData = msodata2;
}
MsoDrawingRecord msoDrawingRecord = new MsoDrawingRecord(firstMsoData);
outputFile.write(msoDrawingRecord);
if (numImages > 0)
{
DrawingGroupObject firstDrawing = (DrawingGroupObject) drawings.get(0);
firstDrawing.writeAdditionalRecords(outputFile);
}
else
{
// first image is a chart
Chart chart = charts[0];
ObjRecord objRecord = chart.getObjRecord();
outputFile.write(objRecord);
outputFile.write(chart);
}
// Now do all the others
for (int i = 1; i < spContainerData.length; i++)
{
byte[] bytes = (byte[]) spContainerData[i];
// test hack for form objects, to remove the ClientTextBox record
// from the end of the SpContainer
if (i < numImages &&
((DrawingGroupObject) drawings.get(i)).isFormObject())
{
byte[] bytes2 = new byte[bytes.length - 8];
System.arraycopy(bytes, 0, bytes2, 0, bytes2.length);
bytes = bytes2;
}
msoDrawingRecord = new MsoDrawingRecord(bytes);
outputFile.write(msoDrawingRecord);
if (i < numImages)
{
// Write anything else the object needs
DrawingGroupObject d = (DrawingGroupObject) drawings.get(i);
d.writeAdditionalRecords(outputFile);
}
else
{
Chart chart = charts[i - numImages];
ObjRecord objRecord = chart.getObjRecord();
outputFile.write(objRecord);
outputFile.write(chart);
}
}
// Write any tail records that need to be written
for (Iterator i = drawings.iterator(); i.hasNext();)
{
DrawingGroupObject dgo2 = (DrawingGroupObject) i.next();
dgo2.writeTailRecords(outputFile);
}
}
/**
* Writes out the drawings and the charts if nothing has been modified
*
* @param outputFile the output file
* @exception IOException
*/
private void writeUnmodified(File outputFile) throws IOException
{
if (charts.length == 0 && drawings.size() == 0)
{
// No drawings or charts
return;
}
else if (charts.length == 0 && drawings.size() != 0)
{
// If there are no charts, then write out the drawings and return
for (Iterator i = drawings.iterator(); i.hasNext();)
{
DrawingGroupObject d = (DrawingGroupObject) i.next();
outputFile.write(d.getMsoDrawingRecord());
d.writeAdditionalRecords(outputFile);
}
for (Iterator i = drawings.iterator(); i.hasNext();)
{
DrawingGroupObject d = (DrawingGroupObject) i.next();
d.writeTailRecords(outputFile);
}
return;
}
else if (drawings.size() == 0 && charts.length != 0)
{
// If there are no drawings, then write out the charts and return
Chart curChart = null;
for (int i = 0; i < charts.length; i++)
{
curChart = charts[i];
if (curChart.getMsoDrawingRecord() != null)
{
outputFile.write(curChart.getMsoDrawingRecord());
}
if (curChart.getObjRecord() != null)
{
outputFile.write(curChart.getObjRecord());
}
outputFile.write(curChart);
}
return;
}
// There are both charts and drawings - the output
// drawing group records will need
// to be re-jigged in order to write the drawings out first, then the
// charts
int numDrawings = drawings.size();
int length = 0;
EscherContainer[] spContainers =
new EscherContainer[numDrawings + charts.length];
boolean[] isFormObject = new boolean[numDrawings + charts.length];
for (int i = 0; i < numDrawings; i++)
{
DrawingGroupObject d = (DrawingGroupObject) drawings.get(i);
spContainers[i] = d.getSpContainer();
if (i > 0)
{
length += spContainers[i].getLength();
}
if (d.isFormObject())
{
isFormObject[i] = true;
}
}
for (int i = 0; i < charts.length; i++)
{
spContainers[i + numDrawings] = charts[i].getSpContainer();
length += spContainers[i + numDrawings].getLength();
}
// Put the generalised stuff around the first item
DgContainer dgContainer = new DgContainer();
Dg dg = new Dg(numDrawings + charts.length);
dgContainer.add(dg);
SpgrContainer spgrContainer = new SpgrContainer();
SpContainer spContainer = new SpContainer();
Spgr spgr = new Spgr();
spContainer.add(spgr);
Sp sp = new Sp(ShapeType.MIN, 1024, 5);
spContainer.add(sp);
spgrContainer.add(spContainer);
spgrContainer.add(spContainers[0]);
dgContainer.add(spgrContainer);
byte[] firstMsoData = dgContainer.getData();
// Adjust the length of the DgContainer
int len = IntegerHelper.getInt(firstMsoData[4],
firstMsoData[5],
firstMsoData[6],
firstMsoData[7]);
IntegerHelper.getFourBytes(len + length, firstMsoData, 4);
// Adjust the length of the SpgrContainer
len = IntegerHelper.getInt(firstMsoData[28],
firstMsoData[29],
firstMsoData[30],
firstMsoData[31]);
IntegerHelper.getFourBytes(len + length, firstMsoData, 28);
// Now write out each MsoDrawing record and object record
// Hack to remove the last eight bytes (text box escher record)
// from the container
if (isFormObject[0] == true)
{
byte[] cbytes = new byte[firstMsoData.length - 8];
System.arraycopy(firstMsoData, 0, cbytes, 0, cbytes.length);
firstMsoData = cbytes;
}
// First MsoRecord
MsoDrawingRecord msoDrawingRecord = new MsoDrawingRecord(firstMsoData);
outputFile.write(msoDrawingRecord);
DrawingGroupObject dgo = (DrawingGroupObject) drawings.get(0);
dgo.writeAdditionalRecords(outputFile);
// Now do all the others
for (int i = 1; i < spContainers.length; i++)
{
byte[] bytes = spContainers[i].getBytes();
byte[] bytes2 = spContainers[i].setHeaderData(bytes);
// Hack to remove the last eight bytes (text box escher record)
// from the container
if (isFormObject[i] == true)
{
byte[] cbytes = new byte[bytes2.length - 8];
System.arraycopy(bytes2, 0, cbytes, 0, cbytes.length);
bytes2 = cbytes;
}
msoDrawingRecord = new MsoDrawingRecord(bytes2);
outputFile.write(msoDrawingRecord);
if (i < numDrawings)
{
dgo = (DrawingGroupObject) drawings.get(i);
dgo.writeAdditionalRecords(outputFile);
}
else
{
Chart chart = charts[i - numDrawings];
ObjRecord objRecord = chart.getObjRecord();
outputFile.write(objRecord);
outputFile.write(chart);
}
}
// Write any tail records that need to be written
for (Iterator i = drawings.iterator(); i.hasNext();)
{
DrawingGroupObject dgo2 = (DrawingGroupObject) i.next();
dgo2.writeTailRecords(outputFile);
}
}
/**
* Sets the charts on the sheet
*
* @param ch the charts
*/
public void setCharts(Chart[] ch)
{
charts = ch;
}
/**
* Accessor for the charts on the sheet
*
* @return the charts
*/
public Chart[] getCharts()
{
return charts;
}
}
|