00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "ooutils.h"
00023 #include <KoDocument.h>
00024 #include <KoStyleStack.h>
00025 #include <qdom.h>
00026 #include <qcolor.h>
00027 #include <qimage.h>
00028 #include <KoUnit.h>
00029 #include <qregexp.h>
00030 #include <kdebug.h>
00031 #include <kzip.h>
00032 #include <KoDom.h>
00033 #include <qxml.h>
00034
00035 const char* const ooNS::office="http://openoffice.org/2000/office";
00036 const char* const ooNS::style="http://openoffice.org/2000/style";
00037 const char* const ooNS::text="http://openoffice.org/2000/text";
00038 const char* const ooNS::table="http://openoffice.org/2000/table";
00039 const char* const ooNS::draw="http://openoffice.org/2000/drawing";
00040 const char* const ooNS::presentation="http://openoffice.org/2000/presentation";
00041 const char* const ooNS::fo="http://www.w3.org/1999/XSL/Format";
00042 const char* const ooNS::xlink="http://www.w3.org/1999/xlink";
00043 const char* const ooNS::number="http://openoffice.org/2000/datastyle";
00044 const char* const ooNS::svg="http://www.w3.org/2000/svg";
00045 const char* const ooNS::dc="http://purl.org/dc/elements/1.1/";
00046 const char* const ooNS::meta="http://openoffice.org/2000/meta";
00047 const char* const ooNS::config="http://openoffice.org/2001/config";
00048
00049 QString OoUtils::expandWhitespace(const QDomElement& tag)
00050 {
00051
00052
00053 int howmany=1;
00054 if (tag.hasAttributeNS( ooNS::text, "c"))
00055 howmany = tag.attributeNS( ooNS::text, "c", QString::null).toInt();
00056
00057 QString result;
00058 return result.fill(32, howmany);
00059 }
00060
00061 bool OoUtils::parseBorder(const QString & tag, double * width, int * style, QColor * color)
00062 {
00063
00064
00065 if (tag.isEmpty() || tag=="none" || tag=="hidden")
00066 return false;
00067
00068 QString _width = tag.section(' ', 0, 0);
00069 QString _style = tag.section(' ', 1, 1);
00070 QString _color = tag.section(' ', 2, 2);
00071
00072 *width = KoUnit::parseValue(_width, 1.0);
00073
00074 if ( _style == "dashed" )
00075 *style = 1;
00076 else if ( _style == "dotted" )
00077 *style = 2;
00078 else if ( _style == "dot-dash" )
00079 *style = 3;
00080 else if ( _style == "dot-dot-dash" )
00081 *style = 4;
00082 else if ( _style == "double" )
00083 *style = 5;
00084 else
00085 *style = 0;
00086
00087 if (_color.isEmpty())
00088 *color = QColor();
00089 else
00090 color->setNamedColor(_color);
00091
00092 return true;
00093 }
00094
00095 void OoUtils::importIndents( QDomElement& parentElement, const KoStyleStack& styleStack )
00096 {
00097 if ( styleStack.hasAttributeNS( ooNS::fo, "margin-left" ) ||
00098 styleStack.hasAttributeNS( ooNS::fo, "margin-right" ) )
00099
00100 {
00101 double marginLeft = KoUnit::parseValue( styleStack.attributeNS( ooNS::fo, "margin-left" ) );
00102 double marginRight = KoUnit::parseValue( styleStack.attributeNS( ooNS::fo, "margin-right" ) );
00103 double first = 0;
00104 if (styleStack.attributeNS( ooNS::style, "auto-text-indent") == "true")
00105
00106
00107
00108 first = 10;
00109 else if (styleStack.hasAttributeNS( ooNS::fo, "text-indent"))
00110 first = KoUnit::parseValue( styleStack.attributeNS( ooNS::fo, "text-indent"));
00111
00112 if ( marginLeft != 0 || marginRight != 0 || first != 0 )
00113 {
00114 QDomElement indent = parentElement.ownerDocument().createElement( "INDENTS" );
00115 if( marginLeft != 0 )
00116 indent.setAttribute( "left", marginLeft );
00117 if( marginRight != 0 )
00118 indent.setAttribute( "right", marginRight );
00119 if( first != 0 )
00120 indent.setAttribute( "first", first );
00121 parentElement.appendChild( indent );
00122 }
00123 }
00124 }
00125
00126 void OoUtils::importLineSpacing( QDomElement& parentElement, const KoStyleStack& styleStack )
00127 {
00128 if( styleStack.hasAttributeNS( ooNS::fo, "line-height") )
00129 {
00130
00131 QString value = styleStack.attributeNS( ooNS::fo, "line-height" );
00132 if ( value != "normal" )
00133 {
00134 QDomElement lineSpacing = parentElement.ownerDocument().createElement( "LINESPACING" );
00135
00136 if ( value.endsWith("%" ) )
00137 {
00138 double percent = value.left(value.length()-1).toDouble();
00139 if( percent == 100 )
00140 lineSpacing.setAttribute("type","single");
00141 else if( percent == 150 )
00142 lineSpacing.setAttribute("type","oneandhalf");
00143 else if( percent == 200 )
00144 lineSpacing.setAttribute("type","double");
00145 else
00146 {
00147 lineSpacing.setAttribute("type", "multiple");
00148 lineSpacing.setAttribute("spacingvalue", percent/100);
00149 }
00150 }
00151 else
00152 {
00153 kdWarning(30519) << "Unhandled value for fo:line-height: " << value << endl;
00154 lineSpacing.setAttribute("type","single");
00155 }
00156 parentElement.appendChild( lineSpacing );
00157 }
00158 }
00159
00160 else if ( styleStack.hasAttributeNS( ooNS::style, "line-height-at-least") )
00161 {
00162 QString value = styleStack.attributeNS( ooNS::style, "line-height-at-least" );
00163
00164
00165
00166
00167
00168 QDomElement lineSpacing = parentElement.ownerDocument().createElement("LINESPACING");
00169 lineSpacing.setAttribute("type", "atleast");
00170 lineSpacing.setAttribute("spacingvalue", KoUnit::parseValue(value));
00171 parentElement.appendChild(lineSpacing);
00172 }
00173
00174 else if ( styleStack.hasAttributeNS( ooNS::style, "line-spacing") )
00175 {
00176 double value = KoUnit::parseValue( styleStack.attributeNS( ooNS::style, "line-spacing" ) );
00177 if ( value != 0.0 )
00178 {
00179 QDomElement lineSpacing = parentElement.ownerDocument().createElement( "LINESPACING" );
00180 lineSpacing.setAttribute( "type", "custom" );
00181 lineSpacing.setAttribute( "spacingvalue", value );
00182 parentElement.appendChild( lineSpacing );
00183 }
00184 }
00185
00186 }
00187
00188 void OoUtils::importTopBottomMargin( QDomElement& parentElement, const KoStyleStack& styleStack )
00189 {
00190 if( styleStack.hasAttributeNS( ooNS::fo, "margin-top") ||
00191 styleStack.hasAttributeNS( ooNS::fo, "margin-bottom"))
00192 {
00193 double mtop = KoUnit::parseValue( styleStack.attributeNS( ooNS::fo, "margin-top" ) );
00194 double mbottom = KoUnit::parseValue( styleStack.attributeNS( ooNS::fo, "margin-bottom" ) );
00195 if( mtop != 0 || mbottom != 0 )
00196 {
00197 QDomElement offset = parentElement.ownerDocument().createElement( "OFFSETS" );
00198 if( mtop != 0 )
00199 offset.setAttribute( "before", mtop );
00200 if( mbottom != 0 )
00201 offset.setAttribute( "after", mbottom );
00202 parentElement.appendChild( offset );
00203 }
00204 }
00205 }
00206
00207 void OoUtils::importTabulators( QDomElement& parentElement, const KoStyleStack& styleStack )
00208 {
00209 if ( !styleStack.hasChildNodeNS( ooNS::style, "tab-stops" ) )
00210 return;
00211 QDomElement tabStops = styleStack.childNodeNS( ooNS::style, "tab-stops" );
00212
00213 for ( QDomNode it = tabStops.firstChild(); !it.isNull(); it = it.nextSibling() )
00214 {
00215 QDomElement tabStop = it.toElement();
00216 Q_ASSERT( tabStop.tagName() == "style:tab-stop" );
00217 QString type = tabStop.attributeNS( ooNS::style, "type", QString::null );
00218
00219 QDomElement elem = parentElement.ownerDocument().createElement( "TABULATOR" );
00220 int kOfficeType = 0;
00221 if ( type == "left" )
00222 kOfficeType = 0;
00223 else if ( type == "center" )
00224 kOfficeType = 1;
00225 else if ( type == "right" )
00226 kOfficeType = 2;
00227 else if ( type == "char" ) {
00228 QString delimiterChar = tabStop.attributeNS( ooNS::style, "char", QString::null );
00229 elem.setAttribute( "alignchar", delimiterChar );
00230 kOfficeType = 3;
00231 }
00232
00233 elem.setAttribute( "type", kOfficeType );
00234
00235 double pos = KoUnit::parseValue( tabStop.attributeNS( ooNS::style, "position", QString::null ) );
00236 elem.setAttribute( "ptpos", pos );
00237
00238
00239
00240 QString leaderChar = tabStop.attributeNS( ooNS::style, "leader-char", QString::null );
00241 if ( !leaderChar.isEmpty() )
00242 {
00243 int filling = 0;
00244 QChar ch = leaderChar[0];
00245 switch (ch.latin1()) {
00246 case '.':
00247 filling = 1; break;
00248 case '-':
00249 case '_':
00250 filling = 2; break;
00251 default:
00252
00253
00254 break;
00255 }
00256 elem.setAttribute( "filling", filling );
00257 }
00258 parentElement.appendChild( elem );
00259 }
00260
00261 }
00262
00263 void OoUtils::importBorders( QDomElement& parentElement, const KoStyleStack& styleStack )
00264 {
00265 if (styleStack.hasAttributeNS( ooNS::fo, "border","left"))
00266 {
00267 double width;
00268 int style;
00269 QColor color;
00270 if (OoUtils::parseBorder(styleStack.attributeNS( ooNS::fo, "border", "left"), &width, &style, &color))
00271 {
00272 QDomElement lbElem = parentElement.ownerDocument().createElement("LEFTBORDER");
00273 lbElem.setAttribute("width", width);
00274 lbElem.setAttribute("style", style);
00275 if (color.isValid()) {
00276 lbElem.setAttribute("red", color.red());
00277 lbElem.setAttribute("green", color.green());
00278 lbElem.setAttribute("blue", color.blue());
00279 }
00280 parentElement.appendChild(lbElem);
00281 }
00282 }
00283
00284 if (styleStack.hasAttributeNS( ooNS::fo, "border", "right"))
00285 {
00286 double width;
00287 int style;
00288 QColor color;
00289 if (OoUtils::parseBorder(styleStack.attributeNS( ooNS::fo, "border", "right"), &width, &style, &color))
00290 {
00291 QDomElement lbElem = parentElement.ownerDocument().createElement("RIGHTBORDER");
00292 lbElem.setAttribute("width", width);
00293 lbElem.setAttribute("style", style);
00294 if (color.isValid()) {
00295 lbElem.setAttribute("red", color.red());
00296 lbElem.setAttribute("green", color.green());
00297 lbElem.setAttribute("blue", color.blue());
00298 }
00299 parentElement.appendChild(lbElem);
00300 }
00301 }
00302
00303 if (styleStack.hasAttributeNS( ooNS::fo, "border", "top"))
00304 {
00305 double width;
00306 int style;
00307 QColor color;
00308 if (OoUtils::parseBorder(styleStack.attributeNS( ooNS::fo, "border", "top"), &width, &style, &color))
00309 {
00310 QDomElement lbElem = parentElement.ownerDocument().createElement("TOPBORDER");
00311 lbElem.setAttribute("width", width);
00312 lbElem.setAttribute("style", style);
00313 if (color.isValid()) {
00314 lbElem.setAttribute("red", color.red());
00315 lbElem.setAttribute("green", color.green());
00316 lbElem.setAttribute("blue", color.blue());
00317 }
00318 parentElement.appendChild(lbElem);
00319 }
00320 }
00321
00322 if (styleStack.hasAttributeNS( ooNS::fo, "border", "bottom"))
00323 {
00324 double width;
00325 int style;
00326 QColor color;
00327 if (OoUtils::parseBorder(styleStack.attributeNS( ooNS::fo, "border", "bottom"), &width, &style, &color))
00328 {
00329 QDomElement lbElem = parentElement.ownerDocument().createElement("BOTTOMBORDER");
00330 lbElem.setAttribute("width", width);
00331 lbElem.setAttribute("style", style);
00332 if (color.isValid()) {
00333 lbElem.setAttribute("red", color.red());
00334 lbElem.setAttribute("green", color.green());
00335 lbElem.setAttribute("blue", color.blue());
00336 }
00337 parentElement.appendChild(lbElem);
00338 }
00339 }
00340 }
00341
00342 void OoUtils::importUnderline( const QString& in, QString& underline, QString& styleline )
00343 {
00344 underline = "single";
00345 if ( in == "none" )
00346 underline = "0";
00347 else if ( in == "single" )
00348 styleline = "solid";
00349 else if ( in == "double" )
00350 {
00351 underline = in;
00352 styleline = "solid";
00353 }
00354 else if ( in == "dotted" || in == "bold-dotted" )
00355 styleline = "dot";
00356 else if ( in == "dash"
00357
00358 || in == "long-dash"
00359 || in == "bold-dash"
00360 || in == "bold-long-dash"
00361 )
00362 styleline = "dash";
00363 else if ( in == "dot-dash"
00364 || in == "bold-dot-dash")
00365 styleline = "dashdot";
00366 else if ( in == "dot-dot-dash"
00367 || in == "bold-dot-dot-dash")
00368 styleline = "dashdotdot";
00369 else if ( in == "wave"
00370 || in == "bold-wave"
00371 || in == "double-wave"
00372 || in == "small-wave" )
00373 {
00374 underline = in;
00375 styleline = "solid";
00376 }
00377 else if( in == "bold" )
00378 {
00379 underline = "single-bold";
00380 styleline = "solid";
00381 }
00382 else
00383 kdWarning(30519) << k_funcinfo << " unsupported text-underline value: " << in << endl;
00384 }
00385
00386 void OoUtils::importTextPosition( const QString& text_position, QString& value, QString& relativetextsize )
00387 {
00388
00389
00390
00391 QStringList lst = QStringList::split( ' ', text_position );
00392 if ( !lst.isEmpty() )
00393 {
00394 QString textPos = lst.front().stripWhiteSpace();
00395 QString textSize;
00396 lst.pop_front();
00397 if ( !lst.isEmpty() )
00398 textSize = lst.front().stripWhiteSpace();
00399 if ( !lst.isEmpty() )
00400 kdWarning(30519) << "Strange text position: " << text_position << endl;
00401 bool super = textPos == "super";
00402 bool sub = textPos == "sub";
00403 if ( textPos.endsWith("%") )
00404 {
00405 textPos.truncate( textPos.length() - 1 );
00406
00407
00408 double val = textPos.toDouble();
00409 if ( val > 0 )
00410 super = true;
00411 else if ( val < 0 )
00412 sub = true;
00413 }
00414 if ( super )
00415 value = "2";
00416 else if ( sub )
00417 value = "1";
00418 else
00419 value = "0";
00420 if ( !textSize.isEmpty() && textSize.endsWith("%") )
00421 {
00422 textSize.truncate( textSize.length() - 1 );
00423 double textSizeValue = textSize.toDouble() / 100;
00424 relativetextsize = QString::number( textSizeValue );
00425 }
00426 }
00427 else
00428 value = "0";
00429 }
00430
00431 void OoUtils::createDocumentInfo(QDomDocument &_meta, QDomDocument & docinfo)
00432 {
00433 QDomNode meta = KoDom::namedItemNS( _meta, ooNS::office, "document-meta" );
00434 QDomNode office = KoDom::namedItemNS( meta, ooNS::office, "meta" );
00435
00436 if ( office.isNull() )
00437 return;
00438 QDomElement elementDocInfo = docinfo.documentElement();
00439
00440 QDomElement e = KoDom::namedItemNS( office, ooNS::dc, "creator" );
00441 if ( !e.isNull() && !e.text().isEmpty() )
00442 {
00443 QDomElement author = docinfo.createElement( "author" );
00444 QDomElement t = docinfo.createElement( "full-name" );
00445 author.appendChild( t );
00446 t.appendChild( docinfo.createTextNode( e.text() ) );
00447 elementDocInfo.appendChild( author);
00448 }
00449
00450 e = KoDom::namedItemNS( office, ooNS::dc, "title" );
00451 if ( !e.isNull() && !e.text().isEmpty() )
00452 {
00453 QDomElement about = docinfo.createElement( "about" );
00454 QDomElement title = docinfo.createElement( "title" );
00455 about.appendChild( title );
00456 title.appendChild( docinfo.createTextNode( e.text() ) );
00457 elementDocInfo.appendChild( about );
00458 }
00459
00460 e = KoDom::namedItemNS( office, ooNS::dc, "description" );
00461 if ( !e.isNull() && !e.text().isEmpty() )
00462 {
00463 QDomElement about = elementDocInfo.namedItem( "about" ).toElement();
00464 if ( about.isNull() ) {
00465 about = docinfo.createElement( "about" );
00466 elementDocInfo.appendChild( about );
00467 }
00468 QDomElement title = docinfo.createElement( "abstract" );
00469 about.appendChild( title );
00470 title.appendChild( docinfo.createTextNode( e.text() ) );
00471 }
00472 e = KoDom::namedItemNS( office, ooNS::dc, "subject" );
00473 if ( !e.isNull() && !e.text().isEmpty() )
00474 {
00475 QDomElement about = elementDocInfo.namedItem( "about" ).toElement();
00476 if ( about.isNull() ) {
00477 about = docinfo.createElement( "about" );
00478 elementDocInfo.appendChild( about );
00479 }
00480 QDomElement subject = docinfo.createElement( "subject" );
00481 about.appendChild( subject );
00482 subject.appendChild( docinfo.createTextNode( e.text() ) );
00483 }
00484 e = KoDom::namedItemNS( office, ooNS::meta, "keywords" );
00485 if ( !e.isNull() )
00486 {
00487 QDomElement about = elementDocInfo.namedItem( "about" ).toElement();
00488 if ( about.isNull() ) {
00489 about = docinfo.createElement( "about" );
00490 elementDocInfo.appendChild( about );
00491 }
00492 QDomElement tmp = KoDom::namedItemNS( e, ooNS::meta, "keyword" );
00493 if ( !tmp.isNull() && !tmp.text().isEmpty() )
00494 {
00495 QDomElement keyword = docinfo.createElement( "keyword" );
00496 about.appendChild( keyword );
00497 keyword.appendChild( docinfo.createTextNode( tmp.text() ) );
00498 }
00499 }
00500 }
00501
00502 KoFilter::ConversionStatus OoUtils::loadAndParse(const QString& fileName, QDomDocument& doc, KoStore *m_store )
00503 {
00504 kdDebug(30518) << "loadAndParse: Trying to open " << fileName << endl;
00505
00506 if (!m_store->open(fileName))
00507 {
00508 kdWarning(30519) << "Entry " << fileName << " not found!" << endl;
00509 return KoFilter::FileNotFound;
00510 }
00511 KoFilter::ConversionStatus convertStatus = loadAndParse( m_store->device(),doc, fileName );
00512 m_store->close();
00513 return convertStatus;
00514
00515 }
00516
00517 KoFilter::ConversionStatus OoUtils::loadAndParse(QIODevice* io, QDomDocument& doc, const QString & fileName)
00518 {
00519 QXmlInputSource source( io );
00520
00521 QXmlSimpleReader reader;
00522 KoDocument::setupXmlReader( reader, true );
00523
00524
00525 QString errorMsg;
00526 int errorLine, errorColumn;
00527 if ( !doc.setContent( &source, &reader, &errorMsg, &errorLine, &errorColumn ) )
00528 {
00529 kdError(30519) << "Parsing error in " << fileName << "! Aborting!" << endl
00530 << " In line: " << errorLine << ", column: " << errorColumn << endl
00531 << " Error message: " << errorMsg << endl;
00532 return KoFilter::ParsingError;
00533 }
00534
00535 kdDebug(30519) << "File " << fileName << " loaded and parsed!" << endl;
00536
00537 return KoFilter::OK;
00538
00539 }
00540
00541 KoFilter::ConversionStatus OoUtils::loadAndParse(const QString& filename, QDomDocument& doc, KZip * m_zip)
00542 {
00543 kdDebug(30519) << "Trying to open " << filename << endl;
00544
00545 if (!m_zip)
00546 {
00547 kdError(30519) << "No ZIP file!" << endl;
00548 return KoFilter::CreationError;
00549 }
00550
00551 const KArchiveEntry* entry = m_zip->directory()->entry( filename );
00552 if (!entry)
00553 {
00554 kdWarning(30519) << "Entry " << filename << " not found!" << endl;
00555 return KoFilter::FileNotFound;
00556 }
00557 if (entry->isDirectory())
00558 {
00559 kdWarning(30519) << "Entry " << filename << " is a directory!" << endl;
00560 return KoFilter::WrongFormat;
00561 }
00562 const KZipFileEntry* f = static_cast<const KZipFileEntry *>(entry);
00563 kdDebug(30519) << "Entry " << filename << " has size " << f->size() << endl;
00564 QIODevice* io = f->device();
00565 KoFilter::ConversionStatus convertStatus = loadAndParse( io,doc, filename );
00566 delete io;
00567 return convertStatus;
00568 }
00569
00570 KoFilter::ConversionStatus OoUtils::loadThumbnail( QImage& thumbnail, KZip * m_zip )
00571 {
00572 const QString filename( "Thumbnails/thumbnail.png" );
00573 kdDebug(30519) << "Trying to open thumbnail " << filename << endl;
00574
00575 if (!m_zip)
00576 {
00577 kdError(30519) << "No ZIP file!" << endl;
00578 return KoFilter::CreationError;
00579 }
00580
00581 const KArchiveEntry* entry = m_zip->directory()->entry( filename );
00582 if (!entry)
00583 {
00584 kdWarning(30519) << "Entry " << filename << " not found!" << endl;
00585 return KoFilter::FileNotFound;
00586 }
00587 if (entry->isDirectory())
00588 {
00589 kdWarning(30519) << "Entry " << filename << " is a directory!" << endl;
00590 return KoFilter::WrongFormat;
00591 }
00592 const KZipFileEntry* f = static_cast<const KZipFileEntry *>(entry);
00593 QIODevice* io=f->device();
00594 kdDebug(30519) << "Entry " << filename << " has size " << f->size() << endl;
00595
00596 if ( ! io->open( IO_ReadOnly ) )
00597 {
00598 kdWarning(30519) << "Thumbnail could not be opened!" <<endl;
00599 delete io;
00600 return KoFilter::StupidError;
00601 }
00602
00603 QImageIO imageIO( io, "PNG" );
00604 if ( ! imageIO.read() )
00605 {
00606 kdWarning(30519) << "Thumbnail could not be read!" <<endl;
00607 delete io;
00608 return KoFilter::StupidError;
00609 }
00610
00611 io->close();
00612
00613 thumbnail = imageIO.image();
00614
00615 if ( thumbnail.isNull() )
00616 {
00617 kdWarning(30519) << "Read thumbnail is null!" <<endl;
00618 delete io;
00619 return KoFilter::StupidError;
00620 }
00621
00622 delete io;
00623
00624 kdDebug(30519) << "File " << filename << " loaded!" << endl;
00625
00626 return KoFilter::OK;
00627 }