filters

kword13oasisgenerator.cpp

00001 /* This file is part of the KDE project
00002    Copyright (C) 2001, 2002, 2003, 2004 Nicolas GOUTTE <goutte@kde.org>
00003 
00004    This library is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Library General Public
00006    License as published by the Free Software Foundation; either
00007    version 2 of the License, or (at your option) any later version.
00008 
00009    This library is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Library General Public License for more details.
00013 
00014    You should have received a copy of the GNU Library General Public License
00015    along with this library; see the file COPYING.LIB.  If not, write to
00016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017  * Boston, MA 02110-1301, USA.
00018 */
00019 
00020 #include <qstring.h>
00021 #include <qdict.h>
00022 #include <qfile.h>
00023 #include <qbuffer.h>
00024 #include <qcolor.h>
00025 #include <qimage.h>
00026 
00027 #include <kdebug.h>
00028 #include <ktempfile.h>
00029 #include <kmimetype.h>
00030 
00031 #include <kofficeversion.h>
00032 #include <KoStore.h>
00033 #include <KoStoreDevice.h>
00034 #include <KoXmlWriter.h>
00035 #include <KoGenStyles.h>
00036 #include <KoDocument.h>
00037 
00038 #include "kword13formatother.h"
00039 #include "kword13picture.h"
00040 #include "kword13document.h"
00041 
00042 #include "kword13oasisgenerator.h"
00043 
00044 KWord13OasisGenerator::KWord13OasisGenerator( void ) : m_kwordDocument( 0 ),  m_store( 0 ), m_manifestWriter( 0 )
00045 {
00046 }
00047 
00048 KWord13OasisGenerator::~KWord13OasisGenerator( void )
00049 {
00050 }
00051 
00052 void KWord13OasisGenerator::prepareTextFrameset( KWordTextFrameset* frameset )
00053 {
00054     if ( ! frameset )
00055     {
00056         kdWarning(30520) << "Tried to prepare a NULL text frameset!" << endl;
00057         return;
00058     }
00059 
00060     for ( QValueList<KWord13Paragraph>::Iterator it = frameset->m_paragraphGroup.begin();
00061         it != frameset->m_paragraphGroup.end(); ++it)
00062     {
00063         declareLayout( (*it).m_layout );
00064         for ( KWord13Format* format = (*it).m_formats.first(); format; format = (*it).m_formats.next() )
00065         {
00066             // ### Provisory, as it does not handle id != 1
00067             KWord13FormatOneData* data = format->getFormatOneData();
00068             if ( data )
00069             {
00070                 // Inspired from KoTextParag::saveOasis, macro WRITESPAN
00071                 KoGenStyle gs( KoGenStyle::STYLE_AUTO, "text", (*it).m_layout.m_autoStyleName );
00072                 fillGenStyleWithFormatOne( *data , gs, false );
00073                 data->m_autoStyleName = m_oasisGenStyles.lookup( gs, "T" );
00074                 kdDebug(30520) << "Format: Parent " << (*it).m_layout.m_autoStyleName << " => " << data->m_autoStyleName << endl;
00075             }
00076         }
00077     }
00078 }
00079 
00080 void KWord13OasisGenerator::preparePageLayout( void )
00081 {
00082     // Inspired by KoPageLayout::saveOasis
00083     KoGenStyle style(KoGenStyle::STYLE_PAGELAYOUT);
00084     style.addPropertyPt("fo:page-width", positiveNumberOrNull ( m_kwordDocument->getProperty( "PAPER:width", "PAPER:ptWidth" ) ) );
00085     style.addPropertyPt("fo:page-height", positiveNumberOrNull ( m_kwordDocument->getProperty( "PAPER:height", "PAPER:ptHeight" ) ) );
00086     style.addPropertyPt("fo:margin-left", positiveNumberOrNull ( m_kwordDocument->getProperty( "PAPERBORDERS:left", "PAPERBORDERS:ptLeft" ) ) );
00087     style.addPropertyPt("fo:margin-right", positiveNumberOrNull ( m_kwordDocument->getProperty( "PAPERBORDERS:right", "PAPERBORDERS:ptRight" ) ) );
00088     style.addPropertyPt("fo:margin-top", positiveNumberOrNull ( m_kwordDocument->getProperty( "PAPERBORDERS:top", "PAPERBORDERS:ptTop" ) ) );
00089     style.addPropertyPt("fo:margin-bottom", positiveNumberOrNull ( m_kwordDocument->getProperty( "PAPERBORDERS:bottom", "PAPERBORDERS:ptBottom" ) ) );
00090     if ( m_kwordDocument->getProperty( "PAPER:orientation" ) == "1" )
00091     {
00092         style.addProperty("style:print-orientation", "landscape" );
00093     }
00094     else
00095     {
00096         style.addProperty("style:print-orientation", "portrait" );
00097     }
00098     // end of "inspiration"
00099     // ### TODO: verify that it fits OASIS spec (as it is OO spec.)
00100     bool ok = false;
00101     const int firstPageNumber = m_kwordDocument->getProperty( "VARIABLESETTINGS:startingPageNumber" ).toInt( &ok );
00102     style.addProperty("style:first-page-number", ( ( ok && firstPageNumber > 1 ) ? firstPageNumber : 1 ) );
00103 
00104     const int columns = m_kwordDocument->getProperty( "PAPER:columns" ).toInt( &ok );
00105     if ( ok && columns > 1 )
00106     {
00107         // ### TODO: test
00108         QBuffer buffer;
00109         buffer.open( IO_WriteOnly );
00110         KoXmlWriter element ( &buffer );
00111         element.startElement("style:columns");
00112         element.addAttribute( "fo:column-count", columns );
00113         element.addAttributePt( "fo:column-gap", positiveNumberOrNull( m_kwordDocument->getProperty( "PAPER:columnspacing", "PAPER:ptColumnspc" ) ) );
00114 
00115         for ( int i=0; i < columns; ++i )
00116         {
00117             element.startElement( "style:column" );
00118             element.addAttribute( "style:rel-width", "1*" );
00119             element.addAttributePt( "fo:margin-left", 0.0 );
00120             element.addAttributePt( "fo:margin-right", 0.0 );
00121             element.endElement();
00122         }
00123 
00124         element.endElement();
00125         buffer.close();
00126         const QString strElement( QString::fromUtf8( buffer.buffer(), buffer.buffer().size() ) );
00127         style.addChildElement( "style:columns", strElement );
00128     }
00129     const QString automaticPageStyle ( m_oasisGenStyles.lookup( style, "pm" ) );
00130     kdDebug(30520) << "Automatic page style: " << automaticPageStyle << endl;
00131 }
00132 
00133 
00134 bool KWord13OasisGenerator::prepare( KWord13Document& kwordDocument )
00135 {
00136     if ( m_kwordDocument && ( (void*) m_kwordDocument ) != ( (void*) &kwordDocument ) )
00137     {
00138         kdWarning(30520) << "KWord Document is different!" <<endl;
00139     }
00140 
00141     m_kwordDocument = &kwordDocument;
00142 
00143     preparePageLayout();
00144 
00145     // Declare styles
00146     for ( QValueList<KWord13Layout>::Iterator it = m_kwordDocument->m_styles.begin();
00147         it != m_kwordDocument->m_styles.end(); ++it)
00148     {
00149         declareStyle( *it );
00150     }
00151 
00152     // Prepare first text frameset
00153     prepareTextFrameset( m_kwordDocument->m_normalTextFramesetList.first() );
00154 
00155     // ### TODO
00156 
00157     return true;
00158 }
00159 
00160 double KWord13OasisGenerator::numberOrNull( const QString& str ) const
00161 {
00162     bool ok = false;
00163     const double d = str.toDouble( &ok );
00164     if ( ok )
00165         return d;
00166     else
00167         return 0.0;
00168 }
00169 
00170 double KWord13OasisGenerator::positiveNumberOrNull( const QString& str ) const
00171 {
00172     bool ok = false;
00173     const double d = str.toDouble( &ok );
00174     if ( ok && d >= 0.0 )
00175         return d;
00176     else
00177         return 0.0;
00178 }
00179 
00180 // Inspired by KoParagStyle::saveStyle
00181 void KWord13OasisGenerator::declareLayout( KWord13Layout& layout )
00182 {
00183     KoGenStyle gs( KoGenStyle::STYLE_AUTO, "paragraph", layout.m_name );
00184 
00185     // ### TODO: any display name? gs.addAttribute( "style:display-name", layout.m_name );
00186 #if 0
00187     // TODO: check that this is correct
00188     if ( m_paragLayout.counter && m_paragLayout.counter->depth() ) {
00189         if ( m_bOutline )
00190             gs.addAttribute( "style:default-outline-level", (int)m_paragLayout.counter->depth() + 1 );
00191         else
00192             gs.addAttribute( "style:default-level", (int)m_paragLayout.counter->depth() + 1 );
00193     }
00194 #endif
00195     fillGenStyleWithLayout( layout, gs, false );
00196     fillGenStyleWithFormatOne( layout.m_format , gs, false );
00197 
00198     layout.m_autoStyleName = m_oasisGenStyles.lookup( gs, "P", true );
00199 
00200     kdDebug(30520) << "Layout: Parent " << layout.m_name << " => " << layout.m_autoStyleName << endl;
00201 }
00202 
00203 
00204 // Inspired by KoParagStyle::saveStyle
00205 void KWord13OasisGenerator::declareStyle( KWord13Layout& layout )
00206 {
00207     KoGenStyle gs( KoGenStyle::STYLE_USER, "paragraph", QString::null );
00208 
00209     gs.addAttribute( "style:display-name", layout.m_name );
00210 #if 0
00211     // TODO: check that this is correct
00212     if ( m_paragLayout.counter && m_paragLayout.counter->depth() ) {
00213         if ( m_bOutline )
00214             gs.addAttribute( "style:default-outline-level", (int)m_paragLayout.counter->depth() + 1 );
00215         else
00216             gs.addAttribute( "style:default-level", (int)m_paragLayout.counter->depth() + 1 );
00217     }
00218 #endif
00219     fillGenStyleWithLayout( layout, gs, true );
00220     fillGenStyleWithFormatOne( layout.m_format , gs, true );
00221 
00222     layout.m_autoStyleName = m_oasisGenStyles.lookup( gs, layout.m_name, false );
00223 
00224     kdDebug(30520) << "Style: " << layout.m_name << " => " << layout.m_autoStyleName << endl;
00225 }
00226 
00227 
00228 // Inspired from KoTextFormat::save but we have not the same data to start with.
00229 void KWord13OasisGenerator::fillGenStyleWithFormatOne( const KWord13FormatOneData& one, KoGenStyle& gs, const bool style ) const
00230 {
00231     QString str; // helper string
00232 
00233     KoGenStyle::PropertyType tt = KoGenStyle::TextType;
00234     bool redok = false, greenok = false, blueok = false, ok = false;
00235     const QColor color(
00236         one.getProperty( "COLOR:red" ).toInt( &redok ),
00237         one.getProperty( "COLOR:green" ).toInt( &greenok ),
00238         one.getProperty( "COLOR:blue" ).toInt( &blueok ) );
00239     if ( color.isValid() && redok && greenok && blueok )
00240     {
00241         gs.addProperty( "fo:color", color.name(), tt );
00242     }
00243     else if ( style )
00244     {
00245         gs.addProperty( "fo:color", "#000000", tt );
00246     }
00247     // TODO declare svg font faces stuff according to the OASIS format;
00248     str = one.getProperty( "FONT:name" );
00249     if ( !str.isEmpty() )
00250     {
00251         gs.addProperty( "style:font-name", str, tt ); // hack
00252     }
00253     // ### TODO What to do if a style does not define a font? Do we leave it empty or do we use our default font?
00254 
00255     double d = numberOrNull( one.getProperty( "SIZE:value" ) );
00256     if ( d >= 1.0 ) // Sane value?
00257     {
00258         gs.addPropertyPt( "fo:font-size", d, tt );
00259     }
00260     // ### TODO If not, same question as with font name.
00261 
00262     ok = false;
00263     const int weight = one.getProperty( "WEIGHT:value" ).toInt( &ok );
00264     if ( ok && weight >=0 )
00265     {
00266         if ( weight == 50 )
00267         {
00268             gs.addProperty( "fo:font-weight", "normal", tt );
00269         }
00270         else if (weight == 75 )
00271         {
00272             gs.addProperty( "fo:font-weight", "bold", tt );
00273         }
00274         else
00275         {
00276             gs.addProperty( "fo:font-weight", QString::number( weight * 10 ), tt );
00277         }
00278     }
00279     else if ( style )
00280     {
00281         gs.addProperty( "fo:font-weight", "normal", tt );
00282     }
00283 
00284     ok = false;
00285     const int italic = one.getProperty( "ITALIC:value" ).toInt( &ok );
00286     if ( ok && ( italic == 1 ) )
00287     {
00288         gs.addProperty( "fo:font-style", "italic", tt );
00289     }
00290     else if ( ( ok && ! italic ) || style )
00291     {
00292         gs.addProperty( "fo:font-style", "normal", tt );
00293     }
00294 
00295     // ### TODO
00296 #if 0
00297     
00298     gs.addProperty( "style:text-underline-mode", d->m_bWordByWord ? "skip-white-space" : "continuous", tt );
00299     gs.addProperty( "style:text-underline-type", m_underlineType == U_NONE ? "none" :
00300                     m_underlineType == U_DOUBLE ? "double" : "single", tt );
00301     QString styleline;
00302     if ( m_underlineType == U_WAVE )
00303         styleline = "wave";
00304     else
00305         styleline = exportOasisUnderline( m_underlineStyle );
00306     gs.addProperty( "style:text-underline-style", styleline, tt );
00307     gs.addProperty( "style:text-underline-color", m_textUnderlineColor.isValid() ? m_textUnderlineColor.name() : "font-color", tt );
00308     // TODO U_SIMPLE_BOLD
00309     // TODO style:text-line-through-mode
00310     gs.addProperty( "style:text-line-through-type", m_strikeOutType == S_NONE ? "none" :
00311                     m_strikeOutType == S_DOUBLE ? "double" : "single", tt );
00312 
00313     styleline = exportOasisUnderline( (UnderlineStyle) m_strikeOutStyle );
00314     gs.addProperty( "style:text-line-through-style", styleline, tt );
00315     //gs.addProperty( "style:text-line-through-color", ...) TODO in kotext
00316 
00317     QString textPos;
00318     if ( d->m_offsetFromBaseLine != 0 )
00319         textPos = QString::number( 100 * d->m_offsetFromBaseLine / fn.pointSizeFloat() ) + '%';
00320     else if ( va == AlignSuperScript ) textPos = "super";
00321     else if ( va == AlignSubScript ) textPos = "sub";
00322     else textPos = "0%";
00323     textPos += ' ';
00324     textPos += QString::number( d->m_relativeTextSize * 100 );
00325     textPos += '%';
00326     gs.addProperty( "style:text-position", textPos, tt );
00327 
00328     if ( m_attributeFont == ATT_SMALL_CAPS )
00329         gs.addProperty( "fo:font-variant", "small-caps", tt );
00330     else if ( m_attributeFont == ATT_UPPER )
00331         gs.addProperty( "fo:text-transform", "uppercase", tt );
00332     else if ( m_attributeFont == ATT_LOWER )
00333         gs.addProperty( "fo:text-transform", "lowercase", tt );
00334 
00335     gs.addProperty( "fo:language", m_language == "en_US" ? QString("en") : m_language, tt );
00336     gs.addProperty( "fo:background-color",
00337                     m_textBackColor.isValid() ? m_textBackColor.name() : "transparent", tt );
00338     gs.addProperty( "fo:text-shadow", shadowAsCss(), tt );
00339     gs.addProperty( "fo:hyphenate", d->m_bHyphenation, tt );
00340 #endif
00341 }
00342 
00343 // Inspired from KoParagLayout::saveOasis but we have not the same data to start with.
00344 void KWord13OasisGenerator::fillGenStyleWithLayout( const KWord13Layout& layout, KoGenStyle& gs, const bool style ) const
00345 {
00346     // ### TODO syntaxVersion < 3
00347 
00348     QString str; // Help string to store each KWord 1.3 layout property
00349 
00350     str = layout.getProperty( "FLOW:align" );
00351     if ( str.isEmpty() && ! style)
00352     {
00353         // Nothing to do!
00354     }
00355     else if ( ( str == "left" ) || ( str == "right") || ( str == "center" ) || ( str == "justify" ) )
00356     {
00357         gs.addProperty( "fo:text-align", str );
00358     }
00359     else // KWord 1.3's "auto" (or empty/unknown string for a style)
00360     {
00361         gs.addProperty( "fo:text-align", "start" ); // i.e. direction-dependent
00362     }
00363 
00364     str = layout.getProperty( "FLOW:dir" );
00365     if ( str == "R" ) // ### TODO: check the right value
00366     {
00367             gs.addProperty( "style:writing-mode", "rl-tb" ); // right-to-left, top-to-bottom
00368     }
00369     else if ( style )
00370     {
00371             gs.addProperty( "style:writing-mode", "lr-tb" ); // left-to-right, top-to-bottom
00372     }
00373 
00374     // ### TODO: do not define if it does not exist and ! style
00375     gs.addPropertyPt( "fo:margin-left", numberOrNull( layout.getProperty( "INDENTS:left" ) ) );
00376     gs.addPropertyPt( "fo:margin-right", numberOrNull( layout.getProperty( "INDENTS:right" ) ) );
00377     gs.addPropertyPt( "fo:text-indent", numberOrNull( layout.getProperty( "INDENTS:first" ) ) );
00378     gs.addPropertyPt( "fo:margin-top", numberOrNull( layout.getProperty( "OFFSETS:before" ) ) );
00379     gs.addPropertyPt( "fo:margin-bottom", numberOrNull( layout.getProperty( "OFFSETS:after" ) ) );
00380 
00381 #if 0
00382     switch ( lineSpacingType ) {
00383     case KoParagLayout::LS_SINGLE:
00384         gs.addProperty( "fo:line-height", "100%" );
00385         break;
00386     case KoParagLayout::LS_ONEANDHALF:
00387         gs.addProperty( "fo:line-height", "150%" );
00388         break;
00389     case KoParagLayout::LS_DOUBLE:
00390         gs.addProperty( "fo:line-height", "200%" );
00391         break;
00392     case KoParagLayout::LS_MULTIPLE:
00393         gs.addProperty( "fo:line-height", QString::number( lineSpacing * 100.0 ) + '%' );
00394         break;
00395     case KoParagLayout::LS_FIXED:
00396         gs.addPropertyPt( "fo:line-height", lineSpacing );
00397         break;
00398     case KoParagLayout::LS_CUSTOM:
00399         gs.addPropertyPt( "style:line-spacing", lineSpacing );
00400         break;
00401     case KoParagLayout::LS_AT_LEAST:
00402         gs.addPropertyPt( "style:line-height-at-least", lineSpacing );
00403         break;
00404     }
00405 #endif
00406 
00407 #if 0
00408     QBuffer buffer;
00409     buffer.open( IO_WriteOnly );
00410     KoXmlWriter tabsWriter( &buffer, 4 ); // indent==4: root,autostyle,style,parag-props
00411     tabsWriter.startElement( "style:tab-stops" );
00412     KoTabulatorList::ConstIterator it = m_tabList.begin();
00413     for ( ; it != m_tabList.end() ; it++ )
00414     {
00415         tabsWriter.startElement( "style:tab-stop" );
00416         tabsWriter.addAttributePt( "style:position", (*it).ptPos );
00417 
00418         switch ( (*it).type ) {
00419         case T_LEFT:
00420             tabsWriter.addAttribute( "style:type", "left" );
00421             break;
00422         case T_CENTER:
00423             tabsWriter.addAttribute( "style:type", "center" );
00424             break;
00425         case T_RIGHT:
00426             tabsWriter.addAttribute( "style:type", "right" );
00427             break;
00428         case T_DEC_PNT:  // "alignment on decimal point"
00429             tabsWriter.addAttribute( "style:type", "char" );
00430             tabsWriter.addAttribute( "style:char", QString( (*it).alignChar ) );
00431             break;
00432         case T_INVALID: // keep compiler happy, this can't happen
00433             break;
00434         }
00435         switch( (*it).filling ) {
00436         case TF_BLANK:
00437             tabsWriter.addAttribute( "style:leader-type", "none" );
00438             break;
00439         case TF_LINE:
00440             tabsWriter.addAttribute( "style:leader-type", "single" );
00441             tabsWriter.addAttribute( "style:leader-style", "solid" );
00442             // Give OOo a chance to show something, since it doesn't support lines here.
00443             tabsWriter.addAttribute( "style:leader-text", "_" );
00444             break;
00445         case TF_DOTS:
00446             tabsWriter.addAttribute( "style:leader-type", "single" );
00447             tabsWriter.addAttribute( "style:leader-style", "dotted" );
00448             // Give OOo a chance to show something, since it doesn't support lines here.
00449             tabsWriter.addAttribute( "style:leader-text", "." );
00450             break;
00451         case TF_DASH:
00452             tabsWriter.addAttribute( "style:leader-type", "single" );
00453             tabsWriter.addAttribute( "style:leader-style", "dash" );
00454             // Give OOo a chance to show something, since it doesn't support lines here.
00455             tabsWriter.addAttribute( "style:leader-text", "_" );
00456             break;
00457         case TF_DASH_DOT:
00458             tabsWriter.addAttribute( "style:leader-type", "single" );
00459             tabsWriter.addAttribute( "style:leader-style", "dot-dash" );
00460             // Give OOo a chance to show something, since it doesn't support lines here.
00461             tabsWriter.addAttribute( "style:leader-text", "." );
00462             break;
00463         case TF_DASH_DOT_DOT:
00464             tabsWriter.addAttribute( "style:leader-type", "single" );
00465             tabsWriter.addAttribute( "style:leader-style", "dot-dot-dash" );
00466             // Give OOo a chance to show something, since it doesn't support lines here.
00467             tabsWriter.addAttribute( "style:leader-text", "." );
00468             break;
00469         }
00470         if ( (*it).filling != TF_BLANK )
00471             tabsWriter.addAttributePt( "style:leader-width", (*it).ptWidth );
00472         // If we want to support it, oasis also defines style:leader-color
00473         tabsWriter.endElement();
00474     }
00475     tabsWriter.endElement();
00476     buffer.close();
00477     QString elementContents = QString::fromUtf8( buffer.buffer(), buffer.buffer().size() );
00478     gs.addChildElement( "style:tab-stops", elementContents );
00479 #endif
00480 
00481 #if 0
00482     bool fourBordersEqual = leftBorder.penWidth() > 0 &&
00483                leftBorder == rightBorder && rightBorder == topBorder && topBorder == bottomBorder;
00484     if ( fourBordersEqual ) {
00485         gs.addProperty( "fo:border", leftBorder.saveFoBorder() );
00486     } else {
00487         if ( leftBorder.penWidth() > 0 )
00488             gs.addProperty( "fo:border-left", leftBorder.saveFoBorder() );
00489         if ( rightBorder.penWidth() > 0 )
00490             gs.addProperty( "fo:border-right", rightBorder.saveFoBorder() );
00491         if ( topBorder.penWidth() > 0 )
00492             gs.addProperty( "fo:border-top", topBorder.saveFoBorder() );
00493         if ( bottomBorder.penWidth() > 0 )
00494             gs.addProperty( "fo:border-bottom", bottomBorder.saveFoBorder() );
00495     }
00496 #endif
00497 
00498 #if 0
00499     if ( pageBreaking & KoParagLayout::HardFrameBreakBefore )
00500         gs.addProperty( "fo:break-before", "column" );
00501     else if ( pageBreaking & KoParagLayout::HardFrameBreakAfter )
00502         gs.addProperty( "fo:break-after", "column" );
00503     if ( pageBreaking & KoParagLayout::KeepLinesTogether )
00504         gs.addProperty( "fo:keep-together", "always" );
00505     if ( pageBreaking & KoParagLayout::KeepWithNext )
00506         gs.addProperty( "fo:keep-with-next", "always" );
00507 #endif
00508 }
00509 
00510 // Inspired by KoTextParag::saveOasis
00511 void KWord13OasisGenerator::generateTextFrameset( KoXmlWriter& writer, KWordTextFrameset* frameset, bool /*main*/ )
00512 {
00513     if ( ! frameset )
00514     {
00515         kdWarning(30520) << "Tried to generate a NULL text frameset!" << endl;
00516         return;
00517     }
00518 
00519     for ( QValueList<KWord13Paragraph>::Iterator it = frameset->m_paragraphGroup.begin();
00520         it != frameset->m_paragraphGroup.end(); ++it)
00521     {
00522         // Write rawly the paragrapgh (see KoTextParag::saveOasis)
00523         writer.startElement( "text:p", false ); // No indent inside!
00524         writer.addAttribute( "text:style-name", (*it).m_layout.m_autoStyleName );
00525 #if 1
00526         const QString paragraphText( (*it).text() );
00527         int currentPos = 0; // Current position where the next character has to be written
00528 
00529         for ( KWord13Format* format = (*it).m_formats.first(); format; format = (*it).m_formats.next() )
00530         {
00531             // Perhaps we have text before the format's position
00532             const int pos = format->m_pos;
00533             const int length = format->length();
00534             if ( currentPos < pos )
00535             {
00536                 writer.addTextSpan( paragraphText.mid( currentPos, pos - currentPos ) );
00537                 currentPos = pos;
00538             }
00539             // Now we have to write the text belonging to the format
00540             KWord13FormatOneData* data = format->getFormatOneData();
00541             if ( data && format->m_id == 1 )
00542             {    // Normal text
00543                 writer.startElement( "text:span" );
00544                 writer.addAttribute( "text:style-name", data->m_autoStyleName );
00545                 writer.addTextSpan( paragraphText.mid( pos, length ) );
00546                 writer.endElement();
00547             }
00548             else if ( format->m_id == 3 )
00549             {    // Old tabulator
00550                 // ### TEMPORARY: do it with KWord13FormatOneData
00551                 writer.addTextSpan("\t"); // Tabulator
00552             }
00553             else if ( format->m_id == 4 )
00554             {    // Variable
00555                 // ### TEMPORARY
00556                 const QString text ( ( (KWord13FormatFour*) format ) -> m_text );
00557                 if ( text.isEmpty() )
00558                     writer.addTextNode( "#" ); // Placeholder
00559                 else
00560                     writer.addTextSpan( text );
00561             }
00562             else
00563             {
00564                 // ### TEMPORARY
00565                 writer.addTextNode("#"); // Placeholder
00566             }
00567             currentPos += length;
00568         }
00569         // We might have still something to write out
00570         const QString tailText( paragraphText.mid( currentPos ) );
00571         if ( ! tailText.isEmpty() )
00572             writer.addTextSpan( tailText );
00573 #else
00574         writer.addTextSpan( (*it).text() );
00575 #endif
00576         writer.endElement(); // text:p
00577     }
00578 }
00579 
00580 // Inspired by KWDocument::saveOasisDocumentStyles
00581 void KWord13OasisGenerator::writeStylesXml( void )
00582 {
00583     if ( !m_store || !m_kwordDocument )
00584     {
00585         kdError(30520) << "Not possible to generate style.xml" << endl;
00586         return;
00587     }
00588 
00589     m_store->open("styles.xml"); // ### TODO: check error!
00590     KoStoreDevice io ( m_store );
00591     io.open( IO_WriteOnly );  // ### TODO: check error!
00592 
00593     KoXmlWriter *stylesWriter = KoDocument::createOasisXmlWriter( &io, "office:document-styles" );
00594 
00595     stylesWriter->startElement( "office:styles" );
00596     QValueList<KoGenStyles::NamedStyle> styles = m_oasisGenStyles.styles( KoGenStyle::STYLE_USER );
00597     QValueList<KoGenStyles::NamedStyle>::const_iterator it = styles.begin();
00598     for ( ; it != styles.end() ; ++it ) {
00599         (*it).style->writeStyle( stylesWriter, m_oasisGenStyles, "style:style", (*it).name, "style:paragraph-properties" );
00600     }
00601     stylesWriter->endElement(); // office:styles
00602 
00603     stylesWriter->startElement( "office:automatic-styles" );
00604 #if 0
00605     styles = m_oasisGenStyles.styles( KWDocument::STYLE_FRAME );
00606     it = styles.begin();
00607     for ( ; it != styles.end() ; ++it ) {
00608         (*it).style->writeStyle( stylesWriter, m_oasisGenStyles, "style:style", (*it).name , "style:graphic-properties"  );
00609     }
00610 #endif
00611 
00612     QString pageLayoutName;
00613     styles = m_oasisGenStyles.styles( KoGenStyle::STYLE_PAGELAYOUT );
00614     Q_ASSERT( styles.count() == 1 );
00615     it = styles.begin();
00616     for ( ; it != styles.end() ; ++it ) {
00617         (*it).style->writeStyle( stylesWriter, m_oasisGenStyles, "style:page-layout", (*it).name, "style:page-layout-properties", false /*don't close*/ );
00618         //if ( m_pageLayout.columns > 1 ) TODO add columns element. This is a bit of a hack,
00619         // which only works as long as we have only one page master
00620         stylesWriter->endElement();
00621         Q_ASSERT( pageLayoutName.isEmpty() ); // if there's more than one pagemaster we need to rethink all this
00622         pageLayoutName = (*it).name;
00623     }
00624 
00625 
00626 
00627     stylesWriter->endElement(); // office:automatic-styles
00628 
00629     stylesWriter->startElement( "office:master-styles" );
00630     stylesWriter->startElement( "style:master-page" );
00631     stylesWriter->addAttribute( "style:name", "Standard" );
00632     stylesWriter->addAttribute( "style:page-layout-name", pageLayoutName );
00633     stylesWriter->endElement();
00634     stylesWriter->endElement(); // office:master-styles
00635 
00636     stylesWriter->endElement(); // root element (office:document-styles)
00637     stylesWriter->endDocument();
00638     io.close();
00639     m_store->close();
00640     delete stylesWriter;
00641 
00642     if ( m_manifestWriter )
00643     {
00644         m_manifestWriter->addManifestEntry( "styles.xml", "text/xml" );
00645     }
00646 
00647 }
00648 
00649 // Inspired by KWDocument::saveOasis
00650 void KWord13OasisGenerator::writeContentXml(void)
00651 {
00652     if ( !m_store || !m_kwordDocument )
00653     {
00654         kdError(30520) << "Not possible to generate content.xml" << endl;
00655         return;
00656     }
00657 
00658     m_store->open("content.xml"); // ### TODO: check error!
00659     KoStoreDevice io ( m_store );
00660     io.open( IO_WriteOnly );  // ### TODO: check error!
00661 
00662     KoXmlWriter *writer = KoDocument::createOasisXmlWriter( &io, "office:document-content" );
00663 
00664 
00665     // Automatic styles
00666     writer->startElement( "office:automatic-styles" );
00667     QValueList<KoGenStyles::NamedStyle> styles = m_oasisGenStyles.styles( KoGenStyle::STYLE_AUTO );
00668     QValueList<KoGenStyles::NamedStyle>::const_iterator it;
00669     for ( it = styles.begin(); it != styles.end() ; ++it ) {
00670         (*it).style->writeStyle( writer, m_oasisGenStyles, "style:style", (*it).name, "style:paragraph-properties" );
00671     }
00672     styles = m_oasisGenStyles.styles( KoGenStyle::STYLE_LIST );
00673     for ( it = styles.begin(); it != styles.end() ; ++it ) {
00674         (*it).style->writeStyle( writer, m_oasisGenStyles, "text:list-style", (*it).name, 0 );
00675     }
00676     writer->endElement(); // office:automatic-styles
00677 
00678     writer->startElement( "office:body" );
00679     writer->startElement( "office:text" );
00680 
00681     // ### TODO: check that there is at least a normal text frameset
00682     generateTextFrameset( *writer, m_kwordDocument->m_normalTextFramesetList.first(), true );
00683 
00684     writer->endElement(); // office:text
00685     writer->endElement(); // office:body
00686 
00687 
00688     // ### TODO
00689 
00690     writer->endElement();
00691     writer->endDocument();
00692     io.close();
00693     delete writer;
00694     m_store->close();
00695 
00696     if ( m_manifestWriter )
00697     {
00698         m_manifestWriter->addManifestEntry( "content.xml", "text/xml" );
00699     }
00700 }
00701 
00702 void KWord13OasisGenerator::writeMetaXml(void)
00703 {
00704     if ( !m_store || !m_kwordDocument )
00705     {
00706         kdError(30520) << "Not possible to generate meta.xml" << endl;
00707         return;
00708     }
00709 
00710     m_store->open("meta.xml"); // ### TODO: check error!
00711     KoStoreDevice io ( m_store );
00712     io.open( IO_WriteOnly );  // ### TODO: check error!
00713 
00714     KoXmlWriter *writer = KoDocument::createOasisXmlWriter( &io, "office:document-meta" );
00715 
00716     writer->startElement( "office:meta" );
00717 
00718     // Tell who we are in case that we have a bug in our filter output!
00719     // According to OASIS spec section 3.1.1, it has to follow section 14.43 of RFC 2616
00720     writer->startElement( "meta:generator" );
00721     QString strVersion;
00722     strVersion += "KWord-OneDotThree-Import-Filter/";
00723     strVersion += QString( "$Revision: 515673 $" ).mid( 10 ).remove( '$' ).stripWhiteSpace();
00724     strVersion += " KOffice/";
00725     strVersion += KOFFICE_VERSION_STRING;
00726     writer->addTextSpan( strVersion );
00727     writer->endElement();
00728 
00729     QString str; // Helper string
00730 
00731     str = m_kwordDocument->getDocumentInfo( "about:title" );
00732     if ( !str.isEmpty() )
00733     {
00734         writer->startElement( "dc:title" );
00735         writer->addTextSpan( str );
00736         writer->endElement();
00737     }
00738 
00739     str = m_kwordDocument->getDocumentInfo( "about:abstract" );
00740     if (!str.isEmpty())
00741     {
00742         writer->startElement( "dc:description");
00743         writer->addTextSpan( str );
00744         writer->endElement();
00745     }
00746 
00747     str = m_kwordDocument->getDocumentInfo( "author:full-name" );
00748     if ( !str.isEmpty() )
00749     {
00750         writer->startElement( "dc:creator" );
00751         writer->addTextSpan( str );
00752         writer->endElement();
00753     }
00754 
00755     // ### TODO: what about the other document info of KWord 1.3?
00756 
00757     QDateTime dt;
00758 
00759     dt = m_kwordDocument->creationDate();
00760     if ( dt.isValid() )
00761     {
00762         writer->startElement( "meta:creation-date");
00763         writer->addTextNode( dt.toString( Qt::ISODate) );
00764         writer->endElement();
00765     }
00766 
00767     dt = m_kwordDocument->modificationDate();
00768     if ( dt.isValid() )
00769     {
00770         writer->startElement( "dc:date");
00771         writer->addTextNode( dt.toString( Qt::ISODate) );
00772         writer->endElement();
00773     }
00774 
00775     dt = m_kwordDocument->lastPrintingDate();
00776     if ( dt.isValid() )
00777     {
00778         writer->startElement( "meta:print-date");
00779         writer->addTextNode( dt.toString( Qt::ISODate) );
00780         writer->endElement();
00781     }
00782 
00783     writer->startElement( "meta:document-statistic" );
00784 
00785     // KWord files coming from import filters mostly do not have any page count
00786     const int numPages = m_kwordDocument->getProperty( "PAPER:pages" ).toInt();
00787     if ( numPages > 0 )
00788     {
00789         writer->addAttribute( "meta:page-count", numPages );
00790     }
00791 
00792 #if 0
00793     zipWriteData( " meta:image-count=\"" ); // This is not specified in the OO specification section 2.1.19, fixed in OASIS (### TODO)
00794 #if 1
00795     zipWriteData( "0" ); // ### TODO
00796 #else
00797     zipWriteData( QString::number ( m_pictureNumber ) );
00798 #endif
00799     zipWriteData( "\"" );
00800 
00801     zipWriteData( " meta:table-count=\"" );
00802 #if 1
00803     zipWriteData( "0" ); // ### TODO
00804 #else
00805     zipWriteData( QString::number ( m_tableNumber ) );
00806 #endif
00807     zipWriteData( "\"" );
00808 #endif
00809     writer->endElement(); // meta:document-statistic
00810     writer->endElement(); // office:meta
00811 
00812     writer->endElement();
00813     writer->endDocument();
00814     delete writer;
00815 
00816     io.close();
00817     m_store->close();
00818 
00819     if ( m_manifestWriter )
00820     {
00821         m_manifestWriter->addManifestEntry( "meta.xml", "text/xml" );
00822     }
00823 }
00824 
00825 void KWord13OasisGenerator::writePreviewFile(void)
00826 {
00827     if ( !m_store || !m_kwordDocument )
00828     {
00829         kdError(30520) << "Not possible to generate preview file" << endl;
00830         return;
00831     }
00832 
00833     // Load image
00834     QImage image( m_kwordDocument->m_previewFile->name() );
00835     if ( image.isNull() )
00836     {
00837         kdWarning(30520) << "Could not re-read preview from temp file!" << endl;
00838         return;
00839     }
00840 
00841     // We have a 256x256x8 preview and we need a 128x128x32 preview with alpha channel
00842     QImage preview( image.convertDepth( 32, Qt::ColorOnly ).smoothScale( 128, 128 ) );
00843     if ( preview.isNull() )
00844     {
00845         kdWarning(30520) << "Could not create preview!" << endl;
00846         return;
00847     }
00848     if ( !preview.hasAlphaBuffer() )
00849     {
00850         preview.setAlphaBuffer( true );
00851     }
00852     m_store->open("Thumbnails/thumbnail.png");
00853     KoStoreDevice io ( m_store );
00854     io.open( IO_WriteOnly );  // ### TODO: check error!
00855     preview.save( &io, "PNG", 0 );
00856     io.close();
00857     m_store->close();
00858 
00859     // No manifest entry, as it is supposed not to be part of the document.
00860 }
00861 
00862 void KWord13OasisGenerator::writePictures( void )
00863 {
00864     if ( !m_store || !m_kwordDocument )
00865     {
00866         kdError(30520) << "Not possible to generate preview file" << endl;
00867         return;
00868     }
00869 
00870     for ( QDictIterator<KWord13Picture> it( m_kwordDocument->m_pictureDict ) ; it.current(); ++it )
00871     {
00872         if ( !it.current()->m_valid || !it.current()->m_tempFile )
00873         {
00874             kdDebug(30520) << "No data for picture: " << it.currentKey() << endl;
00875             continue;
00876         }
00877         const QString fileName( it.current()->m_tempFile->name() );
00878         const QString oasisName( it.current()->getOasisPictureName() );
00879         kdDebug(30520) << "Copying... " << it.currentKey() << endl << " => " << oasisName << endl;
00880         QFile file( fileName );
00881         if ( !file.open( IO_ReadOnly ) )
00882         {
00883             kdWarning(30520) << "Cannot open: " << fileName << endl;
00884             continue;
00885         }
00886         QByteArray array( file.readAll() );
00887         if ( array.isNull() )
00888         {
00889             kdWarning(30520) << "Null picture for " << fileName << endl;
00890             file.close();
00891             continue;
00892         }
00893         file.close();
00894 
00895         m_store->open( oasisName );
00896         m_store->write( array );
00897         m_store->close();
00898 
00899         if ( m_manifestWriter )
00900         {
00901             const QString mimeType ( KMimeType::findByContent( array, 0 )->name() );
00902             if ( mimeType == "application/octet-stream" )
00903             {
00904                 kdWarning(30520) << "Generic mime type for " << it.currentKey() << endl;
00905                 // ### TODO: try harder to find a mime type
00906             }
00907             m_manifestWriter->addManifestEntry( oasisName, mimeType );
00908         }
00909 
00910     }
00911 
00912 }
00913 
00914 bool KWord13OasisGenerator::generate ( const QString& fileName, KWord13Document& kwordDocument )
00915 {
00916     if ( m_kwordDocument && ( (void*) m_kwordDocument ) != ( (void*) &kwordDocument ) )
00917     {
00918         kdWarning(30520) << "KWord Document is different!" <<endl;
00919     }
00920 
00921     m_kwordDocument = &kwordDocument;
00922 
00923     m_store = KoStore::createStore( fileName, KoStore::Write, "application/vnd.sun.xml.writer", KoStore::Zip );
00924     if ( ! m_store )
00925     {
00926         kdError(30520) << "Cannot create output KoStore" << endl;
00927         return false;
00928     }
00929     m_store->disallowNameExpansion();
00930 
00931     // Prepare manifest file - in memory (inspired by KoDocument::saveNativeFormat)
00932     QByteArray manifestData;
00933     QBuffer manifestBuffer( manifestData );
00934     manifestBuffer.open( IO_WriteOnly );
00935     m_manifestWriter = new KoXmlWriter( &manifestBuffer );
00936     m_manifestWriter->startDocument( "manifest:manifest" );
00937     m_manifestWriter->startElement( "manifest:manifest" );
00938     m_manifestWriter->addAttribute( "xmlns:manifest", "urn:oasis:names:tc:openoffice:xmlns:manifest:1.0" );
00939 
00940     // ### TODO: check if writing the store is successful
00941 
00942     writeStylesXml();
00943     writeContentXml();
00944     writeMetaXml();
00945     writePictures();
00946 
00947         // Write out manifest file
00948     m_manifestWriter->endElement();
00949     m_manifestWriter->endDocument();
00950     delete m_manifestWriter;
00951     m_manifestWriter = 0;
00952     if ( m_store->open( "META-INF/manifest.xml" ) )
00953     {
00954         m_store->write( manifestData );
00955         m_store->close();
00956     }
00957 
00958     if ( kwordDocument.m_previewFile )
00959     {
00960         writePreviewFile();
00961     }
00962     else
00963     {
00964         kdDebug(30520) << "No preview file available to make an OASIS thumbnail!" << endl;
00965     }
00966 
00967 
00968 # ifndef NDEBUG // DEBUG (out of specification)
00969     // No entry in the manifest file, as it is not part of the document
00970     if ( m_store->open("Debug/debug.xml") )
00971     {
00972         KoStoreDevice io ( m_store );
00973         io.open( IO_WriteOnly );  // ### TODO: check error!
00974         kwordDocument.xmldump( &io );
00975         io.close();
00976         m_store->close();
00977     }
00978 # endif
00979 
00980 
00981     delete m_store;
00982     m_store = 0;
00983     return true;
00984 }
00985 
KDE Home | KDE Accessibility Home | Description of Access Keys