00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include <qdom.h>
00021 #include <qpainter.h>
00022 #include <qwmatrix.h>
00023 #include <qregexp.h>
00024
00025 #include <KoPoint.h>
00026 #include <KoRect.h>
00027 #include <KoUnit.h>
00028 #include <KoStore.h>
00029 #include <KoXmlWriter.h>
00030 #include <KoXmlNS.h>
00031 #include <KoGenStyles.h>
00032
00033 #include "vcomposite.h"
00034 #include "vcomposite_iface.h"
00035 #include "vfill.h"
00036 #include "vpainter.h"
00037 #include "vsegment.h"
00038 #include "vstroke.h"
00039 #include "vvisitor.h"
00040 #include "vpath.h"
00041 #include "commands/vtransformcmd.h"
00042 #include "vdocument.h"
00043
00044 #include <kdebug.h>
00045
00046
00047 VPath::VPath( VObject* parent, VState state )
00048 : VObject( parent, state ), m_fillRule( winding )
00049 {
00050 m_paths.setAutoDelete( true );
00051
00052
00053 m_paths.append( new VSubpath( this ) );
00054
00055
00056 m_stroke = new VStroke( this );
00057 m_fill = new VFill();
00058
00059 m_drawCenterNode = false;
00060 }
00061
00062 VPath::VPath( const VPath& composite )
00063 : VObject( composite ), SVGPathParser()
00064 {
00065 m_paths.setAutoDelete( true );
00066
00067 VSubpath* path;
00068
00069 VSubpathListIterator itr( composite.m_paths );
00070 for( itr.toFirst(); itr.current(); ++itr )
00071 {
00072 path = itr.current()->clone();
00073 path->setParent( this );
00074 m_paths.append( path );
00075 }
00076
00077 if ( composite.stroke() )
00078 setStroke( *composite.stroke() );
00079
00080 if ( composite.fill() )
00081 setFill( *composite.fill() );
00082
00083 m_drawCenterNode = false;
00084 m_fillRule = composite.m_fillRule;
00085 m_matrix = composite.m_matrix;
00086 }
00087
00088 VPath::~VPath()
00089 {
00090 }
00091
00092 DCOPObject* VPath::dcopObject()
00093 {
00094 if ( !m_dcop )
00095 m_dcop = new VPathIface( this );
00096
00097 return m_dcop;
00098 }
00099
00100
00101 void
00102 VPath::draw( VPainter* painter, const KoRect *rect ) const
00103 {
00104 if(
00105 state() == deleted ||
00106 state() == hidden ||
00107 state() == hidden_locked )
00108 {
00109 return;
00110 }
00111
00112 if( rect && !rect->intersects( boundingBox() ) )
00113 return;
00114
00115 painter->save();
00116
00117 VSubpathListIterator itr( m_paths );
00118
00119
00120 if( state() == edit )
00121 {
00122 for( itr.toFirst(); itr.current(); ++itr )
00123 {
00124 if( !itr.current()->isEmpty() )
00125 {
00126 painter->newPath();
00127 painter->setRasterOp( Qt::XorROP );
00128 painter->setPen( Qt::yellow );
00129 painter->setBrush( Qt::NoBrush );
00130
00131 VSubpathIterator jtr( *( itr.current() ) );
00132 for( ; jtr.current(); ++jtr )
00133 {
00134 jtr.current()->draw( painter );
00135 }
00136
00137 painter->strokePath();
00138 }
00139 }
00140 }
00141 else if( state() != edit )
00142 {
00143
00144 painter->newPath();
00145 painter->setFillRule( m_fillRule );
00146
00147 for( itr.toFirst(); itr.current(); ++itr )
00148 {
00149 if( !itr.current()->isEmpty() )
00150 {
00151 VSubpathIterator jtr( *( itr.current() ) );
00152 for( ; jtr.current(); ++jtr )
00153 {
00154 jtr.current()->draw( painter );
00155 }
00156 }
00157 }
00158
00159 painter->setRasterOp( Qt::CopyROP );
00160 painter->setPen( Qt::NoPen );
00161 painter->setBrush( *fill() );
00162 painter->fillPath();
00163
00164
00165 painter->setPen( *stroke() );
00166 painter->setBrush( Qt::NoBrush );
00167 painter->strokePath();
00168 }
00169
00170 painter->restore();
00171 }
00172
00173 const KoPoint&
00174 VPath::currentPoint() const
00175 {
00176 return m_paths.getLast()->currentPoint();
00177 }
00178
00179 bool
00180 VPath::moveTo( const KoPoint& p )
00181 {
00182
00183 if( !m_paths.getLast()->isEmpty() )
00184 {
00185 VSubpath* path = new VSubpath( this );
00186 m_paths.append( path );
00187 }
00188
00189 return m_paths.getLast()->moveTo( p );
00190 }
00191
00192 bool
00193 VPath::lineTo( const KoPoint& p )
00194 {
00195 return m_paths.getLast()->lineTo( p );
00196 }
00197
00198 bool
00199 VPath::curveTo(
00200 const KoPoint& p1, const KoPoint& p2, const KoPoint& p3 )
00201 {
00202 return m_paths.getLast()->curveTo( p1, p2, p3 );
00203 }
00204
00205 bool
00206 VPath::curve1To( const KoPoint& p2, const KoPoint& p3 )
00207 {
00208 return m_paths.getLast()->curve1To( p2, p3 );
00209 }
00210
00211 bool
00212 VPath::curve2To( const KoPoint& p1, const KoPoint& p3 )
00213 {
00214 return m_paths.getLast()->curve2To( p1, p3 );
00215 }
00216
00217 bool
00218 VPath::arcTo( const KoPoint& p1, const KoPoint& p2, const double r )
00219 {
00220 return m_paths.getLast()->arcTo( p1, p2, r );
00221 }
00222
00223 void
00224 VPath::close()
00225 {
00226 m_paths.getLast()->close();
00227
00228
00229 VSubpath* path = new VSubpath( this );
00230 path->moveTo( currentPoint() );
00231 m_paths.append( path );
00232 }
00233
00234 bool
00235 VPath::isClosed() const
00236 {
00237 return m_paths.getLast()->isEmpty() || m_paths.getLast()->isClosed();
00238 }
00239
00240 void
00241 VPath::combine( const VPath& composite )
00242 {
00243 VSubpathListIterator itr( composite.m_paths );
00244 for( ; itr.current(); ++itr )
00245 {
00246 combinePath( *( itr.current() ) );
00247 }
00248 }
00249
00250 void
00251 VPath::combinePath( const VSubpath& path )
00252 {
00253 VSubpath* p = path.clone();
00254 p->setParent( this );
00255
00256
00257
00258
00259 m_paths.append( p );
00260 m_fillRule = fillMode();
00261 }
00262
00263 bool
00264 VPath::pointIsInside( const KoPoint& p ) const
00265 {
00266
00267 if( !boundingBox().contains( p ) )
00268 return false;
00269
00270
00271 VSubpathListIterator itr( m_paths );
00272
00273 for( itr.toFirst(); itr.current(); ++itr )
00274 {
00275 if( itr.current()->pointIsInside( p ) )
00276 return true;
00277 }
00278
00279 return false;
00280 }
00281
00282 bool
00283 VPath::intersects( const VSegment& segment ) const
00284 {
00285
00286 if( !boundingBox().intersects( segment.boundingBox() ) )
00287 return false;
00288
00289
00290 VSubpathListIterator itr( m_paths );
00291
00292 for( itr.toFirst(); itr.current(); ++itr )
00293 {
00294 if( itr.current()->intersects( segment ) )
00295 return true;
00296 }
00297
00298 return false;
00299 }
00300
00301
00302 VFillRule
00303 VPath::fillMode() const
00304 {
00305 return ( m_paths.count() > 1 ) ? evenOdd : winding;
00306 }
00307
00308 const KoRect&
00309 VPath::boundingBox() const
00310 {
00311 if( m_boundingBoxIsInvalid )
00312 {
00313 VSubpathListIterator itr( m_paths );
00314 itr.toFirst();
00315
00316 m_boundingBox = itr.current() ? itr.current()->boundingBox() : KoRect();
00317
00318 for( ++itr; itr.current(); ++itr )
00319 m_boundingBox |= itr.current()->boundingBox();
00320
00321 if( !m_boundingBox.isNull() )
00322 {
00323
00324 m_boundingBox.setCoords(
00325 m_boundingBox.left() - 0.5 * stroke()->lineWidth(),
00326 m_boundingBox.top() - 0.5 * stroke()->lineWidth(),
00327 m_boundingBox.right() + 0.5 * stroke()->lineWidth(),
00328 m_boundingBox.bottom() + 0.5 * stroke()->lineWidth() );
00329 }
00330 m_boundingBoxIsInvalid = false;
00331 }
00332
00333 return m_boundingBox;
00334 }
00335
00336 VPath*
00337 VPath::clone() const
00338 {
00339 return new VPath( *this );
00340 }
00341
00342 void
00343 VPath::save( QDomElement& element ) const
00344 {
00345 if( state() != deleted )
00346 {
00347 QDomElement me = element.ownerDocument().createElement( "PATH" );
00348 element.appendChild( me );
00349
00350 VObject::save( me );
00351
00352 QString d;
00353 saveSvgPath( d );
00354 me.setAttribute( "d", d );
00355
00356
00357
00358
00359 if( !( m_fillRule == evenOdd ) )
00360 me.setAttribute( "fillRule", m_fillRule );
00361 }
00362 }
00363
00364 void
00365 VPath::saveOasis( KoStore *store, KoXmlWriter *docWriter, KoGenStyles &mainStyles, int &index ) const
00366 {
00367 if( state() != deleted )
00368 {
00369 docWriter->startElement( "draw:path" );
00370
00371 QString d;
00372 saveSvgPath( d );
00373 docWriter->addAttribute( "svg:d", d );
00374
00375 double x = boundingBox().x();
00376 double y = boundingBox().y();
00377 double w = boundingBox().width();
00378 double h = boundingBox().height();
00379
00380 docWriter->addAttribute( "svg:viewBox", QString( "%1 %2 %3 %4" ).arg( x ).arg( y ).arg( w ).arg( h ) );
00381 docWriter->addAttributePt( "svg:x", x );
00382 docWriter->addAttributePt( "svg:y", y );
00383 docWriter->addAttributePt( "svg:width", w );
00384 docWriter->addAttributePt( "svg:height", h );
00385
00386 VObject::saveOasis( store, docWriter, mainStyles, index );
00387
00388 QWMatrix tmpMat;
00389 tmpMat.scale( 1, -1 );
00390 tmpMat.translate( 0, -document()->height() );
00391
00392 QString transform = buildOasisTransform( tmpMat );
00393 if( !transform.isEmpty() )
00394 docWriter->addAttribute( "draw:transform", transform );
00395
00396 docWriter->endElement();
00397 }
00398 }
00399
00400 void
00401 VPath::saveOasisFill( KoGenStyles &mainStyles, KoGenStyle &stylesobjectauto ) const
00402 {
00403 if( m_fill )
00404 {
00405 QWMatrix mat;
00406 mat.scale( 1, -1 );
00407 mat.translate( 0, -document()->height() );
00408
00409
00410 VFill fill( *m_fill );
00411 fill.transform( mat );
00412 fill.saveOasis( mainStyles, stylesobjectauto );
00413
00414 if( !( m_fillRule == evenOdd ) )
00415 stylesobjectauto.addProperty( "svg:fill-rule", "winding" );
00416 }
00417 }
00418
00419 void
00420 VPath::transformByViewbox( const QDomElement &element, QString viewbox )
00421 {
00422 if( ! viewbox.isEmpty() )
00423 {
00424
00425 QStringList points = QStringList::split( ' ', viewbox.replace( ',', ' ' ).simplifyWhiteSpace() );
00426
00427 double w = KoUnit::parseValue( element.attributeNS( KoXmlNS::svg, "width", QString::null ) );
00428 double h = KoUnit::parseValue( element.attributeNS( KoXmlNS::svg, "height", QString::null ) );
00429 double x = KoUnit::parseValue( element.attributeNS( KoXmlNS::svg, "x", QString::null ) );
00430 double y = KoUnit::parseValue( element.attributeNS( KoXmlNS::svg, "y", QString::null ) );
00431
00432 QWMatrix mat;
00433 mat.translate( x-KoUnit::parseValue( points[0] ), y-KoUnit::parseValue( points[1] ) );
00434 mat.scale( w / KoUnit::parseValue( points[2] ) , h / KoUnit::parseValue( points[3] ) );
00435 VTransformCmd cmd( 0L, mat );
00436 cmd.visitVPath( *this );
00437 }
00438 }
00439
00440 bool
00441 VPath::loadOasis( const QDomElement &element, KoOasisLoadingContext &context )
00442 {
00443 setState( normal );
00444
00445 QString viewbox;
00446
00447 if( element.localName() == "path" )
00448 {
00449 QString data = element.attributeNS( KoXmlNS::svg, "d", QString::null );
00450 if( data.length() > 0 )
00451 {
00452 loadSvgPath( data );
00453 }
00454
00455 m_fillRule = element.attributeNS( KoXmlNS::svg, "fill-rule", QString::null ) == "winding" ? winding : evenOdd;
00456
00457 viewbox = element.attributeNS( KoXmlNS::svg, "viewBox", QString::null );
00458 }
00459 else if( element.localName() == "custom-shape" )
00460 {
00461 QDomNodeList list = element.childNodes();
00462 for( uint i = 0; i < list.count(); ++i )
00463 {
00464 if( list.item( i ).isElement() )
00465 {
00466 QDomElement e = list.item( i ).toElement();
00467 if( e.namespaceURI() != KoXmlNS::draw )
00468 continue;
00469
00470 if( e.localName() == "enhanced-geometry" )
00471 {
00472 QString data = e.attributeNS( KoXmlNS::draw, "enhanced-path", QString::null );
00473 if( ! data.isEmpty() )
00474 loadSvgPath( data );
00475
00476 viewbox = e.attributeNS( KoXmlNS::svg, "viewBox", QString::null );
00477 }
00478 }
00479 }
00480 }
00481
00482 transformByViewbox( element, viewbox );
00483
00484 QString trafo = element.attributeNS( KoXmlNS::draw, "transform", QString::null );
00485 if( !trafo.isEmpty() )
00486 transformOasis( trafo );
00487
00488 return VObject::loadOasis( element, context );
00489 }
00490
00491 void
00492 VPath::load( const QDomElement& element )
00493 {
00494 setState( normal );
00495
00496 VObject::load( element );
00497
00498 QString data = element.attribute( "d" );
00499 if( data.length() > 0 )
00500 {
00501 loadSvgPath( data );
00502 }
00503 m_fillRule = element.attribute( "fillRule" ) == 0 ? evenOdd : winding;
00504 QDomNodeList list = element.childNodes();
00505 for( uint i = 0; i < list.count(); ++i )
00506 {
00507 if( list.item( i ).isElement() )
00508 {
00509 QDomElement child = list.item( i ).toElement();
00510
00511 if( child.tagName() == "PATH" )
00512 {
00513 VSubpath path( this );
00514 path.load( child );
00515
00516 combinePath( path );
00517 }
00518 else
00519 {
00520 VObject::load( child );
00521 }
00522 }
00523 }
00524
00525 QString trafo = element.attribute( "transform" );
00526 if( !trafo.isEmpty() )
00527 transform( trafo );
00528 }
00529
00530 void
00531 VPath::loadSvgPath( const QString &d )
00532 {
00533
00534 parseSVG( d, true );
00535
00536 }
00537
00538 void
00539 VPath::saveSvgPath( QString &d ) const
00540 {
00541
00542 VSubpathListIterator itr( m_paths );
00543 for( itr.toFirst(); itr.current(); ++itr )
00544 {
00545 if( !itr.current()->isEmpty() )
00546 itr.current()->saveSvgPath( d );
00547 }
00548 }
00549
00550 void
00551 VPath::svgMoveTo( double x1, double y1, bool )
00552 {
00553 moveTo( KoPoint( x1, y1 ) );
00554 }
00555
00556 void
00557 VPath::svgLineTo( double x1, double y1, bool )
00558 {
00559 lineTo( KoPoint( x1, y1 ) );
00560 }
00561
00562 void
00563 VPath::svgCurveToCubic( double x1, double y1, double x2, double y2, double x, double y, bool )
00564 {
00565 curveTo( KoPoint( x1, y1 ), KoPoint( x2, y2 ), KoPoint( x, y ) );
00566 }
00567
00568 void
00569 VPath::svgClosePath()
00570 {
00571 close();
00572 }
00573
00574 void
00575 VPath::accept( VVisitor& visitor )
00576 {
00577 visitor.visitVPath( *this );
00578 }
00579
00580 void
00581 VPath::transform( const QString &transform )
00582 {
00583 VTransformCmd cmd( 0L, parseTransform( transform ) );
00584 cmd.visitVPath( *this );
00585 }
00586
00587 void
00588 VPath::transformOasis( const QString &transform )
00589 {
00590 VTransformCmd cmd( 0L, parseOasisTransform( transform ) );
00591 cmd.visitVPath( *this );
00592 }
00593
00594 QWMatrix
00595 VPath::parseTransform( const QString &transform )
00596 {
00597 QWMatrix result;
00598
00599
00600 QStringList subtransforms = QStringList::split(')', transform);
00601 QStringList::ConstIterator it = subtransforms.begin();
00602 QStringList::ConstIterator end = subtransforms.end();
00603 for(; it != end; ++it)
00604 {
00605 QStringList subtransform = QStringList::split('(', (*it));
00606
00607 subtransform[0] = subtransform[0].stripWhiteSpace().lower();
00608 subtransform[1] = subtransform[1].simplifyWhiteSpace();
00609 QRegExp reg("[,( ]");
00610 QStringList params = QStringList::split(reg, subtransform[1]);
00611
00612 if(subtransform[0].startsWith(";") || subtransform[0].startsWith(","))
00613 subtransform[0] = subtransform[0].right(subtransform[0].length() - 1);
00614
00615 if(subtransform[0] == "rotate")
00616 {
00617 if(params.count() == 3)
00618 {
00619 double x = params[1].toDouble();
00620 double y = params[2].toDouble();
00621
00622 result.translate(x, y);
00623 result.rotate(params[0].toDouble());
00624 result.translate(-x, -y);
00625 }
00626 else
00627 result.rotate(params[0].toDouble());
00628 }
00629 else if(subtransform[0] == "translate")
00630 {
00631 if(params.count() == 2)
00632 result.translate(params[0].toDouble(), params[1].toDouble());
00633 else
00634 result.translate(params[0].toDouble() , 0);
00635 }
00636 else if(subtransform[0] == "scale")
00637 {
00638 if(params.count() == 2)
00639 result.scale(params[0].toDouble(), params[1].toDouble());
00640 else
00641 result.scale(params[0].toDouble(), params[0].toDouble());
00642 }
00643 else if(subtransform[0] == "skewx")
00644 result.shear(tan(params[0].toDouble() * VGlobal::pi_180), 0.0F);
00645 else if(subtransform[0] == "skewy")
00646 result.shear(tan(params[0].toDouble() * VGlobal::pi_180), 0.0F);
00647 else if(subtransform[0] == "skewy")
00648 result.shear(0.0F, tan(params[0].toDouble() * VGlobal::pi_180));
00649 else if(subtransform[0] == "matrix")
00650 {
00651 if(params.count() >= 6)
00652 result.setMatrix(params[0].toDouble(), params[1].toDouble(), params[2].toDouble(), params[3].toDouble(), params[4].toDouble(), params[5].toDouble());
00653 }
00654 }
00655
00656 return result;
00657 }
00658
00659 QWMatrix
00660 VPath::parseOasisTransform( const QString &transform )
00661 {
00662 QWMatrix result;
00663
00664
00665 QStringList subtransforms = QStringList::split(')', transform);
00666 QStringList::ConstIterator it = subtransforms.begin();
00667 QStringList::ConstIterator end = subtransforms.end();
00668 for(; it != end; ++it)
00669 {
00670 QStringList subtransform = QStringList::split('(', (*it));
00671
00672 subtransform[0] = subtransform[0].stripWhiteSpace().lower();
00673 subtransform[1] = subtransform[1].simplifyWhiteSpace();
00674 QRegExp reg("[,( ]");
00675 QStringList params = QStringList::split(reg, subtransform[1]);
00676
00677 if(subtransform[0].startsWith(";") || subtransform[0].startsWith(","))
00678 subtransform[0] = subtransform[0].right(subtransform[0].length() - 1);
00679
00680 if(subtransform[0] == "rotate")
00681 {
00682
00683 if(params.count() == 3)
00684 {
00685 double x = KoUnit::parseValue( params[1] );
00686 double y = KoUnit::parseValue( params[2] );
00687
00688 result.translate(x, y);
00689
00690 result.rotate( params[0].toDouble()*VGlobal::one_pi_180 );
00691 result.translate(-x, -y);
00692 }
00693 else
00694 {
00695
00696 result.rotate( params[0].toDouble()*VGlobal::one_pi_180 );
00697 }
00698 }
00699 else if(subtransform[0] == "translate")
00700 {
00701 if(params.count() == 2)
00702 {
00703 double x = KoUnit::parseValue( params[0] );
00704 double y = KoUnit::parseValue( params[1] );
00705 result.translate(x, y);
00706 }
00707 else
00708 result.translate( KoUnit::parseValue( params[0] ) , 0);
00709 }
00710 else if(subtransform[0] == "scale")
00711 {
00712 if(params.count() == 2)
00713 result.scale(params[0].toDouble(), params[1].toDouble());
00714 else
00715 result.scale(params[0].toDouble(), params[0].toDouble());
00716 }
00717 else if(subtransform[0] == "skewx")
00718 result.shear(tan(params[0].toDouble()), 0.0F);
00719 else if(subtransform[0] == "skewy")
00720 result.shear(tan(params[0].toDouble()), 0.0F);
00721 else if(subtransform[0] == "skewy")
00722 result.shear(0.0F, tan(params[0].toDouble()));
00723 else if(subtransform[0] == "matrix")
00724 {
00725 if(params.count() >= 6)
00726 result.setMatrix(params[0].toDouble(), params[1].toDouble(), params[2].toDouble(), params[3].toDouble(), KoUnit::parseValue( params[4] ), KoUnit::parseValue( params[5] ) );
00727 }
00728 }
00729
00730 return result;
00731 }
00732
00733 QString
00734 VPath::buildSvgTransform() const
00735 {
00736 return buildSvgTransform( m_matrix );
00737 }
00738
00739 QString
00740 VPath::buildSvgTransform( const QWMatrix &mat ) const
00741 {
00742 QString transform;
00743 if( !mat.isIdentity() )
00744 {
00745 transform = QString( "matrix(%1, %2, %3, %4, %5, %6)" ).arg( mat.m11() )
00746 .arg( mat.m12() )
00747 .arg( mat.m21() )
00748 .arg( mat.m22() )
00749 .arg( mat.dx() )
00750 .arg( mat.dy() );
00751 }
00752 return transform;
00753 }
00754
00755 QString
00756 VPath::buildOasisTransform() const
00757 {
00758 return buildSvgTransform( m_matrix );
00759 }
00760
00761 QString
00762 VPath::buildOasisTransform( const QWMatrix &mat ) const
00763 {
00764 QString transform;
00765 if( !mat.isIdentity() )
00766 {
00767 transform = QString( "matrix(%1, %2, %3, %4, %5pt, %6pt)" ).arg( mat.m11() )
00768 .arg( mat.m12() )
00769 .arg( mat.m21() )
00770 .arg( mat.m22() )
00771 .arg( mat.dx() )
00772 .arg( mat.dy() );
00773 }
00774 return transform;
00775 }