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 }