filters

kword13oasisgenerator.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2001, 2002, 2003, 2004 Nicolas GOUTTE <goutte@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 <tqstring.h>
21#include <tqdict.h>
22#include <tqfile.h>
23#include <tqbuffer.h>
24#include <tqcolor.h>
25#include <tqimage.h>
26
27#include <kdebug.h>
28#include <tdetempfile.h>
29#include <kmimetype.h>
30
31#include <kofficeversion.h>
32#include <KoStore.h>
33#include <KoStoreDevice.h>
34#include <KoXmlWriter.h>
35#include <KoGenStyles.h>
36#include <KoDocument.h>
37
38#include "kword13formatother.h"
39#include "kword13picture.h"
40#include "kword13document.h"
41
42#include "kword13oasisgenerator.h"
43
44KWord13OasisGenerator::KWord13OasisGenerator( void ) : m_kwordDocument( 0 ), m_store( 0 ), m_manifestWriter( 0 )
45{
46}
47
48KWord13OasisGenerator::~KWord13OasisGenerator( void )
49{
50}
51
52void KWord13OasisGenerator::prepareTextFrameset( KWordTextFrameset* frameset )
53{
54 if ( ! frameset )
55 {
56 kdWarning(30520) << "Tried to prepare a NULL text frameset!" << endl;
57 return;
58 }
59
60 for ( TQValueList<KWord13Paragraph>::Iterator it = frameset->m_paragraphGroup.begin();
61 it != frameset->m_paragraphGroup.end(); ++it)
62 {
63 declareLayout( (*it).m_layout );
64 for ( KWord13Format* format = (*it).m_formats.first(); format; format = (*it).m_formats.next() )
65 {
66 // ### Provisory, as it does not handle id != 1
67 KWord13FormatOneData* data = format->getFormatOneData();
68 if ( data )
69 {
70 // Inspired from KoTextParag::saveOasis, macro WRITESPAN
71 KoGenStyle gs( KoGenStyle::STYLE_AUTO, "text", (*it).m_layout.m_autoStyleName );
72 fillGenStyleWithFormatOne( *data , gs, false );
73 data->m_autoStyleName = m_oasisGenStyles.lookup( gs, "T" );
74 kdDebug(30520) << "Format: Parent " << (*it).m_layout.m_autoStyleName << " => " << data->m_autoStyleName << endl;
75 }
76 }
77 }
78}
79
80void KWord13OasisGenerator::preparePageLayout( void )
81{
82 // Inspired by KoPageLayout::saveOasis
83 KoGenStyle style(KoGenStyle::STYLE_PAGELAYOUT);
84 style.addPropertyPt("fo:page-width", positiveNumberOrNull ( m_kwordDocument->getProperty( "PAPER:width", "PAPER:ptWidth" ) ) );
85 style.addPropertyPt("fo:page-height", positiveNumberOrNull ( m_kwordDocument->getProperty( "PAPER:height", "PAPER:ptHeight" ) ) );
86 style.addPropertyPt("fo:margin-left", positiveNumberOrNull ( m_kwordDocument->getProperty( "PAPERBORDERS:left", "PAPERBORDERS:ptLeft" ) ) );
87 style.addPropertyPt("fo:margin-right", positiveNumberOrNull ( m_kwordDocument->getProperty( "PAPERBORDERS:right", "PAPERBORDERS:ptRight" ) ) );
88 style.addPropertyPt("fo:margin-top", positiveNumberOrNull ( m_kwordDocument->getProperty( "PAPERBORDERS:top", "PAPERBORDERS:ptTop" ) ) );
89 style.addPropertyPt("fo:margin-bottom", positiveNumberOrNull ( m_kwordDocument->getProperty( "PAPERBORDERS:bottom", "PAPERBORDERS:ptBottom" ) ) );
90 if ( m_kwordDocument->getProperty( "PAPER:orientation" ) == "1" )
91 {
92 style.addProperty("style:print-orientation", "landscape" );
93 }
94 else
95 {
96 style.addProperty("style:print-orientation", "portrait" );
97 }
98 // end of "inspiration"
99 // ### TODO: verify that it fits OASIS spec (as it is OO spec.)
100 bool ok = false;
101 const int firstPageNumber = m_kwordDocument->getProperty( "VARIABLESETTINGS:startingPageNumber" ).toInt( &ok );
102 style.addProperty("style:first-page-number", ( ( ok && firstPageNumber > 1 ) ? firstPageNumber : 1 ) );
103
104 const int columns = m_kwordDocument->getProperty( "PAPER:columns" ).toInt( &ok );
105 if ( ok && columns > 1 )
106 {
107 // ### TODO: test
108 TQBuffer buffer;
109 buffer.open( IO_WriteOnly );
110 KoXmlWriter element ( &buffer );
111 element.startElement("style:columns");
112 element.addAttribute( "fo:column-count", columns );
113 element.addAttributePt( "fo:column-gap", positiveNumberOrNull( m_kwordDocument->getProperty( "PAPER:columnspacing", "PAPER:ptColumnspc" ) ) );
114
115 for ( int i=0; i < columns; ++i )
116 {
117 element.startElement( "style:column" );
118 element.addAttribute( "style:rel-width", "1*" );
119 element.addAttributePt( "fo:margin-left", 0.0 );
120 element.addAttributePt( "fo:margin-right", 0.0 );
121 element.endElement();
122 }
123
124 element.endElement();
125 buffer.close();
126 const TQString strElement( TQString::fromUtf8( buffer.buffer(), buffer.buffer().size() ) );
127 style.addChildElement( "style:columns", strElement );
128 }
129 const TQString automaticPageStyle ( m_oasisGenStyles.lookup( style, "pm" ) );
130 kdDebug(30520) << "Automatic page style: " << automaticPageStyle << endl;
131}
132
133
134bool KWord13OasisGenerator::prepare( KWord13Document& kwordDocument )
135{
136 if ( m_kwordDocument && ( (void*) m_kwordDocument ) != ( (void*) &kwordDocument ) )
137 {
138 kdWarning(30520) << "KWord Document is different!" <<endl;
139 }
140
141 m_kwordDocument = &kwordDocument;
142
143 preparePageLayout();
144
145 // Declare styles
146 for ( TQValueList<KWord13Layout>::Iterator it = m_kwordDocument->m_styles.begin();
147 it != m_kwordDocument->m_styles.end(); ++it)
148 {
149 declareStyle( *it );
150 }
151
152 // Prepare first text frameset
153 prepareTextFrameset( m_kwordDocument->m_normalTextFramesetList.first() );
154
155 // ### TODO
156
157 return true;
158}
159
160double KWord13OasisGenerator::numberOrNull( const TQString& str ) const
161{
162 bool ok = false;
163 const double d = str.toDouble( &ok );
164 if ( ok )
165 return d;
166 else
167 return 0.0;
168}
169
170double KWord13OasisGenerator::positiveNumberOrNull( const TQString& str ) const
171{
172 bool ok = false;
173 const double d = str.toDouble( &ok );
174 if ( ok && d >= 0.0 )
175 return d;
176 else
177 return 0.0;
178}
179
180// Inspired by KoParagStyle::saveStyle
181void KWord13OasisGenerator::declareLayout( KWord13Layout& layout )
182{
183 KoGenStyle gs( KoGenStyle::STYLE_AUTO, "paragraph", layout.m_name );
184
185 // ### TODO: any display name? gs.addAttribute( "style:display-name", layout.m_name );
186#if 0
187 // TODO: check that this is correct
188 if ( m_paragLayout.counter && m_paragLayout.counter->depth() ) {
189 if ( m_bOutline )
190 gs.addAttribute( "style:default-outline-level", (int)m_paragLayout.counter->depth() + 1 );
191 else
192 gs.addAttribute( "style:default-level", (int)m_paragLayout.counter->depth() + 1 );
193 }
194#endif
195 fillGenStyleWithLayout( layout, gs, false );
196 fillGenStyleWithFormatOne( layout.m_format , gs, false );
197
198 layout.m_autoStyleName = m_oasisGenStyles.lookup( gs, "P", true );
199
200 kdDebug(30520) << "Layout: Parent " << layout.m_name << " => " << layout.m_autoStyleName << endl;
201}
202
203
204// Inspired by KoParagStyle::saveStyle
205void KWord13OasisGenerator::declareStyle( KWord13Layout& layout )
206{
207 KoGenStyle gs( KoGenStyle::STYLE_USER, "paragraph", TQString() );
208
209 gs.addAttribute( "style:display-name", layout.m_name );
210#if 0
211 // TODO: check that this is correct
212 if ( m_paragLayout.counter && m_paragLayout.counter->depth() ) {
213 if ( m_bOutline )
214 gs.addAttribute( "style:default-outline-level", (int)m_paragLayout.counter->depth() + 1 );
215 else
216 gs.addAttribute( "style:default-level", (int)m_paragLayout.counter->depth() + 1 );
217 }
218#endif
219 fillGenStyleWithLayout( layout, gs, true );
220 fillGenStyleWithFormatOne( layout.m_format , gs, true );
221
222 layout.m_autoStyleName = m_oasisGenStyles.lookup( gs, layout.m_name, false );
223
224 kdDebug(30520) << "Style: " << layout.m_name << " => " << layout.m_autoStyleName << endl;
225}
226
227
228// Inspired from KoTextFormat::save but we have not the same data to start with.
229void KWord13OasisGenerator::fillGenStyleWithFormatOne( const KWord13FormatOneData& one, KoGenStyle& gs, const bool style ) const
230{
231 TQString str; // helper string
232
233 KoGenStyle::PropertyType tt = KoGenStyle::TextType;
234 bool redok = false, greenok = false, blueok = false, ok = false;
235 const TQColor color(
236 one.getProperty( "COLOR:red" ).toInt( &redok ),
237 one.getProperty( "COLOR:green" ).toInt( &greenok ),
238 one.getProperty( "COLOR:blue" ).toInt( &blueok ) );
239 if ( color.isValid() && redok && greenok && blueok )
240 {
241 gs.addProperty( "fo:color", color.name(), tt );
242 }
243 else if ( style )
244 {
245 gs.addProperty( "fo:color", "#000000", tt );
246 }
247 // TODO declare svg font faces stuff according to the OASIS format;
248 str = one.getProperty( "FONT:name" );
249 if ( !str.isEmpty() )
250 {
251 gs.addProperty( "style:font-name", str, tt ); // hack
252 }
253 // ### TODO What to do if a style does not define a font? Do we leave it empty or do we use our default font?
254
255 double d = numberOrNull( one.getProperty( "SIZE:value" ) );
256 if ( d >= 1.0 ) // Sane value?
257 {
258 gs.addPropertyPt( "fo:font-size", d, tt );
259 }
260 // ### TODO If not, same question as with font name.
261
262 ok = false;
263 const int weight = one.getProperty( "WEIGHT:value" ).toInt( &ok );
264 if ( ok && weight >=0 )
265 {
266 if ( weight == 50 )
267 {
268 gs.addProperty( "fo:font-weight", "normal", tt );
269 }
270 else if (weight == 75 )
271 {
272 gs.addProperty( "fo:font-weight", "bold", tt );
273 }
274 else
275 {
276 gs.addProperty( "fo:font-weight", TQString::number( weight * 10 ), tt );
277 }
278 }
279 else if ( style )
280 {
281 gs.addProperty( "fo:font-weight", "normal", tt );
282 }
283
284 ok = false;
285 const int italic = one.getProperty( "ITALIC:value" ).toInt( &ok );
286 if ( ok && ( italic == 1 ) )
287 {
288 gs.addProperty( "fo:font-style", "italic", tt );
289 }
290 else if ( ( ok && ! italic ) || style )
291 {
292 gs.addProperty( "fo:font-style", "normal", tt );
293 }
294
295 // ### TODO
296#if 0
297
298 gs.addProperty( "style:text-underline-mode", d->m_bWordByWord ? "skip-white-space" : "continuous", tt );
299 gs.addProperty( "style:text-underline-type", m_underlineType == U_NONE ? "none" :
300 m_underlineType == U_DOUBLE ? "double" : "single", tt );
301 TQString styleline;
302 if ( m_underlineType == U_WAVE )
303 styleline = "wave";
304 else
305 styleline = exportOasisUnderline( m_underlineStyle );
306 gs.addProperty( "style:text-underline-style", styleline, tt );
307 gs.addProperty( "style:text-underline-color", m_textUnderlineColor.isValid() ? m_textUnderlineColor.name() : "font-color", tt );
308 // TODO U_SIMPLE_BOLD
309 // TODO style:text-line-through-mode
310 gs.addProperty( "style:text-line-through-type", m_strikeOutType == S_NONE ? "none" :
311 m_strikeOutType == S_DOUBLE ? "double" : "single", tt );
312
313 styleline = exportOasisUnderline( (UnderlineStyle) m_strikeOutStyle );
314 gs.addProperty( "style:text-line-through-style", styleline, tt );
315 //gs.addProperty( "style:text-line-through-color", ...) TODO in kotext
316
317 TQString textPos;
318 if ( d->m_offsetFromBaseLine != 0 )
319 textPos = TQString::number( 100 * d->m_offsetFromBaseLine / fn.pointSizeFloat() ) + '%';
320 else if ( va == AlignSuperScript ) textPos = "super";
321 else if ( va == AlignSubScript ) textPos = "sub";
322 else textPos = "0%";
323 textPos += ' ';
324 textPos += TQString::number( d->m_relativeTextSize * 100 );
325 textPos += '%';
326 gs.addProperty( "style:text-position", textPos, tt );
327
328 if ( m_attributeFont == ATT_SMALL_CAPS )
329 gs.addProperty( "fo:font-variant", "small-caps", tt );
330 else if ( m_attributeFont == ATT_UPPER )
331 gs.addProperty( "fo:text-transform", "uppercase", tt );
332 else if ( m_attributeFont == ATT_LOWER )
333 gs.addProperty( "fo:text-transform", "lowercase", tt );
334
335 gs.addProperty( "fo:language", m_language == "en_US" ? TQString("en") : m_language, tt );
336 gs.addProperty( "fo:background-color",
337 m_textBackColor.isValid() ? m_textBackColor.name() : "transparent", tt );
338 gs.addProperty( "fo:text-shadow", shadowAsCss(), tt );
339 gs.addProperty( "fo:hyphenate", d->m_bHyphenation, tt );
340#endif
341}
342
343// Inspired from KoParagLayout::saveOasis but we have not the same data to start with.
344void KWord13OasisGenerator::fillGenStyleWithLayout( const KWord13Layout& layout, KoGenStyle& gs, const bool style ) const
345{
346 // ### TODO syntaxVersion < 3
347
348 TQString str; // Help string to store each KWord 1.3 layout property
349
350 str = layout.getProperty( "FLOW:align" );
351 if ( str.isEmpty() && ! style)
352 {
353 // Nothing to do!
354 }
355 else if ( ( str == "left" ) || ( str == "right") || ( str == "center" ) || ( str == "justify" ) )
356 {
357 gs.addProperty( "fo:text-align", str );
358 }
359 else // KWord 1.3's "auto" (or empty/unknown string for a style)
360 {
361 gs.addProperty( "fo:text-align", "start" ); // i.e. direction-dependent
362 }
363
364 str = layout.getProperty( "FLOW:dir" );
365 if ( str == "R" ) // ### TODO: check the right value
366 {
367 gs.addProperty( "style:writing-mode", "rl-tb" ); // right-to-left, top-to-bottom
368 }
369 else if ( style )
370 {
371 gs.addProperty( "style:writing-mode", "lr-tb" ); // left-to-right, top-to-bottom
372 }
373
374 // ### TODO: do not define if it does not exist and ! style
375 gs.addPropertyPt( "fo:margin-left", numberOrNull( layout.getProperty( "INDENTS:left" ) ) );
376 gs.addPropertyPt( "fo:margin-right", numberOrNull( layout.getProperty( "INDENTS:right" ) ) );
377 gs.addPropertyPt( "fo:text-indent", numberOrNull( layout.getProperty( "INDENTS:first" ) ) );
378 gs.addPropertyPt( "fo:margin-top", numberOrNull( layout.getProperty( "OFFSETS:before" ) ) );
379 gs.addPropertyPt( "fo:margin-bottom", numberOrNull( layout.getProperty( "OFFSETS:after" ) ) );
380
381#if 0
382 switch ( lineSpacingType ) {
383 case KoParagLayout::LS_SINGLE:
384 gs.addProperty( "fo:line-height", "100%" );
385 break;
386 case KoParagLayout::LS_ONEANDHALF:
387 gs.addProperty( "fo:line-height", "150%" );
388 break;
389 case KoParagLayout::LS_DOUBLE:
390 gs.addProperty( "fo:line-height", "200%" );
391 break;
392 case KoParagLayout::LS_MULTIPLE:
393 gs.addProperty( "fo:line-height", TQString::number( lineSpacing * 100.0 ) + '%' );
394 break;
395 case KoParagLayout::LS_FIXED:
396 gs.addPropertyPt( "fo:line-height", lineSpacing );
397 break;
398 case KoParagLayout::LS_CUSTOM:
399 gs.addPropertyPt( "style:line-spacing", lineSpacing );
400 break;
401 case KoParagLayout::LS_AT_LEAST:
402 gs.addPropertyPt( "style:line-height-at-least", lineSpacing );
403 break;
404 }
405#endif
406
407#if 0
408 TQBuffer buffer;
409 buffer.open( IO_WriteOnly );
410 KoXmlWriter tabsWriter( &buffer, 4 ); // indent==4: root,autostyle,style,parag-props
411 tabsWriter.startElement( "style:tab-stops" );
412 KoTabulatorList::ConstIterator it = m_tabList.begin();
413 for ( ; it != m_tabList.end() ; it++ )
414 {
415 tabsWriter.startElement( "style:tab-stop" );
416 tabsWriter.addAttributePt( "style:position", (*it).ptPos );
417
418 switch ( (*it).type ) {
419 case T_LEFT:
420 tabsWriter.addAttribute( "style:type", "left" );
421 break;
422 case T_CENTER:
423 tabsWriter.addAttribute( "style:type", "center" );
424 break;
425 case T_RIGHT:
426 tabsWriter.addAttribute( "style:type", "right" );
427 break;
428 case T_DEC_PNT: // "alignment on decimal point"
429 tabsWriter.addAttribute( "style:type", "char" );
430 tabsWriter.addAttribute( "style:char", TQString( (*it).alignChar ) );
431 break;
432 case T_INVALID: // keep compiler happy, this can't happen
433 break;
434 }
435 switch( (*it).filling ) {
436 case TF_BLANK:
437 tabsWriter.addAttribute( "style:leader-type", "none" );
438 break;
439 case TF_LINE:
440 tabsWriter.addAttribute( "style:leader-type", "single" );
441 tabsWriter.addAttribute( "style:leader-style", "solid" );
442 // Give OOo a chance to show something, since it doesn't support lines here.
443 tabsWriter.addAttribute( "style:leader-text", "_" );
444 break;
445 case TF_DOTS:
446 tabsWriter.addAttribute( "style:leader-type", "single" );
447 tabsWriter.addAttribute( "style:leader-style", "dotted" );
448 // Give OOo a chance to show something, since it doesn't support lines here.
449 tabsWriter.addAttribute( "style:leader-text", "." );
450 break;
451 case TF_DASH:
452 tabsWriter.addAttribute( "style:leader-type", "single" );
453 tabsWriter.addAttribute( "style:leader-style", "dash" );
454 // Give OOo a chance to show something, since it doesn't support lines here.
455 tabsWriter.addAttribute( "style:leader-text", "_" );
456 break;
457 case TF_DASH_DOT:
458 tabsWriter.addAttribute( "style:leader-type", "single" );
459 tabsWriter.addAttribute( "style:leader-style", "dot-dash" );
460 // Give OOo a chance to show something, since it doesn't support lines here.
461 tabsWriter.addAttribute( "style:leader-text", "." );
462 break;
463 case TF_DASH_DOT_DOT:
464 tabsWriter.addAttribute( "style:leader-type", "single" );
465 tabsWriter.addAttribute( "style:leader-style", "dot-dot-dash" );
466 // Give OOo a chance to show something, since it doesn't support lines here.
467 tabsWriter.addAttribute( "style:leader-text", "." );
468 break;
469 }
470 if ( (*it).filling != TF_BLANK )
471 tabsWriter.addAttributePt( "style:leader-width", (*it).ptWidth );
472 // If we want to support it, oasis also defines style:leader-color
473 tabsWriter.endElement();
474 }
475 tabsWriter.endElement();
476 buffer.close();
477 TQString elementContents = TQString::fromUtf8( buffer.buffer(), buffer.buffer().size() );
478 gs.addChildElement( "style:tab-stops", elementContents );
479#endif
480
481#if 0
482 bool fourBordersEqual = leftBorder.penWidth() > 0 &&
483 leftBorder == rightBorder && rightBorder == topBorder && topBorder == bottomBorder;
484 if ( fourBordersEqual ) {
485 gs.addProperty( "fo:border", leftBorder.saveFoBorder() );
486 } else {
487 if ( leftBorder.penWidth() > 0 )
488 gs.addProperty( "fo:border-left", leftBorder.saveFoBorder() );
489 if ( rightBorder.penWidth() > 0 )
490 gs.addProperty( "fo:border-right", rightBorder.saveFoBorder() );
491 if ( topBorder.penWidth() > 0 )
492 gs.addProperty( "fo:border-top", topBorder.saveFoBorder() );
493 if ( bottomBorder.penWidth() > 0 )
494 gs.addProperty( "fo:border-bottom", bottomBorder.saveFoBorder() );
495 }
496#endif
497
498#if 0
499 if ( pageBreaking & KoParagLayout::HardFrameBreakBefore )
500 gs.addProperty( "fo:break-before", "column" );
501 else if ( pageBreaking & KoParagLayout::HardFrameBreakAfter )
502 gs.addProperty( "fo:break-after", "column" );
503 if ( pageBreaking & KoParagLayout::KeepLinesTogether )
504 gs.addProperty( "fo:keep-together", "always" );
505 if ( pageBreaking & KoParagLayout::KeepWithNext )
506 gs.addProperty( "fo:keep-with-next", "always" );
507#endif
508}
509
510// Inspired by KoTextParag::saveOasis
511void KWord13OasisGenerator::generateTextFrameset( KoXmlWriter& writer, KWordTextFrameset* frameset, bool /*main*/ )
512{
513 if ( ! frameset )
514 {
515 kdWarning(30520) << "Tried to generate a NULL text frameset!" << endl;
516 return;
517 }
518
519 for ( TQValueList<KWord13Paragraph>::Iterator it = frameset->m_paragraphGroup.begin();
520 it != frameset->m_paragraphGroup.end(); ++it)
521 {
522 // Write rawly the paragrapgh (see KoTextParag::saveOasis)
523 writer.startElement( "text:p", false ); // No indent inside!
524 writer.addAttribute( "text:style-name", (*it).m_layout.m_autoStyleName );
525#if 1
526 const TQString paragraphText( (*it).text() );
527 int currentPos = 0; // Current position where the next character has to be written
528
529 for ( KWord13Format* format = (*it).m_formats.first(); format; format = (*it).m_formats.next() )
530 {
531 // Perhaps we have text before the format's position
532 const int pos = format->m_pos;
533 const int length = format->length();
534 if ( currentPos < pos )
535 {
536 writer.addTextSpan( paragraphText.mid( currentPos, pos - currentPos ) );
537 currentPos = pos;
538 }
539 // Now we have to write the text belonging to the format
540 KWord13FormatOneData* data = format->getFormatOneData();
541 if ( data && format->m_id == 1 )
542 { // Normal text
543 writer.startElement( "text:span" );
544 writer.addAttribute( "text:style-name", data->m_autoStyleName );
545 writer.addTextSpan( paragraphText.mid( pos, length ) );
546 writer.endElement();
547 }
548 else if ( format->m_id == 3 )
549 { // Old tabulator
550 // ### TEMPORARY: do it with KWord13FormatOneData
551 writer.addTextSpan("\t"); // Tabulator
552 }
553 else if ( format->m_id == 4 )
554 { // Variable
555 // ### TEMPORARY
556 const TQString text ( ( (KWord13FormatFour*) format ) -> m_text );
557 if ( text.isEmpty() )
558 writer.addTextNode( "#" ); // Placeholder
559 else
560 writer.addTextSpan( text );
561 }
562 else
563 {
564 // ### TEMPORARY
565 writer.addTextNode("#"); // Placeholder
566 }
567 currentPos += length;
568 }
569 // We might have still something to write out
570 const TQString tailText( paragraphText.mid( currentPos ) );
571 if ( ! tailText.isEmpty() )
572 writer.addTextSpan( tailText );
573#else
574 writer.addTextSpan( (*it).text() );
575#endif
576 writer.endElement(); // text:p
577 }
578}
579
580// Inspired by KWDocument::saveOasisDocumentStyles
581void KWord13OasisGenerator::writeStylesXml( void )
582{
583 if ( !m_store || !m_kwordDocument )
584 {
585 kdError(30520) << "Not possible to generate style.xml" << endl;
586 return;
587 }
588
589 m_store->open("styles.xml"); // ### TODO: check error!
590 KoStoreDevice io ( m_store );
591 io.open( IO_WriteOnly ); // ### TODO: check error!
592
593 KoXmlWriter *stylesWriter = KoDocument::createOasisXmlWriter( &io, "office:document-styles" );
594
595 stylesWriter->startElement( "office:styles" );
596 TQValueList<KoGenStyles::NamedStyle> styles = m_oasisGenStyles.styles( KoGenStyle::STYLE_USER );
597 TQValueList<KoGenStyles::NamedStyle>::const_iterator it = styles.begin();
598 for ( ; it != styles.end() ; ++it ) {
599 (*it).style->writeStyle( stylesWriter, m_oasisGenStyles, "style:style", (*it).name, "style:paragraph-properties" );
600 }
601 stylesWriter->endElement(); // office:styles
602
603 stylesWriter->startElement( "office:automatic-styles" );
604#if 0
605 styles = m_oasisGenStyles.styles( KWDocument::STYLE_FRAME );
606 it = styles.begin();
607 for ( ; it != styles.end() ; ++it ) {
608 (*it).style->writeStyle( stylesWriter, m_oasisGenStyles, "style:style", (*it).name , "style:graphic-properties" );
609 }
610#endif
611
612 TQString pageLayoutName;
613 styles = m_oasisGenStyles.styles( KoGenStyle::STYLE_PAGELAYOUT );
614 Q_ASSERT( styles.count() == 1 );
615 it = styles.begin();
616 for ( ; it != styles.end() ; ++it ) {
617 (*it).style->writeStyle( stylesWriter, m_oasisGenStyles, "style:page-layout", (*it).name, "style:page-layout-properties", false /*don't close*/ );
618 //if ( m_pageLayout.columns > 1 ) TODO add columns element. This is a bit of a hack,
619 // which only works as long as we have only one page master
620 stylesWriter->endElement();
621 Q_ASSERT( pageLayoutName.isEmpty() ); // if there's more than one pagemaster we need to rethink all this
622 pageLayoutName = (*it).name;
623 }
624
625
626
627 stylesWriter->endElement(); // office:automatic-styles
628
629 stylesWriter->startElement( "office:master-styles" );
630 stylesWriter->startElement( "style:master-page" );
631 stylesWriter->addAttribute( "style:name", "Standard" );
632 stylesWriter->addAttribute( "style:page-layout-name", pageLayoutName );
633 stylesWriter->endElement();
634 stylesWriter->endElement(); // office:master-styles
635
636 stylesWriter->endElement(); // root element (office:document-styles)
637 stylesWriter->endDocument();
638 io.close();
639 m_store->close();
640 delete stylesWriter;
641
642 if ( m_manifestWriter )
643 {
644 m_manifestWriter->addManifestEntry( "styles.xml", "text/xml" );
645 }
646
647}
648
649// Inspired by KWDocument::saveOasis
650void KWord13OasisGenerator::writeContentXml(void)
651{
652 if ( !m_store || !m_kwordDocument )
653 {
654 kdError(30520) << "Not possible to generate content.xml" << endl;
655 return;
656 }
657
658 m_store->open("content.xml"); // ### TODO: check error!
659 KoStoreDevice io ( m_store );
660 io.open( IO_WriteOnly ); // ### TODO: check error!
661
662 KoXmlWriter *writer = KoDocument::createOasisXmlWriter( &io, "office:document-content" );
663
664
665 // Automatic styles
666 writer->startElement( "office:automatic-styles" );
667 TQValueList<KoGenStyles::NamedStyle> styles = m_oasisGenStyles.styles( KoGenStyle::STYLE_AUTO );
668 TQValueList<KoGenStyles::NamedStyle>::const_iterator it;
669 for ( it = styles.begin(); it != styles.end() ; ++it ) {
670 (*it).style->writeStyle( writer, m_oasisGenStyles, "style:style", (*it).name, "style:paragraph-properties" );
671 }
672 styles = m_oasisGenStyles.styles( KoGenStyle::STYLE_LIST );
673 for ( it = styles.begin(); it != styles.end() ; ++it ) {
674 (*it).style->writeStyle( writer, m_oasisGenStyles, "text:list-style", (*it).name, 0 );
675 }
676 writer->endElement(); // office:automatic-styles
677
678 writer->startElement( "office:body" );
679 writer->startElement( "office:text" );
680
681 // ### TODO: check that there is at least a normal text frameset
682 generateTextFrameset( *writer, m_kwordDocument->m_normalTextFramesetList.first(), true );
683
684 writer->endElement(); // office:text
685 writer->endElement(); // office:body
686
687
688 // ### TODO
689
690 writer->endElement();
691 writer->endDocument();
692 io.close();
693 delete writer;
694 m_store->close();
695
696 if ( m_manifestWriter )
697 {
698 m_manifestWriter->addManifestEntry( "content.xml", "text/xml" );
699 }
700}
701
702void KWord13OasisGenerator::writeMetaXml(void)
703{
704 if ( !m_store || !m_kwordDocument )
705 {
706 kdError(30520) << "Not possible to generate meta.xml" << endl;
707 return;
708 }
709
710 m_store->open("meta.xml"); // ### TODO: check error!
711 KoStoreDevice io ( m_store );
712 io.open( IO_WriteOnly ); // ### TODO: check error!
713
714 KoXmlWriter *writer = KoDocument::createOasisXmlWriter( &io, "office:document-meta" );
715
716 writer->startElement( "office:meta" );
717
718 // Tell who we are in case that we have a bug in our filter output!
719 // According to OASIS spec section 3.1.1, it has to follow section 14.43 of RFC 2616
720 writer->startElement( "meta:generator" );
721 TQString strVersion;
722 strVersion += "KWord-OneDotThree-Import-Filter/";
723 strVersion += TQString( "$Revision: 515673 $" ).mid( 10 ).remove( '$' ).stripWhiteSpace();
724 strVersion += " KOffice/";
725 strVersion += KOFFICE_VERSION_STRING;
726 writer->addTextSpan( strVersion );
727 writer->endElement();
728
729 TQString str; // Helper string
730
731 str = m_kwordDocument->getDocumentInfo( "about:title" );
732 if ( !str.isEmpty() )
733 {
734 writer->startElement( "dc:title" );
735 writer->addTextSpan( str );
736 writer->endElement();
737 }
738
739 str = m_kwordDocument->getDocumentInfo( "about:abstract" );
740 if (!str.isEmpty())
741 {
742 writer->startElement( "dc:description");
743 writer->addTextSpan( str );
744 writer->endElement();
745 }
746
747 str = m_kwordDocument->getDocumentInfo( "author:full-name" );
748 if ( !str.isEmpty() )
749 {
750 writer->startElement( "dc:creator" );
751 writer->addTextSpan( str );
752 writer->endElement();
753 }
754
755 // ### TODO: what about the other document info of KWord 1.3?
756
757 TQDateTime dt;
758
759 dt = m_kwordDocument->creationDate();
760 if ( dt.isValid() )
761 {
762 writer->startElement( "meta:creation-date");
763 writer->addTextNode( dt.toString( TQt::ISODate) );
764 writer->endElement();
765 }
766
767 dt = m_kwordDocument->modificationDate();
768 if ( dt.isValid() )
769 {
770 writer->startElement( "dc:date");
771 writer->addTextNode( dt.toString( TQt::ISODate) );
772 writer->endElement();
773 }
774
775 dt = m_kwordDocument->lastPrintingDate();
776 if ( dt.isValid() )
777 {
778 writer->startElement( "meta:print-date");
779 writer->addTextNode( dt.toString( TQt::ISODate) );
780 writer->endElement();
781 }
782
783 writer->startElement( "meta:document-statistic" );
784
785 // KWord files coming from import filters mostly do not have any page count
786 const int numPages = m_kwordDocument->getProperty( "PAPER:pages" ).toInt();
787 if ( numPages > 0 )
788 {
789 writer->addAttribute( "meta:page-count", numPages );
790 }
791
792#if 0
793 zipWriteData( " meta:image-count=\"" ); // This is not specified in the OO specification section 2.1.19, fixed in OASIS (### TODO)
794#if 1
795 zipWriteData( "0" ); // ### TODO
796#else
797 zipWriteData( TQString::number ( m_pictureNumber ) );
798#endif
799 zipWriteData( "\"" );
800
801 zipWriteData( " meta:table-count=\"" );
802#if 1
803 zipWriteData( "0" ); // ### TODO
804#else
805 zipWriteData( TQString::number ( m_tableNumber ) );
806#endif
807 zipWriteData( "\"" );
808#endif
809 writer->endElement(); // meta:document-statistic
810 writer->endElement(); // office:meta
811
812 writer->endElement();
813 writer->endDocument();
814 delete writer;
815
816 io.close();
817 m_store->close();
818
819 if ( m_manifestWriter )
820 {
821 m_manifestWriter->addManifestEntry( "meta.xml", "text/xml" );
822 }
823}
824
825void KWord13OasisGenerator::writePreviewFile(void)
826{
827 if ( !m_store || !m_kwordDocument )
828 {
829 kdError(30520) << "Not possible to generate preview file" << endl;
830 return;
831 }
832
833 // Load image
834 TQImage image( m_kwordDocument->m_previewFile->name() );
835 if ( image.isNull() )
836 {
837 kdWarning(30520) << "Could not re-read preview from temp file!" << endl;
838 return;
839 }
840
841 // We have a 256x256x8 preview and we need a 128x128x32 preview with alpha channel
842 TQImage preview( image.convertDepth( 32, TQt::ColorOnly ).smoothScale( 128, 128 ) );
843 if ( preview.isNull() )
844 {
845 kdWarning(30520) << "Could not create preview!" << endl;
846 return;
847 }
848 if ( !preview.hasAlphaBuffer() )
849 {
850 preview.setAlphaBuffer( true );
851 }
852 m_store->open("Thumbnails/thumbnail.png");
853 KoStoreDevice io ( m_store );
854 io.open( IO_WriteOnly ); // ### TODO: check error!
855 preview.save( &io, "PNG", 0 );
856 io.close();
857 m_store->close();
858
859 // No manifest entry, as it is supposed not to be part of the document.
860}
861
862void KWord13OasisGenerator::writePictures( void )
863{
864 if ( !m_store || !m_kwordDocument )
865 {
866 kdError(30520) << "Not possible to generate preview file" << endl;
867 return;
868 }
869
870 for ( TQDictIterator<KWord13Picture> it( m_kwordDocument->m_pictureDict ) ; it.current(); ++it )
871 {
872 if ( !it.current()->m_valid || !it.current()->m_tempFile )
873 {
874 kdDebug(30520) << "No data for picture: " << it.currentKey() << endl;
875 continue;
876 }
877 const TQString fileName( it.current()->m_tempFile->name() );
878 const TQString oasisName( it.current()->getOasisPictureName() );
879 kdDebug(30520) << "Copying... " << it.currentKey() << endl << " => " << oasisName << endl;
880 TQFile file( fileName );
881 if ( !file.open( IO_ReadOnly ) )
882 {
883 kdWarning(30520) << "Cannot open: " << fileName << endl;
884 continue;
885 }
886 TQByteArray array( file.readAll() );
887 if ( array.isNull() )
888 {
889 kdWarning(30520) << "Null picture for " << fileName << endl;
890 file.close();
891 continue;
892 }
893 file.close();
894
895 m_store->open( oasisName );
896 m_store->write( array );
897 m_store->close();
898
899 if ( m_manifestWriter )
900 {
901 const TQString mimeType ( KMimeType::findByContent( array, 0 )->name() );
902 if ( mimeType == "application/octet-stream" )
903 {
904 kdWarning(30520) << "Generic mime type for " << it.currentKey() << endl;
905 // ### TODO: try harder to find a mime type
906 }
907 m_manifestWriter->addManifestEntry( oasisName, mimeType );
908 }
909
910 }
911
912}
913
914bool KWord13OasisGenerator::generate ( const TQString& fileName, KWord13Document& kwordDocument )
915{
916 if ( m_kwordDocument && ( (void*) m_kwordDocument ) != ( (void*) &kwordDocument ) )
917 {
918 kdWarning(30520) << "KWord Document is different!" <<endl;
919 }
920
921 m_kwordDocument = &kwordDocument;
922
923 m_store = KoStore::createStore( fileName, KoStore::Write, "application/vnd.sun.xml.writer", KoStore::Zip );
924 if ( ! m_store )
925 {
926 kdError(30520) << "Cannot create output KoStore" << endl;
927 return false;
928 }
929 m_store->disallowNameExpansion();
930
931 // Prepare manifest file - in memory (inspired by KoDocument::saveNativeFormat)
932 TQByteArray manifestData;
933 TQBuffer manifestBuffer( manifestData );
934 manifestBuffer.open( IO_WriteOnly );
935 m_manifestWriter = new KoXmlWriter( &manifestBuffer );
936 m_manifestWriter->startDocument( "manifest:manifest" );
937 m_manifestWriter->startElement( "manifest:manifest" );
938 m_manifestWriter->addAttribute( "xmlns:manifest", "urn:oasis:names:tc:openoffice:xmlns:manifest:1.0" );
939
940 // ### TODO: check if writing the store is successful
941
942 writeStylesXml();
943 writeContentXml();
944 writeMetaXml();
945 writePictures();
946
947 // Write out manifest file
948 m_manifestWriter->endElement();
949 m_manifestWriter->endDocument();
950 delete m_manifestWriter;
951 m_manifestWriter = 0;
952 if ( m_store->open( "META-INF/manifest.xml" ) )
953 {
954 m_store->write( manifestData );
955 m_store->close();
956 }
957
958 if ( kwordDocument.m_previewFile )
959 {
960 writePreviewFile();
961 }
962 else
963 {
964 kdDebug(30520) << "No preview file available to make an OASIS thumbnail!" << endl;
965 }
966
967
968# ifndef NDEBUG // DEBUG (out of specification)
969 // No entry in the manifest file, as it is not part of the document
970 if ( m_store->open("Debug/debug.xml") )
971 {
972 KoStoreDevice io ( m_store );
973 io.open( IO_WriteOnly ); // ### TODO: check error!
974 kwordDocument.xmldump( &io );
975 io.close();
976 m_store->close();
977 }
978# endif
979
980
981 delete m_store;
982 m_store = 0;
983 return true;
984}
985
Contains the data of the <FORMAT id="1"> children also the grand-children of <STYLE> and <LAYOUT>.
TQString m_autoStyleName
Name of the OASIS automatic style.
Layout or style.
TQString m_autoStyleName
Name of the OASIS automatic style.
TQString m_name
Name of the style (either the used one or the one currently being defined).
KWord13FormatOneData m_format
Character format properties.