filters

svgimport.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 <svgimport.h>
00021 #include "color.h"
00022 #include <KoFilterChain.h>
00023 #include <KoPageLayout.h>
00024 #include <kgenericfactory.h>
00025 #include <kdebug.h>
00026 #include <KoUnit.h>
00027 #include <KoGlobal.h>
00028 #include <shapes/vellipse.h>
00029 #include <shapes/vrectangle.h>
00030 #include <shapes/vpolygon.h>
00031 #include <commands/vtransformcmd.h>
00032 #include <core/vsegment.h>
00033 #include <core/vtext.h>
00034 #include <core/vglobal.h>
00035 #include <core/vgroup.h>
00036 #include <core/vimage.h>
00037 #include <core/vlayer.h>
00038 #include <qcolor.h>
00039 #include <qfile.h>
00040 #include <kfilterdev.h>
00041 
00042 typedef KGenericFactory<SvgImport, KoFilter> SvgImportFactory;
00043 K_EXPORT_COMPONENT_FACTORY( libkarbonsvgimport, SvgImportFactory( "kofficefilters" ) )
00044 
00045 SvgImport::SvgImport(KoFilter *, const char *, const QStringList&) :
00046     KoFilter(),
00047     outdoc( "DOC" )
00048 {
00049     m_gc.setAutoDelete( true );
00050 }
00051 
00052 SvgImport::~SvgImport()
00053 {
00054 }
00055 
00056 KoFilter::ConversionStatus SvgImport::convert(const QCString& from, const QCString& to)
00057 {
00058     // check for proper conversion
00059     if( to != "application/x-karbon" || from != "image/svg+xml" )
00060         return KoFilter::NotImplemented;
00061 
00062     //Find the last extension
00063     QString strExt;
00064     QString fileIn ( m_chain->inputFile() );
00065     const int result=fileIn.findRev('.');
00066     if (result>=0)
00067         strExt=fileIn.mid(result).lower();
00068 
00069     QString strMime; // Mime type of the compressor
00070     if ((strExt==".gz")      //in case of .svg.gz (logical extension)
00071        ||(strExt==".svgz")) //in case of .svgz (extension used prioritary)
00072         strMime="application/x-gzip"; // Compressed with gzip
00073     else if (strExt==".bz2") //in case of .svg.bz2 (logical extension)
00074         strMime="application/x-bzip2"; // Compressed with bzip2
00075     else
00076         strMime="text/plain";
00077 
00078     /*kdDebug(30514) << "File extension: -" << strExt << "- Compression: " << strMime << endl;*/
00079 
00080     QIODevice* in = KFilterDev::deviceForFile(fileIn,strMime);
00081 
00082     if (!in->open(IO_ReadOnly))
00083     {
00084         kdError(30514) << "Cannot open file! Aborting!" << endl;
00085         delete in;
00086         return KoFilter::FileNotFound;
00087     }
00088 
00089     int line, col;
00090     QString errormessage;
00091 
00092     const bool parsed=inpdoc.setContent( in, &errormessage, &line, &col );
00093 
00094     in->close();
00095     delete in;
00096 
00097     if ( ! parsed )
00098     {
00099         kdError(30514) << "Error while parsing file: "
00100                 << "at line " << line << " column: " << col
00101                 << " message: " << errormessage << endl;
00102         // ### TODO: feedback to the user
00103         return KoFilter::ParsingError;
00104     }
00105 
00106     // Do the conversion!
00107     convert();
00108     // add paper info, we always need custom for svg (Rob)
00109     QDomElement paper = outdoc.createElement( "PAPER" );
00110     outdoc.documentElement().appendChild( paper );
00111     paper.setAttribute( "format", PG_CUSTOM );
00112     paper.setAttribute( "width", m_document.width() );
00113     paper.setAttribute( "height", m_document.height() );
00114 
00115     KoStoreDevice* out = m_chain->storageFile( "root", KoStore::Write );
00116     if( !out )
00117     {
00118         kdError(30514) << "Unable to open output file!" << endl;
00119         return KoFilter::StorageCreationError;
00120     }
00121     QCString cstring = outdoc.toCString(); // utf-8 already
00122     out->writeBlock( cstring.data(), cstring.length() );
00123 
00124     return KoFilter::OK; // was successful
00125 }
00126 
00127 void SvgImport::convert()
00128 {
00129     SvgGraphicsContext *gc = new SvgGraphicsContext;
00130     QDomElement docElem = inpdoc.documentElement();
00131     KoRect bbox( 0, 0, 550.0, 841.0 );
00132     double width    = !docElem.attribute( "width" ).isEmpty() ? parseUnit( docElem.attribute( "width" ), true, false, bbox ) : 550.0;
00133     double height   = !docElem.attribute( "height" ).isEmpty() ? parseUnit( docElem.attribute( "height" ), false, true, bbox ) : 841.0;
00134     m_document.setWidth( width );
00135     m_document.setHeight( height );
00136 
00137     m_outerRect = m_document.boundingBox();
00138 
00139     // undo y-mirroring
00140     //m_debug->append(QString("%1\tUndo Y-mirroring.").arg(m_time.elapsed()));
00141     if( !docElem.attribute( "viewBox" ).isEmpty() )
00142     {
00143         // allow for viewbox def with ',' or whitespace
00144         QString viewbox( docElem.attribute( "viewBox" ) );
00145         QStringList points = QStringList::split( ' ', viewbox.replace( ',', ' ').simplifyWhiteSpace() );
00146 
00147         gc->matrix.scale( width / points[2].toFloat() , height / points[3].toFloat() );
00148         m_outerRect.setWidth( m_outerRect.width() * ( points[2].toFloat() / width ) );
00149         m_outerRect.setHeight( m_outerRect.height() * ( points[3].toFloat() / height ) );
00150     }
00151 
00152     m_gc.push( gc );
00153     parseGroup( 0L, docElem );
00154     
00155     QWMatrix mat;
00156     mat.scale( 1, -1 );
00157     mat.translate( 0, -m_document.height() );
00158     VTransformCmd trafo( 0L, mat );
00159     trafo.visit( m_document );
00160     outdoc = m_document.saveXML();
00161 }
00162 
00163 #define DPI 90
00164 
00165 // Helper functions
00166 // ---------------------------------------------------------------------------------------
00167 
00168 double SvgImport::toPercentage( QString s )
00169 {
00170     if( s.endsWith( "%" ) )
00171         return s.remove( '%' ).toDouble();
00172     else
00173         return s.toDouble() * 100.0;
00174 }
00175 
00176 double SvgImport::fromPercentage( QString s )
00177 {
00178     if( s.endsWith( "%" ) )
00179         return s.remove( '%' ).toDouble() / 100.0;
00180     else
00181         return s.toDouble();
00182 }
00183 
00184 double SvgImport::getScalingFromMatrix( QWMatrix &matrix )
00185 {
00186     double xscale = matrix.m11() + matrix.m12();
00187     double yscale = matrix.m22() + matrix.m21();
00188     return sqrt( xscale*xscale + yscale*yscale ) / sqrt( 2.0 );
00189 }
00190 
00191 // parses the number into parameter number
00192 const char * getNumber( const char *ptr, double &number )
00193 {
00194     int integer, exponent;
00195     double decimal, frac;
00196     int sign, expsign;
00197 
00198     exponent = 0;
00199     integer = 0;
00200     frac = 1.0;
00201     decimal = 0;
00202     sign = 1;
00203     expsign = 1;
00204 
00205     // read the sign
00206     if(*ptr == '+')
00207         ptr++;
00208     else if(*ptr == '-')
00209     {
00210         ptr++;
00211         sign = -1;
00212     }
00213 
00214     // read the integer part
00215     while(*ptr != '\0' && *ptr >= '0' && *ptr <= '9')
00216         integer = (integer * 10) + *(ptr++) - '0';
00217     if(*ptr == '.') // read the decimals
00218     {
00219         ptr++;
00220         while(*ptr != '\0' && *ptr >= '0' && *ptr <= '9')
00221             decimal += (*(ptr++) - '0') * (frac *= 0.1);
00222     }
00223 
00224     if(*ptr == 'e' || *ptr == 'E') // read the exponent part
00225     {
00226         ptr++;
00227 
00228         // read the sign of the exponent
00229         if(*ptr == '+')
00230             ptr++;
00231         else if(*ptr == '-')
00232         {
00233             ptr++;
00234             expsign = -1;
00235         }
00236 
00237         exponent = 0;
00238         while(*ptr != '\0' && *ptr >= '0' && *ptr <= '9')
00239         {
00240             exponent *= 10;
00241             exponent += *ptr - '0';
00242             ptr++;
00243         }
00244     }
00245     number = integer + decimal;
00246     number *= sign * pow( (double)10, double( expsign * exponent ) );
00247 
00248     return ptr;
00249 }
00250 
00251 void SvgImport::addGraphicContext()
00252 {
00253     SvgGraphicsContext *gc = new SvgGraphicsContext;
00254     // set as default
00255     if( m_gc.current() )
00256         *gc = *( m_gc.current() );
00257     m_gc.push( gc );
00258 }
00259 
00260 void SvgImport::setupTransform( const QDomElement &e )
00261 {
00262     SvgGraphicsContext *gc = m_gc.current();
00263 
00264     QWMatrix mat = VPath::parseTransform( e.attribute( "transform" ) );
00265     gc->matrix = mat * gc->matrix;
00266 }
00267 
00268 VObject* SvgImport::findObject( const QString &name, VGroup* group )
00269 {
00270     if( ! group )
00271         return 0L;
00272 
00273     VObjectListIterator itr = group->objects();
00274 
00275     for( uint objcount = 1; itr.current(); ++itr, objcount++ )
00276         if( itr.current()->state() != VObject::deleted )
00277         {
00278             if( itr.current()->name() == name )
00279                 return itr.current();
00280             
00281             if( dynamic_cast<VGroup *>( itr.current() ) )
00282             {
00283                 VObject *obj = findObject( name, dynamic_cast<VGroup *>( itr.current() ) );
00284                 if( obj )
00285                     return obj;
00286             }
00287         }
00288     
00289     return 0L;
00290 }
00291 
00292 VObject* SvgImport::findObject( const QString &name )
00293 {
00294     QPtrVector<VLayer> vector;
00295     m_document.layers().toVector( &vector );
00296     for( int i = vector.count() - 1; i >= 0; i-- )
00297     {
00298         if ( vector[i]->state() != VObject::deleted )
00299         {
00300             VObject* obj = findObject( name, dynamic_cast<VGroup *>( vector[i] ) );
00301             if( obj )
00302                 return obj;
00303         }
00304     }
00305     
00306     return 0L;
00307 }
00308 
00309 SvgImport::GradientHelper* SvgImport::findGradient( const QString &id, const QString &href)
00310 {
00311     // check if gradient was already parsed, and return it 
00312     if( m_gradients.contains( id ) )
00313         return &m_gradients[ id ];
00314 
00315     // check if gradient was stored for later parsing
00316     if( !m_defs.contains( id ) )
00317         return 0L;
00318 
00319     QDomElement e = m_defs[ id ];
00320     if(e.childNodes().count() == 0)
00321     {
00322         QString mhref = e.attribute("xlink:href").mid(1);
00323         
00324         if(m_defs.contains(mhref))
00325             return findGradient(mhref, id);
00326         else
00327             return 0L;
00328     }
00329     else
00330     {
00331         // ok parse gradient now
00332         parseGradient( m_defs[ id ], m_defs[ href ] );
00333     }
00334     
00335     // return successfully parsed gradient or NULL
00336     QString n;
00337     if(href.isEmpty())
00338         n = id;
00339     else
00340         n = href;
00341 
00342     if( m_gradients.contains( n ) )
00343         return &m_gradients[ n ];
00344     else
00345         return 0L;
00346 }
00347 
00348 QDomElement SvgImport::mergeStyles( const QDomElement &referencedBy, const QDomElement &referencedElement )
00349 {
00350     // First use all the style attributes of the element being referenced.
00351     QDomElement e = referencedElement;
00352 
00353     // Now go through the style attributes of the element that is referencing and substitute the original ones.
00354     if( !referencedBy.attribute( "color" ).isEmpty() )
00355         e.setAttribute( "color", referencedBy.attribute( "color" ) );
00356     if( !referencedBy.attribute( "fill" ).isEmpty() )
00357         e.setAttribute( "fill", referencedBy.attribute( "fill" ) );
00358     if( !referencedBy.attribute( "fill-rule" ).isEmpty() )
00359         e.setAttribute( "fill-rule", referencedBy.attribute( "fill-rule" ) );
00360     if( !referencedBy.attribute( "stroke" ).isEmpty() )
00361         e.setAttribute( "stroke", referencedBy.attribute( "stroke" ) );
00362     if( !referencedBy.attribute( "stroke-width" ).isEmpty() )
00363         e.setAttribute( "stroke-width", referencedBy.attribute( "stroke-width" ) );
00364     if( !referencedBy.attribute( "stroke-linejoin" ).isEmpty() )
00365         e.setAttribute( "stroke-linejoin", referencedBy.attribute( "stroke-linejoin" ) );
00366     if( !referencedBy.attribute( "stroke-linecap" ).isEmpty() )
00367         e.setAttribute( "stroke-linecap", referencedBy.attribute( "stroke-linecap" ) );
00368     if( !referencedBy.attribute( "stroke-dasharray" ).isEmpty() )
00369         e.setAttribute( "stroke-dasharray", referencedBy.attribute( "stroke-dasharray" ) );
00370     if( !referencedBy.attribute( "stroke-dashoffset" ).isEmpty() )
00371         e.setAttribute( "stroke-dashoffset", referencedBy.attribute( "stroke-dashoffset" ) );
00372     if( !referencedBy.attribute( "stroke-opacity" ).isEmpty() )
00373         e.setAttribute( "stroke-opacity", referencedBy.attribute( "stroke-opacity" ) );
00374     if( !referencedBy.attribute( "stroke-miterlimit" ).isEmpty() )
00375         e.setAttribute( "stroke-miterlimit", referencedBy.attribute( "stroke-miterlimit" ) );
00376     if( !referencedBy.attribute( "fill-opacity" ).isEmpty() )
00377         e.setAttribute( "fill-opacity", referencedBy.attribute( "fill-opacity" ) );
00378     if( !referencedBy.attribute( "opacity" ).isEmpty() )
00379         e.setAttribute( "opacity", referencedBy.attribute( "opacity" ) );
00380 
00381     // TODO merge style attribute too.
00382 
00383     return e;
00384 }
00385 
00386 
00387 // Parsing functions
00388 // ---------------------------------------------------------------------------------------
00389 
00390 double SvgImport::parseUnit( const QString &unit, bool horiz, bool vert, KoRect bbox )
00391 {
00392     // TODO : percentage?
00393     double value = 0;
00394     const char *start = unit.latin1();
00395     if(!start) {
00396         return 0;
00397     }
00398     const char *end = getNumber( start, value );
00399 
00400     if( uint( end - start ) < unit.length() )
00401     {
00402         if( unit.right( 2 ) == "pt" )
00403             value = ( value / 72.0 ) * DPI;
00404         else if( unit.right( 2 ) == "cm" )
00405             value = ( value / 2.54 ) * DPI;
00406         else if( unit.right( 2 ) == "pc" )
00407             value = ( value / 6.0 ) * DPI;
00408         else if( unit.right( 2 ) == "mm" )
00409             value = ( value / 25.4 ) * DPI;
00410         else if( unit.right( 2 ) == "in" )
00411             value = value * DPI;
00412         else if( unit.right( 2 ) == "pt" )
00413             value = ( value / 72.0 ) * DPI;
00414         else if( unit.right( 2 ) == "em" )
00415             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 ) );
00416         else if( unit.right( 1 ) == "%" )
00417         {
00418             if( horiz && vert )
00419                 value = ( value / 100.0 ) * (sqrt( pow( bbox.width(), 2 ) + pow( bbox.height(), 2 ) ) / sqrt( 2.0 ) );
00420             else if( horiz )
00421                 value = ( value / 100.0 ) * bbox.width();
00422             else if( vert )
00423                 value = ( value / 100.0 ) * bbox.height();
00424         }
00425     }
00426     /*else
00427     {
00428         if( m_gc.current() )
00429         {
00430             if( horiz && vert )
00431                 value *= sqrt( pow( m_gc.current()->matrix.m11(), 2 ) + pow( m_gc.current()->matrix.m22(), 2 ) ) / sqrt( 2.0 );
00432             else if( horiz )
00433                 value /= m_gc.current()->matrix.m11();
00434             else if( vert )
00435                 value /= m_gc.current()->matrix.m22();
00436         }
00437     }*/
00438     return value;
00439 }
00440 
00441 QColor SvgImport::parseColor( const QString &rgbColor )
00442 {
00443     int r, g, b;
00444     keywordToRGB( rgbColor, r, g, b );
00445     return QColor( r, g, b );
00446 }
00447 
00448 void SvgImport::parseColor( VColor &color, const QString &s )
00449 {
00450     if( s.startsWith( "rgb(" ) )
00451     {
00452         QString parse = s.stripWhiteSpace();
00453         QStringList colors = QStringList::split( ',', parse );
00454         QString r = colors[0].right( ( colors[0].length() - 4 ) );
00455         QString g = colors[1];
00456         QString b = colors[2].left( ( colors[2].length() - 1 ) );
00457 
00458         if( r.contains( "%" ) )
00459         {
00460             r = r.left( r.length() - 1 );
00461             r = QString::number( int( ( double( 255 * r.toDouble() ) / 100.0 ) ) );
00462         }
00463 
00464         if( g.contains( "%" ) )
00465         {
00466             g = g.left( g.length() - 1 );
00467             g = QString::number( int( ( double( 255 * g.toDouble() ) / 100.0 ) ) );
00468         }
00469 
00470         if( b.contains( "%" ) )
00471         {
00472             b = b.left( b.length() - 1 );
00473             b = QString::number( int( ( double( 255 * b.toDouble() ) / 100.0 ) ) );
00474         }
00475 
00476         QColor c( r.toInt(), g.toInt(), b.toInt() );
00477         color.set( c.red() / 255.0, c.green() / 255.0, c.blue() / 255.0 );
00478     }
00479     else if( s == "currentColor" )
00480     {
00481         SvgGraphicsContext *gc = m_gc.current();
00482         color = gc->color;
00483     }
00484     else
00485     {
00486         QString rgbColor = s.stripWhiteSpace();
00487         QColor c;
00488         if( rgbColor.startsWith( "#" ) )
00489             c.setNamedColor( rgbColor );
00490         else
00491             c = parseColor( rgbColor );
00492         color.set( c.red() / 255.0, c.green() / 255.0, c.blue() / 255.0 );
00493     }
00494 }
00495 
00496 void SvgImport::parseColorStops( VGradient *gradient, const QDomElement &e )
00497 {
00498     VColor c;
00499     for( QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling() )
00500     {
00501         QDomElement stop = n.toElement();
00502         if( stop.tagName() == "stop" )
00503         {
00504             float offset;
00505             QString temp = stop.attribute( "offset" );
00506             if( temp.contains( '%' ) )
00507             {
00508                 temp = temp.left( temp.length() - 1 );
00509                 offset = temp.toFloat() / 100.0;
00510             }
00511             else
00512                 offset = temp.toFloat();
00513 
00514             if( !stop.attribute( "stop-color" ).isEmpty() )
00515                 parseColor( c, stop.attribute( "stop-color" ) );
00516             else
00517             {
00518                 // try style attr
00519                 QString style = stop.attribute( "style" ).simplifyWhiteSpace();
00520                 QStringList substyles = QStringList::split( ';', style );
00521                 for( QStringList::Iterator it = substyles.begin(); it != substyles.end(); ++it )
00522                 {
00523                     QStringList substyle = QStringList::split( ':', (*it) );
00524                     QString command = substyle[0].stripWhiteSpace();
00525                     QString params  = substyle[1].stripWhiteSpace();
00526                     if( command == "stop-color" )
00527                         parseColor( c, params );
00528                     if( command == "stop-opacity" )
00529                         c.setOpacity( params.toDouble() );
00530                 }
00531 
00532             }
00533             if( !stop.attribute( "stop-opacity" ).isEmpty() )
00534                 c.setOpacity( stop.attribute( "stop-opacity" ).toDouble() );
00535             gradient->addStop( c, offset, 0.5 );
00536         }
00537     } 
00538 }
00539 
00540 void SvgImport::parseGradient( const QDomElement &e , const QDomElement &referencedBy)
00541 {
00542     // IMPROVEMENTS:
00543     // - Store the parsed colorstops in some sort of a cache so they don't need to be parsed again.
00544     // - A gradient inherits attributes it does not have from the referencing gradient.
00545     // - Gradients with no color stops have no fill or stroke.
00546     // - Gradients with one color stop have a solid color.
00547     
00548     SvgGraphicsContext *gc = m_gc.current();
00549     if( !gc ) return;
00550 
00551     GradientHelper gradhelper;
00552     gradhelper.gradient.clearStops();
00553     gradhelper.gradient.setRepeatMethod( VGradient::none );
00554 
00555     if(e.childNodes().count() == 0)
00556     {
00557         QString href = e.attribute("xlink:href").mid(1);
00558         
00559         if(href.isEmpty())
00560         {
00561             //gc->fill.setType( VFill::none ); // <--- TODO Fill OR Stroke are none
00562             return;
00563         }
00564         else 
00565         {
00566             // copy the referenced gradient if found
00567             GradientHelper *pGrad = findGradient( href );
00568             if( pGrad )
00569                 gradhelper = *pGrad;
00570         }
00571     }
00572 
00573     // Use the gradient that is referencing, or if there isn't one, the original gradient.
00574     QDomElement b;
00575     if( !referencedBy.isNull() )
00576         b = referencedBy;
00577     else
00578         b = e;
00579     
00580     QString id = b.attribute("id");
00581     if( !id.isEmpty() )
00582     { 
00583         // Copy existing gradient if it exists
00584         if( m_gradients.find( id ) != m_gradients.end() )
00585             gradhelper.gradient = m_gradients[ id ].gradient;
00586     }
00587 
00588     gradhelper.bbox = b.attribute( "gradientUnits" ) != "userSpaceOnUse";
00589 
00590     // parse color prop
00591     VColor c = m_gc.current()->color;
00592     
00593     if( !b.attribute( "color" ).isEmpty() )
00594     {
00595         parseColor( c, b.attribute( "color" ) );
00596     }
00597     else
00598     {
00599         // try style attr
00600         QString style = b.attribute( "style" ).simplifyWhiteSpace();
00601         QStringList substyles = QStringList::split( ';', style );
00602         for( QStringList::Iterator it = substyles.begin(); it != substyles.end(); ++it )
00603         {
00604             QStringList substyle = QStringList::split( ':', (*it) );
00605             QString command = substyle[0].stripWhiteSpace();
00606             QString params  = substyle[1].stripWhiteSpace();
00607             if( command == "color" )
00608                 parseColor( c, params );
00609         }
00610     }
00611     m_gc.current()->color = c;
00612 
00613     if( b.tagName() == "linearGradient" )
00614     {
00615         if( gradhelper.bbox )
00616         {           
00617             gradhelper.gradient.setOrigin( KoPoint( toPercentage( b.attribute( "x1", "0%" ) ), toPercentage( b.attribute( "y1", "0%" ) ) ) );
00618             gradhelper.gradient.setVector( KoPoint( toPercentage( b.attribute( "x2", "100%" ) ), toPercentage( b.attribute( "y2", "0%" ) ) ) );
00619         }
00620         else
00621         {
00622             gradhelper.gradient.setOrigin( KoPoint( b.attribute( "x1" ).toDouble(), b.attribute( "y1" ).toDouble() ) );
00623             gradhelper.gradient.setVector( KoPoint( b.attribute( "x2" ).toDouble(), b.attribute( "y2" ).toDouble() ) );
00624         }
00625         gradhelper.gradient.setType( VGradient::linear );
00626     }
00627     else
00628     {
00629         if( gradhelper.bbox )
00630         {
00631             gradhelper.gradient.setOrigin( KoPoint( toPercentage( b.attribute( "cx", "50%" ) ), toPercentage( b.attribute( "cy", "50%" ) ) ) );
00632             gradhelper.gradient.setVector( KoPoint( toPercentage( b.attribute( "cx", "50%" ) ) + toPercentage( b.attribute( "r", "50%" ) ), toPercentage( b.attribute( "cy", "50%" ) ) ) );
00633             gradhelper.gradient.setFocalPoint( KoPoint( toPercentage( b.attribute( "fx", "50%" ) ), toPercentage( b.attribute( "fy", "50%" ) ) ) );
00634         }
00635         else
00636         {
00637             gradhelper.gradient.setOrigin( KoPoint( b.attribute( "cx" ).toDouble(), b.attribute( "cy" ).toDouble() ) );
00638             gradhelper.gradient.setFocalPoint( KoPoint( b.attribute( "fx" ).toDouble(), b.attribute( "fy" ).toDouble() ) );
00639             gradhelper.gradient.setVector( KoPoint( b.attribute( "cx" ).toDouble() + b.attribute( "r" ).toDouble(), b.attribute( "cy" ).toDouble() ) );
00640         }
00641         gradhelper.gradient.setType( VGradient::radial );
00642     }
00643     // handle spread method
00644     QString spreadMethod = b.attribute( "spreadMethod" );
00645     if( !spreadMethod.isEmpty() )
00646     {
00647         if( spreadMethod == "reflect" )
00648             gradhelper.gradient.setRepeatMethod( VGradient::reflect );
00649         else if( spreadMethod == "repeat" )
00650             gradhelper.gradient.setRepeatMethod( VGradient::repeat );
00651         else
00652             gradhelper.gradient.setRepeatMethod( VGradient::none );
00653     }
00654     else
00655         gradhelper.gradient.setRepeatMethod( VGradient::none );
00656 
00657     // Parse the color stops. The referencing gradient does not have colorstops, 
00658     // so use the stops from the gradient it references to (e in this case and not b)
00659     parseColorStops( &gradhelper.gradient, e );
00660     //gradient.setGradientTransform( parseTransform( e.attribute( "gradientTransform" ) ) );
00661     gradhelper.gradientTransform = VPath::parseTransform( b.attribute( "gradientTransform" ) );
00662     m_gradients.insert( b.attribute( "id" ), gradhelper );
00663 }
00664 
00665 void SvgImport::parsePA( VObject *obj, SvgGraphicsContext *gc, const QString &command, const QString &params )
00666 {
00667     VColor fillcolor = gc->fill.color();
00668     VColor strokecolor = gc->stroke.color();
00669 
00670     if( params == "inherit" ) return;
00671 
00672     if( command == "fill" )
00673     {
00674         if( params == "none" )
00675             gc->fill.setType( VFill::none );
00676         else if( params.startsWith( "url(" ) )
00677         {
00678             unsigned int start = params.find("#") + 1;
00679             unsigned int end = params.findRev(")");
00680             QString key = params.mid( start, end - start );
00681             GradientHelper *gradHelper = findGradient( key );
00682             if( gradHelper )
00683             {
00684                 gc->fill.gradient() = gradHelper->gradient;
00685     
00686                 if( gradHelper->bbox )
00687                 {
00688                     // adjust to bbox
00689                     KoRect bbox = obj->boundingBox();
00690                     //kdDebug() << "bbox x : " << bbox.x() << endl;
00691                     //kdDebug() << "!!!!!!bbox y : " << bbox.y() << endl;
00692                     //kdDebug() << gc->fill.gradient().origin().x() << endl;
00693                     //kdDebug() << gc->fill.gradient().vector().x() << endl;
00694                     double offsetx = parseUnit( QString( "%1%" ).arg( gc->fill.gradient().origin().x() ), true, false, bbox );
00695                     double offsety = parseUnit( QString( "%1%" ).arg( gc->fill.gradient().origin().y() ), false, true, bbox );
00696                     gc->fill.gradient().setOrigin( KoPoint( bbox.x() + offsetx, bbox.y() + offsety ) );
00697                     if(gc->fill.gradient().type() == VGradient::radial)
00698                     {
00699                         offsetx = parseUnit( QString( "%1%" ).arg( gc->fill.gradient().focalPoint().x() ), true, false, bbox );
00700                         offsety = parseUnit( QString( "%1%" ).arg( gc->fill.gradient().focalPoint().y() ), false, true, bbox );
00701                         gc->fill.gradient().setFocalPoint( KoPoint( bbox.x() + offsetx, bbox.y() + offsety ) );
00702                     }
00703                     offsetx = parseUnit( QString( "%1%" ).arg( gc->fill.gradient().vector().x() ), true, false, bbox );
00704                     offsety = parseUnit( QString( "%1%" ).arg( gc->fill.gradient().vector().y() ), false, true, bbox );
00705                     gc->fill.gradient().setVector( KoPoint( bbox.x() + offsetx, bbox.y() + offsety ) );
00706                     //kdDebug() << offsety << endl;
00707                     //kdDebug() << gc->fill.gradient().origin().x() << endl;
00708                     //kdDebug() << gc->fill.gradient().origin().y() << endl;
00709                     //kdDebug() << gc->fill.gradient().vector().x() << endl;
00710                     //kdDebug() << gc->fill.gradient().vector().y() << endl;
00711                 }
00712                 gc->fill.gradient().transform( gradHelper->gradientTransform );
00713 
00714                 if( !gradHelper->bbox )
00715                     gc->fill.gradient().transform( gc->matrix );
00716 
00717                 gc->fill.setType( VFill::grad );
00718             }
00719             else
00720                 gc->fill.setType( VFill::none );
00721         }
00722         else
00723         {
00724             parseColor( fillcolor,  params );
00725             gc->fill.setType( VFill::solid );
00726         }
00727     }
00728     else if( command == "fill-rule" )
00729     {
00730         if( params == "nonzero" )
00731             gc->fillRule = winding;
00732         else if( params == "evenodd" )
00733             gc->fillRule = evenOdd;
00734     }
00735     else if( command == "stroke" )
00736     {
00737         if( params == "none" )
00738             gc->stroke.setType( VStroke::none );
00739         else if( params.startsWith( "url(" ) )
00740         {
00741             unsigned int start = params.find("#") + 1;
00742             unsigned int end = params.findRev(")");
00743             QString key = params.mid( start, end - start );
00744 
00745             GradientHelper *gradHelper = findGradient( key );
00746             if( gradHelper )
00747             {
00748                 gc->stroke.gradient() = gradHelper->gradient;
00749                 gc->stroke.gradient().transform( gradHelper->gradientTransform );
00750                 gc->stroke.gradient().transform( gc->matrix );
00751                 gc->stroke.setType( VStroke::grad );
00752             }
00753             else 
00754                 gc->stroke.setType( VStroke::none );
00755         }
00756         else
00757         {
00758             parseColor( strokecolor, params );
00759             gc->stroke.setType( VStroke::solid );
00760         }
00761     }
00762     else if( command == "stroke-width" )
00763         gc->stroke.setLineWidth( parseUnit( params, true, true, m_outerRect ) );
00764     else if( command == "stroke-linejoin" )
00765     {
00766         if( params == "miter" )
00767             gc->stroke.setLineJoin( VStroke::joinMiter );
00768         else if( params == "round" )
00769             gc->stroke.setLineJoin( VStroke::joinRound );
00770         else if( params == "bevel" )
00771             gc->stroke.setLineJoin( VStroke::joinBevel );
00772     }
00773     else if( command == "stroke-linecap" )
00774     {
00775         if( params == "butt" )
00776             gc->stroke.setLineCap( VStroke::capButt );
00777         else if( params == "round" )
00778             gc->stroke.setLineCap( VStroke::capRound );
00779         else if( params == "square" )
00780             gc->stroke.setLineCap( VStroke::capSquare );
00781     }
00782     else if( command == "stroke-miterlimit" )
00783         gc->stroke.setMiterLimit( params.toFloat() );
00784     else if( command == "stroke-dasharray" )
00785     {
00786         QValueList<float> array;
00787         if(params != "none")
00788         {
00789             QStringList dashes = QStringList::split( ' ', params );
00790             for( QStringList::Iterator it = dashes.begin(); it != dashes.end(); ++it )
00791                 array.append( (*it).toFloat() );
00792         }
00793         gc->stroke.dashPattern().setArray( array );
00794     }
00795     else if( command == "stroke-dashoffset" )
00796         gc->stroke.dashPattern().setOffset( params.toFloat() );
00797     // handle opacity
00798     else if( command == "stroke-opacity" )
00799         strokecolor.setOpacity( fromPercentage( params ) );
00800     else if( command == "fill-opacity" )
00801         fillcolor.setOpacity( fromPercentage( params ) );
00802     else if( command == "opacity" )
00803     {
00804         fillcolor.setOpacity( fromPercentage( params ) );
00805         strokecolor.setOpacity( fromPercentage( params ) );
00806     }
00807     else if( command == "font-family" )
00808     {
00809         QString family = params;
00810         family.replace( '\'' , ' ' );
00811         gc->font.setFamily( family );
00812     }
00813     else if( command == "font-size" )
00814     {
00815         float pointSize = parseUnit( params );
00816         gc->font.setPointSizeFloat( pointSize * getScalingFromMatrix( gc->matrix ) );
00817     }
00818     else if( command == "font-weight" )
00819     {
00820         int weight = QFont::Normal;
00821 
00822         // map svg weight to qt weight
00823         // svg value        qt value
00824         // 100,200,300      1, 17, 33
00825         // 400              50          (normal)
00826         // 500,600          58,66
00827         // 700              75          (bold)
00828         // 800,900          87,99
00829 
00830         if( params == "bold" )
00831             weight = QFont::Bold;
00832         else if( params == "lighter" )
00833         {
00834             weight = gc->font.weight();
00835             if( weight <= 17 ) 
00836                 weight = 1;
00837             else if( weight <= 33 )
00838                 weight = 17;
00839             else if( weight <= 50 )
00840                 weight = 33;
00841             else if( weight <= 58 )
00842                 weight = 50;
00843             else if( weight <= 66 )
00844                 weight = 58;
00845             else if( weight <= 75 )
00846                 weight = 66;
00847             else if( weight <= 87 )
00848                 weight = 75;
00849             else if( weight <= 99 )
00850                 weight = 87;
00851         }
00852         else if( params == "bolder" )
00853         {
00854             weight = gc->font.weight();
00855             if( weight >= 87 ) 
00856                 weight = 99;
00857             else if( weight >= 75 )
00858                 weight = 87;
00859             else if( weight >= 66 )
00860                 weight = 75;
00861             else if( weight >= 58 )
00862                 weight = 66;
00863             else if( weight >= 50 )
00864                 weight = 58;
00865             else if( weight >= 33 )
00866                 weight = 50;
00867             else if( weight >= 17 )
00868                 weight = 50;
00869             else if( weight >= 1 )
00870                 weight = 17;
00871         }
00872         else
00873         {
00874             bool ok;
00875             // try to read numerical weight value
00876             weight = params.toInt( &ok, 10 );
00877 
00878             if( !ok )
00879                 return;
00880 
00881             switch( weight )
00882             {
00883                 case 100: weight = 1; break;
00884                 case 200: weight = 17; break;
00885                 case 300: weight = 33; break;
00886                 case 400: weight = 50; break;
00887                 case 500: weight = 58; break;
00888                 case 600: weight = 66; break;
00889                 case 700: weight = 75; break;
00890                 case 800: weight = 87; break;
00891                 case 900: weight = 99; break;
00892             }
00893         }
00894         gc->font.setWeight( weight );
00895     }
00896     else if( command == "text-decoration" )
00897     {
00898         if( params == "line-through" )
00899             gc->font.setStrikeOut( true );
00900         else if( params == "underline" )
00901             gc->font.setUnderline( true );
00902     }
00903     else if( command == "color" )
00904     {
00905         VColor color;
00906         parseColor( color, params );
00907         gc->color = color;
00908     }
00909     if( gc->fill.type() != VFill::none )
00910         gc->fill.setColor( fillcolor, false );
00911     //if( gc->stroke.type() == VStroke::solid )
00912         gc->stroke.setColor( strokecolor );
00913 }
00914 
00915 void SvgImport::parseStyle( VObject *obj, const QDomElement &e )
00916 {
00917     SvgGraphicsContext *gc = m_gc.current();
00918     if( !gc ) return;
00919 
00920     // try normal PA
00921     if( !e.attribute( "color" ).isEmpty() )
00922         parsePA( obj, gc, "color", e.attribute( "color" ) );
00923     if( !e.attribute( "fill" ).isEmpty() )
00924         parsePA( obj, gc, "fill", e.attribute( "fill" ) );
00925     if( !e.attribute( "fill-rule" ).isEmpty() )
00926         parsePA( obj, gc, "fill-rule", e.attribute( "fill-rule" ) );
00927     if( !e.attribute( "stroke" ).isEmpty() )
00928         parsePA( obj, gc, "stroke", e.attribute( "stroke" ) );
00929     if( !e.attribute( "stroke-width" ).isEmpty() )
00930         parsePA( obj, gc, "stroke-width", e.attribute( "stroke-width" ) );
00931     if( !e.attribute( "stroke-linejoin" ).isEmpty() )
00932         parsePA( obj, gc, "stroke-linejoin", e.attribute( "stroke-linejoin" ) );
00933     if( !e.attribute( "stroke-linecap" ).isEmpty() )
00934         parsePA( obj, gc, "stroke-linecap", e.attribute( "stroke-linecap" ) );
00935     if( !e.attribute( "stroke-dasharray" ).isEmpty() )
00936         parsePA( obj, gc, "stroke-dasharray", e.attribute( "stroke-dasharray" ) );
00937     if( !e.attribute( "stroke-dashoffset" ).isEmpty() )
00938         parsePA( obj, gc, "stroke-dashoffset", e.attribute( "stroke-dashoffset" ) );
00939     if( !e.attribute( "stroke-opacity" ).isEmpty() )
00940         parsePA( obj, gc, "stroke-opacity", e.attribute( "stroke-opacity" ) );
00941     if( !e.attribute( "stroke-miterlimit" ).isEmpty() )
00942         parsePA( obj, gc, "stroke-miterlimit", e.attribute( "stroke-miterlimit" ) );
00943     if( !e.attribute( "fill-opacity" ).isEmpty() )
00944         parsePA( obj, gc, "fill-opacity", e.attribute( "fill-opacity" ) );
00945     if( !e.attribute( "opacity" ).isEmpty() )
00946         parsePA( obj, gc, "opacity", e.attribute( "opacity" ) );
00947 
00948     // try style attr
00949     QString style = e.attribute( "style" ).simplifyWhiteSpace();
00950     QStringList substyles = QStringList::split( ';', style );
00951     for( QStringList::Iterator it = substyles.begin(); it != substyles.end(); ++it )
00952     {
00953         QStringList substyle = QStringList::split( ':', (*it) );
00954         QString command = substyle[0].stripWhiteSpace();
00955         QString params  = substyle[1].stripWhiteSpace();
00956         parsePA( obj, gc, command, params );
00957     }
00958 
00959     if(!obj)
00960         return;
00961 
00962     obj->setFill( gc->fill );
00963     if( dynamic_cast<VPath *>( obj ) )
00964         dynamic_cast<VPath *>( obj )->setFillRule( gc->fillRule );
00965     // stroke scaling
00966     double lineWidth = gc->stroke.lineWidth();
00967     gc->stroke.setLineWidth( lineWidth * getScalingFromMatrix( gc->matrix ) );
00968     obj->setStroke( gc->stroke );
00969     gc->stroke.setLineWidth( lineWidth );
00970 }
00971 
00972 void SvgImport::parseFont( const QDomElement &e )
00973 {
00974     SvgGraphicsContext *gc = m_gc.current();
00975     if( !gc ) return;
00976 
00977     if( ! e.attribute( "font-family" ).isEmpty() )  
00978         parsePA( 0L, m_gc.current(), "font-family", e.attribute( "font-family" ) );
00979     if( ! e.attribute( "font-size" ).isEmpty() )    
00980         parsePA( 0L, m_gc.current(), "font-size", e.attribute( "font-size" ) );
00981     if( ! e.attribute( "font-weight" ).isEmpty() )  
00982         parsePA( 0L, m_gc.current(), "font-weight", e.attribute( "font-weight" ) );
00983     if( ! e.attribute( "text-decoration" ).isEmpty() )
00984         parsePA( 0L, m_gc.current(), "text-decoration", e.attribute( "text-decoration" ) );
00985 }
00986 
00987 void SvgImport::parseUse( VGroup *grp, const QDomElement &e )
00988 {
00989     QString id = e.attribute( "xlink:href" );
00990 
00991     if( !id.isEmpty() )
00992     {
00993         addGraphicContext();
00994         setupTransform( e );
00995 
00996         QString key = id.mid( 1 );
00997 
00998         if( !e.attribute( "x" ).isEmpty() && !e.attribute( "y" ).isEmpty() )
00999         {
01000             double tx = e.attribute( "x" ).toDouble();
01001             double ty = e.attribute( "y" ).toDouble();
01002 
01003             m_gc.current()->matrix.translate(tx,ty);
01004         }
01005 
01006         if(m_defs.contains(key))
01007         {
01008             QDomElement a = m_defs[key];
01009             if(a.tagName() == "g" || a.tagName() == "a")
01010                 parseGroup( grp, a);
01011             else
01012             {
01013                 // Create the object with the merged styles.
01014                 // The object inherits all style attributes from the use tag, but keeps it's own attributes.
01015                 // So, not just use the style attributes of the use tag, but merge them first.
01016                 createObject( grp, a, VObject::normal, mergeStyles(e, a) );
01017             }
01018         }
01019         delete( m_gc.pop() );
01020     }
01021 }
01022 
01023 void SvgImport::parseGroup( VGroup *grp, const QDomElement &e )
01024 {
01025     for( QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling() )
01026     {
01027         QDomElement b = n.toElement();
01028         if( b.isNull() ) continue;
01029         
01030         // treat svg link <a> as group so we don't miss its child elements
01031         if( b.tagName() == "g" || b.tagName() == "a" )
01032         {
01033             VGroup *group;
01034             if ( grp )
01035                 group = new VGroup( grp );
01036             else
01037                 group = new VGroup( &m_document );
01038 
01039             addGraphicContext();
01040             setupTransform( b );
01041             parseStyle( group, b );
01042             parseFont( b );
01043             parseGroup( group, b );
01044 
01045             // handle id
01046             if( !b.attribute("id").isEmpty() )
01047                 group->setName( b.attribute("id") );
01048             if( grp )
01049                 grp->append( group );
01050             else
01051                 m_document.append( group );
01052             delete( m_gc.pop() );
01053             continue;
01054         }
01055         if( b.tagName() == "defs" )
01056         {
01057             parseDefs( b );
01058             continue;
01059         }
01060         else if( b.tagName() == "linearGradient" || b.tagName() == "radialGradient" )
01061         {
01062             parseGradient( b );
01063             continue;
01064         }
01065         if( b.tagName() == "rect" ||
01066             b.tagName() == "ellipse" ||
01067             b.tagName() == "circle" ||
01068             b.tagName() == "line" ||
01069             b.tagName() == "polyline" ||
01070             b.tagName() == "polygon" ||
01071             b.tagName() == "path" ||
01072             b.tagName() == "image" )
01073         {
01074             createObject( grp, b );
01075             continue;
01076         }
01077         else if( b.tagName() == "text" )
01078         {
01079             createText( grp, b );
01080             continue;
01081         }
01082         else if( b.tagName() == "use" )
01083         {
01084             parseUse( grp, b );
01085             continue;
01086         }
01087     }
01088 }
01089 
01090 void SvgImport::parseDefs( const QDomElement &e )
01091 {
01092     for( QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling() )
01093     {
01094         QDomElement b = n.toElement();
01095         if( b.isNull() ) continue;
01096 
01097         QString definition = b.attribute( "id" );
01098         if( !definition.isEmpty() )
01099         {
01100             if( !m_defs.contains( definition ) )
01101                 m_defs.insert( definition, b );
01102         }
01103     }
01104 }
01105 
01106 
01107 // Creating functions
01108 // ---------------------------------------------------------------------------------------
01109 
01110 void SvgImport::createText( VGroup *grp, const QDomElement &b )
01111 {
01112     const double pathLength = 10.0;
01113 
01114     VText *text = 0L;
01115     QString content;
01116     QString anchor;
01117     VSubpath base( 0L );
01118     VPath *path = 0L;
01119     double offset = 0.0;
01120 
01121     addGraphicContext();
01122     setupTransform( b );
01123     
01124     parseFont( b );
01125 
01126     if( ! b.attribute( "text-anchor" ).isEmpty() )
01127         anchor = b.attribute( "text-anchor" );
01128 
01129     if( b.hasChildNodes() )
01130     {
01131         if( base.isEmpty() && ! b.attribute( "x" ).isEmpty() && ! b.attribute( "y" ).isEmpty() )
01132         {
01133             double x = parseUnit( b.attribute( "x" ) );
01134             double y = parseUnit( b.attribute( "y" ) );
01135             base.moveTo( KoPoint( x, y ) );
01136             base.lineTo( KoPoint( x + pathLength, y ) );
01137         }
01138 
01139         for( QDomNode n = b.firstChild(); !n.isNull(); n = n.nextSibling() )
01140         {
01141             QDomElement e = n.toElement();
01142             if( e.isNull() ) 
01143             {
01144                 content += n.toCharacterData().data();
01145             }
01146             else if( e.tagName() == "textPath" )
01147             {
01148                 if( e.attribute( "xlink:href" ).isEmpty() )
01149                     continue;
01150 
01151                 QString key = e.attribute( "xlink:href" ).mid( 1 );
01152                 if( ! m_defs.contains(key) )
01153                 {
01154                     // try to find referenced object in document
01155                     VObject* obj = findObject( key );
01156                     // try to find referenced object in actual group, which is not yet part of document
01157                     if( ! obj )
01158                         obj = findObject( key, grp );
01159                     if( obj ) 
01160                         path = dynamic_cast<VPath*>( obj );
01161                 }
01162                 else
01163                 {
01164                     QDomElement p = m_defs[key];
01165                     createObject( grp, p, VObject::deleted);
01166                 }
01167                 if( ! path )
01168                     continue;
01169                 base = *path->paths().getFirst();
01170                 content += e.text();
01171                 
01172                 if( ! e.attribute( "startOffset" ).isEmpty() )
01173                 {
01174                     QString start = e.attribute( "startOffset" );
01175                     if( start.endsWith( "%" ) )
01176                         offset = 0.01 * start.remove( '%' ).toDouble();
01177                     else
01178                     {
01179                         float pathLength = 0;
01180                         VSubpathIterator pIt( base );
01181                             
01182                         for( ; pIt.current(); ++pIt )
01183                             pathLength += pIt.current()->length();
01184                         
01185                         if( pathLength > 0.0 )
01186                             offset = start.toDouble() / pathLength;
01187                     }
01188                 }
01189             }
01190             else if( e.tagName() == "tspan" )
01191             {
01192                 // only use text of tspan element, as we are not supporting text 
01193                 // with different styles
01194                 content += e.text();
01195                 if( base.isEmpty() && ! e.attribute( "x" ).isEmpty() && ! e.attribute( "y" ).isEmpty() )
01196                 {
01197                     QStringList posX = QStringList::split( ", ", e.attribute( "x" ) );
01198                     QStringList posY = QStringList::split( ", ", e.attribute( "y" ) );
01199                     if( posX.count() && posY.count() )
01200                     {
01201                         double x = parseUnit( posX.first() );
01202                         double y = parseUnit( posY.first() );
01203                         base.moveTo( KoPoint( x, y ) );
01204                         base.lineTo( KoPoint( x + pathLength, y ) );
01205                     }
01206                 }
01207             }
01208             else if( e.tagName() == "tref" )
01209             {
01210                 if( e.attribute( "xlink:href" ).isEmpty() )
01211                     continue;
01212 
01213                 QString key = e.attribute( "xlink:href" ).mid( 1 );
01214                 if( ! m_defs.contains(key) )
01215                 {
01216                     // try to find referenced object in document
01217                     VObject* obj = findObject( key );
01218                     // try to find referenced object in actual group, which is not yet part of document
01219                     if( ! obj )
01220                         obj = findObject( key, grp );
01221                     if( obj ) 
01222                         content += dynamic_cast<VText*>( obj )->text();
01223                 }
01224                 else
01225                 {
01226                     QDomElement p = m_defs[key];
01227                     content += p.text();
01228                 }
01229             }
01230             else 
01231                 continue;
01232 
01233             if( ! e.attribute( "text-anchor" ).isEmpty() )
01234                 anchor = e.attribute( "text-anchor" );
01235         }
01236         text = new VText( m_gc.current()->font, base, VText::Above, VText::Left, content.simplifyWhiteSpace() );
01237     }
01238     else
01239     {
01240         VSubpath base( 0L );
01241         double x = parseUnit( b.attribute( "x" ) );
01242         double y = parseUnit( b.attribute( "y" ) );
01243         base.moveTo( KoPoint( x, y ) );
01244         base.lineTo( KoPoint( x + pathLength, y ) );
01245         text = new VText( m_gc.current()->font, base, VText::Above, VText::Left, b.text().simplifyWhiteSpace() );
01246     }
01247 
01248     if( text )
01249     {
01250         text->setParent( &m_document );
01251         
01252         parseStyle( text, b );
01253 
01254         text->setFont( m_gc.current()->font );
01255 
01256         VTransformCmd trafo( 0L, m_gc.current()->matrix );
01257         trafo.visit( *text );
01258 
01259         if( !b.attribute("id").isEmpty() )
01260             text->setName( b.attribute("id") );
01261 
01262         if( anchor == "middle" )
01263             text->setAlignment( VText::Center );
01264         else if( anchor == "end" )
01265             text->setAlignment( VText::Right );
01266         
01267         if( offset > 0.0 )
01268             text->setOffset( offset );
01269 
01270         if( grp ) 
01271             grp->append( text );
01272         else 
01273             m_document.append( text );
01274     }
01275 
01276     delete( m_gc.pop() );
01277 }
01278 
01279 void SvgImport::createObject( VGroup *grp, const QDomElement &b, const VObject::VState state, const QDomElement &style )
01280 {
01281     VObject *obj = 0L;
01282 
01283     addGraphicContext();
01284     setupTransform( b );
01285 
01286     if( b.tagName() == "rect" )
01287     {
01288         double x        = parseUnit( b.attribute( "x" ), true, false, m_outerRect );
01289         double y        = parseUnit( b.attribute( "y" ), false, true, m_outerRect );
01290         double width    = parseUnit( b.attribute( "width" ), true, false, m_outerRect );
01291         double height   = parseUnit( b.attribute( "height" ), false, true, m_outerRect );
01292         obj = new VRectangle( 0L, KoPoint( x, height + y ) , width, height );
01293     }
01294     else if( b.tagName() == "ellipse" )
01295     {
01296         double rx       = parseUnit( b.attribute( "rx" ) );
01297         double ry       = parseUnit( b.attribute( "ry" ) );
01298         double left     = parseUnit( b.attribute( "cx" ) ) - rx;
01299         double top      = parseUnit( b.attribute( "cy" ) ) - ry;
01300         obj = new VEllipse( 0L, KoPoint( left, top ), rx * 2.0, ry * 2.0 );
01301     }
01302     else if( b.tagName() == "circle" )
01303     {
01304         double r        = parseUnit( b.attribute( "r" ) );
01305         double left     = parseUnit( b.attribute( "cx" ) ) - r;
01306         double top      = parseUnit( b.attribute( "cy" ) ) - r;
01307         obj = new VEllipse( 0L, KoPoint( left, top ), r * 2.0, r * 2.0 );
01308     }
01309     else if( b.tagName() == "line" )
01310     {
01311         VPath *path = new VPath( &m_document );
01312         double x1 = b.attribute( "x1" ).isEmpty() ? 0.0 : parseUnit( b.attribute( "x1" ) );
01313         double y1 = b.attribute( "y1" ).isEmpty() ? 0.0 : parseUnit( b.attribute( "y1" ) );
01314         double x2 = b.attribute( "x2" ).isEmpty() ? 0.0 : parseUnit( b.attribute( "x2" ) );
01315         double y2 = b.attribute( "y2" ).isEmpty() ? 0.0 : parseUnit( b.attribute( "y2" ) );
01316         path->moveTo( KoPoint( x1, y1 ) );
01317         path->lineTo( KoPoint( x2, y2 ) );
01318         obj = path;
01319     }
01320     else if( b.tagName() == "polyline" || b.tagName() == "polygon" )
01321     {
01322         VPath *path = new VPath( &m_document );
01323         bool bFirst = true;
01324 
01325         QString points = b.attribute( "points" ).simplifyWhiteSpace();
01326         points.replace( ',', ' ' );
01327         points.remove( '\r' );
01328         points.remove( '\n' );
01329         QStringList pointList = QStringList::split( ' ', points );
01330         for( QStringList::Iterator it = pointList.begin(); it != pointList.end(); ++it)
01331         {
01332             KoPoint point;
01333             point.setX( (*it).toDouble() );
01334             ++it;
01335             point.setY( (*it).toDouble() );
01336             if( bFirst )
01337             {
01338                 path->moveTo( point );
01339                 bFirst = false;
01340             }
01341             else
01342                 path->lineTo( point );
01343         }
01344         if( b.tagName() == "polygon" ) path->close();
01345         obj = path;
01346     }
01347     else if( b.tagName() == "path" )
01348     {
01349         VPath *path = new VPath( &m_document );
01350         path->loadSvgPath( b.attribute( "d" ) );
01351         obj = path;
01352     }
01353     else if( b.tagName() == "image" )
01354     {
01355         QString fname = b.attribute("xlink:href");
01356         obj = new VImage( 0L, fname );
01357     }
01358 
01359     if( !obj ) 
01360         return;
01361 
01362     if (state != VObject::normal)
01363         obj->setState(state);
01364 
01365     VTransformCmd trafo( 0L, m_gc.current()->matrix );
01366     trafo.visit( *obj );
01367     
01368     if( !style.isNull() )
01369         parseStyle( obj, style );
01370     else
01371         parseStyle( obj, b );
01372 
01373     // handle id
01374     if( !b.attribute("id").isEmpty() )
01375         obj->setName( b.attribute("id") );
01376     if( grp )
01377         grp->append( obj );
01378     else
01379         m_document.append( obj );
01380 
01381     delete( m_gc.pop() );
01382 }
01383 
01384 #include <svgimport.moc>
KDE Home | KDE Accessibility Home | Description of Access Keys