3 * $Date: 2006-03-18 15:59:33 -0600 (Sat, 18 Mar 2006) $
6 * Some portions of this file have been modified by Robert Hanson hansonr.at.stolaf.edu 2012-2017
7 * for use in SwingJS via transpilation into JavaScript using Java2Script.
9 * Copyright (C) 2003-2005 Miguel, Jmol Development, www.jmol.org
11 * Contact: hansonr@stolaf.edu
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2.1 of the License, or (at your option) any later version.
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
30 import java.io.DataInputStream;
31 import java.io.BufferedInputStream;
36 import javajs.api.GenericZipTools;
40 /* a simple compound document reader.
42 * DIRECTORY STRUCTURE IS NOT REGENERATED
44 * See http://sc.openoffice.org/compdocfileformat.pdf
46 * random access file info:
47 * http://java.sun.com/docs/books/tutorial/essential/io/rafs.html
49 * SHOOT! random access is only for applications, not applets!
51 * With a bit more work, this could be set up to deliver binary files, but
52 * right now I've only implemented it for string-based data.
56 public class CompoundDocument extends BinaryDocument{
58 // RandomAccessFile file;
59 CompoundDocHeader header = new CompoundDocHeader(this);
60 Lst<CompoundDocDirEntry> directory = new Lst<CompoundDocDirEntry>();
61 CompoundDocDirEntry rootEntry;
63 protected GenericZipTools jzt;
69 int nShortSectorsPerStandardSector;
71 int nDirEntriesperSector;
73 // called by reflection
75 public CompoundDocument(){
77 this.isBigEndian = true;
80 public void setDocStream(GenericZipTools jzt, BufferedInputStream bis) {
83 stream = new DataInputStream(bis);
85 stream.mark(Integer.MAX_VALUE);
88 getSectorAllocationTable();
89 getShortSectorAllocationTable();
93 public Lst<CompoundDocDirEntry> getDirectory() {
97 public String getDirectoryListing(String separator) {
99 for (int i = 0; i < directory.size(); i++) {
100 CompoundDocDirEntry thisEntry = directory.get(i);
101 if (!thisEntry.isEmpty)
102 sb.append(separator).append(thisEntry.entryName)
103 .append("\tlen=").appendI(thisEntry.lenStream)
104 .append("\tSID=").appendI(thisEntry.SIDfirstSector)
105 .append(thisEntry.isStandard ? "\tfileOffset="
106 + getOffset(thisEntry.SIDfirstSector) : "");
108 return sb.toString();
111 public SB getAllData() {
112 return getAllDataFiles(null, null);
116 * reads a compound document directory and saves all data in a Hashtable
117 * so that the files may be organized later in a different order. Also adds
118 * a #Directory_Listing entry.
120 * Files are bracketed by BEGIN Directory Entry and END Directory Entry lines,
121 * similar to ZipUtil.getAllData.
124 * @param binaryFileList |-separated list of files that should be saved
125 * as xx xx xx hex byte strings. The directory listing
126 * is appended with ":asBinaryString"
130 public void getAllDataMapped(String prefix,
131 String binaryFileList, Map<String, String> fileData) {
132 fileData.put("#Directory_Listing", getDirectoryListing("|"));
133 binaryFileList = "|" + binaryFileList + "|";
134 for (int i = 0; i < directory.size(); i++) {
135 CompoundDocDirEntry thisEntry = directory.get(i);
136 if (!thisEntry.isEmpty && thisEntry.entryType != 5) {
137 String name = thisEntry.entryName;
138 System.out.println("CompoundDocument file " + name);
139 boolean isBinary = (binaryFileList.indexOf("|" + name + "|") >= 0);
141 name += ":asBinaryString";
142 fileData.put(prefix + "/" + name, appendData(new SB(), name, thisEntry, isBinary).toString());
149 public SB getAllDataFiles(String binaryFileList, String firstFile) {
150 // firstFile is now ignored
151 // if (firstFile != null) {
152 // for (int i = 0; i < directory.size(); i++) {
153 // CompoundDocDirEntry thisEntry = directory.get(i);
154 // if (thisEntry.entryName.equals(firstFile)) {
155 // directory.remove(i);
156 // directory.add(1, thisEntry); // after ROOT_ENTRY
162 data.append("Compound Document File Directory: ");
163 data.append(getDirectoryListing("|"));
165 CompoundDocDirEntry thisEntry;
166 binaryFileList = "|" + binaryFileList + "|";
167 for (int i = 0, n = directory.size(); i < n; i++) {
168 thisEntry = directory.get(i);
169 //System.out.println("CompoundDocument reading " + thisEntry.entryName);
170 String name = thisEntry.entryName;
171 switch (thisEntry.entryType) {
174 case 1: // user storage (dir)
175 data.append("NEW Directory ").append(name).append("\n");
177 case 2: // user stream (file)
178 if (name.endsWith(".gz"))
179 name = name.substring(0, name.length() - 3);
180 appendData(data, name, thisEntry, binaryFileList.indexOf("|" + thisEntry.entryName + "|") >= 0);
188 private SB appendData(SB data, String name, CompoundDocDirEntry thisEntry,
190 data.append("BEGIN Directory Entry ").append(name).append("\n");
191 data.appendSB(getEntryAsString(thisEntry, isBinary));
192 data.append("\nEND Directory Entry ").append(name).append("\n");
196 public SB getFileAsString(String entryName) {
197 for (int i = 0; i < directory.size(); i++) {
198 CompoundDocDirEntry thisEntry = directory.get(i);
199 if (thisEntry.entryName.equals(entryName))
200 return getEntryAsString(thisEntry, false);
205 private long getOffset(int SID) {
206 return (SID + 1) * sectorSize;
209 private void gotoSector(int SID) {
210 seek(getOffset(SID));
213 private boolean readHeader() {
214 if (!header.readData())
216 sectorSize = 1 << header.sectorPower;
217 shortSectorSize = 1 << header.shortSectorPower;
218 nShortSectorsPerStandardSector = sectorSize / shortSectorSize; // e.g. 512 / 64 = 8
219 nIntPerSector = sectorSize / 4; // e.g. 512 / 4 = 128
220 nDirEntriesperSector = sectorSize / 128; // e.g. 512 / 128 = 4
221 // System.out.println(
222 // "compound document: revNum=" + header.revNumber +
223 // " verNum=" + header.verNumber + " isBigEndian=" + isBigEndian +
224 // " bytes per standard/short sector=" + sectorSize + "/" + shortSectorSize);
228 private void getSectorAllocationTable() {
231 SAT = new int[header.nSATsectors * nIntPerSector + 109];
234 for (int i = 0; i < 109; i++) {
235 thisSID = header.MSAT0[i];
239 for (int j = 0; j < nIntPerSector; j++) {
240 SAT[nSID++] = readInt();
241 //Logger.debug(thisSID+"."+j + "/" + (nSID - 1) + " : " + SAT[nSID - 1]);
244 int nMaster = header.nAdditionalMATsectors;
245 thisSID = header.SID_MSAT_next;
246 int[] MSAT = new int[nIntPerSector];
247 out: while (nMaster-- > 0 && thisSID >= 0) {
248 // read a page of sector identifiers pointing to SAT sectors
250 for (int i = 0; i < nIntPerSector; i++)
252 // read each page of SAT sector identifiers
253 // last entry is pointer to next master sector allocation table page
254 for (int i = 0; i < nIntPerSector - 1; i++) {
259 for (int j = nIntPerSector; --j >= 0;)
260 SAT[nSID++] = readInt();
262 thisSID = MSAT[nIntPerSector - 1];
264 } catch (Exception e) {
265 System.out.println(e.toString());
269 private void getShortSectorAllocationTable() {
271 int thisSID = header.SID_SSAT_start;
272 int nMax = header.nSSATsectors * nIntPerSector;
273 SSAT = new int[nMax];
275 while (thisSID > 0 && nSSID < nMax) {
277 for (int j = 0; j < nIntPerSector; j++) {
278 SSAT[nSSID++] = readInt();
279 //System.out.println("short: " + thisSID+"."+j+" SSID=" +(nSSID-1)+" "+SSAT[nSSID-1]);
281 thisSID = SAT[thisSID];
283 } catch (Exception e) {
284 System.out.println(e.toString());
288 private void getDirectoryTable() {
289 int thisSID = header.SID_DIR_start;
290 CompoundDocDirEntry thisEntry;
293 while (thisSID > 0) {
295 for (int j = nDirEntriesperSector; --j >= 0;) {
296 thisEntry = new CompoundDocDirEntry(this);
297 thisEntry.readData();
298 directory.addLast(thisEntry);
299 if (thisEntry.entryType == 5)
300 rootEntry = thisEntry;
302 thisSID = SAT[thisSID];
304 } catch (Exception e) {
305 System.out.println(e.toString());
307 // System.out.println("CompoundDocument directory entry: \n"
308 // + getDirectoryListing("\n"));
311 private SB getEntryAsString(CompoundDocDirEntry thisEntry, boolean asBinaryString) {
312 if(thisEntry.isEmpty)
314 //System.out.println(thisEntry.entryName + " " + thisEntry.entryType + " " + thisEntry.lenStream + " " + thisEntry.isStandard + " " + thisEntry.SIDfirstSector);
315 return (thisEntry.isStandard ? getStandardStringData(
316 thisEntry.SIDfirstSector, thisEntry.lenStream, asBinaryString)
317 : getShortStringData(thisEntry.SIDfirstSector, thisEntry.lenStream, asBinaryString));
319 private SB getStandardStringData(int thisSID, int nBytes,
320 boolean asBinaryString) {
322 byte[] byteBuf = new byte[sectorSize];
323 ZipData gzipData = new ZipData(nBytes);
325 while (thisSID > 0 && nBytes > 0) {
327 nBytes = getSectorData(data, byteBuf, sectorSize, nBytes, asBinaryString, gzipData);
328 thisSID = SAT[thisSID];
332 } catch (Exception e) {
333 System.out.println(e.toString());
335 if (gzipData.isEnabled)
336 gzipData.addTo(jzt, data);
340 private int getSectorData(SB data, byte[] byteBuf,
341 int nSectorBytes, int nBytes,
342 boolean asBinaryString, ZipData gzipData)
344 readByteArray(byteBuf, 0, byteBuf.length);
345 int n = gzipData.addBytes(byteBuf, nSectorBytes, nBytes);
348 if (asBinaryString) {
349 for (int i = 0; i < nSectorBytes; i++) {
350 data.append(Integer.toHexString(byteBuf[i] & 0xFF)).appendC(' ');
355 for (int i = 0; i < nSectorBytes; i++) {
357 return -9999; // don't allow binary data
358 data.appendC((char) byteBuf[i]);
366 private SB getShortStringData(int shortSID, int nBytes, boolean asBinaryString) {
368 if (rootEntry == null)
370 int thisSID = rootEntry.SIDfirstSector;
372 byte[] byteBuf = new byte[shortSectorSize];
373 ZipData gzipData = new ZipData(nBytes);
375 //System.out.println("CD shortSID=" + shortSID);
376 // point to correct short data sector, 512/64 = 4 per page
377 while (thisSID >= 0 && shortSID >= 0 && nBytes > 0) {
378 while (shortSID - ptShort >= nShortSectorsPerStandardSector) {
379 ptShort += nShortSectorsPerStandardSector;
380 thisSID = SAT[thisSID];
382 seek(getOffset(thisSID) + (shortSID - ptShort) * shortSectorSize);
383 nBytes = getSectorData(data, byteBuf, shortSectorSize, nBytes, asBinaryString, gzipData);
384 shortSID = SSAT[shortSID];
385 //System.out.println("CD shortSID=" + shortSID);
387 } catch (Exception e) {
388 System.out.println(data.toString());
389 System.out.println("reader error in CompoundDocument " + e.toString());
391 if (gzipData.isEnabled)
392 gzipData.addTo(jzt, data);