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