filters

xamlimport.cc

00001 /* This file is part of the KDE project
00002    Copyright (C) 2002, 2003, The Karbon Developers
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 "xamlimport.h"
00021 #include "color.h"
00022 #include <KoFilterChain.h>
00023 #include <kgenericfactory.h>
00024 #include <kdebug.h>
00025 #include <KoUnit.h>
00026 #include <KoGlobal.h>
00027 #include <shapes/vellipse.h>
00028 #include <shapes/vrectangle.h>
00029 #include <shapes/vpolygon.h>
00030 #include <commands/vtransformcmd.h>
00031 #include <core/vsegment.h>
00032 #include <core/vtext.h>
00033 #include <core/vglobal.h>
00034 #include <core/vgroup.h>
00035 #include <core/vimage.h>
00036 #include <core/vlayer.h>
00037 #include <qcolor.h>
00038 #include <qfile.h>
00039 #include <kfilterdev.h>
00040 
00041 typedef KGenericFactory<XAMLImport, KoFilter> XAMLImportFactory;
00042 K_EXPORT_COMPONENT_FACTORY( libkarbonxamlimport, XAMLImportFactory( "kofficefilters" ) )
00043 
00044 XAMLImport::XAMLImport(KoFilter *, const char *, const QStringList&) :
00045     KoFilter(),
00046     outdoc( "DOC" )
00047 {
00048     m_gc.setAutoDelete( true );
00049 }
00050 
00051 XAMLImport::~XAMLImport()
00052 {
00053 }
00054 
00055 KoFilter::ConversionStatus XAMLImport::convert(const QCString& from, const QCString& to)
00056 {
00057     // check for proper conversion
00058     if( to != "application/x-karbon" || from != "image/wvg+xml" )
00059         return KoFilter::NotImplemented;
00060 
00061         //Find the last extension
00062         QString strExt;
00063         QString fileIn ( m_chain->inputFile() );
00064         const int result=fileIn.findRev('.');
00065         if (result>=0)
00066         {
00067                 strExt=fileIn.mid(result).lower();
00068         }
00069 
00070         QString strMime; // Mime type of the compressor
00071         if ((strExt==".gz")      //in case of .svg.gz (logical extension)
00072                 ||(strExt==".wvgz")) //in case of .svgz (extension used prioritary)
00073                 strMime="application/x-gzip"; // Compressed with gzip
00074         else if (strExt==".bz2") //in case of .svg.bz2 (logical extension)
00075                 strMime="application/x-bzip2"; // Compressed with bzip2
00076         else
00077                 strMime="text/plain";
00078 
00079         kdDebug(30514) << "File extension: -" << strExt << "- Compression: " << strMime << endl;
00080 
00081         QIODevice* in = KFilterDev::deviceForFile(fileIn,strMime);
00082 
00083         if (!in->open(IO_ReadOnly))
00084         {
00085                 kdError(30514) << "Cannot open file! Aborting!" << endl;
00086                 delete in;
00087                 return KoFilter::FileNotFound;
00088         }
00089 
00090     int line, col;
00091     QString errormessage;
00092         const bool parsed=inpdoc.setContent( in, &errormessage, &line, &col );
00093         in->close();
00094         delete in;
00095     if ( ! parsed )
00096     {
00097             kdError(30514) << "Error while parsing file: "
00098                 << "at line " << line << " column: " << col
00099                 << " message: " << errormessage << endl;
00100         // ### TODO: feedback to the user
00101             return KoFilter::ParsingError;
00102     }
00103 
00104     // Do the conversion!
00105     convert();
00106 
00107     KoStoreDevice* out = m_chain->storageFile( "root", KoStore::Write );
00108     if( !out )
00109     {
00110         kdError(30514) << "Unable to open output file!" << endl;
00111         return KoFilter::StorageCreationError;
00112     }
00113     QCString cstring = outdoc.toCString(); // utf-8 already
00114     out->writeBlock( cstring.data(), cstring.length() );
00115 
00116     return KoFilter::OK; // was successful
00117 }
00118 
00119 void
00120 XAMLImport::convert()
00121 {
00122     XAMLGraphicsContext *gc = new XAMLGraphicsContext;
00123     QDomElement docElem = inpdoc.documentElement();
00124     KoRect bbox( 0, 0, 550.0, 841.0 );
00125     double width    = !docElem.attribute( "width" ).isEmpty() ? parseUnit( docElem.attribute( "width" ), true, false, bbox ) : 550.0;
00126     double height   = !docElem.attribute( "height" ).isEmpty() ? parseUnit( docElem.attribute( "height" ), false, true, bbox ) : 841.0;
00127     m_document.setWidth( width );
00128     m_document.setHeight( height );
00129     m_outerRect = m_document.boundingBox();
00130 
00131     // undo y-mirroring
00132     if( !docElem.attribute( "viewBox" ).isEmpty() )
00133     {
00134         // allow for viewbox def with ',' or whitespace
00135         QString viewbox( docElem.attribute( "viewBox" ) );
00136         QStringList points = QStringList::split( ' ', viewbox.replace( ',', ' ').simplifyWhiteSpace() );
00137 
00138         gc->matrix.scale( width / points[2].toFloat() , height / points[3].toFloat() );
00139         m_outerRect.setWidth( m_outerRect.width() * ( points[2].toFloat() / width ) );
00140         m_outerRect.setHeight( m_outerRect.height() * ( points[3].toFloat() / height ) );
00141     }
00142     m_gc.push( gc );
00143     parseGroup( 0L, docElem );
00144 
00145     QWMatrix mat;
00146     mat.scale( 1, -1 );
00147     mat.translate( 0, -m_document.height() );
00148     VTransformCmd trafo( 0L, mat );
00149     trafo.visit( m_document );
00150     outdoc = m_document.saveXML();
00151 }
00152 
00153 #define DPI 90
00154 
00155 double
00156 XAMLImport::toPercentage( QString s )
00157 {
00158     if( s.endsWith( "%" ) )
00159         return s.remove( '%' ).toDouble();
00160     else
00161         return s.toDouble() * 100.0;
00162 }
00163 
00164 double
00165 XAMLImport::fromPercentage( QString s )
00166 {
00167     if( s.endsWith( "%" ) )
00168         return s.remove( '%' ).toDouble() / 100.0;
00169     else
00170         return s.toDouble();
00171 }
00172 
00173 // parses the number into parameter number
00174 const char *
00175 getNumber( const char *ptr, double &number )
00176 {
00177     int integer, exponent;
00178     double decimal, frac;
00179     int sign, expsign;
00180 
00181     exponent = 0;
00182     integer = 0;
00183     frac = 1.0;
00184     decimal = 0;
00185     sign = 1;
00186     expsign = 1;
00187 
00188     // read the sign
00189     if(*ptr == '+')
00190         ptr++;
00191     else if(*ptr == '-')
00192     {
00193         ptr++;
00194         sign = -1;
00195     }
00196 
00197     // read the integer part
00198     while(*ptr != '\0' && *ptr >= '0' && *ptr <= '9')
00199         integer = (integer * 10) + *(ptr++) - '0';
00200     if(*ptr == '.') // read the decimals
00201     {
00202         ptr++;
00203         while(*ptr != '\0' && *ptr >= '0' && *ptr <= '9')
00204             decimal += (*(ptr++) - '0') * (frac *= 0.1);
00205     }
00206 
00207     if(*ptr == 'e' || *ptr == 'E') // read the exponent part
00208     {
00209         ptr++;
00210 
00211         // read the sign of the exponent
00212         if(*ptr == '+')
00213             ptr++;
00214         else if(*ptr == '-')
00215         {
00216             ptr++;
00217             expsign = -1;
00218         }
00219 
00220         exponent = 0;
00221         while(*ptr != '\0' && *ptr >= '0' && *ptr <= '9')
00222         {
00223             exponent *= 10;
00224             exponent += *ptr - '0';
00225             ptr++;
00226         }
00227     }
00228     number = integer + decimal;
00229     number *= sign * pow( (double)10, double( expsign * exponent ) );
00230 
00231     return ptr;
00232 }
00233 
00234 
00235 double
00236 XAMLImport::parseUnit( const QString &unit, bool horiz, bool vert, KoRect bbox )
00237 {
00238     // TODO : percentage?
00239     double value = 0;
00240     const char *start = unit.latin1();
00241     if(!start) {
00242         return 0;
00243     }
00244     const char *end = getNumber( start, value );
00245 
00246     if( uint( end - start ) < unit.length() )
00247     {
00248         if( unit.right( 2 ) == "pt" )
00249             value = ( value / 72.0 ) * DPI;
00250         else if( unit.right( 2 ) == "cm" )
00251             value = ( value / 2.54 ) * DPI;
00252         else if( unit.right( 2 ) == "pc" )
00253             value = ( value / 6.0 ) * DPI;
00254         else if( unit.right( 2 ) == "mm" )
00255             value = ( value / 25.4 ) * DPI;
00256         else if( unit.right( 2 ) == "in" )
00257             value = value * DPI;
00258         else if( unit.right( 2 ) == "pt" )
00259             value = ( value / 72.0 ) * DPI;
00260         else if( unit.right( 2 ) == "em" )
00261             value = value * m_gc.current()->font.pointSize() / ( sqrt( pow( m_gc.current()->matrix.m11(), 2 ) + pow( m_gc.current()->matrix.m22(), 2 ) ) / sqrt( 2.0 ) );
00262         else if( unit.right( 1 ) == "%" )
00263         {
00264             if( horiz && vert )
00265                 value = ( value / 100.0 ) * (sqrt( pow( bbox.width(), 2 ) + pow( bbox.height(), 2 ) ) / sqrt( 2.0 ) );
00266             else if( horiz )
00267                 value = ( value / 100.0 ) * bbox.width();
00268             else if( vert )
00269                 value = ( value / 100.0 ) * bbox.height();
00270         }
00271     }
00272     /*else
00273     {
00274         if( m_gc.current() )
00275         {
00276             if( horiz && vert )
00277                 value *= sqrt( pow( m_gc.current()->matrix.m11(), 2 ) + pow( m_gc.current()->matrix.m22(), 2 ) ) / sqrt( 2.0 );
00278             else if( horiz )
00279                 value /= m_gc.current()->matrix.m11();
00280             else if( vert )
00281                 value /= m_gc.current()->matrix.m22();
00282         }
00283     }*/
00284     return value;
00285 }
00286 
00287 QColor
00288 XAMLImport::parseColor( const QString &rgbColor )
00289 {
00290     int r, g, b;
00291     keywordToRGB( rgbColor, r, g, b );
00292     return QColor( r, g, b );
00293 }
00294 
00295 void
00296 XAMLImport::parseColor( VColor &color, const QString &s )
00297 {
00298     if( s.startsWith( "rgb(" ) )
00299     {
00300         QString parse = s.stripWhiteSpace();
00301         QStringList colors = QStringList::split( ',', parse );
00302         QString r = colors[0].right( ( colors[0].length() - 4 ) );
00303         QString g = colors[1];
00304         QString b = colors[2].left( ( colors[2].length() - 1 ) );
00305 
00306         if( r.contains( "%" ) )
00307         {
00308             r = r.left( r.length() - 1 );
00309             r = QString::number( int( ( double( 255 * r.toDouble() ) / 100.0 ) ) );
00310         }
00311 
00312         if( g.contains( "%" ) )
00313         {
00314             g = g.left( g.length() - 1 );
00315             g = QString::number( int( ( double( 255 * g.toDouble() ) / 100.0 ) ) );
00316         }
00317 
00318         if( b.contains( "%" ) )
00319         {
00320             b = b.left( b.length() - 1 );
00321             b = QString::number( int( ( double( 255 * b.toDouble() ) / 100.0 ) ) );
00322         }
00323 
00324         QColor c( r.toInt(), g.toInt(), b.toInt() );
00325         color.set( c.red() / 255.0, c.green() / 255.0, c.blue() / 255.0 );
00326     }
00327     else
00328     {
00329         QString rgbColor = s.stripWhiteSpace();
00330         QColor c;
00331         if( rgbColor.startsWith( "#" ) )
00332             c.setNamedColor( rgbColor );
00333         else
00334             c = parseColor( rgbColor );
00335         color.set( c.red() / 255.0, c.green() / 255.0, c.blue() / 255.0 );
00336     }
00337 }
00338 
00339 void
00340 XAMLImport::parseColorStops( VGradient *gradient, const QDomElement &e )
00341 {
00342     VColor c;
00343     for( QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling() )
00344     {
00345         QDomElement stop = n.toElement();
00346         if( stop.tagName() == "stop" )
00347         {
00348             float offset;
00349             QString temp = stop.attribute( "offset" );
00350             if( temp.contains( '%' ) )
00351             {
00352                 temp = temp.left( temp.length() - 1 );
00353                 offset = temp.toFloat() / 100.0;
00354             }
00355             else
00356                 offset = temp.toFloat();
00357 
00358             if( !stop.attribute( "stop-color" ).isEmpty() )
00359                 parseColor( c, stop.attribute( "stop-color" ) );
00360             else
00361             {
00362                 // try style attr
00363                 QString style = stop.attribute( "style" ).simplifyWhiteSpace();
00364                 QStringList substyles = QStringList::split( ';', style );
00365                 for( QStringList::Iterator it = substyles.begin(); it != substyles.end(); ++it )
00366                 {
00367                     QStringList substyle = QStringList::split( ':', (*it) );
00368                     QString command = substyle[0].stripWhiteSpace();
00369                     QString params  = substyle[1].stripWhiteSpace();
00370                     if( command == "stop-color" )
00371                         parseColor( c, params );
00372                     if( command == "stop-opacity" )
00373                         c.setOpacity( params.toDouble() );
00374                 }
00375 
00376             }
00377             if( !stop.attribute( "stop-opacity" ).isEmpty() )
00378                 c.setOpacity( stop.attribute( "stop-opacity" ).toDouble() );
00379             gradient->addStop( c, offset, 0.5 );
00380         }
00381     }
00382 }
00383 
00384 void
00385 XAMLImport::parseGradient( const QDomElement &e )
00386 {
00387     GradientHelper gradhelper;
00388     gradhelper.gradient.clearStops();
00389     gradhelper.gradient.setRepeatMethod( VGradient::none );
00390 
00391     QString href = e.attribute( "xlink:href" ).mid( 1 );
00392     if( !href.isEmpty() )
00393     {
00394         //kdDebug() << "Indexing with href : " << href.latin1() << endl;
00395         gradhelper.gradient = m_gradients[ href ].gradient;
00396     }
00397 
00398     gradhelper.bbox = e.attribute( "gradientUnits" ) != "userSpaceOnUse";
00399 
00400     if( e.tagName() == "linearGradient" )
00401     {
00402         if( gradhelper.bbox )
00403         {
00404             gradhelper.gradient.setOrigin( KoPoint( toPercentage( e.attribute( "x1", "0%" ) ), toPercentage( e.attribute( "y1", "0%" ) ) ) );
00405             gradhelper.gradient.setVector( KoPoint( toPercentage( e.attribute( "x2", "100%" ) ), toPercentage( e.attribute( "y2", "0%" ) ) ) );
00406         }
00407         else
00408         {
00409             gradhelper.gradient.setOrigin( KoPoint( e.attribute( "x1" ).toDouble(), e.attribute( "y1" ).toDouble() ) );
00410             gradhelper.gradient.setVector( KoPoint( e.attribute( "x2" ).toDouble(), e.attribute( "y2" ).toDouble() ) );
00411         }
00412     }
00413     else
00414     {
00415         if( gradhelper.bbox )
00416         {
00417             gradhelper.gradient.setOrigin( KoPoint( toPercentage( e.attribute( "cx", "50%" ) ), toPercentage( e.attribute( "cy", "50%" ) ) ) );
00418             gradhelper.gradient.setVector( KoPoint( toPercentage( e.attribute( "cx", "50%" ) ) + toPercentage( e.attribute( "r", "50%" ) ),
00419                                                     toPercentage( e.attribute( "cy", "50%" ) ) ) );
00420             gradhelper.gradient.setFocalPoint( KoPoint( toPercentage( e.attribute( "fx", "50%" ) ), toPercentage( e.attribute( "fy", "50%" ) ) ) );
00421         }
00422         else
00423         {
00424             gradhelper.gradient.setOrigin( KoPoint( e.attribute( "cx" ).toDouble(), e.attribute( "cy" ).toDouble() ) );
00425             gradhelper.gradient.setFocalPoint( KoPoint( e.attribute( "fx" ).toDouble(), e.attribute( "fy" ).toDouble() ) );
00426             gradhelper.gradient.setVector( KoPoint( e.attribute( "cx" ).toDouble() + e.attribute( "r" ).toDouble(), e.attribute( "cy" ).toDouble() ) );
00427         }
00428         gradhelper.gradient.setType( VGradient::radial );
00429     }
00430     // handle spread method
00431     QString spreadMethod = e.attribute( "spreadMethod" );
00432     if( !spreadMethod.isEmpty() )
00433     {
00434         if( spreadMethod == "reflect" )
00435             gradhelper.gradient.setRepeatMethod( VGradient::reflect );
00436         else if( spreadMethod == "repeat" )
00437             gradhelper.gradient.setRepeatMethod( VGradient::repeat );
00438     }
00439     parseColorStops( &gradhelper.gradient, e );
00440     //gradient.setGradientTransform( parseTransform( e.attribute( "gradientTransform" ) ) );
00441     gradhelper.gradientTransform = VPath::parseTransform( e.attribute( "gradientTransform" ) );
00442     m_gradients.insert( e.attribute( "id" ), gradhelper );
00443 }
00444 
00445 void
00446 XAMLImport::parsePA( VObject *obj, XAMLGraphicsContext *gc, const QString &command, const QString &params )
00447 {
00448     VColor fillcolor = gc->fill.color();
00449     VColor strokecolor = gc->stroke.color();
00450 
00451     if( command == "fill" )
00452     {
00453         if( params == "none" )
00454             gc->fill.setType( VFill::none );
00455         else if( params.startsWith( "url(" ) )
00456         {
00457             unsigned int start = params.find("#") + 1;
00458             unsigned int end = params.findRev(")");
00459             QString key = params.mid( start, end - start );
00460             gc->fill.gradient() = m_gradients[ key ].gradient;
00461             if( m_gradients[ key ].bbox )
00462             {
00463                 // adjust to bbox
00464                 KoRect bbox = obj->boundingBox();
00465                 //kdDebug() << "bbox x : " << bbox.x() << endl;
00466                 //kdDebug() << "!!!!!!bbox y : " << bbox.y() << endl;
00467                 //kdDebug() << gc->fill.gradient().origin().x() << endl;
00468                 //kdDebug() << gc->fill.gradient().vector().x() << endl;
00469                 double offsetx = parseUnit( QString( "%1%" ).arg( gc->fill.gradient().origin().x() ), true, false, bbox );
00470                 double offsety = parseUnit( QString( "%1%" ).arg( gc->fill.gradient().origin().y() ), false, true, bbox );
00471                 gc->fill.gradient().setOrigin( KoPoint( bbox.x() + offsetx, bbox.y() + offsety ) );
00472                 offsetx = parseUnit( QString( "%1%" ).arg( gc->fill.gradient().focalPoint().x() ), true, false, bbox );
00473                 offsety = parseUnit( QString( "%1%" ).arg( gc->fill.gradient().focalPoint().y() ), false, true, bbox );
00474                 gc->fill.gradient().setFocalPoint( KoPoint( bbox.x() + offsetx, bbox.y() + offsety ) );
00475                 offsetx = parseUnit( QString( "%1%" ).arg( gc->fill.gradient().vector().x() ), true, false, bbox );
00476                 offsety = parseUnit( QString( "%1%" ).arg( gc->fill.gradient().vector().y() ), false, true, bbox );
00477                 gc->fill.gradient().setVector( KoPoint( bbox.x() + offsetx, bbox.y() + offsety ) );
00478                 //kdDebug() << offsety << endl;
00479                 //kdDebug() << gc->fill.gradient().origin().x() << endl;
00480                 //kdDebug() << gc->fill.gradient().origin().y() << endl;
00481                 //kdDebug() << gc->fill.gradient().vector().x() << endl;
00482                 //kdDebug() << gc->fill.gradient().vector().y() << endl;
00483             }
00484             gc->fill.gradient().transform( m_gradients[ key ].gradientTransform );
00485             if( !m_gradients[ key ].bbox )
00486                 gc->fill.gradient().transform( gc->matrix );
00487             gc->fill.setType( VFill::grad );
00488         }
00489         else
00490         {
00491             parseColor( fillcolor,  params );
00492             gc->fill.setType( VFill::solid );
00493         }
00494     }
00495     else if( command == "fill-rule" )
00496     {
00497         if( params == "nonzero" )
00498             gc->fillRule = winding;
00499         else if( params == "evenodd" )
00500             gc->fillRule = evenOdd;
00501     }
00502     else if( command == "stroke" )
00503     {
00504         if( params == "none" )
00505             gc->stroke.setType( VStroke::none );
00506         else if( params.startsWith( "url(" ) )
00507         {
00508             unsigned int start = params.find("#") + 1;
00509             unsigned int end = params.findRev(")");
00510             QString key = params.mid( start, end - start );
00511             gc->stroke.gradient() = m_gradients[ key ].gradient;
00512             gc->stroke.gradient().transform( m_gradients[ key ].gradientTransform );
00513             gc->stroke.gradient().transform( gc->matrix );
00514             gc->stroke.setType( VStroke::grad );
00515         }
00516         else
00517         {
00518             parseColor( strokecolor, params );
00519             gc->stroke.setType( VStroke::solid );
00520         }
00521     }
00522     else if( command == "stroke-width" )
00523         gc->stroke.setLineWidth( parseUnit( params, true, true, m_outerRect ) );
00524     else if( command == "stroke-linejoin" )
00525     {
00526         if( params == "miter" )
00527             gc->stroke.setLineJoin( VStroke::joinMiter );
00528         else if( params == "round" )
00529             gc->stroke.setLineJoin( VStroke::joinRound );
00530         else if( params == "bevel" )
00531             gc->stroke.setLineJoin( VStroke::joinBevel );
00532     }
00533     else if( command == "stroke-linecap" )
00534     {
00535         if( params == "butt" )
00536             gc->stroke.setLineCap( VStroke::capButt );
00537         else if( params == "round" )
00538             gc->stroke.setLineCap( VStroke::capRound );
00539         else if( params == "square" )
00540             gc->stroke.setLineCap( VStroke::capSquare );
00541     }
00542     else if( command == "stroke-miterlimit" )
00543         gc->stroke.setMiterLimit( params.toFloat() );
00544     else if( command == "stroke-dasharray" )
00545     {
00546         QValueList<float> array;
00547         if(params != "none")
00548         {
00549             QStringList dashes = QStringList::split( ' ', params );
00550             for( QStringList::Iterator it = dashes.begin(); it != dashes.end(); ++it )
00551                 array.append( (*it).toFloat() );
00552         }
00553         gc->stroke.dashPattern().setArray( array );
00554     }
00555     else if( command == "stroke-dashoffset" )
00556         gc->stroke.dashPattern().setOffset( params.toFloat() );
00557     // handle opacity
00558     else if( command == "stroke-opacity" )
00559         strokecolor.setOpacity( fromPercentage( params ) );
00560     else if( command == "fill-opacity" )
00561         fillcolor.setOpacity( fromPercentage( params ) );
00562     else if( command == "opacity" )
00563     {
00564         fillcolor.setOpacity( fromPercentage( params ) );
00565         strokecolor.setOpacity( fromPercentage( params ) );
00566     }
00567     else if( command == "font-family" )
00568     {
00569         QString family = params;
00570         family.replace( '\'' , ' ' );
00571         gc->font.setFamily( family );
00572     }
00573     else if( command == "font-size" )
00574     {
00575         float pointSize = parseUnit( params );
00576         pointSize *= gc->matrix.m22() > 0 ? gc->matrix.m22() : -1.0 * gc->matrix.m22();
00577         gc->font.setPointSizeFloat( pointSize );
00578     }
00579     else if( command == "text-decoration" )
00580     {
00581         if( params == "line-through" )
00582             gc->font.setStrikeOut( true );
00583         else if( params == "underline" )
00584             gc->font.setUnderline( true );
00585     }
00586     if( gc->fill.type() != VFill::none )
00587         gc->fill.setColor( fillcolor, false );
00588     //if( gc->stroke.type() == VStroke::solid )
00589         gc->stroke.setColor( strokecolor );
00590 }
00591 
00592 void
00593 XAMLImport::addGraphicContext()
00594 {
00595     XAMLGraphicsContext *gc = new XAMLGraphicsContext;
00596     // set as default
00597     if( m_gc.current() )
00598         *gc = *( m_gc.current() );
00599     m_gc.push( gc );
00600 }
00601 
00602 void
00603 XAMLImport::setupTransform( const QDomElement &e )
00604 {
00605     XAMLGraphicsContext *gc = m_gc.current();
00606 
00607     QWMatrix mat = VPath::parseTransform( e.attribute( "transform" ) );
00608     gc->matrix = mat * gc->matrix;
00609 }
00610 
00611 void
00612 XAMLImport::parseStyle( VObject *obj, const QDomElement &e )
00613 {
00614     XAMLGraphicsContext *gc = m_gc.current();
00615     if( !gc ) return;
00616 
00617     // try normal PA
00618     if( !e.attribute( "fill" ).isEmpty() )
00619         parsePA( obj, gc, "fill", e.attribute( "fill" ) );
00620     if( !e.attribute( "fill-rule" ).isEmpty() )
00621         parsePA( obj, gc, "fill-rule", e.attribute( "fill-rule" ) );
00622     if( !e.attribute( "stroke" ).isEmpty() )
00623         parsePA( obj, gc, "stroke", e.attribute( "stroke" ) );
00624     if( !e.attribute( "stroke-width" ).isEmpty() )
00625         parsePA( obj, gc, "stroke-width", e.attribute( "stroke-width" ) );
00626     if( !e.attribute( "stroke-linejoin" ).isEmpty() )
00627         parsePA( obj, gc, "stroke-linejoin", e.attribute( "stroke-linejoin" ) );
00628     if( !e.attribute( "stroke-linecap" ).isEmpty() )
00629         parsePA( obj, gc, "stroke-linecap", e.attribute( "stroke-linecap" ) );
00630     if( !e.attribute( "stroke-dasharray" ).isEmpty() )
00631         parsePA( obj, gc, "stroke-dasharray", e.attribute( "stroke-dasharray" ) );
00632     if( !e.attribute( "stroke-dashoffset" ).isEmpty() )
00633         parsePA( obj, gc, "stroke-dashoffset", e.attribute( "stroke-dashoffset" ) );
00634     if( !e.attribute( "stroke-opacity" ).isEmpty() )
00635         parsePA( obj, gc, "stroke-opacity", e.attribute( "stroke-opacity" ) );
00636     if( !e.attribute( "stroke-miterlimit" ).isEmpty() )
00637         parsePA( obj, gc, "stroke-miterlimit", e.attribute( "stroke-miterlimit" ) );
00638     if( !e.attribute( "fill-opacity" ).isEmpty() )
00639         parsePA( obj, gc, "fill-opacity", e.attribute( "fill-opacity" ) );
00640     if( !e.attribute( "opacity" ).isEmpty() )
00641         parsePA( obj, gc, "opacity", e.attribute( "opacity" ) );
00642 
00643     // try style attr
00644     QString style = e.attribute( "style" ).simplifyWhiteSpace();
00645     QStringList substyles = QStringList::split( ';', style );
00646     for( QStringList::Iterator it = substyles.begin(); it != substyles.end(); ++it )
00647     {
00648         QStringList substyle = QStringList::split( ':', (*it) );
00649         QString command = substyle[0].stripWhiteSpace();
00650         QString params  = substyle[1].stripWhiteSpace();
00651         parsePA( obj, gc, command, params );
00652     }
00653 
00654     obj->setFill( gc->fill );
00655     if( dynamic_cast<VPath *>( obj ) )
00656         dynamic_cast<VPath *>( obj )->setFillRule( gc->fillRule );
00657     // stroke scaling
00658     double lineWidth = gc->stroke.lineWidth();
00659     gc->stroke.setLineWidth( lineWidth * sqrt( pow( m_gc.current()->matrix.m11(), 2 ) + pow( m_gc.current()->matrix.m22(), 2 ) ) / sqrt( 2.0 ) );
00660     obj->setStroke( gc->stroke );
00661     gc->stroke.setLineWidth( lineWidth );
00662 }
00663 
00664 void
00665 XAMLImport::parseFont( const QDomElement &e )
00666 {
00667     XAMLGraphicsContext *gc = m_gc.current();
00668     if( !gc ) return;
00669 
00670     if( ! e.attribute( "font-family" ).isEmpty() )  
00671         parsePA( 0L, m_gc.current(), "font-family", e.attribute( "font-family" ) );
00672     if( ! e.attribute( "font-size" ).isEmpty() )    
00673         parsePA( 0L, m_gc.current(), "font-size", e.attribute( "font-size" ) );
00674     if( ! e.attribute( "text-decoration" ).isEmpty() )
00675         parsePA( 0L, m_gc.current(), "text-decoration", e.attribute( "text-decoration" ) );
00676 }
00677 
00678 void
00679 XAMLImport::parseGroup( VGroup *grp, const QDomElement &e )
00680 {
00681     bool isDef = false;
00682     if( e.tagName() == "defs" )
00683         isDef = true;
00684 
00685     for( QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling() )
00686     {
00687         QDomElement b = n.toElement();
00688         if( b.isNull() ) continue;
00689         VObject *obj = 0L;
00690         if( b.tagName() == "g" )
00691         {
00692             VGroup *group;
00693             if ( grp )
00694                 group = new VGroup( grp );
00695             else
00696                 group = new VGroup( &m_document );
00697 
00698             addGraphicContext();
00699             setupTransform( b );
00700             parseStyle( group, b );
00701             parseFont( b );
00702             parseGroup( group, b );
00703             
00704             // handle id
00705             if( !b.attribute("id").isEmpty() )
00706                 group->setName( b.attribute("id") );
00707             if( grp )
00708                 grp->append( group );
00709             else
00710                 m_document.append( group );
00711             delete( m_gc.pop() );
00712             continue;
00713         }
00714         if( b.tagName() == "defs" )
00715         {
00716             parseGroup( 0L, b );    // try for gradients at least
00717             continue;
00718         }
00719         else if( b.tagName() == "linearGradient" || b.tagName() == "radialGradient" )
00720         {
00721             parseGradient( b );
00722             continue;
00723         }
00724         else if( b.tagName() == "rect" ||
00725                  b.tagName() == "ellipse" ||
00726                  b.tagName() == "circle" ||
00727                  b.tagName() == "line" ||
00728                  b.tagName() == "polyline" ||
00729                  b.tagName() == "polygon" ||
00730                  b.tagName() == "path" ||
00731                  b.tagName() == "image" )
00732         {
00733             if (!isDef)
00734                 obj = createObject( b );
00735             else
00736                 m_paths.insert( b.attribute( "id" ), b );
00737         }
00738         else if( b.tagName() == "text" )
00739         {
00740             if( isDef )
00741                 m_paths.insert( b.attribute( "id" ), b );
00742             else
00743                 createText( grp, b );
00744         }
00745         else if( b.tagName() == "use" )
00746         {
00747             double tx = b.attribute( "x" ).toDouble();
00748             double ty = b.attribute( "y" ).toDouble();
00749 
00750             if( !b.attribute( "xlink:href" ).isEmpty() )
00751             {
00752                 QString params = b.attribute( "xlink:href" );
00753                 unsigned int start = params.find("#") + 1;
00754                 unsigned int end = params.findRev(")");
00755                 QString key = params.mid( start, end - start );
00756                 if(m_paths.contains(key))
00757                 {
00758                     QDomElement a = m_paths[key];
00759                     obj = createObject( a );
00760                     m_gc.current()->matrix.translate(tx,ty);
00761                     parsePA( grp, m_gc.current(), "fill", b.attribute( "fill" ) );
00762                 }
00763             }
00764         }
00765         if( !obj ) continue;
00766         VTransformCmd trafo( 0L, m_gc.current()->matrix );
00767         trafo.visit( *obj );
00768         parseStyle( obj, b );
00769         // handle id
00770         if( !b.attribute("id").isEmpty() )
00771             obj->setName( b.attribute("id") );
00772         if( grp )
00773             grp->append( obj );
00774         else
00775             m_document.append( obj );
00776         delete( m_gc.pop() );
00777     }
00778 }
00779 
00780 VObject* XAMLImport::findObject( const QString &name, VGroup* group )
00781 {
00782     if( ! group )
00783         return 0L;
00784 
00785     VObjectListIterator itr = group->objects();
00786 
00787     for( uint objcount = 1; itr.current(); ++itr, objcount++ )
00788         if( itr.current()->state() != VObject::deleted )
00789         {
00790             if( itr.current()->name() == name )
00791                 return itr.current();
00792             
00793             if( dynamic_cast<VGroup *>( itr.current() ) )
00794             {
00795                 VObject *obj = findObject( name, dynamic_cast<VGroup *>( itr.current() ) );
00796                 if( obj ) 
00797                     return obj;
00798             }
00799         }
00800     
00801     return 0L;
00802 }
00803 
00804 VObject* XAMLImport::findObject( const QString &name )
00805 {
00806     QPtrVector<VLayer> vector;
00807     m_document.layers().toVector( &vector );
00808     for( int i = vector.count() - 1; i >= 0; i-- )
00809     {
00810         if ( vector[i]->state() != VObject::deleted )
00811         {
00812             VObject* obj = findObject( name, dynamic_cast<VGroup *>( vector[i] ) );
00813             if( obj ) 
00814                 return obj;
00815         }
00816     }
00817     
00818     return 0L;
00819 }
00820 
00821 void XAMLImport::createText( VGroup *grp, const QDomElement &b )
00822 {
00823     VText *text = 0L;
00824     QString content;
00825     VSubpath base( 0L );
00826     VPath *path = 0L;
00827 
00828     addGraphicContext();
00829     setupTransform( b );
00830     VTransformCmd trafo( 0L, m_gc.current()->matrix );
00831     
00832     parseFont( b );
00833 
00834     if( b.hasChildNodes() )
00835     {
00836         if( base.isEmpty() && ! b.attribute( "x" ).isEmpty() && ! b.attribute( "y" ).isEmpty() )
00837         {
00838             double x = parseUnit( b.attribute( "x" ) );
00839             double y = parseUnit( b.attribute( "y" ) );
00840             base.moveTo( KoPoint( x, y ) );
00841             base.lineTo( KoPoint( x + 10, y ) );
00842         }
00843 
00844         for( QDomNode n = b.firstChild(); !n.isNull(); n = n.nextSibling() )
00845         {
00846             QDomElement e = n.toElement();
00847             if( e.isNull() ) 
00848             {
00849                 content += n.toCharacterData().data();
00850             }
00851             else if( e.tagName() == "textPath" )
00852             {
00853                 if( e.attribute( "xlink:href" ).isEmpty() )
00854                     continue;
00855 
00856                 QString uri = e.attribute( "xlink:href" );
00857                 unsigned int start = uri.find("#") + 1;
00858                 unsigned int end = uri.findRev(")");
00859                 QString key = uri.mid( start, end - start );
00860                 if( ! m_paths.contains(key) )
00861                 {
00862                     VObject* obj = findObject( key );
00863                     if( obj ) 
00864                         path = dynamic_cast<VPath*>( obj );
00865                 }
00866                 else
00867                 {
00868                     QDomElement p = m_paths[key];
00869                     path = dynamic_cast<VPath*>( createObject( p ) );
00870                     if( path )
00871                         path->setState( VObject::deleted );
00872                 }
00873                 if( ! path )
00874                     continue;
00875                 base = *path->paths().getFirst();
00876                 content += e.text();
00877             }
00878             else if( e.tagName() == "tspan" )
00879             {
00880                 // only use text of tspan element, as we are not supporting text 
00881                 // with different styles
00882                 content += e.text();
00883                 if( base.isEmpty() && ! e.attribute( "x" ).isEmpty() && ! e.attribute( "y" ).isEmpty() )
00884                 {
00885                     QStringList posX = QStringList::split( ", ", e.attribute( "x" ) );
00886                     QStringList posY = QStringList::split( ", ", e.attribute( "y" ) );
00887                     if( posX.count() && posY.count() )
00888                     {
00889                         double x = parseUnit( posX.first() );
00890                         double y = parseUnit( posY.first() );
00891                         base.moveTo( KoPoint( x, y ) );
00892                         base.lineTo( KoPoint( x + 10, y ) );
00893                     }
00894                 }
00895             }
00896             else if( e.tagName() == "tref" )
00897             {
00898                 if( e.attribute( "xlink:href" ).isEmpty() )
00899                     continue;
00900 
00901                 QString uri = e.attribute( "xlink:href" );
00902                 unsigned int start = uri.find("#") + 1;
00903                 unsigned int end = uri.findRev(")");
00904                 QString key = uri.mid( start, end - start );
00905 
00906                 if( ! m_paths.contains(key) )
00907                 {
00908                     VObject* obj = findObject( key );
00909                     if( obj ) 
00910                         content += dynamic_cast<VText*>( obj )->text();
00911                 }
00912                 else
00913                 {
00914                     QDomElement p = m_paths[key];
00915                     content += p.text();
00916                 }
00917             }
00918             else 
00919                 continue;
00920         }
00921         text = new VText( m_gc.current()->font, base, VText::Above, VText::Left, content.simplifyWhiteSpace() );
00922     }
00923     else
00924     {
00925         VSubpath base( 0L );
00926         double x = parseUnit( b.attribute( "x" ) );
00927         double y = parseUnit( b.attribute( "y" ) );
00928         base.moveTo( KoPoint( x, y ) );
00929         base.lineTo( KoPoint( x + 10, y ) );
00930         text = new VText( m_gc.current()->font, base, VText::Above, VText::Left, b.text().simplifyWhiteSpace() );
00931     }
00932 
00933     if( text )
00934     {
00935         text->setParent( &m_document );
00936         
00937         parseStyle( text, b );
00938         trafo.visit( *text );
00939 
00940         if( !b.attribute("id").isEmpty() )
00941             text->setName( b.attribute("id") );
00942 
00943         if( grp ) 
00944             grp->append( text );
00945         else 
00946             m_document.append( text );
00947     }
00948     delete( m_gc.pop() );
00949 }
00950 
00951 VObject* XAMLImport::createObject( const QDomElement &b )
00952 {
00953     if( b.tagName() == "rect" )
00954     {
00955         addGraphicContext();
00956         double x        = parseUnit( b.attribute( "x" ), true, false, m_outerRect );
00957         double y        = parseUnit( b.attribute( "y" ), false, true, m_outerRect );
00958         double width    = parseUnit( b.attribute( "width" ), true, false, m_outerRect );
00959         double height   = parseUnit( b.attribute( "height" ), false, true, m_outerRect );
00960         setupTransform( b );
00961         return new VRectangle( 0L, KoPoint( x, height + y ) , width, height );
00962     }
00963     else if( b.tagName() == "ellipse" )
00964     {
00965         addGraphicContext();
00966         setupTransform( b );
00967         double rx       = parseUnit( b.attribute( "rx" ) );
00968         double ry       = parseUnit( b.attribute( "ry" ) );
00969         double left     = parseUnit( b.attribute( "cx" ) ) - rx;
00970         double top      = parseUnit( b.attribute( "cy" ) ) - ry;
00971         return new VEllipse( 0L, KoPoint( left, top ), rx * 2.0, ry * 2.0 );
00972     }
00973     else if( b.tagName() == "circle" )
00974     {
00975         addGraphicContext();
00976         setupTransform( b );
00977         double r        = parseUnit( b.attribute( "r" ) );
00978         double left     = parseUnit( b.attribute( "cx" ) ) - r;
00979         double top      = parseUnit( b.attribute( "cy" ) ) - r;
00980         return new VEllipse( 0L, KoPoint( left, top ), r * 2.0, r * 2.0 );
00981     }
00982     else if( b.tagName() == "line" )
00983     {
00984         addGraphicContext();
00985         setupTransform( b );
00986         VPath *path = new VPath( &m_document );
00987         double x1 = b.attribute( "x1" ).isEmpty() ? 0.0 : parseUnit( b.attribute( "x1" ) );
00988         double y1 = b.attribute( "y1" ).isEmpty() ? 0.0 : parseUnit( b.attribute( "y1" ) );
00989         double x2 = b.attribute( "x2" ).isEmpty() ? 0.0 : parseUnit( b.attribute( "x2" ) );
00990         double y2 = b.attribute( "y2" ).isEmpty() ? 0.0 : parseUnit( b.attribute( "y2" ) );
00991         path->moveTo( KoPoint( x1, y1 ) );
00992         path->lineTo( KoPoint( x2, y2 ) );
00993         return path;
00994     }
00995     else if( b.tagName() == "polyline" || b.tagName() == "polygon" )
00996     {
00997         addGraphicContext();
00998         setupTransform( b );
00999         VPath *path = new VPath( &m_document );
01000         bool bFirst = true;
01001 
01002         QString points = b.attribute( "points" ).simplifyWhiteSpace();
01003         points.replace( ',', ' ' );
01004         points.remove( '\r' );
01005         points.remove( '\n' );
01006         QStringList pointList = QStringList::split( ' ', points );
01007         for( QStringList::Iterator it = pointList.begin(); it != pointList.end(); ++it)
01008         {
01009             KoPoint point;
01010             point.setX( (*it).toDouble() );
01011             point.setY( (*it).toDouble() );
01012             if( bFirst )
01013             {
01014                 path->moveTo( point );
01015                 bFirst = false;
01016             }
01017             else
01018                 path->lineTo( point );
01019         }
01020         if( b.tagName() == "polygon" ) path->close();
01021         return path;
01022     }
01023     else if( b.tagName() == "path" )
01024     {
01025         addGraphicContext();
01026         setupTransform( b );
01027         VPath *path = new VPath( &m_document );
01028         path->loadSvgPath( b.attribute( "d" ) );
01029         return path;
01030     }
01031     else if( b.tagName() == "image" )
01032     {
01033         addGraphicContext();
01034         setupTransform( b );
01035         QString fname = b.attribute("xlink:href");
01036         return new VImage( 0L, fname );
01037     }
01038     
01039     return 0L;
01040 }
01041 
01042 #include <xamlimport.moc>
KDE Home | KDE Accessibility Home | Description of Access Keys