wpimport.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2001-2005 Ariya Hidayat <ariya@kde.org>
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 Boston, MA 02110-1301, USA.
18*/
19
20#include <config.h>
21
22#ifdef HAVE_UNISTD_H
23#include <unistd.h>
24#endif
25
26#include <kdebug.h>
27#include <KoFilterChain.h>
28#include <kgenericfactory.h>
29
30#include <wpimport.h>
31
32#include <stdio.h> // debug
33
34typedef KGenericFactory<WPImport, KoFilter> WPImportFactory;
35K_EXPORT_COMPONENT_FACTORY( libwpimport, WPImportFactory( "kofficefilters" ) )
36
37#include <libwpd/libwpd.h>
38#include "DocumentHandler.h"
39#include "WordPerfectCollector.h"
40
41class _SH_MemoryInputStream : public _SH_InputStream
42{
43public:
44 _SH_MemoryInputStream(unsigned char *data, unsigned long size);
45 virtual ~_SH_MemoryInputStream();
46
47 virtual bool isOLEStream() { return false; }
48#if defined(HAVE_LIBWPD_0100) || defined(HAVE_LIBWPD_090)
49 virtual _SH_InputStream * getDocumentOLEStream(const char *name = NULL) { return NULL; }
50 const virtual unsigned char *read(unsigned long numBytes, unsigned long &numBytesRead);
51#else
52 virtual _SH_InputStream * getDocumentOLEStream() { return NULL; }
53 const virtual uint8_t *read(size_t numBytes, size_t &numBytesRead);
54#endif
55
56 virtual int seek(long offset, _SH_SEEK_TYPE seekType);
57 virtual long tell();
58 virtual bool atEOS();
59
60#ifdef HAVE_LIBWPD_0100
61 virtual bool isStructured() { return false; }
62 virtual unsigned subStreamCount() { return 0U; }
63 virtual const char *subStreamName(unsigned id) { return NULL; }
64 virtual bool existsSubStream(const char *name) { return false; }
65 virtual RVNGInputStream *getSubStreamByName(const char *name) { return NULL; }
66 virtual RVNGInputStream *getSubStreamById(unsigned id) { return NULL; }
67 virtual bool isEnd() { return false; }
68#endif
69
70private:
71 long m_offset;
72 size_t m_size;
73 unsigned char *m_data;
74};
75
76_SH_MemoryInputStream::_SH_MemoryInputStream(unsigned char *data, unsigned long size) :
77#if defined(HAVE_LIBWPD_090) || defined(HAVE_LIBWPD_0100)
78 _SH_InputStream(),
79#else
80 _SH_InputStream(false),
81#endif
82 m_offset(0),
83 m_size(size),
84 m_data(data)
85{
86}
87
88_SH_MemoryInputStream::~_SH_MemoryInputStream()
89{
90 if (m_data)
91 delete [] m_data;
92}
93
94#if defined(HAVE_LIBWPD_090) || defined(HAVE_LIBWPD_0100)
95const unsigned char * _SH_MemoryInputStream::read(unsigned long numBytes, unsigned long &numBytesRead)
96#else
97const uint8_t * _SH_MemoryInputStream::read(size_t numBytes, size_t &numBytesRead)
98#endif
99{
100 numBytesRead = 0;
101
102 if (numBytes == 0)
103 return 0;
104
105 int numBytesToRead;
106
107 if ((m_offset+numBytes) < m_size)
108 numBytesToRead = numBytes;
109 else
110 numBytesToRead = m_size - m_offset;
111
112 numBytesRead = numBytesToRead; // about as paranoid as we can be..
113
114 if (numBytesToRead == 0)
115 return 0;
116
117 long oldOffset = m_offset;
118 m_offset += numBytesToRead;
119
120 return &m_data[oldOffset];
121}
122
123int _SH_MemoryInputStream::seek(long offset, _SH_SEEK_TYPE seekType)
124{
125 if (seekType == _SH_SEEK_CUR)
126 m_offset += offset;
127 else if (seekType == _SH_SEEK_SET)
128 m_offset = offset;
129
130 if (m_offset < 0)
131 {
132 m_offset = 0;
133 return 1;
134 }
135 if ((long)m_offset > (long)m_size)
136 {
137 m_offset = m_size;
138 return 1;
139 }
140
141 return 0;
142}
143
144long _SH_MemoryInputStream::tell()
145{
146 return m_offset;
147}
148
149bool _SH_MemoryInputStream::atEOS()
150{
151 if ((long)m_offset == (long)m_size)
152 return true;
153
154 return false;
155}
156
157class KWordHandler : public DocumentHandler
158{
159public:
160 KWordHandler();
161 virtual ~KWordHandler() {};
162 void startDocument();
163 void endDocument();
164 void startElement(const char *psName, const _SH_PropertyList &xPropList);
165 void endElement(const char *psName);
166 void characters(const _SH_String &sCharacters);
167 _SH_String documentstring;
168private:
169 bool isTagOpened;
170 _SH_String openedTagName;
171};
172
173KWordHandler::KWordHandler() :
174 isTagOpened(false)
175{
176}
177
178void KWordHandler::startDocument()
179{
180 documentstring.clear();
181}
182
183void KWordHandler::startElement(const char *psName, const _SH_PropertyList &xPropList)
184{
185 if (isTagOpened)
186 {
187 documentstring.append( ">" );
188 isTagOpened = false;
189 }
190 _SH_String tempString;
191 tempString.sprintf("<%s", psName);
192 documentstring.append( tempString );
193 _SH_PropertyList::Iter i(xPropList);
194 for (i.rewind(); i.next(); )
195 {
196 // filter out libwpd elements
197 if (strlen(i.key()) > 6 && strncmp(i.key(), "libwpd", 6) != 0)
198 {
199 tempString.sprintf(" %s=\"%s\"", i.key(), i()->getStr().cstr());
200 documentstring.append( tempString );
201 }
202 }
203 isTagOpened = true;
204 openedTagName.sprintf("%s", psName);
205}
206
207void KWordHandler::endElement(const char *psName)
208{
209 if ((isTagOpened) && (openedTagName == psName))
210 documentstring.append( " />" );
211 else
212 {
213 _SH_String tempString;
214 tempString.sprintf("</%s>", psName);
215 documentstring.append( tempString );
216 }
217 isTagOpened = false;
218}
219
220void KWordHandler::characters(const _SH_String &sCharacters)
221{
222 if (isTagOpened)
223 {
224 documentstring.append( ">" );
225 isTagOpened = false;
226 }
227#ifdef HAVE_LIBWPD_0100
228 documentstring.append( _SH_String::escapeXML(sCharacters) );
229#else
230 documentstring.append( _SH_String(sCharacters, true) );
231#endif
232}
233
234
235void KWordHandler::endDocument()
236{
237 if (isTagOpened)
238 {
239 documentstring.append( ">" );
240 isTagOpened = false;
241 }
242}
243
244
245
246WPImport::WPImport( KoFilter *, const char *, const TQStringList& ): KoFilter()
247{
248}
249
250KoFilter::ConversionStatus WPImport::convert( const TQCString& from, const TQCString& to )
251{
252 // check for proper conversion
253 if(to!= "application/vnd.sun.xml.writer" || from != "application/wordperfect" )
254 return KoFilter::NotImplemented;
255
256 // open input file
257 const char* infile = m_chain->inputFile().latin1();
258 FILE *f = fopen( infile, "rb" );
259 if( !f )
260 return KoFilter::StupidError;
261
262 fseek( f, 0, SEEK_END );
263 long fsize = ftell( f );
264 fseek( f, 0, SEEK_SET );
265
266 unsigned char* buf = new unsigned char[fsize];
267 fread( buf, 1, fsize, f );
268 fclose( f );
269
270 // instream now owns buf, no need to delete buf later
271 _SH_MemoryInputStream instream = _SH_MemoryInputStream( buf, fsize );
272
273#if defined(HAVE_LIBWPD_0100)
274 libwpd::WPDConfidence confidence = libwpd::WPDocument::isFileFormatSupported(&instream);
275#elif defined(HAVE_LIBWPD_090)
276 WPDConfidence confidence = WPDocument::isFileFormatSupported(&instream);
277#else
278 WPDConfidence confidence = WPDocument::isFileFormatSupported(&instream, false);
279#endif
280#if defined(HAVE_LIBWPD_0100)
281 if( confidence == libwpd::WPD_CONFIDENCE_NONE )
282#else
283 if( confidence == WPD_CONFIDENCE_NONE )
284#endif
285 {
286 fprintf(stderr, "ERROR: We have no confidence that you are giving us a valid WordPerfect document.\n");
287 return KoFilter::StupidError;
288 }
289 instream.seek(0, _SH_SEEK_SET);
290
291 // open and parse the file
292 KWordHandler handler;
293
294 WordPerfectCollector collector(&instream, &handler);
295
296 if ( !collector.filter() ) return KoFilter::StupidError;
297
298 // prepare storage
299 KoStoreDevice* manifest = m_chain->storageFile( "META-INF/manifest.xml", KoStore::Write );
300 if ( manifest )
301 {
302 TQCString manifeststring = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\
303<!DOCTYPE manifest:manifest PUBLIC \"-//OpenOffice.org//DTD Manifest 1.0//EN\" \"Manifest.dtd\">\n\
304<manifest:manifest xmlns:manifest=\"http://openoffice.org/2001/manifest\">\n\
305<manifest:file-entry manifest:media-type=\"application/vnd.sun.xml.writer\" manifest:full-path=\"/\"/>\n\
306<manifest:file-entry manifest:media-type=\"text/xml\" manifest:full-path=\"content.xml\"/>\n\
307<manifest:file-entry manifest:media-type=\"text/xml\" manifest:full-path=\"styles.xml\"/>\n\
308</manifest:manifest>\n";
309 manifest->writeBlock( (const char*) manifeststring, manifeststring.length() );
310 }
311
312 KoStoreDevice* styles = m_chain->storageFile( "styles.xml", KoStore::Write );
313 if ( styles )
314 {
315 TQCString stylesstring = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\
316<!DOCTYPE office:document-styles PUBLIC \"-//OpenOffice.org//DTD OfficeDocument 1.0//EN\" \"office.dtd\">\
317<office:document-styles xmlns:office=\"http://openoffice.org/2000/office\" xmlns:style=\"http://openoffice.org/2000/style\"\
318 xmlns:text=\"http://openoffice.org/2000/text\" xmlns:table=\"http://openoffice.org/2000/table\"\
319 xmlns:draw=\"http://openoffice.org/2000/drawing\" xmlns:fo=\"http://www.w3.org/1999/XSL/Format\"\
320 xmlns:xlink=\"http://www.w3.org/1999/xlink\" xmlns:number=\"http://openoffice.org/2000/datastyle\"\
321 xmlns:svg=\"http://www.w3.org/2000/svg\" xmlns:chart=\"http://openoffice.org/2000/chart\" xmlns:dr3d=\"http://openoffice.org/2000/dr3d\"\
322 xmlns:math=\"http://www.w3.org/1998/Math/MathML\" xmlns:form=\"http://openoffice.org/2000/form\"\
323 xmlns:script=\"http://openoffice.org/2000/script\" office:version=\"1.0\">\
324<office:styles>\
325<style:default-style style:family=\"paragraph\">\
326<style:properties style:use-window-font-color=\"true\" style:text-autospace=\"ideograph-alpha\"\
327 style:punctuation-wrap=\"hanging\" style:line-break=\"strict\" style:writing-mode=\"page\"/>\
328</style:default-style>\
329<style:default-style style:family=\"table\"/>\
330<style:default-style style:family=\"table-row\"/>\
331<style:default-style style:family=\"table-column\"/>\
332<style:style style:name=\"Standard\" style:family=\"paragraph\" style:class=\"text\"/>\
333<style:style style:name=\"Text body\" style:family=\"paragraph\" style:parent-style-name=\"Standard\" style:class=\"text\"/>\
334<style:style style:name=\"List\" style:family=\"paragraph\" style:parent-style-name=\"Text body\" style:class=\"list\"/>\
335<style:style style:name=\"Header\" style:family=\"paragraph\" style:parent-style-name=\"Standard\" style:class=\"extra\"/>\
336<style:style style:name=\"Footer\" style:family=\"paragraph\" style:parent-style-name=\"Standard\" style:class=\"extra\"/>\
337<style:style style:name=\"Caption\" style:family=\"paragraph\" style:parent-style-name=\"Standard\" style:class=\"extra\"/>\
338<style:style style:name=\"Footnote\" style:family=\"paragraph\" style:parent-style-name=\"Standard\" style:class=\"extra\"/>\
339<style:style style:name=\"Endnote\" style:family=\"paragraph\" style:parent-style-name=\"Standard\" style:class=\"extra\"/>\
340<style:style style:name=\"Index\" style:family=\"paragraph\" style:parent-style-name=\"Standard\" style:class=\"index\"/>\
341<style:style style:name=\"Footnote Symbol\" style:family=\"text\">\
342<style:properties style:text-position=\"super 58%\"/>\
343</style:style>\
344<style:style style:name=\"Endnote Symbol\" style:family=\"text\">\
345<style:properties style:text-position=\"super 58%\"/>\
346</style:style>\
347<style:style style:name=\"Footnote anchor\" style:family=\"text\">\
348<style:properties style:text-position=\"super 58%\"/>\
349</style:style>\
350<style:style style:name=\"Endnote anchor\" style:family=\"text\">\
351<style:properties style:text-position=\"super 58%\"/>\
352</style:style>\
353<text:footnotes-configuration text:citation-style-name=\"Footnote Symbol\" text:citation-body-style-name=\"Footnote anchor\"\
354 style:num-format=\"1\" text:start-value=\"0\" text:footnotes-position=\"page\" text:start-numbering-at=\"document\"/>\
355<text:endnotes-configuration text:citation-style-name=\"Endnote Symbol\" text:citation-body-style-name=\"Endnote anchor\"\
356 text:master-page-name=\"Endnote\" style:num-format=\"i\" text:start-value=\"0\"/>\
357<text:linenumbering-configuration text:number-lines=\"false\" text:offset=\"0.1965inch\" style:num-format=\"1\"\
358 text:number-position=\"left\" text:increment=\"5\"/>\
359</office:styles>\
360<office:automatic-styles>\
361<style:page-master style:name=\"PM0\">\
362<style:properties fo:margin-bottom=\"1.0000inch\" fo:margin-left=\"1.0000inch\" fo:margin-right=\"1.0000inch\" fo:margin-top=\"1.0000inch\"\
363 fo:page-height=\"11.0000inch\" fo:page-width=\"8.5000inch\" style:print-orientation=\"portrait\">\
364<style:footnote-sep style:adjustment=\"left\" style:color=\"#000000\" style:distance-after-sep=\"0.0398inch\"\
365 style:distance-before-sep=\"0.0398inch\" style:rel-width=\"25%\" style:width=\"0.0071inch\"/>\
366</style:properties>\
367</style:page-master>\
368<style:page-master style:name=\"PM1\">\
369<style:properties fo:margin-bottom=\"1.0000inch\" fo:margin-left=\"1.0000inch\" fo:margin-right=\"1.0000inch\" fo:margin-top=\"1.0000inch\"\
370 fo:page-height=\"11.0000inch\" fo:page-width=\"8.5000inch\" style:print-orientation=\"portrait\">\
371<style:footnote-sep style:adjustment=\"left\" style:color=\"#000000\" style:rel-width=\"25%\"/>\
372</style:properties>\
373</style:page-master>\
374</office:automatic-styles>\
375<office:master-styles>\
376<style:master-page style:name=\"Standard\" style:page-master-name=\"PM0\"/>\
377<style:master-page style:name=\"Endnote\" style:page-master-name=\"PM1\"/>\
378</office:master-styles>\
379</office:document-styles>";
380 styles->writeBlock( (const char*) stylesstring, stylesstring.length() );
381 }
382
383 KoStoreDevice* out = m_chain->storageFile( "content.xml", KoStore::Write );
384
385 if( out )
386 out->writeBlock( (const char*) handler.documentstring.cstr(), strlen(handler.documentstring.cstr()) );
387
388 return KoFilter::OK;
389}
390
391#include "wpimport.moc"