You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1390 lines
40 KiB
1390 lines
40 KiB
15 years ago
|
/* This file is part of the KDE project
|
||
|
Copyright (C) 2002, 2003, The Karbon Developers
|
||
|
|
||
|
This library is free software; you can redistribute it and/or
|
||
|
modify it under the terms of the GNU Library General Public
|
||
|
License as published by the Free Software Foundation; either
|
||
|
version 2 of the License, or (at your option) any later version.
|
||
|
|
||
|
This library is distributed in the hope that it will be useful,
|
||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
|
Library General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU Library General Public License
|
||
|
along with this library; see the file COPYING.LIB. If not, write to
|
||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||
|
* Boston, MA 02110-1301, USA.
|
||
|
*/
|
||
|
|
||
|
#include <svgimport.h>
|
||
|
#include "color.h"
|
||
|
#include <KoFilterChain.h>
|
||
|
#include <KoPageLayout.h>
|
||
|
#include <kgenericfactory.h>
|
||
|
#include <kdebug.h>
|
||
|
#include <KoUnit.h>
|
||
|
#include <KoGlobal.h>
|
||
|
#include <shapes/vellipse.h>
|
||
|
#include <shapes/vrectangle.h>
|
||
|
#include <shapes/vpolygon.h>
|
||
|
#include <commands/vtransformcmd.h>
|
||
|
#include <core/vsegment.h>
|
||
|
#include <core/vtext.h>
|
||
|
#include <core/vglobal.h>
|
||
|
#include <core/vgroup.h>
|
||
|
#include <core/vimage.h>
|
||
|
#include <core/vlayer.h>
|
||
13 years ago
|
#include <tqcolor.h>
|
||
|
#include <tqfile.h>
|
||
|
#include <tqregexp.h>
|
||
15 years ago
|
#include <kfilterdev.h>
|
||
|
|
||
|
typedef KGenericFactory<SvgImport, KoFilter> SvgImportFactory;
|
||
|
K_EXPORT_COMPONENT_FACTORY( libkarbonsvgimport, SvgImportFactory( "kofficefilters" ) )
|
||
|
|
||
13 years ago
|
SvgImport::SvgImport(KoFilter *, const char *, const TQStringList&) :
|
||
15 years ago
|
KoFilter(),
|
||
|
outdoc( "DOC" )
|
||
|
{
|
||
|
m_gc.setAutoDelete( true );
|
||
|
}
|
||
|
|
||
|
SvgImport::~SvgImport()
|
||
|
{
|
||
|
}
|
||
|
|
||
13 years ago
|
KoFilter::ConversionStatus SvgImport::convert(const TQCString& from, const TQCString& to)
|
||
15 years ago
|
{
|
||
|
// check for proper conversion
|
||
|
if( to != "application/x-karbon" || from != "image/svg+xml" )
|
||
|
return KoFilter::NotImplemented;
|
||
|
|
||
|
//Find the last extension
|
||
13 years ago
|
TQString strExt;
|
||
|
TQString fileIn ( m_chain->inputFile() );
|
||
13 years ago
|
const int result=fileIn.findRev('.');
|
||
15 years ago
|
if (result>=0)
|
||
|
strExt=fileIn.mid(result).lower();
|
||
|
|
||
13 years ago
|
TQString strMime; // Mime type of the compressor
|
||
15 years ago
|
if ((strExt==".gz") //in case of .svg.gz (logical extension)
|
||
|
||(strExt==".svgz")) //in case of .svgz (extension used prioritary)
|
||
|
strMime="application/x-gzip"; // Compressed with gzip
|
||
|
else if (strExt==".bz2") //in case of .svg.bz2 (logical extension)
|
||
|
strMime="application/x-bzip2"; // Compressed with bzip2
|
||
|
else
|
||
|
strMime="text/plain";
|
||
|
|
||
|
/*kdDebug(30514) << "File extension: -" << strExt << "- Compression: " << strMime << endl;*/
|
||
|
|
||
13 years ago
|
TQIODevice* in = KFilterDev::deviceForFile(fileIn,strMime);
|
||
15 years ago
|
|
||
|
if (!in->open(IO_ReadOnly))
|
||
|
{
|
||
|
kdError(30514) << "Cannot open file! Aborting!" << endl;
|
||
|
delete in;
|
||
|
return KoFilter::FileNotFound;
|
||
|
}
|
||
|
|
||
|
int line, col;
|
||
13 years ago
|
TQString errormessage;
|
||
15 years ago
|
|
||
|
const bool parsed=inpdoc.setContent( in, &errormessage, &line, &col );
|
||
|
|
||
|
in->close();
|
||
|
delete in;
|
||
|
|
||
|
if ( ! parsed )
|
||
|
{
|
||
|
kdError(30514) << "Error while parsing file: "
|
||
|
<< "at line " << line << " column: " << col
|
||
|
<< " message: " << errormessage << endl;
|
||
|
// ### TODO: feedback to the user
|
||
|
return KoFilter::ParsingError;
|
||
|
}
|
||
|
|
||
|
// Do the conversion!
|
||
|
convert();
|
||
|
// add paper info, we always need custom for svg (Rob)
|
||
13 years ago
|
TQDomElement paper = outdoc.createElement( "PAPER" );
|
||
15 years ago
|
outdoc.documentElement().appendChild( paper );
|
||
|
paper.setAttribute( "format", PG_CUSTOM );
|
||
|
paper.setAttribute( "width", m_document.width() );
|
||
|
paper.setAttribute( "height", m_document.height() );
|
||
|
|
||
|
KoStoreDevice* out = m_chain->storageFile( "root", KoStore::Write );
|
||
|
if( !out )
|
||
|
{
|
||
|
kdError(30514) << "Unable to open output file!" << endl;
|
||
|
return KoFilter::StorageCreationError;
|
||
|
}
|
||
13 years ago
|
TQCString cstring = outdoc.toCString(); // utf-8 already
|
||
15 years ago
|
out->writeBlock( cstring.data(), cstring.length() );
|
||
|
|
||
|
return KoFilter::OK; // was successful
|
||
|
}
|
||
|
|
||
|
void SvgImport::convert()
|
||
|
{
|
||
|
SvgGraphicsContext *gc = new SvgGraphicsContext;
|
||
13 years ago
|
TQDomElement docElem = inpdoc.documentElement();
|
||
15 years ago
|
KoRect bbox( 0, 0, 550.0, 841.0 );
|
||
|
double width = !docElem.attribute( "width" ).isEmpty() ? parseUnit( docElem.attribute( "width" ), true, false, bbox ) : 550.0;
|
||
|
double height = !docElem.attribute( "height" ).isEmpty() ? parseUnit( docElem.attribute( "height" ), false, true, bbox ) : 841.0;
|
||
|
m_document.setWidth( width );
|
||
|
m_document.setHeight( height );
|
||
|
|
||
|
m_outerRect = m_document.boundingBox();
|
||
|
|
||
|
// undo y-mirroring
|
||
13 years ago
|
//m_debug->append(TQString("%1\tUndo Y-mirroring.").arg(m_time.elapsed()));
|
||
15 years ago
|
if( !docElem.attribute( "viewBox" ).isEmpty() )
|
||
|
{
|
||
|
// allow for viewbox def with ',' or whitespace
|
||
13 years ago
|
TQString viewbox( docElem.attribute( "viewBox" ) );
|
||
13 years ago
|
TQStringList points = TQStringList::split( ' ', viewbox.replace( ',', ' ').simplifyWhiteSpace() );
|
||
15 years ago
|
|
||
|
gc->matrix.scale( width / points[2].toFloat() , height / points[3].toFloat() );
|
||
|
m_outerRect.setWidth( m_outerRect.width() * ( points[2].toFloat() / width ) );
|
||
|
m_outerRect.setHeight( m_outerRect.height() * ( points[3].toFloat() / height ) );
|
||
|
}
|
||
|
|
||
|
m_gc.push( gc );
|
||
|
parseGroup( 0L, docElem );
|
||
|
|
||
13 years ago
|
TQWMatrix mat;
|
||
15 years ago
|
mat.scale( 1, -1 );
|
||
|
mat.translate( 0, -m_document.height() );
|
||
|
VTransformCmd trafo( 0L, mat );
|
||
|
trafo.visit( m_document );
|
||
|
outdoc = m_document.saveXML();
|
||
|
}
|
||
|
|
||
|
#define DPI 90
|
||
|
|
||
|
// Helper functions
|
||
|
// ---------------------------------------------------------------------------------------
|
||
|
|
||
13 years ago
|
double SvgImport::toPercentage( TQString s )
|
||
15 years ago
|
{
|
||
|
if( s.endsWith( "%" ) )
|
||
|
return s.remove( '%' ).toDouble();
|
||
|
else
|
||
|
return s.toDouble() * 100.0;
|
||
|
}
|
||
|
|
||
13 years ago
|
double SvgImport::fromPercentage( TQString s )
|
||
15 years ago
|
{
|
||
|
if( s.endsWith( "%" ) )
|
||
|
return s.remove( '%' ).toDouble() / 100.0;
|
||
|
else
|
||
|
return s.toDouble();
|
||
|
}
|
||
|
|
||
13 years ago
|
double SvgImport::getScalingFromMatrix( TQWMatrix &matrix )
|
||
15 years ago
|
{
|
||
|
double xscale = matrix.m11() + matrix.m12();
|
||
|
double yscale = matrix.m22() + matrix.m21();
|
||
|
return sqrt( xscale*xscale + yscale*yscale ) / sqrt( 2.0 );
|
||
|
}
|
||
|
|
||
|
// parses the number into parameter number
|
||
|
const char * getNumber( const char *ptr, double &number )
|
||
|
{
|
||
|
int integer, exponent;
|
||
|
double decimal, frac;
|
||
|
int sign, expsign;
|
||
|
|
||
|
exponent = 0;
|
||
|
integer = 0;
|
||
|
frac = 1.0;
|
||
|
decimal = 0;
|
||
|
sign = 1;
|
||
|
expsign = 1;
|
||
|
|
||
|
// read the sign
|
||
|
if(*ptr == '+')
|
||
|
ptr++;
|
||
|
else if(*ptr == '-')
|
||
|
{
|
||
|
ptr++;
|
||
|
sign = -1;
|
||
|
}
|
||
|
|
||
|
// read the integer part
|
||
|
while(*ptr != '\0' && *ptr >= '0' && *ptr <= '9')
|
||
|
integer = (integer * 10) + *(ptr++) - '0';
|
||
|
if(*ptr == '.') // read the decimals
|
||
|
{
|
||
|
ptr++;
|
||
|
while(*ptr != '\0' && *ptr >= '0' && *ptr <= '9')
|
||
|
decimal += (*(ptr++) - '0') * (frac *= 0.1);
|
||
|
}
|
||
|
|
||
|
if(*ptr == 'e' || *ptr == 'E') // read the exponent part
|
||
|
{
|
||
|
ptr++;
|
||
|
|
||
|
// read the sign of the exponent
|
||
|
if(*ptr == '+')
|
||
|
ptr++;
|
||
|
else if(*ptr == '-')
|
||
|
{
|
||
|
ptr++;
|
||
|
expsign = -1;
|
||
|
}
|
||
|
|
||
|
exponent = 0;
|
||
|
while(*ptr != '\0' && *ptr >= '0' && *ptr <= '9')
|
||
|
{
|
||
|
exponent *= 10;
|
||
|
exponent += *ptr - '0';
|
||
|
ptr++;
|
||
|
}
|
||
|
}
|
||
|
number = integer + decimal;
|
||
|
number *= sign * pow( (double)10, double( expsign * exponent ) );
|
||
|
|
||
|
return ptr;
|
||
|
}
|
||
|
|
||
|
void SvgImport::addGraphicContext()
|
||
|
{
|
||
|
SvgGraphicsContext *gc = new SvgGraphicsContext;
|
||
|
// set as default
|
||
|
if( m_gc.current() )
|
||
|
*gc = *( m_gc.current() );
|
||
|
m_gc.push( gc );
|
||
|
}
|
||
|
|
||
13 years ago
|
void SvgImport::setupTransform( const TQDomElement &e )
|
||
15 years ago
|
{
|
||
|
SvgGraphicsContext *gc = m_gc.current();
|
||
|
|
||
13 years ago
|
TQWMatrix mat = VPath::parseTransform( e.attribute( "transform" ) );
|
||
15 years ago
|
gc->matrix = mat * gc->matrix;
|
||
|
}
|
||
|
|
||
13 years ago
|
VObject* SvgImport::findObject( const TQString &name, VGroup* group )
|
||
15 years ago
|
{
|
||
|
if( ! group )
|
||
|
return 0L;
|
||
|
|
||
|
VObjectListIterator itr = group->objects();
|
||
|
|
||
|
for( uint objcount = 1; itr.current(); ++itr, objcount++ )
|
||
|
if( itr.current()->state() != VObject::deleted )
|
||
|
{
|
||
|
if( itr.current()->name() == name )
|
||
|
return itr.current();
|
||
|
|
||
|
if( dynamic_cast<VGroup *>( itr.current() ) )
|
||
|
{
|
||
|
VObject *obj = findObject( name, dynamic_cast<VGroup *>( itr.current() ) );
|
||
|
if( obj )
|
||
|
return obj;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0L;
|
||
|
}
|
||
|
|
||
13 years ago
|
VObject* SvgImport::findObject( const TQString &name )
|
||
15 years ago
|
{
|
||
13 years ago
|
TQPtrVector<VLayer> vector;
|
||
15 years ago
|
m_document.layers().toVector( &vector );
|
||
|
for( int i = vector.count() - 1; i >= 0; i-- )
|
||
|
{
|
||
|
if ( vector[i]->state() != VObject::deleted )
|
||
|
{
|
||
|
VObject* obj = findObject( name, dynamic_cast<VGroup *>( vector[i] ) );
|
||
|
if( obj )
|
||
|
return obj;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0L;
|
||
|
}
|
||
|
|
||
13 years ago
|
SvgImport::GradientHelper* SvgImport::findGradient( const TQString &id, const TQString &href)
|
||
15 years ago
|
{
|
||
|
// check if gradient was already parsed, and return it
|
||
13 years ago
|
if( m_gradients.contains( id ) )
|
||
15 years ago
|
return &m_gradients[ id ];
|
||
|
|
||
|
// check if gradient was stored for later parsing
|
||
13 years ago
|
if( !m_defs.contains( id ) )
|
||
15 years ago
|
return 0L;
|
||
|
|
||
13 years ago
|
TQDomElement e = m_defs[ id ];
|
||
15 years ago
|
if(e.childNodes().count() == 0)
|
||
|
{
|
||
13 years ago
|
TQString mhref = e.attribute("xlink:href").mid(1);
|
||
15 years ago
|
|
||
13 years ago
|
if(m_defs.contains(mhref))
|
||
15 years ago
|
return findGradient(mhref, id);
|
||
|
else
|
||
|
return 0L;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// ok parse gradient now
|
||
|
parseGradient( m_defs[ id ], m_defs[ href ] );
|
||
|
}
|
||
|
|
||
|
// return successfully parsed gradient or NULL
|
||
13 years ago
|
TQString n;
|
||
15 years ago
|
if(href.isEmpty())
|
||
|
n = id;
|
||
|
else
|
||
|
n = href;
|
||
|
|
||
13 years ago
|
if( m_gradients.contains( n ) )
|
||
15 years ago
|
return &m_gradients[ n ];
|
||
|
else
|
||
|
return 0L;
|
||
|
}
|
||
|
|
||
13 years ago
|
TQDomElement SvgImport::mergeStyles( const TQDomElement &referencedBy, const TQDomElement &referencedElement )
|
||
15 years ago
|
{
|
||
|
// First use all the style attributes of the element being referenced.
|
||
13 years ago
|
TQDomElement e = referencedElement;
|
||
15 years ago
|
|
||
|
// Now go through the style attributes of the element that is referencing and substitute the original ones.
|
||
|
if( !referencedBy.attribute( "color" ).isEmpty() )
|
||
|
e.setAttribute( "color", referencedBy.attribute( "color" ) );
|
||
|
if( !referencedBy.attribute( "fill" ).isEmpty() )
|
||
|
e.setAttribute( "fill", referencedBy.attribute( "fill" ) );
|
||
|
if( !referencedBy.attribute( "fill-rule" ).isEmpty() )
|
||
|
e.setAttribute( "fill-rule", referencedBy.attribute( "fill-rule" ) );
|
||
|
if( !referencedBy.attribute( "stroke" ).isEmpty() )
|
||
|
e.setAttribute( "stroke", referencedBy.attribute( "stroke" ) );
|
||
|
if( !referencedBy.attribute( "stroke-width" ).isEmpty() )
|
||
|
e.setAttribute( "stroke-width", referencedBy.attribute( "stroke-width" ) );
|
||
|
if( !referencedBy.attribute( "stroke-linejoin" ).isEmpty() )
|
||
|
e.setAttribute( "stroke-linejoin", referencedBy.attribute( "stroke-linejoin" ) );
|
||
|
if( !referencedBy.attribute( "stroke-linecap" ).isEmpty() )
|
||
|
e.setAttribute( "stroke-linecap", referencedBy.attribute( "stroke-linecap" ) );
|
||
|
if( !referencedBy.attribute( "stroke-dasharray" ).isEmpty() )
|
||
|
e.setAttribute( "stroke-dasharray", referencedBy.attribute( "stroke-dasharray" ) );
|
||
|
if( !referencedBy.attribute( "stroke-dashoffset" ).isEmpty() )
|
||
|
e.setAttribute( "stroke-dashoffset", referencedBy.attribute( "stroke-dashoffset" ) );
|
||
|
if( !referencedBy.attribute( "stroke-opacity" ).isEmpty() )
|
||
|
e.setAttribute( "stroke-opacity", referencedBy.attribute( "stroke-opacity" ) );
|
||
|
if( !referencedBy.attribute( "stroke-miterlimit" ).isEmpty() )
|
||
|
e.setAttribute( "stroke-miterlimit", referencedBy.attribute( "stroke-miterlimit" ) );
|
||
|
if( !referencedBy.attribute( "fill-opacity" ).isEmpty() )
|
||
|
e.setAttribute( "fill-opacity", referencedBy.attribute( "fill-opacity" ) );
|
||
|
if( !referencedBy.attribute( "opacity" ).isEmpty() )
|
||
|
e.setAttribute( "opacity", referencedBy.attribute( "opacity" ) );
|
||
|
|
||
|
// TODO merge style attribute too.
|
||
|
|
||
|
return e;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Parsing functions
|
||
|
// ---------------------------------------------------------------------------------------
|
||
|
|
||
13 years ago
|
double SvgImport::parseUnit( const TQString &unit, bool horiz, bool vert, KoRect bbox )
|
||
15 years ago
|
{
|
||
|
// TODO : percentage?
|
||
|
double value = 0;
|
||
|
const char *start = unit.latin1();
|
||
|
if(!start) {
|
||
|
return 0;
|
||
|
}
|
||
|
const char *end = getNumber( start, value );
|
||
|
|
||
|
if( uint( end - start ) < unit.length() )
|
||
|
{
|
||
|
if( unit.right( 2 ) == "pt" )
|
||
|
value = ( value / 72.0 ) * DPI;
|
||
|
else if( unit.right( 2 ) == "cm" )
|
||
|
value = ( value / 2.54 ) * DPI;
|
||
|
else if( unit.right( 2 ) == "pc" )
|
||
|
value = ( value / 6.0 ) * DPI;
|
||
|
else if( unit.right( 2 ) == "mm" )
|
||
|
value = ( value / 25.4 ) * DPI;
|
||
|
else if( unit.right( 2 ) == "in" )
|
||
|
value = value * DPI;
|
||
|
else if( unit.right( 2 ) == "pt" )
|
||
|
value = ( value / 72.0 ) * DPI;
|
||
|
else if( unit.right( 2 ) == "em" )
|
||
|
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 ) );
|
||
|
else if( unit.right( 1 ) == "%" )
|
||
|
{
|
||
|
if( horiz && vert )
|
||
|
value = ( value / 100.0 ) * (sqrt( pow( bbox.width(), 2 ) + pow( bbox.height(), 2 ) ) / sqrt( 2.0 ) );
|
||
|
else if( horiz )
|
||
|
value = ( value / 100.0 ) * bbox.width();
|
||
|
else if( vert )
|
||
|
value = ( value / 100.0 ) * bbox.height();
|
||
|
}
|
||
|
}
|
||
|
/*else
|
||
|
{
|
||
|
if( m_gc.current() )
|
||
|
{
|
||
|
if( horiz && vert )
|
||
|
value *= sqrt( pow( m_gc.current()->matrix.m11(), 2 ) + pow( m_gc.current()->matrix.m22(), 2 ) ) / sqrt( 2.0 );
|
||
|
else if( horiz )
|
||
|
value /= m_gc.current()->matrix.m11();
|
||
|
else if( vert )
|
||
|
value /= m_gc.current()->matrix.m22();
|
||
|
}
|
||
|
}*/
|
||
|
return value;
|
||
|
}
|
||
|
|
||
13 years ago
|
TQColor SvgImport::parseColor( const TQString &rgbColor )
|
||
15 years ago
|
{
|
||
|
int r, g, b;
|
||
|
keywordToRGB( rgbColor, r, g, b );
|
||
13 years ago
|
return TQColor( r, g, b );
|
||
15 years ago
|
}
|
||
|
|
||
13 years ago
|
void SvgImport::parseColor( VColor &color, const TQString &s )
|
||
15 years ago
|
{
|
||
|
if( s.startsWith( "rgb(" ) )
|
||
|
{
|
||
13 years ago
|
TQString parse = s.stripWhiteSpace();
|
||
|
TQStringList colors = TQStringList::split( ',', parse );
|
||
|
TQString r = colors[0].right( ( colors[0].length() - 4 ) );
|
||
|
TQString g = colors[1];
|
||
|
TQString b = colors[2].left( ( colors[2].length() - 1 ) );
|
||
15 years ago
|
|
||
13 years ago
|
if( r.contains( "%" ) )
|
||
15 years ago
|
{
|
||
|
r = r.left( r.length() - 1 );
|
||
13 years ago
|
r = TQString::number( int( ( double( 255 * r.toDouble() ) / 100.0 ) ) );
|
||
15 years ago
|
}
|
||
|
|
||
13 years ago
|
if( g.contains( "%" ) )
|
||
15 years ago
|
{
|
||
|
g = g.left( g.length() - 1 );
|
||
13 years ago
|
g = TQString::number( int( ( double( 255 * g.toDouble() ) / 100.0 ) ) );
|
||
15 years ago
|
}
|
||
|
|
||
13 years ago
|
if( b.contains( "%" ) )
|
||
15 years ago
|
{
|
||
|
b = b.left( b.length() - 1 );
|
||
13 years ago
|
b = TQString::number( int( ( double( 255 * b.toDouble() ) / 100.0 ) ) );
|
||
15 years ago
|
}
|
||
|
|
||
13 years ago
|
TQColor c( r.toInt(), g.toInt(), b.toInt() );
|
||
15 years ago
|
color.set( c.red() / 255.0, c.green() / 255.0, c.blue() / 255.0 );
|
||
|
}
|
||
|
else if( s == "currentColor" )
|
||
|
{
|
||
|
SvgGraphicsContext *gc = m_gc.current();
|
||
|
color = gc->color;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
13 years ago
|
TQString rgbColor = s.stripWhiteSpace();
|
||
|
TQColor c;
|
||
15 years ago
|
if( rgbColor.startsWith( "#" ) )
|
||
|
c.setNamedColor( rgbColor );
|
||
|
else
|
||
|
c = parseColor( rgbColor );
|
||
|
color.set( c.red() / 255.0, c.green() / 255.0, c.blue() / 255.0 );
|
||
|
}
|
||
|
}
|
||
|
|
||
13 years ago
|
void SvgImport::parseColorStops( VGradient *gradient, const TQDomElement &e )
|
||
15 years ago
|
{
|
||
|
VColor c;
|
||
13 years ago
|
for( TQDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling() )
|
||
15 years ago
|
{
|
||
13 years ago
|
TQDomElement stop = n.toElement();
|
||
15 years ago
|
if( stop.tagName() == "stop" )
|
||
|
{
|
||
|
float offset;
|
||
13 years ago
|
TQString temp = stop.attribute( "offset" );
|
||
13 years ago
|
if( temp.contains( '%' ) )
|
||
15 years ago
|
{
|
||
|
temp = temp.left( temp.length() - 1 );
|
||
|
offset = temp.toFloat() / 100.0;
|
||
|
}
|
||
|
else
|
||
|
offset = temp.toFloat();
|
||
|
|
||
|
if( !stop.attribute( "stop-color" ).isEmpty() )
|
||
|
parseColor( c, stop.attribute( "stop-color" ) );
|
||
|
else
|
||
|
{
|
||
|
// try style attr
|
||
13 years ago
|
TQString style = stop.attribute( "style" ).simplifyWhiteSpace();
|
||
|
TQStringList substyles = TQStringList::split( ';', style );
|
||
|
for( TQStringList::Iterator it = substyles.begin(); it != substyles.end(); ++it )
|
||
15 years ago
|
{
|
||
13 years ago
|
TQStringList substyle = TQStringList::split( ':', (*it) );
|
||
|
TQString command = substyle[0].stripWhiteSpace();
|
||
|
TQString params = substyle[1].stripWhiteSpace();
|
||
15 years ago
|
if( command == "stop-color" )
|
||
|
parseColor( c, params );
|
||
|
if( command == "stop-opacity" )
|
||
|
c.setOpacity( params.toDouble() );
|
||
|
}
|
||
|
|
||
|
}
|
||
|
if( !stop.attribute( "stop-opacity" ).isEmpty() )
|
||
|
c.setOpacity( stop.attribute( "stop-opacity" ).toDouble() );
|
||
|
gradient->addStop( c, offset, 0.5 );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
13 years ago
|
void SvgImport::parseGradient( const TQDomElement &e , const TQDomElement &referencedBy)
|
||
15 years ago
|
{
|
||
|
// IMPROVEMENTS:
|
||
|
// - Store the parsed colorstops in some sort of a cache so they don't need to be parsed again.
|
||
|
// - A gradient inherits attributes it does not have from the referencing gradient.
|
||
|
// - Gradients with no color stops have no fill or stroke.
|
||
|
// - Gradients with one color stop have a solid color.
|
||
|
|
||
|
SvgGraphicsContext *gc = m_gc.current();
|
||
|
if( !gc ) return;
|
||
|
|
||
|
GradientHelper gradhelper;
|
||
|
gradhelper.gradient.clearStops();
|
||
|
gradhelper.gradient.setRepeatMethod( VGradient::none );
|
||
|
|
||
|
if(e.childNodes().count() == 0)
|
||
|
{
|
||
13 years ago
|
TQString href = e.attribute("xlink:href").mid(1);
|
||
15 years ago
|
|
||
|
if(href.isEmpty())
|
||
|
{
|
||
|
//gc->fill.setType( VFill::none ); // <--- TODO Fill OR Stroke are none
|
||
|
return;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// copy the referenced gradient if found
|
||
|
GradientHelper *pGrad = findGradient( href );
|
||
|
if( pGrad )
|
||
|
gradhelper = *pGrad;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Use the gradient that is referencing, or if there isn't one, the original gradient.
|
||
13 years ago
|
TQDomElement b;
|
||
15 years ago
|
if( !referencedBy.isNull() )
|
||
|
b = referencedBy;
|
||
|
else
|
||
|
b = e;
|
||
|
|
||
13 years ago
|
TQString id = b.attribute("id");
|
||
15 years ago
|
if( !id.isEmpty() )
|
||
|
{
|
||
|
// Copy existing gradient if it exists
|
||
13 years ago
|
if( m_gradients.find( id ) != m_gradients.end() )
|
||
15 years ago
|
gradhelper.gradient = m_gradients[ id ].gradient;
|
||
|
}
|
||
|
|
||
|
gradhelper.bbox = b.attribute( "gradientUnits" ) != "userSpaceOnUse";
|
||
|
|
||
|
// parse color prop
|
||
|
VColor c = m_gc.current()->color;
|
||
|
|
||
|
if( !b.attribute( "color" ).isEmpty() )
|
||
|
{
|
||
|
parseColor( c, b.attribute( "color" ) );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// try style attr
|
||
13 years ago
|
TQString style = b.attribute( "style" ).simplifyWhiteSpace();
|
||
|
TQStringList substyles = TQStringList::split( ';', style );
|
||
|
for( TQStringList::Iterator it = substyles.begin(); it != substyles.end(); ++it )
|
||
15 years ago
|
{
|
||
13 years ago
|
TQStringList substyle = TQStringList::split( ':', (*it) );
|
||
|
TQString command = substyle[0].stripWhiteSpace();
|
||
|
TQString params = substyle[1].stripWhiteSpace();
|
||
15 years ago
|
if( command == "color" )
|
||
|
parseColor( c, params );
|
||
|
}
|
||
|
}
|
||
|
m_gc.current()->color = c;
|
||
|
|
||
|
if( b.tagName() == "linearGradient" )
|
||
|
{
|
||
|
if( gradhelper.bbox )
|
||
|
{
|
||
|
gradhelper.gradient.setOrigin( KoPoint( toPercentage( b.attribute( "x1", "0%" ) ), toPercentage( b.attribute( "y1", "0%" ) ) ) );
|
||
|
gradhelper.gradient.setVector( KoPoint( toPercentage( b.attribute( "x2", "100%" ) ), toPercentage( b.attribute( "y2", "0%" ) ) ) );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
gradhelper.gradient.setOrigin( KoPoint( b.attribute( "x1" ).toDouble(), b.attribute( "y1" ).toDouble() ) );
|
||
|
gradhelper.gradient.setVector( KoPoint( b.attribute( "x2" ).toDouble(), b.attribute( "y2" ).toDouble() ) );
|
||
|
}
|
||
|
gradhelper.gradient.setType( VGradient::linear );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if( gradhelper.bbox )
|
||
|
{
|
||
|
gradhelper.gradient.setOrigin( KoPoint( toPercentage( b.attribute( "cx", "50%" ) ), toPercentage( b.attribute( "cy", "50%" ) ) ) );
|
||
|
gradhelper.gradient.setVector( KoPoint( toPercentage( b.attribute( "cx", "50%" ) ) + toPercentage( b.attribute( "r", "50%" ) ), toPercentage( b.attribute( "cy", "50%" ) ) ) );
|
||
|
gradhelper.gradient.setFocalPoint( KoPoint( toPercentage( b.attribute( "fx", "50%" ) ), toPercentage( b.attribute( "fy", "50%" ) ) ) );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
gradhelper.gradient.setOrigin( KoPoint( b.attribute( "cx" ).toDouble(), b.attribute( "cy" ).toDouble() ) );
|
||
|
gradhelper.gradient.setFocalPoint( KoPoint( b.attribute( "fx" ).toDouble(), b.attribute( "fy" ).toDouble() ) );
|
||
|
gradhelper.gradient.setVector( KoPoint( b.attribute( "cx" ).toDouble() + b.attribute( "r" ).toDouble(), b.attribute( "cy" ).toDouble() ) );
|
||
|
}
|
||
|
gradhelper.gradient.setType( VGradient::radial );
|
||
|
}
|
||
|
// handle spread method
|
||
13 years ago
|
TQString spreadMethod = b.attribute( "spreadMethod" );
|
||
15 years ago
|
if( !spreadMethod.isEmpty() )
|
||
|
{
|
||
|
if( spreadMethod == "reflect" )
|
||
|
gradhelper.gradient.setRepeatMethod( VGradient::reflect );
|
||
|
else if( spreadMethod == "repeat" )
|
||
|
gradhelper.gradient.setRepeatMethod( VGradient::repeat );
|
||
|
else
|
||
|
gradhelper.gradient.setRepeatMethod( VGradient::none );
|
||
|
}
|
||
|
else
|
||
|
gradhelper.gradient.setRepeatMethod( VGradient::none );
|
||
|
|
||
|
// Parse the color stops. The referencing gradient does not have colorstops,
|
||
|
// so use the stops from the gradient it references to (e in this case and not b)
|
||
|
parseColorStops( &gradhelper.gradient, e );
|
||
|
//gradient.setGradientTransform( parseTransform( e.attribute( "gradientTransform" ) ) );
|
||
|
gradhelper.gradientTransform = VPath::parseTransform( b.attribute( "gradientTransform" ) );
|
||
|
m_gradients.insert( b.attribute( "id" ), gradhelper );
|
||
|
}
|
||
|
|
||
13 years ago
|
void SvgImport::parsePA( VObject *obj, SvgGraphicsContext *gc, const TQString &command, const TQString ¶ms )
|
||
15 years ago
|
{
|
||
|
VColor fillcolor = gc->fill.color();
|
||
|
VColor strokecolor = gc->stroke.color();
|
||
|
|
||
|
if( params == "inherit" ) return;
|
||
|
|
||
|
if( command == "fill" )
|
||
|
{
|
||
|
if( params == "none" )
|
||
|
gc->fill.setType( VFill::none );
|
||
|
else if( params.startsWith( "url(" ) )
|
||
|
{
|
||
13 years ago
|
unsigned int start = params.find("#") + 1;
|
||
|
unsigned int end = params.findRev(")");
|
||
13 years ago
|
TQString key = params.mid( start, end - start );
|
||
15 years ago
|
GradientHelper *gradHelper = findGradient( key );
|
||
|
if( gradHelper )
|
||
|
{
|
||
|
gc->fill.gradient() = gradHelper->gradient;
|
||
|
|
||
|
if( gradHelper->bbox )
|
||
|
{
|
||
|
// adjust to bbox
|
||
|
KoRect bbox = obj->boundingBox();
|
||
|
//kdDebug() << "bbox x : " << bbox.x() << endl;
|
||
|
//kdDebug() << "!!!!!!bbox y : " << bbox.y() << endl;
|
||
|
//kdDebug() << gc->fill.gradient().origin().x() << endl;
|
||
|
//kdDebug() << gc->fill.gradient().vector().x() << endl;
|
||
13 years ago
|
double offsetx = parseUnit( TQString( "%1%" ).arg( gc->fill.gradient().origin().x() ), true, false, bbox );
|
||
|
double offsety = parseUnit( TQString( "%1%" ).arg( gc->fill.gradient().origin().y() ), false, true, bbox );
|
||
15 years ago
|
gc->fill.gradient().setOrigin( KoPoint( bbox.x() + offsetx, bbox.y() + offsety ) );
|
||
|
if(gc->fill.gradient().type() == VGradient::radial)
|
||
|
{
|
||
13 years ago
|
offsetx = parseUnit( TQString( "%1%" ).arg( gc->fill.gradient().focalPoint().x() ), true, false, bbox );
|
||
|
offsety = parseUnit( TQString( "%1%" ).arg( gc->fill.gradient().focalPoint().y() ), false, true, bbox );
|
||
15 years ago
|
gc->fill.gradient().setFocalPoint( KoPoint( bbox.x() + offsetx, bbox.y() + offsety ) );
|
||
|
}
|
||
13 years ago
|
offsetx = parseUnit( TQString( "%1%" ).arg( gc->fill.gradient().vector().x() ), true, false, bbox );
|
||
|
offsety = parseUnit( TQString( "%1%" ).arg( gc->fill.gradient().vector().y() ), false, true, bbox );
|
||
15 years ago
|
gc->fill.gradient().setVector( KoPoint( bbox.x() + offsetx, bbox.y() + offsety ) );
|
||
|
//kdDebug() << offsety << endl;
|
||
|
//kdDebug() << gc->fill.gradient().origin().x() << endl;
|
||
|
//kdDebug() << gc->fill.gradient().origin().y() << endl;
|
||
|
//kdDebug() << gc->fill.gradient().vector().x() << endl;
|
||
|
//kdDebug() << gc->fill.gradient().vector().y() << endl;
|
||
|
}
|
||
|
gc->fill.gradient().transform( gradHelper->gradientTransform );
|
||
|
|
||
|
if( !gradHelper->bbox )
|
||
|
gc->fill.gradient().transform( gc->matrix );
|
||
|
|
||
|
gc->fill.setType( VFill::grad );
|
||
|
}
|
||
|
else
|
||
|
gc->fill.setType( VFill::none );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
parseColor( fillcolor, params );
|
||
|
gc->fill.setType( VFill::solid );
|
||
|
}
|
||
|
}
|
||
|
else if( command == "fill-rule" )
|
||
|
{
|
||
|
if( params == "nonzero" )
|
||
|
gc->fillRule = winding;
|
||
|
else if( params == "evenodd" )
|
||
|
gc->fillRule = evenOdd;
|
||
|
}
|
||
|
else if( command == "stroke" )
|
||
|
{
|
||
|
if( params == "none" )
|
||
|
gc->stroke.setType( VStroke::none );
|
||
|
else if( params.startsWith( "url(" ) )
|
||
|
{
|
||
13 years ago
|
unsigned int start = params.find("#") + 1;
|
||
|
unsigned int end = params.findRev(")");
|
||
13 years ago
|
TQString key = params.mid( start, end - start );
|
||
15 years ago
|
|
||
|
GradientHelper *gradHelper = findGradient( key );
|
||
|
if( gradHelper )
|
||
|
{
|
||
|
gc->stroke.gradient() = gradHelper->gradient;
|
||
|
gc->stroke.gradient().transform( gradHelper->gradientTransform );
|
||
|
gc->stroke.gradient().transform( gc->matrix );
|
||
|
gc->stroke.setType( VStroke::grad );
|
||
|
}
|
||
|
else
|
||
|
gc->stroke.setType( VStroke::none );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
parseColor( strokecolor, params );
|
||
|
gc->stroke.setType( VStroke::solid );
|
||
|
}
|
||
|
}
|
||
|
else if( command == "stroke-width" )
|
||
|
gc->stroke.setLineWidth( parseUnit( params, true, true, m_outerRect ) );
|
||
|
else if( command == "stroke-linejoin" )
|
||
|
{
|
||
|
if( params == "miter" )
|
||
|
gc->stroke.setLineJoin( VStroke::joinMiter );
|
||
|
else if( params == "round" )
|
||
|
gc->stroke.setLineJoin( VStroke::joinRound );
|
||
|
else if( params == "bevel" )
|
||
|
gc->stroke.setLineJoin( VStroke::joinBevel );
|
||
|
}
|
||
|
else if( command == "stroke-linecap" )
|
||
|
{
|
||
|
if( params == "butt" )
|
||
|
gc->stroke.setLineCap( VStroke::capButt );
|
||
|
else if( params == "round" )
|
||
|
gc->stroke.setLineCap( VStroke::capRound );
|
||
|
else if( params == "square" )
|
||
|
gc->stroke.setLineCap( VStroke::capSquare );
|
||
|
}
|
||
|
else if( command == "stroke-miterlimit" )
|
||
|
gc->stroke.setMiterLimit( params.toFloat() );
|
||
|
else if( command == "stroke-dasharray" )
|
||
|
{
|
||
13 years ago
|
TQValueList<float> array;
|
||
15 years ago
|
if(params != "none")
|
||
|
{
|
||
|
// with "stroke-dasharray", the separator is a white space
|
||
|
// inside style attribute, stroke-dasharray is separated by comma (,)
|
||
13 years ago
|
TQStringList dashes = TQStringList::split( TQRegExp("[\\s,]"), params );
|
||
|
for( TQStringList::Iterator it = dashes.begin(); it != dashes.end(); ++it )
|
||
15 years ago
|
array.append( (*it).toFloat() );
|
||
|
}
|
||
|
gc->stroke.dashPattern().setArray( array );
|
||
|
}
|
||
|
else if( command == "stroke-dashoffset" )
|
||
|
gc->stroke.dashPattern().setOffset( params.toFloat() );
|
||
|
// handle opacity
|
||
|
else if( command == "stroke-opacity" )
|
||
|
strokecolor.setOpacity( fromPercentage( params ) );
|
||
|
else if( command == "fill-opacity" )
|
||
|
fillcolor.setOpacity( fromPercentage( params ) );
|
||
|
else if( command == "opacity" )
|
||
|
{
|
||
|
fillcolor.setOpacity( fromPercentage( params ) );
|
||
|
strokecolor.setOpacity( fromPercentage( params ) );
|
||
|
}
|
||
|
else if( command == "font-family" )
|
||
|
{
|
||
13 years ago
|
TQString family = params;
|
||
13 years ago
|
family.replace( '\'' , ' ' );
|
||
15 years ago
|
gc->font.setFamily( family );
|
||
|
}
|
||
|
else if( command == "font-size" )
|
||
|
{
|
||
|
float pointSize = parseUnit( params );
|
||
|
gc->font.setPointSizeFloat( pointSize * getScalingFromMatrix( gc->matrix ) );
|
||
|
}
|
||
|
else if( command == "font-weight" )
|
||
|
{
|
||
13 years ago
|
int weight = TQFont::Normal;
|
||
15 years ago
|
|
||
|
// map svg weight to qt weight
|
||
|
// svg value qt value
|
||
|
// 100,200,300 1, 17, 33
|
||
|
// 400 50 (normal)
|
||
|
// 500,600 58,66
|
||
|
// 700 75 (bold)
|
||
|
// 800,900 87,99
|
||
|
|
||
|
if( params == "bold" )
|
||
13 years ago
|
weight = TQFont::Bold;
|
||
15 years ago
|
else if( params == "lighter" )
|
||
|
{
|
||
|
weight = gc->font.weight();
|
||
|
if( weight <= 17 )
|
||
|
weight = 1;
|
||
|
else if( weight <= 33 )
|
||
|
weight = 17;
|
||
|
else if( weight <= 50 )
|
||
|
weight = 33;
|
||
|
else if( weight <= 58 )
|
||
|
weight = 50;
|
||
|
else if( weight <= 66 )
|
||
|
weight = 58;
|
||
|
else if( weight <= 75 )
|
||
|
weight = 66;
|
||
|
else if( weight <= 87 )
|
||
|
weight = 75;
|
||
|
else if( weight <= 99 )
|
||
|
weight = 87;
|
||
|
}
|
||
|
else if( params == "bolder" )
|
||
|
{
|
||
|
weight = gc->font.weight();
|
||
|
if( weight >= 87 )
|
||
|
weight = 99;
|
||
|
else if( weight >= 75 )
|
||
|
weight = 87;
|
||
|
else if( weight >= 66 )
|
||
|
weight = 75;
|
||
|
else if( weight >= 58 )
|
||
|
weight = 66;
|
||
|
else if( weight >= 50 )
|
||
|
weight = 58;
|
||
|
else if( weight >= 33 )
|
||
|
weight = 50;
|
||
|
else if( weight >= 17 )
|
||
|
weight = 50;
|
||
|
else if( weight >= 1 )
|
||
|
weight = 17;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
bool ok;
|
||
|
// try to read numerical weight value
|
||
|
weight = params.toInt( &ok, 10 );
|
||
|
|
||
|
if( !ok )
|
||
|
return;
|
||
|
|
||
|
switch( weight )
|
||
|
{
|
||
|
case 100: weight = 1; break;
|
||
|
case 200: weight = 17; break;
|
||
|
case 300: weight = 33; break;
|
||
|
case 400: weight = 50; break;
|
||
|
case 500: weight = 58; break;
|
||
|
case 600: weight = 66; break;
|
||
|
case 700: weight = 75; break;
|
||
|
case 800: weight = 87; break;
|
||
|
case 900: weight = 99; break;
|
||
|
}
|
||
|
}
|
||
|
gc->font.setWeight( weight );
|
||
|
}
|
||
|
else if( command == "text-decoration" )
|
||
|
{
|
||
|
if( params == "line-through" )
|
||
|
gc->font.setStrikeOut( true );
|
||
|
else if( params == "underline" )
|
||
|
gc->font.setUnderline( true );
|
||
|
}
|
||
|
else if( command == "color" )
|
||
|
{
|
||
|
VColor color;
|
||
|
parseColor( color, params );
|
||
|
gc->color = color;
|
||
|
}
|
||
|
if( gc->fill.type() != VFill::none )
|
||
|
gc->fill.setColor( fillcolor, false );
|
||
|
//if( gc->stroke.type() == VStroke::solid )
|
||
|
gc->stroke.setColor( strokecolor );
|
||
|
}
|
||
|
|
||
13 years ago
|
void SvgImport::parseStyle( VObject *obj, const TQDomElement &e )
|
||
15 years ago
|
{
|
||
|
SvgGraphicsContext *gc = m_gc.current();
|
||
|
if( !gc ) return;
|
||
|
|
||
|
// try normal PA
|
||
|
if( !e.attribute( "color" ).isEmpty() )
|
||
|
parsePA( obj, gc, "color", e.attribute( "color" ) );
|
||
|
if( !e.attribute( "fill" ).isEmpty() )
|
||
|
parsePA( obj, gc, "fill", e.attribute( "fill" ) );
|
||
|
if( !e.attribute( "fill-rule" ).isEmpty() )
|
||
|
parsePA( obj, gc, "fill-rule", e.attribute( "fill-rule" ) );
|
||
|
if( !e.attribute( "stroke" ).isEmpty() )
|
||
|
parsePA( obj, gc, "stroke", e.attribute( "stroke" ) );
|
||
|
if( !e.attribute( "stroke-width" ).isEmpty() )
|
||
|
parsePA( obj, gc, "stroke-width", e.attribute( "stroke-width" ) );
|
||
|
if( !e.attribute( "stroke-linejoin" ).isEmpty() )
|
||
|
parsePA( obj, gc, "stroke-linejoin", e.attribute( "stroke-linejoin" ) );
|
||
|
if( !e.attribute( "stroke-linecap" ).isEmpty() )
|
||
|
parsePA( obj, gc, "stroke-linecap", e.attribute( "stroke-linecap" ) );
|
||
|
if( !e.attribute( "stroke-dasharray" ).isEmpty() )
|
||
|
parsePA( obj, gc, "stroke-dasharray", e.attribute( "stroke-dasharray" ) );
|
||
|
if( !e.attribute( "stroke-dashoffset" ).isEmpty() )
|
||
|
parsePA( obj, gc, "stroke-dashoffset", e.attribute( "stroke-dashoffset" ) );
|
||
|
if( !e.attribute( "stroke-opacity" ).isEmpty() )
|
||
|
parsePA( obj, gc, "stroke-opacity", e.attribute( "stroke-opacity" ) );
|
||
|
if( !e.attribute( "stroke-miterlimit" ).isEmpty() )
|
||
|
parsePA( obj, gc, "stroke-miterlimit", e.attribute( "stroke-miterlimit" ) );
|
||
|
if( !e.attribute( "fill-opacity" ).isEmpty() )
|
||
|
parsePA( obj, gc, "fill-opacity", e.attribute( "fill-opacity" ) );
|
||
|
if( !e.attribute( "opacity" ).isEmpty() )
|
||
|
parsePA( obj, gc, "opacity", e.attribute( "opacity" ) );
|
||
|
|
||
|
// try style attr
|
||
13 years ago
|
TQString style = e.attribute( "style" ).simplifyWhiteSpace();
|
||
|
TQStringList substyles = TQStringList::split( ';', style );
|
||
|
for( TQStringList::Iterator it = substyles.begin(); it != substyles.end(); ++it )
|
||
15 years ago
|
{
|
||
13 years ago
|
TQStringList substyle = TQStringList::split( ':', (*it) );
|
||
|
TQString command = substyle[0].stripWhiteSpace();
|
||
|
TQString params = substyle[1].stripWhiteSpace();
|
||
15 years ago
|
parsePA( obj, gc, command, params );
|
||
|
}
|
||
|
|
||
|
if(!obj)
|
||
|
return;
|
||
|
|
||
|
obj->setFill( gc->fill );
|
||
|
if( dynamic_cast<VPath *>( obj ) )
|
||
|
dynamic_cast<VPath *>( obj )->setFillRule( gc->fillRule );
|
||
|
// stroke scaling
|
||
|
double lineWidth = gc->stroke.lineWidth();
|
||
|
gc->stroke.setLineWidth( lineWidth * getScalingFromMatrix( gc->matrix ) );
|
||
|
obj->setStroke( gc->stroke );
|
||
|
gc->stroke.setLineWidth( lineWidth );
|
||
|
}
|
||
|
|
||
13 years ago
|
void SvgImport::parseFont( const TQDomElement &e )
|
||
15 years ago
|
{
|
||
|
SvgGraphicsContext *gc = m_gc.current();
|
||
|
if( !gc ) return;
|
||
|
|
||
|
if( ! e.attribute( "font-family" ).isEmpty() )
|
||
|
parsePA( 0L, m_gc.current(), "font-family", e.attribute( "font-family" ) );
|
||
|
if( ! e.attribute( "font-size" ).isEmpty() )
|
||
|
parsePA( 0L, m_gc.current(), "font-size", e.attribute( "font-size" ) );
|
||
|
if( ! e.attribute( "font-weight" ).isEmpty() )
|
||
|
parsePA( 0L, m_gc.current(), "font-weight", e.attribute( "font-weight" ) );
|
||
|
if( ! e.attribute( "text-decoration" ).isEmpty() )
|
||
|
parsePA( 0L, m_gc.current(), "text-decoration", e.attribute( "text-decoration" ) );
|
||
|
}
|
||
|
|
||
13 years ago
|
void SvgImport::parseUse( VGroup *grp, const TQDomElement &e )
|
||
15 years ago
|
{
|
||
13 years ago
|
TQString id = e.attribute( "xlink:href" );
|
||
15 years ago
|
|
||
|
if( !id.isEmpty() )
|
||
|
{
|
||
|
addGraphicContext();
|
||
|
setupTransform( e );
|
||
|
|
||
13 years ago
|
TQString key = id.mid( 1 );
|
||
15 years ago
|
|
||
|
if( !e.attribute( "x" ).isEmpty() && !e.attribute( "y" ).isEmpty() )
|
||
|
{
|
||
|
double tx = e.attribute( "x" ).toDouble();
|
||
|
double ty = e.attribute( "y" ).toDouble();
|
||
|
|
||
|
m_gc.current()->matrix.translate(tx,ty);
|
||
|
}
|
||
|
|
||
13 years ago
|
if(m_defs.contains(key))
|
||
15 years ago
|
{
|
||
13 years ago
|
TQDomElement a = m_defs[key];
|
||
15 years ago
|
if(a.tagName() == "g" || a.tagName() == "a")
|
||
|
parseGroup( grp, a);
|
||
|
else
|
||
|
{
|
||
|
// Create the object with the merged styles.
|
||
|
// The object inherits all style attributes from the use tag, but keeps it's own attributes.
|
||
|
// So, not just use the style attributes of the use tag, but merge them first.
|
||
|
createObject( grp, a, VObject::normal, mergeStyles(e, a) );
|
||
|
}
|
||
|
}
|
||
|
delete( m_gc.pop() );
|
||
|
}
|
||
|
}
|
||
|
|
||
13 years ago
|
void SvgImport::parseGroup( VGroup *grp, const TQDomElement &e )
|
||
15 years ago
|
{
|
||
13 years ago
|
for( TQDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling() )
|
||
15 years ago
|
{
|
||
13 years ago
|
TQDomElement b = n.toElement();
|
||
15 years ago
|
if( b.isNull() ) continue;
|
||
|
|
||
|
// treat svg link <a> as group so we don't miss its child elements
|
||
|
if( b.tagName() == "g" || b.tagName() == "a" )
|
||
|
{
|
||
|
VGroup *group;
|
||
|
if ( grp )
|
||
|
group = new VGroup( grp );
|
||
|
else
|
||
|
group = new VGroup( &m_document );
|
||
|
|
||
|
addGraphicContext();
|
||
|
setupTransform( b );
|
||
|
parseStyle( group, b );
|
||
|
parseFont( b );
|
||
|
parseGroup( group, b );
|
||
|
|
||
|
// handle id
|
||
|
if( !b.attribute("id").isEmpty() )
|
||
|
group->setName( b.attribute("id") );
|
||
|
if( grp )
|
||
|
grp->append( group );
|
||
|
else
|
||
|
m_document.append( group );
|
||
|
delete( m_gc.pop() );
|
||
|
continue;
|
||
|
}
|
||
|
if( b.tagName() == "defs" )
|
||
|
{
|
||
|
parseDefs( b );
|
||
|
continue;
|
||
|
}
|
||
|
else if( b.tagName() == "linearGradient" || b.tagName() == "radialGradient" )
|
||
|
{
|
||
|
parseGradient( b );
|
||
|
continue;
|
||
|
}
|
||
|
if( b.tagName() == "rect" ||
|
||
|
b.tagName() == "ellipse" ||
|
||
|
b.tagName() == "circle" ||
|
||
|
b.tagName() == "line" ||
|
||
|
b.tagName() == "polyline" ||
|
||
|
b.tagName() == "polygon" ||
|
||
|
b.tagName() == "path" ||
|
||
|
b.tagName() == "image" )
|
||
|
{
|
||
|
createObject( grp, b );
|
||
|
continue;
|
||
|
}
|
||
|
else if( b.tagName() == "text" )
|
||
|
{
|
||
|
createText( grp, b );
|
||
|
continue;
|
||
|
}
|
||
|
else if( b.tagName() == "use" )
|
||
|
{
|
||
|
parseUse( grp, b );
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
13 years ago
|
void SvgImport::parseDefs( const TQDomElement &e )
|
||
15 years ago
|
{
|
||
13 years ago
|
for( TQDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling() )
|
||
15 years ago
|
{
|
||
13 years ago
|
TQDomElement b = n.toElement();
|
||
15 years ago
|
if( b.isNull() ) continue;
|
||
|
|
||
13 years ago
|
TQString definition = b.attribute( "id" );
|
||
15 years ago
|
if( !definition.isEmpty() )
|
||
|
{
|
||
13 years ago
|
if( !m_defs.contains( definition ) )
|
||
15 years ago
|
m_defs.insert( definition, b );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// Creating functions
|
||
|
// ---------------------------------------------------------------------------------------
|
||
|
|
||
13 years ago
|
void SvgImport::createText( VGroup *grp, const TQDomElement &b )
|
||
15 years ago
|
{
|
||
|
const double pathLength = 10.0;
|
||
|
|
||
|
VText *text = 0L;
|
||
13 years ago
|
TQString content;
|
||
|
TQString anchor;
|
||
15 years ago
|
VSubpath base( 0L );
|
||
|
VPath *path = 0L;
|
||
|
double offset = 0.0;
|
||
|
|
||
|
addGraphicContext();
|
||
|
setupTransform( b );
|
||
|
|
||
|
parseFont( b );
|
||
|
|
||
|
if( ! b.attribute( "text-anchor" ).isEmpty() )
|
||
|
anchor = b.attribute( "text-anchor" );
|
||
|
|
||
|
if( b.hasChildNodes() )
|
||
|
{
|
||
|
if( base.isEmpty() && ! b.attribute( "x" ).isEmpty() && ! b.attribute( "y" ).isEmpty() )
|
||
|
{
|
||
|
double x = parseUnit( b.attribute( "x" ) );
|
||
|
double y = parseUnit( b.attribute( "y" ) );
|
||
|
base.moveTo( KoPoint( x, y ) );
|
||
|
base.lineTo( KoPoint( x + pathLength, y ) );
|
||
|
}
|
||
|
|
||
13 years ago
|
for( TQDomNode n = b.firstChild(); !n.isNull(); n = n.nextSibling() )
|
||
15 years ago
|
{
|
||
13 years ago
|
TQDomElement e = n.toElement();
|
||
15 years ago
|
if( e.isNull() )
|
||
|
{
|
||
|
content += n.toCharacterData().data();
|
||
|
}
|
||
|
else if( e.tagName() == "textPath" )
|
||
|
{
|
||
|
if( e.attribute( "xlink:href" ).isEmpty() )
|
||
|
continue;
|
||
|
|
||
13 years ago
|
TQString key = e.attribute( "xlink:href" ).mid( 1 );
|
||
13 years ago
|
if( ! m_defs.contains(key) )
|
||
15 years ago
|
{
|
||
|
// try to find referenced object in document
|
||
|
VObject* obj = findObject( key );
|
||
|
// try to find referenced object in actual group, which is not yet part of document
|
||
|
if( ! obj )
|
||
|
obj = findObject( key, grp );
|
||
|
if( obj )
|
||
|
path = dynamic_cast<VPath*>( obj );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
13 years ago
|
TQDomElement p = m_defs[key];
|
||
15 years ago
|
createObject( grp, p, VObject::deleted);
|
||
|
}
|
||
|
if( ! path )
|
||
|
continue;
|
||
|
base = *path->paths().getFirst();
|
||
|
content += e.text();
|
||
|
|
||
|
if( ! e.attribute( "startOffset" ).isEmpty() )
|
||
|
{
|
||
13 years ago
|
TQString start = e.attribute( "startOffset" );
|
||
15 years ago
|
if( start.endsWith( "%" ) )
|
||
|
offset = 0.01 * start.remove( '%' ).toDouble();
|
||
|
else
|
||
|
{
|
||
|
float pathLength = 0;
|
||
|
VSubpathIterator pIt( base );
|
||
|
|
||
|
for( ; pIt.current(); ++pIt )
|
||
|
pathLength += pIt.current()->length();
|
||
|
|
||
|
if( pathLength > 0.0 )
|
||
|
offset = start.toDouble() / pathLength;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else if( e.tagName() == "tspan" )
|
||
|
{
|
||
|
// only use text of tspan element, as we are not supporting text
|
||
|
// with different styles
|
||
|
content += e.text();
|
||
|
if( base.isEmpty() && ! e.attribute( "x" ).isEmpty() && ! e.attribute( "y" ).isEmpty() )
|
||
|
{
|
||
13 years ago
|
TQStringList posX = TQStringList::split( ", ", e.attribute( "x" ) );
|
||
|
TQStringList posY = TQStringList::split( ", ", e.attribute( "y" ) );
|
||
15 years ago
|
if( posX.count() && posY.count() )
|
||
|
{
|
||
|
double x = parseUnit( posX.first() );
|
||
|
double y = parseUnit( posY.first() );
|
||
|
base.moveTo( KoPoint( x, y ) );
|
||
|
base.lineTo( KoPoint( x + pathLength, y ) );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else if( e.tagName() == "tref" )
|
||
|
{
|
||
|
if( e.attribute( "xlink:href" ).isEmpty() )
|
||
|
continue;
|
||
|
|
||
13 years ago
|
TQString key = e.attribute( "xlink:href" ).mid( 1 );
|
||
13 years ago
|
if( ! m_defs.contains(key) )
|
||
15 years ago
|
{
|
||
|
// try to find referenced object in document
|
||
|
VObject* obj = findObject( key );
|
||
|
// try to find referenced object in actual group, which is not yet part of document
|
||
|
if( ! obj )
|
||
|
obj = findObject( key, grp );
|
||
|
if( obj )
|
||
|
content += dynamic_cast<VText*>( obj )->text();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
13 years ago
|
TQDomElement p = m_defs[key];
|
||
15 years ago
|
content += p.text();
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
continue;
|
||
|
|
||
|
if( ! e.attribute( "text-anchor" ).isEmpty() )
|
||
|
anchor = e.attribute( "text-anchor" );
|
||
|
}
|
||
|
text = new VText( m_gc.current()->font, base, VText::Above, VText::Left, content.simplifyWhiteSpace() );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
VSubpath base( 0L );
|
||
|
double x = parseUnit( b.attribute( "x" ) );
|
||
|
double y = parseUnit( b.attribute( "y" ) );
|
||
|
base.moveTo( KoPoint( x, y ) );
|
||
|
base.lineTo( KoPoint( x + pathLength, y ) );
|
||
|
text = new VText( m_gc.current()->font, base, VText::Above, VText::Left, b.text().simplifyWhiteSpace() );
|
||
|
}
|
||
|
|
||
|
if( text )
|
||
|
{
|
||
|
text->setParent( &m_document );
|
||
|
|
||
|
parseStyle( text, b );
|
||
|
|
||
|
text->setFont( m_gc.current()->font );
|
||
|
|
||
|
VTransformCmd trafo( 0L, m_gc.current()->matrix );
|
||
|
trafo.visit( *text );
|
||
|
|
||
|
if( !b.attribute("id").isEmpty() )
|
||
|
text->setName( b.attribute("id") );
|
||
|
|
||
|
if( anchor == "middle" )
|
||
13 years ago
|
text->setAlignment( VText::Center );
|
||
15 years ago
|
else if( anchor == "end" )
|
||
13 years ago
|
text->setAlignment( VText::Right );
|
||
15 years ago
|
|
||
|
if( offset > 0.0 )
|
||
|
text->setOffset( offset );
|
||
|
|
||
|
if( grp )
|
||
|
grp->append( text );
|
||
|
else
|
||
|
m_document.append( text );
|
||
|
}
|
||
|
|
||
|
delete( m_gc.pop() );
|
||
|
}
|
||
|
|
||
13 years ago
|
void SvgImport::createObject( VGroup *grp, const TQDomElement &b, const VObject::VState state, const TQDomElement &style )
|
||
15 years ago
|
{
|
||
|
VObject *obj = 0L;
|
||
|
|
||
|
addGraphicContext();
|
||
|
setupTransform( b );
|
||
|
|
||
|
if( b.tagName() == "rect" )
|
||
|
{
|
||
|
double x = parseUnit( b.attribute( "x" ), true, false, m_outerRect );
|
||
|
double y = parseUnit( b.attribute( "y" ), false, true, m_outerRect );
|
||
|
double width = parseUnit( b.attribute( "width" ), true, false, m_outerRect );
|
||
|
double height = parseUnit( b.attribute( "height" ), false, true, m_outerRect );
|
||
|
double rx = parseUnit( b.attribute( "rx" ) );
|
||
|
double ry = parseUnit( b.attribute( "ry" ) );
|
||
|
obj = new VRectangle( 0L, KoPoint( x, height + y ) , width, height, rx, ry );
|
||
|
}
|
||
|
else if( b.tagName() == "ellipse" )
|
||
|
{
|
||
|
double rx = parseUnit( b.attribute( "rx" ) );
|
||
|
double ry = parseUnit( b.attribute( "ry" ) );
|
||
|
double left = parseUnit( b.attribute( "cx" ) ) - rx;
|
||
|
double top = parseUnit( b.attribute( "cy" ) ) - ry;
|
||
|
obj = new VEllipse( 0L, KoPoint( left, top ), rx * 2.0, ry * 2.0 );
|
||
|
}
|
||
|
else if( b.tagName() == "circle" )
|
||
|
{
|
||
|
double r = parseUnit( b.attribute( "r" ) );
|
||
|
double left = parseUnit( b.attribute( "cx" ) ) - r;
|
||
|
double top = parseUnit( b.attribute( "cy" ) ) - r;
|
||
|
obj = new VEllipse( 0L, KoPoint( left, top ), r * 2.0, r * 2.0 );
|
||
|
}
|
||
|
else if( b.tagName() == "line" )
|
||
|
{
|
||
|
VPath *path = new VPath( &m_document );
|
||
|
double x1 = b.attribute( "x1" ).isEmpty() ? 0.0 : parseUnit( b.attribute( "x1" ) );
|
||
|
double y1 = b.attribute( "y1" ).isEmpty() ? 0.0 : parseUnit( b.attribute( "y1" ) );
|
||
|
double x2 = b.attribute( "x2" ).isEmpty() ? 0.0 : parseUnit( b.attribute( "x2" ) );
|
||
|
double y2 = b.attribute( "y2" ).isEmpty() ? 0.0 : parseUnit( b.attribute( "y2" ) );
|
||
|
path->moveTo( KoPoint( x1, y1 ) );
|
||
|
path->lineTo( KoPoint( x2, y2 ) );
|
||
|
obj = path;
|
||
|
}
|
||
|
else if( b.tagName() == "polyline" || b.tagName() == "polygon" )
|
||
|
{
|
||
|
VPath *path = new VPath( &m_document );
|
||
|
bool bFirst = true;
|
||
|
|
||
13 years ago
|
TQString points = b.attribute( "points" ).simplifyWhiteSpace();
|
||
13 years ago
|
points.replace( ',', ' ' );
|
||
15 years ago
|
points.remove( '\r' );
|
||
|
points.remove( '\n' );
|
||
13 years ago
|
TQStringList pointList = TQStringList::split( ' ', points );
|
||
|
for( TQStringList::Iterator it = pointList.begin(); it != pointList.end(); ++it)
|
||
15 years ago
|
{
|
||
|
KoPoint point;
|
||
|
point.setX( (*it).toDouble() );
|
||
|
++it;
|
||
|
point.setY( (*it).toDouble() );
|
||
|
if( bFirst )
|
||
|
{
|
||
|
path->moveTo( point );
|
||
|
bFirst = false;
|
||
|
}
|
||
|
else
|
||
|
path->lineTo( point );
|
||
|
}
|
||
|
if( b.tagName() == "polygon" ) path->close();
|
||
|
obj = path;
|
||
|
}
|
||
|
else if( b.tagName() == "path" )
|
||
|
{
|
||
|
VPath *path = new VPath( &m_document );
|
||
|
path->loadSvgPath( b.attribute( "d" ) );
|
||
|
obj = path;
|
||
|
}
|
||
|
else if( b.tagName() == "image" )
|
||
|
{
|
||
13 years ago
|
TQString fname = b.attribute("xlink:href");
|
||
15 years ago
|
obj = new VImage( 0L, fname );
|
||
|
}
|
||
|
|
||
|
if( !obj )
|
||
|
return;
|
||
|
|
||
|
if (state != VObject::normal)
|
||
|
obj->setState(state);
|
||
|
|
||
|
VTransformCmd trafo( 0L, m_gc.current()->matrix );
|
||
|
trafo.visit( *obj );
|
||
|
|
||
|
if( !style.isNull() )
|
||
|
parseStyle( obj, style );
|
||
|
else
|
||
|
parseStyle( obj, b );
|
||
|
|
||
|
// handle id
|
||
|
if( !b.attribute("id").isEmpty() )
|
||
|
obj->setName( b.attribute("id") );
|
||
|
if( grp )
|
||
|
grp->append( obj );
|
||
|
else
|
||
|
m_document.append( obj );
|
||
|
|
||
|
delete( m_gc.pop() );
|
||
|
}
|
||
|
|
||
|
#include <svgimport.moc>
|