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 if ( value == "100%" )
00136 lineSpacing.setAttribute("type","single");
00137 else if( value=="150%")
00138 lineSpacing.setAttribute("type","oneandhalf");
00139 else if( value=="200%")
00140 lineSpacing.setAttribute("type","double");
00141 else if ( value.find('%') > -1 )
00142 {
00143 double percent = value.toDouble();
00144 lineSpacing.setAttribute("type", "multiple");
00145 lineSpacing.setAttribute("spacingvalue", percent/100);
00146 }
00147 else
00148 {
00149 kdWarning(30519) << "Unhandled value for fo:line-height: " << value << endl;
00150 }
00151 parentElement.appendChild( lineSpacing );
00152 }
00153 }
00154
00155 else if ( styleStack.hasAttributeNS( ooNS::style, "line-height-at-least") )
00156 {
00157 QString value = styleStack.attributeNS( ooNS::style, "line-height-at-least" );
00158
00159
00160
00161
00162
00163 QDomElement lineSpacing = parentElement.ownerDocument().createElement("LINESPACING");
00164 lineSpacing.setAttribute("type", "atleast");
00165 lineSpacing.setAttribute("spacingvalue", KoUnit::parseValue(value));
00166 parentElement.appendChild(lineSpacing);
00167 }
00168
00169 else if ( styleStack.hasAttributeNS( ooNS::style, "line-spacing") )
00170 {
00171 double value = KoUnit::parseValue( styleStack.attributeNS( ooNS::style, "line-spacing" ) );
00172 if ( value != 0.0 )
00173 {
00174 QDomElement lineSpacing = parentElement.ownerDocument().createElement( "LINESPACING" );
00175 lineSpacing.setAttribute( "type", "custom" );
00176 lineSpacing.setAttribute( "spacingvalue", value );
00177 parentElement.appendChild( lineSpacing );
00178 }
00179 }
00180
00181 }
00182
00183 void OoUtils::importTopBottomMargin( QDomElement& parentElement, const KoStyleStack& styleStack )
00184 {
00185 if( styleStack.hasAttributeNS( ooNS::fo, "margin-top") ||
00186 styleStack.hasAttributeNS( ooNS::fo, "margin-bottom"))
00187 {
00188 double mtop = KoUnit::parseValue( styleStack.attributeNS( ooNS::fo, "margin-top" ) );
00189 double mbottom = KoUnit::parseValue( styleStack.attributeNS( ooNS::fo, "margin-bottom" ) );
00190 if( mtop != 0 || mbottom != 0 )
00191 {
00192 QDomElement offset = parentElement.ownerDocument().createElement( "OFFSETS" );
00193 if( mtop != 0 )
00194 offset.setAttribute( "before", mtop );
00195 if( mbottom != 0 )
00196 offset.setAttribute( "after", mbottom );
00197 parentElement.appendChild( offset );
00198 }
00199 }
00200 }
00201
00202 void OoUtils::importTabulators( QDomElement& parentElement, const KoStyleStack& styleStack )
00203 {
00204 if ( !styleStack.hasChildNodeNS( ooNS::style, "tab-stops" ) )
00205 return;
00206 QDomElement tabStops = styleStack.childNodeNS( ooNS::style, "tab-stops" );
00207
00208 for ( QDomNode it = tabStops.firstChild(); !it.isNull(); it = it.nextSibling() )
00209 {
00210 QDomElement tabStop = it.toElement();
00211 Q_ASSERT( tabStop.tagName() == "style:tab-stop" );
00212 QString type = tabStop.attributeNS( ooNS::style, "type", QString::null );
00213
00214 QDomElement elem = parentElement.ownerDocument().createElement( "TABULATOR" );
00215 int kOfficeType = 0;
00216 if ( type == "left" )
00217 kOfficeType = 0;
00218 else if ( type == "center" )
00219 kOfficeType = 1;
00220 else if ( type == "right" )
00221 kOfficeType = 2;
00222 else if ( type == "char" ) {
00223 QString delimiterChar = tabStop.attributeNS( ooNS::style, "char", QString::null );
00224 elem.setAttribute( "alignchar", delimiterChar );
00225 kOfficeType = 3;
00226 }
00227
00228 elem.setAttribute( "type", kOfficeType );
00229
00230 double pos = KoUnit::parseValue( tabStop.attributeNS( ooNS::style, "position", QString::null ) );
00231 elem.setAttribute( "ptpos", pos );
00232
00233
00234
00235 QString leaderChar = tabStop.attributeNS( ooNS::style, "leader-char", QString::null );
00236 if ( !leaderChar.isEmpty() )
00237 {
00238 int filling = 0;
00239 QChar ch = leaderChar[0];
00240 switch (ch.latin1()) {
00241 case '.':
00242 filling = 1; break;
00243 case '-':
00244 case '_':
00245 filling = 2; break;
00246 default:
00247
00248
00249 break;
00250 }
00251 elem.setAttribute( "filling", filling );
00252 }
00253 parentElement.appendChild( elem );
00254 }
00255
00256 }
00257
00258 void OoUtils::importBorders( QDomElement& parentElement, const KoStyleStack& styleStack )
00259 {
00260 if (styleStack.hasAttributeNS( ooNS::fo, "border","left"))
00261 {
00262 double width;
00263 int style;
00264 QColor color;
00265 if (OoUtils::parseBorder(styleStack.attributeNS( ooNS::fo, "border", "left"), &width, &style, &color))
00266 {
00267 QDomElement lbElem = parentElement.ownerDocument().createElement("LEFTBORDER");
00268 lbElem.setAttribute("width", width);
00269 lbElem.setAttribute("style", style);
00270 if (color.isValid()) {
00271 lbElem.setAttribute("red", color.red());
00272 lbElem.setAttribute("green", color.green());
00273 lbElem.setAttribute("blue", color.blue());
00274 }
00275 parentElement.appendChild(lbElem);
00276 }
00277 }
00278
00279 if (styleStack.hasAttributeNS( ooNS::fo, "border", "right"))
00280 {
00281 double width;
00282 int style;
00283 QColor color;
00284 if (OoUtils::parseBorder(styleStack.attributeNS( ooNS::fo, "border", "right"), &width, &style, &color))
00285 {
00286 QDomElement lbElem = parentElement.ownerDocument().createElement("RIGHTBORDER");
00287 lbElem.setAttribute("width", width);
00288 lbElem.setAttribute("style", style);
00289 if (color.isValid()) {
00290 lbElem.setAttribute("red", color.red());
00291 lbElem.setAttribute("green", color.green());
00292 lbElem.setAttribute("blue", color.blue());
00293 }
00294 parentElement.appendChild(lbElem);
00295 }
00296 }
00297
00298 if (styleStack.hasAttributeNS( ooNS::fo, "border", "top"))
00299 {
00300 double width;
00301 int style;
00302 QColor color;
00303 if (OoUtils::parseBorder(styleStack.attributeNS( ooNS::fo, "border", "top"), &width, &style, &color))
00304 {
00305 QDomElement lbElem = parentElement.ownerDocument().createElement("TOPBORDER");
00306 lbElem.setAttribute("width", width);
00307 lbElem.setAttribute("style", style);
00308 if (color.isValid()) {
00309 lbElem.setAttribute("red", color.red());
00310 lbElem.setAttribute("green", color.green());
00311 lbElem.setAttribute("blue", color.blue());
00312 }
00313 parentElement.appendChild(lbElem);
00314 }
00315 }
00316
00317 if (styleStack.hasAttributeNS( ooNS::fo, "border", "bottom"))
00318 {
00319 double width;
00320 int style;
00321 QColor color;
00322 if (OoUtils::parseBorder(styleStack.attributeNS( ooNS::fo, "border", "bottom"), &width, &style, &color))
00323 {
00324 QDomElement lbElem = parentElement.ownerDocument().createElement("BOTTOMBORDER");
00325 lbElem.setAttribute("width", width);
00326 lbElem.setAttribute("style", style);
00327 if (color.isValid()) {
00328 lbElem.setAttribute("red", color.red());
00329 lbElem.setAttribute("green", color.green());
00330 lbElem.setAttribute("blue", color.blue());
00331 }
00332 parentElement.appendChild(lbElem);
00333 }
00334 }
00335 }
00336
00337 void OoUtils::importUnderline( const QString& in, QString& underline, QString& styleline )
00338 {
00339 underline = "single";
00340 if ( in == "none" )
00341 underline = "0";
00342 else if ( in == "single" )
00343 styleline = "solid";
00344 else if ( in == "double" )
00345 {
00346 underline = in;
00347 styleline = "solid";
00348 }
00349 else if ( in == "dotted" || in == "bold-dotted" )
00350 styleline = "dot";
00351 else if ( in == "dash"
00352
00353 || in == "long-dash"
00354 || in == "bold-dash"
00355 || in == "bold-long-dash"
00356 )
00357 styleline = "dash";
00358 else if ( in == "dot-dash"
00359 || in == "bold-dot-dash")
00360 styleline = "dashdot";
00361 else if ( in == "dot-dot-dash"
00362 || in == "bold-dot-dot-dash")
00363 styleline = "dashdotdot";
00364 else if ( in == "wave"
00365 || in == "bold-wave"
00366 || in == "double-wave"
00367 || in == "small-wave" )
00368 {
00369 underline = in;
00370 styleline = "solid";
00371 }
00372 else if( in == "bold" )
00373 {
00374 underline = "single-bold";
00375 styleline = "solid";
00376 }
00377 else
00378 kdWarning(30519) << k_funcinfo << " unsupported text-underline value: " << in << endl;
00379 }
00380
00381 void OoUtils::importTextPosition( const QString& text_position, QString& value, QString& relativetextsize )
00382 {
00383
00384
00385
00386 QStringList lst = QStringList::split( ' ', text_position );
00387 if ( !lst.isEmpty() )
00388 {
00389 QString textPos = lst.front().stripWhiteSpace();
00390 QString textSize;
00391 lst.pop_front();
00392 if ( !lst.isEmpty() )
00393 textSize = lst.front().stripWhiteSpace();
00394 if ( !lst.isEmpty() )
00395 kdWarning(30519) << "Strange text position: " << text_position << endl;
00396 bool super = textPos == "super";
00397 bool sub = textPos == "sub";
00398 if ( textPos.endsWith("%") )
00399 {
00400 textPos.truncate( textPos.length() - 1 );
00401
00402
00403 double val = textPos.toDouble();
00404 if ( val > 0 )
00405 super = true;
00406 else if ( val < 0 )
00407 sub = true;
00408 }
00409 if ( super )
00410 value = "2";
00411 else if ( sub )
00412 value = "1";
00413 else
00414 value = "0";
00415 if ( !textSize.isEmpty() && textSize.endsWith("%") )
00416 {
00417 textSize.truncate( textSize.length() - 1 );
00418 double textSizeValue = textSize.toDouble() / 100;
00419 relativetextsize = QString::number( textSizeValue );
00420 }
00421 }
00422 else
00423 value = "0";
00424 }
00425
00426 void OoUtils::createDocumentInfo(QDomDocument &_meta, QDomDocument & docinfo)
00427 {
00428 QDomNode meta = KoDom::namedItemNS( _meta, ooNS::office, "document-meta" );
00429 QDomNode office = KoDom::namedItemNS( meta, ooNS::office, "meta" );
00430
00431 if ( office.isNull() )
00432 return;
00433 QDomElement elementDocInfo = docinfo.documentElement();
00434
00435 QDomElement e = KoDom::namedItemNS( office, ooNS::dc, "creator" );
00436 if ( !e.isNull() && !e.text().isEmpty() )
00437 {
00438 QDomElement author = docinfo.createElement( "author" );
00439 QDomElement t = docinfo.createElement( "full-name" );
00440 author.appendChild( t );
00441 t.appendChild( docinfo.createTextNode( e.text() ) );
00442 elementDocInfo.appendChild( author);
00443 }
00444
00445 e = KoDom::namedItemNS( office, ooNS::dc, "title" );
00446 if ( !e.isNull() && !e.text().isEmpty() )
00447 {
00448 QDomElement about = docinfo.createElement( "about" );
00449 QDomElement title = docinfo.createElement( "title" );
00450 about.appendChild( title );
00451 title.appendChild( docinfo.createTextNode( e.text() ) );
00452 elementDocInfo.appendChild( about );
00453 }
00454
00455 e = KoDom::namedItemNS( office, ooNS::dc, "description" );
00456 if ( !e.isNull() && !e.text().isEmpty() )
00457 {
00458 QDomElement about = elementDocInfo.namedItem( "about" ).toElement();
00459 if ( about.isNull() ) {
00460 about = docinfo.createElement( "about" );
00461 elementDocInfo.appendChild( about );
00462 }
00463 QDomElement title = docinfo.createElement( "abstract" );
00464 about.appendChild( title );
00465 title.appendChild( docinfo.createTextNode( e.text() ) );
00466 }
00467 e = KoDom::namedItemNS( office, ooNS::dc, "subject" );
00468 if ( !e.isNull() && !e.text().isEmpty() )
00469 {
00470 QDomElement about = elementDocInfo.namedItem( "about" ).toElement();
00471 if ( about.isNull() ) {
00472 about = docinfo.createElement( "about" );
00473 elementDocInfo.appendChild( about );
00474 }
00475 QDomElement subject = docinfo.createElement( "subject" );
00476 about.appendChild( subject );
00477 subject.appendChild( docinfo.createTextNode( e.text() ) );
00478 }
00479 e = KoDom::namedItemNS( office, ooNS::meta, "keywords" );
00480 if ( !e.isNull() )
00481 {
00482 QDomElement about = elementDocInfo.namedItem( "about" ).toElement();
00483 if ( about.isNull() ) {
00484 about = docinfo.createElement( "about" );
00485 elementDocInfo.appendChild( about );
00486 }
00487 QDomElement tmp = KoDom::namedItemNS( e, ooNS::meta, "keyword" );
00488 if ( !tmp.isNull() && !tmp.text().isEmpty() )
00489 {
00490 QDomElement keyword = docinfo.createElement( "keyword" );
00491 about.appendChild( keyword );
00492 keyword.appendChild( docinfo.createTextNode( tmp.text() ) );
00493 }
00494 }
00495 }
00496
00497 KoFilter::ConversionStatus OoUtils::loadAndParse(const QString& fileName, QDomDocument& doc, KoStore *m_store )
00498 {
00499 kdDebug(30518) << "loadAndParse: Trying to open " << fileName << endl;
00500
00501 if (!m_store->open(fileName))
00502 {
00503 kdWarning(30519) << "Entry " << fileName << " not found!" << endl;
00504 return KoFilter::FileNotFound;
00505 }
00506 KoFilter::ConversionStatus convertStatus = loadAndParse( m_store->device(),doc, fileName );
00507 m_store->close();
00508 return convertStatus;
00509
00510 }
00511
00512 KoFilter::ConversionStatus OoUtils::loadAndParse(QIODevice* io, QDomDocument& doc, const QString & fileName)
00513 {
00514 QXmlInputSource source( io );
00515
00516 QXmlSimpleReader reader;
00517 KoDocument::setupXmlReader( reader, true );
00518
00519
00520 QString errorMsg;
00521 int errorLine, errorColumn;
00522 if ( !doc.setContent( &source, &reader, &errorMsg, &errorLine, &errorColumn ) )
00523 {
00524 kdError(30519) << "Parsing error in " << fileName << "! Aborting!" << endl
00525 << " In line: " << errorLine << ", column: " << errorColumn << endl
00526 << " Error message: " << errorMsg << endl;
00527 return KoFilter::ParsingError;
00528 }
00529
00530 kdDebug(30519) << "File " << fileName << " loaded and parsed!" << endl;
00531
00532 return KoFilter::OK;
00533
00534 }
00535
00536 KoFilter::ConversionStatus OoUtils::loadAndParse(const QString& filename, QDomDocument& doc, KZip * m_zip)
00537 {
00538 kdDebug(30519) << "Trying to open " << filename << endl;
00539
00540 if (!m_zip)
00541 {
00542 kdError(30519) << "No ZIP file!" << endl;
00543 return KoFilter::CreationError;
00544 }
00545
00546 const KArchiveEntry* entry = m_zip->directory()->entry( filename );
00547 if (!entry)
00548 {
00549 kdWarning(30519) << "Entry " << filename << " not found!" << endl;
00550 return KoFilter::FileNotFound;
00551 }
00552 if (entry->isDirectory())
00553 {
00554 kdWarning(30519) << "Entry " << filename << " is a directory!" << endl;
00555 return KoFilter::WrongFormat;
00556 }
00557 const KZipFileEntry* f = static_cast<const KZipFileEntry *>(entry);
00558 kdDebug(30519) << "Entry " << filename << " has size " << f->size() << endl;
00559 QIODevice* io = f->device();
00560 KoFilter::ConversionStatus convertStatus = loadAndParse( io,doc, filename );
00561 delete io;
00562 return convertStatus;
00563 }
00564
00565 KoFilter::ConversionStatus OoUtils::loadThumbnail( QImage& thumbnail, KZip * m_zip )
00566 {
00567 const QString filename( "Thumbnails/thumbnail.png" );
00568 kdDebug(30519) << "Trying to open thumbnail " << filename << endl;
00569
00570 if (!m_zip)
00571 {
00572 kdError(30519) << "No ZIP file!" << endl;
00573 return KoFilter::CreationError;
00574 }
00575
00576 const KArchiveEntry* entry = m_zip->directory()->entry( filename );
00577 if (!entry)
00578 {
00579 kdWarning(30519) << "Entry " << filename << " not found!" << endl;
00580 return KoFilter::FileNotFound;
00581 }
00582 if (entry->isDirectory())
00583 {
00584 kdWarning(30519) << "Entry " << filename << " is a directory!" << endl;
00585 return KoFilter::WrongFormat;
00586 }
00587 const KZipFileEntry* f = static_cast<const KZipFileEntry *>(entry);
00588 QIODevice* io=f->device();
00589 kdDebug(30519) << "Entry " << filename << " has size " << f->size() << endl;
00590
00591 if ( ! io->open( IO_ReadOnly ) )
00592 {
00593 kdWarning(30519) << "Thumbnail could not be opened!" <<endl;
00594 delete io;
00595 return KoFilter::StupidError;
00596 }
00597
00598 QImageIO imageIO( io, "PNG" );
00599 if ( ! imageIO.read() )
00600 {
00601 kdWarning(30519) << "Thumbnail could not be read!" <<endl;
00602 delete io;
00603 return KoFilter::StupidError;
00604 }
00605
00606 io->close();
00607
00608 thumbnail = imageIO.image();
00609
00610 if ( thumbnail.isNull() )
00611 {
00612 kdWarning(30519) << "Read thumbnail is null!" <<endl;
00613 delete io;
00614 return KoFilter::StupidError;
00615 }
00616
00617 delete io;
00618
00619 kdDebug(30519) << "File " << filename << " loaded!" << endl;
00620
00621 return KoFilter::OK;
00622 }