/********************************************************************* * * 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 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; } }