1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 24 package org.openoffice.xmerge.converter.xml.sxw.pocketword; 25 26 import org.openoffice.xmerge.util.EndianConverter; 27 28 import java.io.ByteArrayOutputStream; 29 import java.io.OutputStream; 30 import java.io.IOException; 31 32 import java.util.Vector; 33 34 35 /** 36 * This class to represent the data structure stored by a Pocket Word file that 37 * describes that file. 38 * 39 * The data structure is of variable length, beginning at the end of the 40 * font declarations and ending 10 bytes before the first instance of 0xFF 0xFF 41 * marking a paragraph block. 42 * 43 * The variable length component arises from an 8 byte structure describing each 44 * paragraph in the document. These paragraph descriptors appear at the end 45 * of the Document Descriptor. 46 * 47 * @author Mark Murnane 48 * @version 1.1 49 */ 50 class DocumentDescriptor { 51 private short numParagraphs = 0; 52 private short length = 0; 53 private short numLines = 0; 54 55 private Vector paragraphDesc = null; 56 DocumentDescriptor()57 DocumentDescriptor() { 58 paragraphDesc = new Vector(0, 1); 59 } 60 61 62 63 /** 64 * Updates the <code>DocumentDescriptor</code> to include details of another 65 * paragraph in the document. 66 * 67 * @param len The number of characters in the paragraph. 68 * @param lines The number of lines on screen that the paragraph uses. 69 */ addParagraph(short len, short lines)70 public void addParagraph(short len, short lines) { 71 ParagraphDescriptor pd = new ParagraphDescriptor(len, lines); 72 73 paragraphDesc.add(pd); 74 numParagraphs++; 75 numLines += lines; 76 length += pd.length; 77 } 78 79 80 /** 81 * Retrieve the <code>DocumentDescriptor's</code> data. Due to the variable 82 * length nature of the descriptor, certain fields can only be 83 * calculated/written after the addition of all paragraphs. 84 * 85 * @return Byte array containing the Pocket Word representation of this 86 * <code>DocumentDescriptor</code>. 87 */ getDescriptor()88 public byte[] getDescriptor () { 89 ByteArrayOutputStream descStream = new ByteArrayOutputStream(); 90 91 writeHeader(descStream); 92 93 /* 94 * This value seems to increment by 0x02 for each paragraph. 95 * For a single paragraph doc, the value is 0x08, 0x0A for two, 96 * 0x0C for three ... 97 */ 98 try { 99 descStream.write(EndianConverter.writeShort((short)(6 + 100 (numParagraphs * 2)))); 101 102 descStream.write(EndianConverter.writeShort(numParagraphs)); 103 descStream.write(EndianConverter.writeShort((short)0)); 104 descStream.write(EndianConverter.writeShort(numParagraphs)); 105 106 descStream.write(EndianConverter.writeShort((short)0)); 107 descStream.write(EndianConverter.writeShort((short)length)); 108 descStream.write(EndianConverter.writeShort((short)0)); 109 110 descStream.write(EndianConverter.writeShort(numLines)); 111 descStream.write(new byte[] { 0x00, 0x00, 0x00, 0x00, 112 0x00, 0x00, 0x00, 0x00 } ); 113 114 for (int i = 0; i < paragraphDesc.size(); i++) { 115 ParagraphDescriptor pd = (ParagraphDescriptor)paragraphDesc.elementAt(i); 116 117 descStream.write(pd.getDescriptor()); 118 } 119 120 // Byte sequence marking the end of this DocumentDescriptor 121 descStream.write(EndianConverter.writeShort((short)0)); 122 descStream.write(EndianConverter.writeShort((short)0x41)); 123 } 124 catch (IOException ioe) { 125 // Should never happen as this is a memory based stream. 126 } 127 128 return descStream.toByteArray(); 129 } 130 131 132 /* 133 * This method loads the initial fixed portion of the descriptor and the 134 * mid-section. The mid-section is variable but Pocket Word doesn't seem 135 * to mind default values. 136 */ writeHeader(OutputStream descStream)137 private void writeHeader(OutputStream descStream) { 138 139 try { 140 descStream.write(new byte[] { 0x00, 0x00, 0x00, 0x00, 141 0x07, 0x00, 0x06, 0x00, 142 0x15, 0x00, 0x10, 0x00, 143 0x01, 0x00, (byte)0xD0, 0x2F, 144 0x00, 0x00, (byte)0xE0, 0x3D, 145 0x00, 0x00, (byte)0xF0, 0x00, 146 0x00, 0x00, (byte)0xA0, 0x05, 147 0x00, 0x00, (byte)0xA0, 0x05, 148 0x00, 0x00, (byte)0xA0, 0x05, 149 0x00, 0x00, (byte)0xA0, 0x05, 150 0x00, 0x00, 0x00, 0x00, 151 0x00, 0x00, 0x00, 0x00, 152 0x00, 0x00, 0x00, 0x00, 153 0x00, 0x00, 0x00, 0x00, 154 0x0A, 0x00, 0x00, 0x00, 155 0x00, 0x00, 0x04, 0x00, 156 0x00, 0x00, 0x00, 0x00, 157 0x00, 0x00, 0x00, 0x00, 158 0x0A, 0x00, 0x00, 0x00, 159 0x00, 0x00, 0x04, 0x00, 160 0x00, 0x00, 0x00, 0x00, 161 0x00, 0x00, 0x00, 0x00, 162 0x00, 0x00, 0x00, 0x00, 163 0x00, 0x00, 0x08, 0x00, 164 0x07, 0x00, 0x10, 0x00, 165 0x01, 0x00, 0x00, 0x00, 166 0x00, 0x00, 0x00, 0x00, 167 0x00, 0x00, 0x00, 0x00, 168 0x00, 0x00, 0x00, 0x00, 169 0x00, 0x00, 0x00, 0x00, 170 0x12, 0x00, 0x00, 0x00, 171 0x00, 0x00, 0x00, 0x00, 172 0x1F, 0x04, 0x00, 0x00 } ); 173 174 /* 175 * The next four bytes are variable, but a pattern hasn't yet been 176 * established. Pocket Word seems to accept this constant value. 177 * 178 * The bytes are repeated after another 12 byte sequence which does 179 * not seem to change from one file to the next. 180 */ 181 descStream.write(new byte[] { (byte)0xE2, 0x02, 0x00, 0x00 } ); 182 descStream.write(new byte[] { 0x00, 0x00, 0x00, 0x00, 183 0x00, 0x00, 0x00, 0x00, 184 0x3D, 0x04, 0x00, 0x00 } ); 185 descStream.write(new byte[] { (byte)0xE2, 0x02, 0x00, 0x00 } ); 186 187 descStream.write(new byte[] { 0x00, 0x00, 0x00, 0x00, 188 0x00, 0x00, 0x00, 0x00, 189 0x00, 0x00, 0x00, 0x00, 190 0x40, 0x00, 0x08, 0x00 } ); 191 } 192 catch (IOException ioe) { 193 /* Shouldn't happen with a ByteArrayOutputStream */ 194 } 195 } 196 197 198 /** 199 * <code>ParagraphDescriptor</code> represents the data structure used to 200 * describe individual paragraphs within a <code>DocumentDescriptor.</code> 201 * 202 * It is used solely by the <code>DocumentDescriptor<code> class. 203 */ 204 private class ParagraphDescriptor { 205 private short filler = 0; 206 private short lines = 0; 207 private short length = 0; 208 private short unknown = 0x23; 209 ParagraphDescriptor(short len, short numLines)210 public ParagraphDescriptor(short len, short numLines) { 211 lines = numLines; 212 length = (short)(len + 1); 213 } 214 getDescriptor()215 public byte[] getDescriptor() { 216 ByteArrayOutputStream desc = new ByteArrayOutputStream(); 217 218 try { 219 desc.write(EndianConverter.writeShort(filler)); 220 desc.write(EndianConverter.writeShort(lines)); 221 desc.write(EndianConverter.writeShort(length)); 222 desc.write(EndianConverter.writeShort(unknown)); 223 } 224 catch (IOException ioe) { 225 /* Should never happen */ 226 } 227 228 return desc.toByteArray(); 229 } 230 } 231 }