starwriterimport.cpp
1/*
2 This file is part of the KDE project
3 Copyright (C) 2002 Marco Zanon <info@marcozanon.com>
4 and Ariya Hidayat <ariya@kde.org>
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public
8 License as published by the Free Software Foundation; either
9 version 2 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Library General Public License for more details.
15
16 You should have received a copy of the GNU Library General Public License
17 along with this library; see the file COPYING.LIB. If not, write to
18 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20*/
21
22#include <starwriterimport.h>
23
24#include <tqdatastream.h>
25#include <tqiodevice.h>
26#include <tqstring.h>
27
28#include <KoFilterChain.h>
29#include <kgenericfactory.h>
30
31#include "pole.h"
32
33typedef KGenericFactory<StarWriterImport, KoFilter> StarWriterImportFactory;
34K_EXPORT_COMPONENT_FACTORY(libstarwriterimport, StarWriterImportFactory("kofficefilters"))
35
36// Get unsigned 24-bits integer at given offset
37static inline TQ_UINT32 readU24(TQByteArray array, TQ_UINT32 p)
38{
39 TQ_UINT8* ptr = (TQ_UINT8*) array.data();
40 return (TQ_UINT32) (ptr[p] + (ptr[p+1] << 8) + (ptr[p+2] << 16));
41}
42
43// Get unsigned 16-bits integer at given offset
44static inline TQ_UINT16 readU16(TQByteArray array, TQ_UINT32 p)
45{
46 TQ_UINT8* ptr = (TQ_UINT8*) array.data();
47 return (TQ_UINT16) (ptr[p] + (ptr[p+1] << 8));
48}
49
50StarWriterImport::StarWriterImport(KoFilter *, const char *, const TQStringList&) : KoFilter()
51{
52 hasHeader = false;
53 hasFooter = false;
54 tablesNumber = 1;
55}
56
57StarWriterImport::~StarWriterImport()
58{
59}
60
61KoFilter::ConversionStatus StarWriterImport::convert(const TQCString& from, const TQCString& to)
62{
63 // Check for proper conversion
64 // When 4.x is supported, use also: || (from != "application/x-starwriter")
65 if ((to != "application/x-kword") || (from != "application/vnd.stardivision.writer"))
66 return KoFilter::NotImplemented;
67
68 // Read streams
69 POLE::Storage storage;
70 storage.open(m_chain->inputFile().latin1());
71
72 POLE::Stream* stream;
73
74 stream = storage.stream("StarWriterDocument");
75 if (!stream) return KoFilter::WrongFormat;
76 StarWriterDocument.resize(stream->size());
77 stream->read((unsigned char*)StarWriterDocument.data(), StarWriterDocument.size());
78 delete stream;
79
80 stream = storage.stream("SwPageStyleSheets");
81 if (!stream) return KoFilter::WrongFormat;
82 SwPageStyleSheets.resize(stream->size());
83 stream->read((unsigned char*)SwPageStyleSheets.data(), SwPageStyleSheets.size());
84 delete stream;
85
86 // Check document version
87 if (!checkDocumentVersion()) return KoFilter::WrongFormat;
88
89 // Algorithm for creating the main document
90 if (!addBody()) return KoFilter::ParsingError;
91 if (!addHeaders()) return KoFilter::ParsingError;
92 if (!addFooters()) return KoFilter::ParsingError;
93 if (!addStyles()) return KoFilter::ParsingError;
94 if (!addPageProperties()) return KoFilter::ParsingError;
95 maindoc = bodyStuff + tablesStuff + picturesStuff; // + lots of other things :)
96
97 if (!addKWordHeader()) return KoFilter::ParsingError;
98
99 // Prepare storage device and return
100 KoStoreDevice *out = m_chain->storageFile("maindoc.xml", KoStore::Write);
101 if (out) {
102 TQCString cstring = maindoc.utf8();
103 cstring.prepend("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
104 out->writeBlock((const char*) cstring, cstring.length());
105 }
106
107 return KoFilter::OK;
108}
109
110bool StarWriterImport::checkDocumentVersion()
111{
112 if (SwPageStyleSheets.size() < 0x0B) return false;
113 if (StarWriterDocument.size() < 0x0B) return false;
114
115 if (SwPageStyleSheets[0x00] != 'S') return false;
116 if (SwPageStyleSheets[0x01] != 'W') return false;
117 // When 4.x is supported use also: || (SwPageStyleSheets[0x02] != '4')
118 if (SwPageStyleSheets[0x02] != '5') return false;
119 if (SwPageStyleSheets[0x03] != 'H') return false;
120 if (SwPageStyleSheets[0x04] != 'D') return false;
121 if (SwPageStyleSheets[0x05] != 'R') return false;
122
123 if (StarWriterDocument[0x00] != 'S') return false;
124 if (StarWriterDocument[0x01] != 'W') return false;
125 // When 4.x is supported use also: || (StarWriterDocument[0x02] != '4')
126 if (StarWriterDocument[0x02] != '5') return false;
127 if (StarWriterDocument[0x03] != 'H') return false;
128 if (StarWriterDocument[0x04] != 'D') return false;
129 if (StarWriterDocument[0x05] != 'R') return false;
130
131 // Password-protection is not supported for the moment
132 TQ_UINT16 flags = readU16(StarWriterDocument, 0x0A);
133 if (flags & 0x0008) return false;
134
135 return true;
136}
137
138bool StarWriterImport::addKWordHeader()
139{
140 // Proper prolog and epilog
141 TQString prolog;
142 prolog = "<!DOCTYPE DOC>\n";
143 prolog.append("<DOC mime=\"application/x-kword\" syntaxVersion=\"2\" editor=\"KWord\">\n");
144 prolog.append("<PAPER width=\"595\" height=\"841\" format=\"1\" fType=\"0\" orientation=\"0\" hType=\"0\" columns=\"1\">\n");
145 prolog.append(" <PAPERBORDERS left=\"36\" right=\"36\" top=\"36\" bottom=\"36\" />\n");
146 prolog.append("</PAPER>\n");
147 prolog.append("<ATTRIBUTES standardpage=\"1\" hasFooter=\"0\" hasHeader=\"0\" processing=\"0\" />\n");
148 prolog.append("<FRAMESETS>\n");
149
150 maindoc.prepend(prolog);
151
152 maindoc.append("</FRAMESETS>\n");
153 maindoc.append("</DOC>");
154
155 return true;
156}
157
158bool StarWriterImport::addPageProperties()
159{
160 return true;
161}
162
163bool StarWriterImport::addStyles()
164{
165 return true;
166}
167
168// FIXME
169// 1. search for the right starting point
170// 2. determine the length
171// 3. parse everything with parseNodes()
172bool StarWriterImport::addHeaders()
173{
174 return true;
175}
176
177bool StarWriterImport::addFooters()
178{
179 return true;
180}
181
182bool StarWriterImport::addBody()
183{
184 // Find the starting point, by:
185 // 1. skipping the header
186 TQ_UINT32 len = StarWriterDocument[0x07];
187 TQ_UINT32 p = len;
188
189 // 2. skipping 8 more bytes
190 p += 0x08;
191
192 // 3. skipping useless sections
193 char c = StarWriterDocument[p];
194 while (c != 'N') {
195 len = readU24(StarWriterDocument, p+1);
196 p += len;
197 c = StarWriterDocument[p];
198 }; // there is at least one empty paragraph!
199
200 // Select nodes and pass them to parseNodes()
201 len = readU24(StarWriterDocument, p+1);
202 TQByteArray data(len);
203 for (TQ_UINT32 k=0; k<len; k++)
204 data[k] = StarWriterDocument[p+k];
205 bool retval = parseNodes(data);
206
207 // add proper tags
208 bodyStuff.prepend(" <FRAME right=\"567\" left=\"28\" top=\"42\" bottom=\"799\" />\n");
209 bodyStuff.prepend(" <FRAMESET removable=\"0\" frameType=\"1\" frameInfo=\"0\" name=\"Text Frameset 1\" autoCreateNewFrame=\"1\">\n");
210 bodyStuff.append(" </FRAMESET>\n");
211
212 return retval;
213}
214
215TQString StarWriterImport::convertToKWordString(TQByteArray s)
216{
217 TQString result;
218
219 for (TQ_UINT32 i = 0x00; i < s.size(); i++)
220 if (s[i] == '&') result += "&amp;";
221 else if (s[i] == '<') result += "&lt;";
222 else if (s[i] == '>') result += "&gt;";
223 else if (s[i] == '"') result += "&quot;";
224 else if (s[i] == 0x27) result += "&apos;";
225 else if (s[i] == 0x09) result += "\t";
226 // FIXME: more to add here
227 // (manual breaks, soft-hyphens, non-breaking spaces, variables)
228 else result += TQChar(s[i]);
229
230 return result;
231}
232
233bool StarWriterImport::parseNodes(TQByteArray n)
234{
235 TQByteArray s;
236 TQ_UINT32 len, p;
237
238 // Loop
239 p = 0x09; // is this a fixed value? is it the same for headers/footers?
240
241 while (p < n.size()) {
242 char c = n[p];
243 len = readU24(n, p+1);
244
245 s.resize(len);
246 for (TQ_UINT32 k = 0x00; k < len; k++)
247 s[k] = n[p+k];
248
249 switch (c) {
250 case 'T':
251 //if ((s[0x0A] == 0x01) && (s[0x0B] == 0x00) && (s[0x0C] == 0xFF)) {
252 // if (!parseGraphics(s)) return false;
253 //}
254 //else {
255 if (!parseText(s)) return false;
256 //}
257 break;
258 case 'E':
259 if (!parseTable(s)) return false;
260 break;
261 default:
262 break;
263 };
264 p += len;
265 };
266
267 return true;
268}
269
270bool StarWriterImport::parseText(TQByteArray n)
271{
272 TQByteArray s;
273 TQ_UINT16 len;
274 TQ_UINT32 p;
275 TQString text;
276 // TQ_UINT16 attributeStart, attributeEnd, formatPos, formatLen;
277 // TQString pAttributes, cAttributes, tempCAttributes;
278 // TQStringList cAttributesList;
279
280 // Retrieve the paragraph (text-only)
281 len = readU16(n, 0x09);
282 s.resize(len);
283 for (TQ_UINT16 k = 0x00; k < len; k++)
284 s[k] = n[0x0B+k];
285
286 /*
287 // Retrieve paragraph and character attributes
288 P = len;
289 while (n[p] == 'S') {
290 p += 0x04;
291 // parse 'A' sub-sections and write to pAttributes
292 // FIXME: all this part
293 // get section length
294 // if (length > ...) {
295 // if (special characters found) {
296 // get start, len, type
297 // write to the list
298 // }
299 // }
300 // increment p
301 }
302 while (n[p] == 'A') {
303 // parse 'A' sections and fill cAttributesList
304 // FIXME: similar as above
305 }
306 // Parse list
307 while ((!cAttributesList.isEmpty()) || (formatPos < len)) {
308 formatLen = 65535;
309 // FIXME: point to first list item
310 while (FIXME: current list item < last list item) {
311 // FIXME: get item members
312 if ((attributeStart <= formatPos) && (formatPos <= attributeEnd)) { // this attribute has to be considered
313 // FIXME: write attribute to tempCAttributes
314 formatLen = min(formatLen, (attributeStart - attributeEnd));
315 }
316 else if (attributeLen < formatPos) [ // this attribute has to be removed
317 // FIXME: remove list item
318 }
319 // FIXME: point to next list item
320 }
321 // FIXME: copy tempCAttributes to cAttributes
322 formatPos += formatLen;
323 }
324 */
325
326 // Write everything to the variable
327 text = convertToKWordString(s);
328 bodyStuff.append(" <PARAGRAPH>\n");
329 bodyStuff.append(" <TEXT xml:space=\"preserve\">" + text + "</TEXT>\n");
330 // FIXME: add FORMATS for pAttributes and cAttributes
331 bodyStuff.append(" </PARAGRAPH>\n");
332
333 return true;
334}
335
336bool StarWriterImport::parseTable(TQByteArray n)
337{
338/*
339 TQByteArray s;
340 TQ_UINT32 len, len2;
341 TQ_UINT16 len3;
342 TQ_UINT32 p, p2;
343 TQString text;
344 TQString tableCell, tableText, tableName;
345 TQ_UINT8 row, column;
346
347 // Set table name
348 tableName = TQString("Table %1").arg(tablesNumber);
349 tablesNumber++;
350
351 // Skip useless sections and retrieve the right point
352 p = 0x13;
353 while (n[p] != 'L') {
354 len = readU24(n, p+1);
355 p += len;
356 }
357
358 row = 0;
359
360 // Read rows
361 while (n[p] == 'L') {
362 column = 0;
363
364 // Find the first 't'
365 while (n[p] != 't') p++;
366
367 // Read cells
368 while (n[p] == 't') {
369 // Get cell length
370 len2 = readU24(n, p+1);
371 p2 = p + len2;
372
373 // Find the 'T' section
374 while (n[p] != 'T') p++;
375
376 // Get cell text/value
377 len3 = readU16(n, p+0x09);
378 s.resize(len3);
379 for (TQ_UINT16 k = 0x00; k < len3; k++)
380 s[k] = n[p+0x0B+k];
381 text = convertToKWordString(s);
382
383 // FIXME: check this stuff
384 TQString frameName = TQString("%1 Cell %2,%3").arg(tableName).arg(row).arg(column);
385 tableText.append(TQString(" <FRAMESET name=\"%1\" frameType=\"1\" frameInfo=\"0\" removable=\"0\" visible=\"1\" grpMgr=\"%2\" row=\"%3\" col=\"%4\" rows=\"1\" cols=\"1\" protectSize=\"0\">\n").arg(frameName).arg(tableName).arg(row).arg(column));
386 tableText.append(" <FRAME runaround=\"1\" copy=\"0\" newFrameBehavior=\"1\" runaroundSide=\"biggest\" autoCreateNewFrame=\"0\" bleftpt=\"2.8\" brightpt=\"2.8\" btoppt=\"2.8\" bbottompt=\"2.8\" runaroundGap=\"2.8\" />\n");
387 tableText.append(" <PARAGRAPH>\n");
388 tableText.append(" <TEXT xml:space=\"preserve\">" + text + "</TEXT>\n");
389 tableText.append(" </PARAGRAPH>\n");
390 tableText.append(" </FRAMESET>\n");
391
392 // Skip other sections or bytes
393 p = p2;
394
395 // Increase column pointers
396 column++;
397 }
398
399 // Increase row pointer
400 row++;
401 }
402
403 // Add everything to tablesStuff
404 tablesStuff.append(tableText);
405
406 // Add anchor to bodyStuff
407 bodyStuff.append(" <PARAGRAPH>\n");
408 bodyStuff.append(" <TEXT xml:space=\"preserve\">#</TEXT>\n");
409 bodyStuff.append(" <FORMATS>\n");
410 bodyStuff.append(" <FORMAT id=\"6\" pos=\"0\" len=\"1\">\n");
411 bodyStuff.append(TQString(" <ANCHOR type=\"frameset\" instance=\"%1\" />\n").arg(tableName));
412 bodyStuff.append(" </FORMAT>\n");
413 bodyStuff.append(" </FORMATS>\n");
414 bodyStuff.append(" </PARAGRAPH>\n");
415
416*/
417 return true;
418}
419
420bool StarWriterImport::parseGraphics(TQByteArray n)
421{
422 return true;
423}
424
425#include <starwriterimport.moc>