Warning, file /office/calligra/libs/flake/KoPathShape.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /* This file is part of the KDE project
0002    Copyright (C) 2006-2008, 2010-2011 Thorsten Zachmann <zachmann@kde.org>
0003    Copyright (C) 2006-2011 Jan Hambrecht <jaham@gmx.net>
0004    Copyright (C) 2007-2009 Thomas Zander <zander@kde.org>
0005    Copyright (C) 2011 Jean-Nicolas Artaud <jeannicolasartaud@gmail.com>
0006 
0007    This library is free software; you can redistribute it and/or
0008    modify it under the terms of the GNU Library General Public
0009    License as published by the Free Software Foundation; either
0010    version 2 of the License, or (at your option) any later version.
0011 
0012    This library is distributed in the hope that it will be useful,
0013    but WITHOUT ANY WARRANTY without even the implied warranty of
0014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0015    Library General Public License for more details.
0016 
0017    You should have received a copy of the GNU Library General Public License
0018    along with this library; see the file COPYING.LIB.  If not, write to
0019    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0020  * Boston, MA 02110-1301, USA.
0021 */
0022 
0023 #include "KoPathShape.h"
0024 #include "KoPathShape_p.h"
0025 
0026 #include "KoPathSegment.h"
0027 #include "KoOdfWorkaround.h"
0028 #include "KoPathPoint.h"
0029 #include "KoShapeStrokeModel.h"
0030 #include "KoViewConverter.h"
0031 #include "KoPathShapeLoader.h"
0032 #include "KoShapeSavingContext.h"
0033 #include "KoShapeLoadingContext.h"
0034 #include "KoShapeShadow.h"
0035 #include "KoShapeBackground.h"
0036 #include "KoShapeContainer.h"
0037 #include "KoFilterEffectStack.h"
0038 #include "KoMarker.h"
0039 #include "KoMarkerSharedLoadingData.h"
0040 #include "KoShapeStroke.h"
0041 #include "KoInsets.h"
0042 
0043 #include <KoXmlReader.h>
0044 #include <KoXmlWriter.h>
0045 #include <KoXmlNS.h>
0046 #include <KoUnit.h>
0047 #include <KoGenStyle.h>
0048 #include <KoStyleStack.h>
0049 #include <KoOdfLoadingContext.h>
0050 
0051 #include <FlakeDebug.h>
0052 #include <QPainter>
0053 #include <QPainterPath>
0054 
0055 #include <qnumeric.h> // for qIsNaN
0056 static bool qIsNaNPoint(const QPointF &p) {
0057     return qIsNaN(p.x()) || qIsNaN(p.y());
0058 }
0059 static const qreal DefaultMarkerWidth = 3.0;
0060 
0061 KoPathShapePrivate::KoPathShapePrivate(KoPathShape *q)
0062     : KoTosContainerPrivate(q),
0063     fillRule(Qt::OddEvenFill),
0064     startMarker(KoMarkerData::MarkerStart),
0065     endMarker(KoMarkerData::MarkerEnd)
0066 {
0067 }
0068 
0069 QRectF KoPathShapePrivate::handleRect(const QPointF &p, qreal radius) const
0070 {
0071     return QRectF(p.x() - radius, p.y() - radius, 2*radius, 2*radius);
0072 }
0073 
0074 void KoPathShapePrivate::applyViewboxTransformation(const KoXmlElement &element)
0075 {
0076     // apply viewbox transformation
0077     const QRect viewBox = KoPathShape::loadOdfViewbox(element);
0078     if (! viewBox.isEmpty()) {
0079         // load the desired size
0080         QSizeF size;
0081         size.setWidth(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "width", QString())));
0082         size.setHeight(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "height", QString())));
0083 
0084         // load the desired position
0085         QPointF pos;
0086         pos.setX(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "x", QString())));
0087         pos.setY(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "y", QString())));
0088 
0089         // create matrix to transform original path data into desired size and position
0090         QTransform viewMatrix;
0091         viewMatrix.translate(-viewBox.left(), -viewBox.top());
0092         viewMatrix.scale(size.width() / viewBox.width(), size.height() / viewBox.height());
0093         viewMatrix.translate(pos.x(), pos.y());
0094 
0095         // transform the path data
0096         map(viewMatrix);
0097     }
0098 }
0099 
0100 KoPathShape::KoPathShape()
0101     :KoTosContainer(*(new KoPathShapePrivate(this)))
0102 {
0103 }
0104 
0105 KoPathShape::KoPathShape(KoPathShapePrivate &dd)
0106     : KoTosContainer(dd)
0107 {
0108 }
0109 
0110 KoPathShape::~KoPathShape()
0111 {
0112     clear();
0113 }
0114 
0115 void KoPathShape::saveContourOdf(KoShapeSavingContext &context, const QSizeF &scaleFactor) const
0116 {
0117     Q_D(const KoPathShape);
0118 
0119     if (m_subpaths.length() <= 1) {
0120         QTransform matrix;
0121         matrix.scale(scaleFactor.width(), scaleFactor.height());
0122         QString points;
0123         KoSubpath *subPath = m_subpaths.first();
0124         KoSubpath::const_iterator pointIt(subPath->constBegin());
0125 
0126         KoPathPoint *currPoint= 0;
0127         // iterate over all points
0128         for (; pointIt != subPath->constEnd(); ++pointIt) {
0129             currPoint = *pointIt;
0130 
0131             if (currPoint->activeControlPoint1() || currPoint->activeControlPoint2()) {
0132                 break;
0133             }
0134             const QPointF p = matrix.map(currPoint->point());
0135             points += QString("%1,%2 ").arg(qRound(1000*p.x())).arg(qRound(1000*p.y()));
0136         }
0137 
0138         if (currPoint && !(currPoint->activeControlPoint1() || currPoint->activeControlPoint2())) {
0139             context.xmlWriter().startElement("draw:contour-polygon");
0140             context.xmlWriter().addAttributePt("svg:width", size().width());
0141             context.xmlWriter().addAttributePt("svg:height", size().height());
0142 
0143             const QSizeF s(size());
0144             QString viewBox = QString("0 0 %1 %2").arg(qRound(1000*s.width())).arg(qRound(1000*s.height()));
0145             context.xmlWriter().addAttribute("svg:viewBox", viewBox);
0146 
0147             context.xmlWriter().addAttribute("draw:points", points);
0148 
0149             context.xmlWriter().addAttribute("draw:recreate-on-edit", "true");
0150             context.xmlWriter().endElement();
0151 
0152             return;
0153         }
0154     }
0155 
0156     // if we get here we couldn't save as polygon - let-s try contour-path
0157     context.xmlWriter().startElement("draw:contour-path");
0158     saveOdfAttributes(context, OdfViewbox);
0159 
0160     context.xmlWriter().addAttribute("svg:d", toString());
0161     context.xmlWriter().addAttribute("calligra:nodeTypes", d->nodeTypes());
0162     context.xmlWriter().addAttribute("draw:recreate-on-edit", "true");
0163     context.xmlWriter().endElement();
0164 }
0165 
0166 void KoPathShape::saveOdf(KoShapeSavingContext & context) const
0167 {
0168     Q_D(const KoPathShape);
0169     context.xmlWriter().startElement("draw:path");
0170     saveOdfAttributes(context, OdfAllAttributes | OdfViewbox);
0171 
0172     context.xmlWriter().addAttribute("svg:d", toString());
0173     context.xmlWriter().addAttribute("calligra:nodeTypes", d->nodeTypes());
0174 
0175     saveOdfCommonChildElements(context);
0176     saveText(context);
0177     context.xmlWriter().endElement();
0178 }
0179 
0180 bool KoPathShape::loadContourOdf(const KoXmlElement &element, KoShapeLoadingContext &, const QSizeF &scaleFactor)
0181 {
0182     Q_D(KoPathShape);
0183 
0184     // first clear the path data from the default path
0185     clear();
0186 
0187     if (element.localName() == "contour-polygon") {
0188         QString points = element.attributeNS(KoXmlNS::draw, "points").simplified();
0189         points.replace(',', ' ');
0190         points.remove('\r');
0191         points.remove('\n');
0192         bool firstPoint = true;
0193         const QStringList coordinateList = points.split(' ');
0194         for (QStringList::ConstIterator it = coordinateList.constBegin(); it != coordinateList.constEnd(); ++it) {
0195             QPointF point;
0196             point.setX((*it).toDouble());
0197             ++it;
0198             point.setY((*it).toDouble());
0199             if (firstPoint) {
0200                 moveTo(point);
0201                 firstPoint = false;
0202             } else
0203                 lineTo(point);
0204         }
0205         close();
0206     } else if (element.localName() == "contour-path") {
0207         KoPathShapeLoader loader(this);
0208         loader.parseSvg(element.attributeNS(KoXmlNS::svg, "d"), true);
0209         d->loadNodeTypes(element);
0210     }
0211 
0212     // apply viewbox transformation
0213     const QRect viewBox = KoPathShape::loadOdfViewbox(element);
0214     if (! viewBox.isEmpty()) {
0215         QSizeF size;
0216         size.setWidth(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "width", QString())));
0217         size.setHeight(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "height", QString())));
0218 
0219         // create matrix to transform original path data into desired size and position
0220         QTransform viewMatrix;
0221         viewMatrix.translate(-viewBox.left(), -viewBox.top());
0222         viewMatrix.scale(scaleFactor.width(), scaleFactor.height());
0223         viewMatrix.scale(size.width() / viewBox.width(), size.height() / viewBox.height());
0224 
0225         // transform the path data
0226         d->map(viewMatrix);
0227     }
0228     setTransformation(QTransform());
0229 
0230     return true;
0231 }
0232 
0233 bool KoPathShape::loadOdf(const KoXmlElement & element, KoShapeLoadingContext &context)
0234 {
0235     Q_D(KoPathShape);
0236     loadOdfAttributes(element, context, OdfMandatories | OdfAdditionalAttributes | OdfCommonChildElements);
0237 
0238     // first clear the path data from the default path
0239     clear();
0240 
0241     if (element.localName() == "line") {
0242         QPointF start;
0243         start.setX(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "x1", "")));
0244         start.setY(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "y1", "")));
0245         QPointF end;
0246         end.setX(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "x2", "")));
0247         end.setY(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "y2", "")));
0248         moveTo(start);
0249         lineTo(end);
0250     } else if (element.localName() == "polyline" || element.localName() == "polygon") {
0251         QString points = element.attributeNS(KoXmlNS::draw, "points").simplified();
0252         points.replace(',', ' ');
0253         points.remove('\r');
0254         points.remove('\n');
0255         bool firstPoint = true;
0256         const QStringList coordinateList = points.split(' ');
0257         for (QStringList::ConstIterator it = coordinateList.constBegin(); it != coordinateList.constEnd(); ++it) {
0258             QPointF point;
0259             point.setX((*it).toDouble());
0260             ++it;
0261             point.setY((*it).toDouble());
0262             if (firstPoint) {
0263                 moveTo(point);
0264                 firstPoint = false;
0265             } else
0266                 lineTo(point);
0267         }
0268         if (element.localName() == "polygon")
0269             close();
0270     } else { // path loading
0271         KoPathShapeLoader loader(this);
0272         loader.parseSvg(element.attributeNS(KoXmlNS::svg, "d"), true);
0273         d->loadNodeTypes(element);
0274     }
0275 
0276     d->applyViewboxTransformation(element);
0277     QPointF pos = normalize();
0278     setTransformation(QTransform());
0279 
0280     if (element.hasAttributeNS(KoXmlNS::svg, "x") || element.hasAttributeNS(KoXmlNS::svg, "y")) {
0281         pos.setX(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "x", QString())));
0282         pos.setY(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "y", QString())));
0283     }
0284 
0285     setPosition(pos);
0286 
0287     loadOdfAttributes(element, context, OdfTransformation);
0288 
0289     // now that the correct transformation is set up
0290     // apply that matrix to the path geometry so that
0291     // we don't transform the stroke
0292     d->map(transformation());
0293     setTransformation(QTransform());
0294     normalize();
0295 
0296     loadText(element, context);
0297 
0298     return true;
0299 }
0300 
0301 QString KoPathShape::saveStyle(KoGenStyle &style, KoShapeSavingContext &context) const
0302 {
0303     Q_D(const KoPathShape);
0304 
0305     style.addProperty("svg:fill-rule", d->fillRule == Qt::OddEvenFill ? "evenodd" : "nonzero");
0306 
0307     KoShapeStroke *lineBorder = dynamic_cast<KoShapeStroke*>(stroke());
0308     qreal lineWidth = 0;
0309     if (lineBorder) {
0310         lineWidth = lineBorder->lineWidth();
0311     }
0312     d->startMarker.saveStyle(style, lineWidth, context);
0313     d->endMarker.saveStyle(style, lineWidth, context);
0314 
0315     return KoTosContainer::saveStyle(style, context);
0316 }
0317 
0318 void KoPathShape::loadStyle(const KoXmlElement & element, KoShapeLoadingContext &context)
0319 {
0320     Q_D(KoPathShape);
0321     KoTosContainer::loadStyle(element, context);
0322 
0323     KoStyleStack &styleStack = context.odfLoadingContext().styleStack();
0324     styleStack.setTypeProperties("graphic");
0325 
0326     if (styleStack.hasProperty(KoXmlNS::svg, "fill-rule")) {
0327         QString rule = styleStack.property(KoXmlNS::svg, "fill-rule");
0328         d->fillRule = (rule == "nonzero") ?  Qt::WindingFill : Qt::OddEvenFill;
0329     } else {
0330         d->fillRule = Qt::WindingFill;
0331 #ifndef NWORKAROUND_ODF_BUGS
0332         KoOdfWorkaround::fixMissingFillRule(d->fillRule, context);
0333 #endif
0334     }
0335 
0336     KoShapeStroke *lineBorder = dynamic_cast<KoShapeStroke*>(stroke());
0337     qreal lineWidth = 0;
0338     if (lineBorder) {
0339         lineWidth = lineBorder->lineWidth();
0340     }
0341 
0342     d->startMarker.loadOdf(lineWidth, context);
0343     d->endMarker.loadOdf(lineWidth, context);
0344 }
0345 
0346 QRect KoPathShape::loadOdfViewbox(const KoXmlElement & element)
0347 {
0348     QRect viewbox;
0349 
0350     QString data = element.attributeNS(KoXmlNS::svg, QLatin1String("viewBox"));
0351     if (! data.isEmpty()) {
0352         data.replace(QLatin1Char(','), QLatin1Char(' '));
0353         const QStringList coordinates = data.simplified().split(QLatin1Char(' '), QString::SkipEmptyParts);
0354         if (coordinates.count() == 4) {
0355             viewbox.setRect(coordinates.at(0).toInt(), coordinates.at(1).toInt(),
0356                             coordinates.at(2).toInt(), coordinates.at(3).toInt());
0357         }
0358     }
0359 
0360     return viewbox;
0361 }
0362 
0363 void KoPathShape::clear()
0364 {
0365     foreach(KoSubpath *subpath, m_subpaths) {
0366         foreach(KoPathPoint *point, *subpath)
0367             delete point;
0368         delete subpath;
0369     }
0370     m_subpaths.clear();
0371 }
0372 
0373 void KoPathShape::paint(QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &paintContext)
0374 {
0375     Q_D(KoPathShape);
0376     applyConversion(painter, converter);
0377     QPainterPath path(outline());
0378     path.setFillRule(d->fillRule);
0379 
0380     if (background()) {
0381         background()->paint(painter, converter, paintContext, path);
0382     }
0383     //d->paintDebug(painter);
0384 }
0385 
0386 
0387 #ifndef NDEBUG
0388 void KoPathShapePrivate::paintDebug(QPainter &painter)
0389 {
0390     Q_Q(KoPathShape);
0391     KoSubpathList::const_iterator pathIt(q->m_subpaths.constBegin());
0392     int i = 0;
0393 
0394     QPen pen(Qt::black, 0);
0395     painter.save();
0396     painter.setPen(pen);
0397     for (; pathIt != q->m_subpaths.constEnd(); ++pathIt) {
0398         KoSubpath::const_iterator it((*pathIt)->constBegin());
0399         for (; it != (*pathIt)->constEnd(); ++it) {
0400             ++i;
0401             KoPathPoint *point = (*it);
0402             QRectF r(point->point(), QSizeF(5, 5));
0403             r.translate(-2.5, -2.5);
0404             QPen pen(Qt::black, 0);
0405             painter.setPen(pen);
0406             if (point->activeControlPoint1() && point->activeControlPoint2()) {
0407                 QBrush b(Qt::red);
0408                 painter.setBrush(b);
0409             } else if (point->activeControlPoint1()) {
0410                 QBrush b(Qt::yellow);
0411                 painter.setBrush(b);
0412             } else if (point->activeControlPoint2()) {
0413                 QBrush b(Qt::darkYellow);
0414                 painter.setBrush(b);
0415             }
0416             painter.drawEllipse(r);
0417         }
0418     }
0419     painter.restore();
0420     debugFlake << "nop =" << i;
0421 }
0422 
0423 void KoPathShapePrivate::debugPath() const
0424 {
0425     Q_Q(const KoPathShape);
0426     KoSubpathList::const_iterator pathIt(q->m_subpaths.constBegin());
0427     for (; pathIt != q->m_subpaths.constEnd(); ++pathIt) {
0428         KoSubpath::const_iterator it((*pathIt)->constBegin());
0429         for (; it != (*pathIt)->constEnd(); ++it) {
0430             debugFlake << "p:" << (*pathIt) << "," << *it << "," << (*it)->point() << "," << (*it)->properties();
0431         }
0432     }
0433 }
0434 #endif
0435 
0436 void KoPathShape::paintPoints(QPainter &painter, const KoViewConverter &converter, int handleRadius)
0437 {
0438     applyConversion(painter, converter);
0439 
0440     KoSubpathList::const_iterator pathIt(m_subpaths.constBegin());
0441 
0442     for (; pathIt != m_subpaths.constEnd(); ++pathIt) {
0443         KoSubpath::const_iterator it((*pathIt)->constBegin());
0444         for (; it != (*pathIt)->constEnd(); ++it)
0445             (*it)->paint(painter, handleRadius, KoPathPoint::Node);
0446     }
0447 }
0448 
0449 QPainterPath KoPathShape::outline() const
0450 {
0451     QPainterPath path;
0452     foreach(KoSubpath * subpath, m_subpaths) {
0453         KoPathPoint * lastPoint = subpath->first();
0454         bool activeCP = false;
0455         foreach(KoPathPoint * currPoint, *subpath) {
0456             KoPathPoint::PointProperties currProperties = currPoint->properties();
0457             if (currPoint == subpath->first()) {
0458                 if (currProperties & KoPathPoint::StartSubpath) {
0459                     Q_ASSERT(!qIsNaNPoint(currPoint->point()));
0460                     path.moveTo(currPoint->point());
0461                 }
0462             } else if (activeCP && currPoint->activeControlPoint1()) {
0463                 Q_ASSERT(!qIsNaNPoint(lastPoint->controlPoint2()));
0464                 Q_ASSERT(!qIsNaNPoint(currPoint->controlPoint1()));
0465                 Q_ASSERT(!qIsNaNPoint(currPoint->point()));
0466                 path.cubicTo(
0467                     lastPoint->controlPoint2(),
0468                     currPoint->controlPoint1(),
0469                     currPoint->point());
0470             } else if (activeCP || currPoint->activeControlPoint1()) {
0471                 Q_ASSERT(!qIsNaNPoint(lastPoint->controlPoint2()));
0472                 Q_ASSERT(!qIsNaNPoint(currPoint->controlPoint1()));
0473                 path.quadTo(
0474                     activeCP ? lastPoint->controlPoint2() : currPoint->controlPoint1(),
0475                     currPoint->point());
0476             } else {
0477                 Q_ASSERT(!qIsNaNPoint(currPoint->point()));
0478                 path.lineTo(currPoint->point());
0479             }
0480             if (currProperties & KoPathPoint::CloseSubpath && currProperties & KoPathPoint::StopSubpath) {
0481                 // add curve when there is a curve on the way to the first point
0482                 KoPathPoint * firstPoint = subpath->first();
0483                 Q_ASSERT(!qIsNaNPoint(firstPoint->point()));
0484                 if (currPoint->activeControlPoint2() && firstPoint->activeControlPoint1()) {
0485                     path.cubicTo(
0486                         currPoint->controlPoint2(),
0487                         firstPoint->controlPoint1(),
0488                         firstPoint->point());
0489                 }
0490                 else if (currPoint->activeControlPoint2() || firstPoint->activeControlPoint1()) {
0491                     Q_ASSERT(!qIsNaNPoint(currPoint->point()));
0492                     Q_ASSERT(!qIsNaNPoint(currPoint->controlPoint1()));
0493                     path.quadTo(
0494                         currPoint->activeControlPoint2() ? currPoint->controlPoint2() : firstPoint->controlPoint1(),
0495                         firstPoint->point());
0496                 }
0497                 path.closeSubpath();
0498             }
0499 
0500             if (currPoint->activeControlPoint2()) {
0501                 activeCP = true;
0502             } else {
0503                 activeCP = false;
0504             }
0505             lastPoint = currPoint;
0506         }
0507     }
0508 
0509     return path;
0510 }
0511 
0512 QRectF KoPathShape::boundingRect() const
0513 {
0514     QTransform transform = absoluteTransformation(0);
0515     // calculate the bounding rect of the transformed outline
0516     QRectF bb;
0517     KoShapeStroke *lineBorder = dynamic_cast<KoShapeStroke*>(stroke());
0518     QPen pen;
0519     if (lineBorder) {
0520         pen.setWidthF(lineBorder->lineWidth());
0521     }
0522     bb = transform.map(pathStroke(pen)).boundingRect();
0523 
0524     if (stroke()) {
0525         KoInsets inset;
0526         stroke()->strokeInsets(this, inset);
0527 
0528         // calculate transformed border insets
0529         QPointF center = transform.map(QPointF());
0530         QPointF tl = transform.map(QPointF(-inset.left,-inset.top)) - center;
0531         QPointF br = transform.map(QPointF(inset.right,inset.bottom)) -center;
0532         qreal left = qMin(tl.x(),br.x());
0533         qreal right = qMax(tl.x(),br.x());
0534         qreal top = qMin(tl.y(),br.y());
0535         qreal bottom = qMax(tl.y(),br.y());
0536         bb.adjust(left, top, right, bottom);
0537     }
0538     if (shadow()) {
0539         KoInsets insets;
0540         shadow()->insets(insets);
0541         bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
0542     }
0543     if (filterEffectStack()) {
0544         QRectF clipRect = filterEffectStack()->clipRectForBoundingRect(QRectF(QPointF(), size()));
0545         bb |= transform.mapRect(clipRect);
0546     }
0547     return bb;
0548 }
0549 
0550 QSizeF KoPathShape::size() const
0551 {
0552     // don't call boundingRect here as it uses absoluteTransformation
0553     // which itself uses size() -> leads to infinite reccursion
0554     return outline().boundingRect().size();
0555 }
0556 
0557 void KoPathShape::setSize(const QSizeF &newSize)
0558 {
0559     Q_D(KoPathShape);
0560     QTransform matrix(resizeMatrix(newSize));
0561 
0562     KoShape::setSize(newSize);
0563     d->map(matrix);
0564 }
0565 
0566 QTransform KoPathShape::resizeMatrix(const QSizeF & newSize) const
0567 {
0568     QSizeF oldSize = size();
0569     if (oldSize.width() == 0.0) {
0570         oldSize.setWidth(0.000001);
0571     }
0572     if (oldSize.height() == 0.0) {
0573         oldSize.setHeight(0.000001);
0574     }
0575 
0576     QSizeF sizeNew(newSize);
0577     if (sizeNew.width() == 0.0) {
0578         sizeNew.setWidth(0.000001);
0579     }
0580     if (sizeNew.height() == 0.0) {
0581         sizeNew.setHeight(0.000001);
0582     }
0583 
0584     return QTransform(sizeNew.width() / oldSize.width(), 0, 0, sizeNew.height() / oldSize.height(), 0, 0);
0585 }
0586 
0587 KoPathPoint * KoPathShape::moveTo(const QPointF &p)
0588 {
0589     KoPathPoint * point = new KoPathPoint(this, p, KoPathPoint::StartSubpath | KoPathPoint::StopSubpath);
0590     KoSubpath * path = new KoSubpath;
0591     path->push_back(point);
0592     m_subpaths.push_back(path);
0593     return point;
0594 }
0595 
0596 KoPathPoint * KoPathShape::lineTo(const QPointF &p)
0597 {
0598     Q_D(KoPathShape);
0599     if (m_subpaths.empty()) {
0600         moveTo(QPointF(0, 0));
0601     }
0602     KoPathPoint * point = new KoPathPoint(this, p, KoPathPoint::StopSubpath);
0603     KoPathPoint * lastPoint = m_subpaths.last()->last();
0604     d->updateLast(&lastPoint);
0605     m_subpaths.last()->push_back(point);
0606     return point;
0607 }
0608 
0609 KoPathPoint * KoPathShape::curveTo(const QPointF &c1, const QPointF &c2, const QPointF &p)
0610 {
0611     Q_D(KoPathShape);
0612     if (m_subpaths.empty()) {
0613         moveTo(QPointF(0, 0));
0614     }
0615     KoPathPoint * lastPoint = m_subpaths.last()->last();
0616     d->updateLast(&lastPoint);
0617     lastPoint->setControlPoint2(c1);
0618     KoPathPoint * point = new KoPathPoint(this, p, KoPathPoint::StopSubpath);
0619     point->setControlPoint1(c2);
0620     m_subpaths.last()->push_back(point);
0621     return point;
0622 }
0623 
0624 KoPathPoint * KoPathShape::curveTo(const QPointF &c, const QPointF &p)
0625 {
0626     Q_D(KoPathShape);
0627     if (m_subpaths.empty())
0628         moveTo(QPointF(0, 0));
0629 
0630     KoPathPoint * lastPoint = m_subpaths.last()->last();
0631     d->updateLast(&lastPoint);
0632     lastPoint->setControlPoint2(c);
0633     KoPathPoint * point = new KoPathPoint(this, p, KoPathPoint::StopSubpath);
0634     m_subpaths.last()->push_back(point);
0635 
0636     return point;
0637 }
0638 
0639 KoPathPoint * KoPathShape::arcTo(qreal rx, qreal ry, qreal startAngle, qreal sweepAngle)
0640 {
0641     if (m_subpaths.empty()) {
0642         moveTo(QPointF(0, 0));
0643     }
0644 
0645     KoPathPoint * lastPoint = m_subpaths.last()->last();
0646     if (lastPoint->properties() & KoPathPoint::CloseSubpath) {
0647         lastPoint = m_subpaths.last()->first();
0648     }
0649     QPointF startpoint(lastPoint->point());
0650 
0651     KoPathPoint * newEndPoint = lastPoint;
0652 
0653     QPointF curvePoints[12];
0654     int pointCnt = arcToCurve(rx, ry, startAngle, sweepAngle, startpoint, curvePoints);
0655     for (int i = 0; i < pointCnt; i += 3) {
0656         newEndPoint = curveTo(curvePoints[i], curvePoints[i+1], curvePoints[i+2]);
0657     }
0658     return newEndPoint;
0659 }
0660 
0661 int KoPathShape::arcToCurve(qreal rx, qreal ry, qreal startAngle, qreal sweepAngle, const QPointF & offset, QPointF * curvePoints) const
0662 {
0663     int pointCnt = 0;
0664 
0665     // check Parameters
0666     if (sweepAngle == 0)
0667         return pointCnt;
0668     if (sweepAngle > 360)
0669         sweepAngle = 360;
0670     else if (sweepAngle < -360)
0671         sweepAngle = - 360;
0672 
0673     if (rx == 0 || ry == 0) {
0674         //TODO
0675     }
0676 
0677     // split angles bigger than 90° so that it gives a good approximation to the circle
0678     qreal parts = ceil(qAbs(sweepAngle / 90.0));
0679 
0680     qreal sa_rad = startAngle * M_PI / 180.0;
0681     qreal partangle = sweepAngle / parts;
0682     qreal endangle = startAngle + partangle;
0683     qreal se_rad = endangle * M_PI / 180.0;
0684     qreal sinsa = sin(sa_rad);
0685     qreal cossa = cos(sa_rad);
0686     qreal kappa = 4.0 / 3.0 * tan((se_rad - sa_rad) / 4);
0687 
0688     // startpoint is at the last point is the path but when it is closed
0689     // it is at the first point
0690     QPointF startpoint(offset);
0691 
0692     //center berechnen
0693     QPointF center(startpoint - QPointF(cossa * rx, -sinsa * ry));
0694 
0695     //debugFlake <<"kappa" << kappa <<"parts" << parts;
0696 
0697     for (int part = 0; part < parts; ++part) {
0698         // start tangent
0699         curvePoints[pointCnt++] = QPointF(startpoint - QPointF(sinsa * rx * kappa, cossa * ry * kappa));
0700 
0701         qreal sinse = sin(se_rad);
0702         qreal cosse = cos(se_rad);
0703 
0704         // end point
0705         QPointF endpoint(center + QPointF(cosse * rx, -sinse * ry));
0706         // end tangent
0707         curvePoints[pointCnt++] = QPointF(endpoint - QPointF(-sinse * rx * kappa, -cosse * ry * kappa));
0708         curvePoints[pointCnt++] = endpoint;
0709 
0710         // set the endpoint as next start point
0711         startpoint = endpoint;
0712         sinsa = sinse;
0713         cossa = cosse;
0714         endangle += partangle;
0715         se_rad = endangle * M_PI / 180.0;
0716     }
0717 
0718     return pointCnt;
0719 }
0720 
0721 void KoPathShape::close()
0722 {
0723     Q_D(KoPathShape);
0724     if (m_subpaths.empty()) {
0725         return;
0726     }
0727     d->closeSubpath(m_subpaths.last());
0728 }
0729 
0730 void KoPathShape::closeMerge()
0731 {
0732     Q_D(KoPathShape);
0733     if (m_subpaths.empty()) {
0734         return;
0735     }
0736     d->closeMergeSubpath(m_subpaths.last());
0737 }
0738 
0739 QPointF KoPathShape::normalize()
0740 {
0741     Q_D(KoPathShape);
0742     QPointF tl(outline().boundingRect().topLeft());
0743     QTransform matrix;
0744     matrix.translate(-tl.x(), -tl.y());
0745     d->map(matrix);
0746 
0747     // keep the top left point of the object
0748     applyTransformation(matrix.inverted());
0749     d->shapeChanged(ContentChanged);
0750     return tl;
0751 }
0752 
0753 void KoPathShapePrivate::map(const QTransform &matrix)
0754 {
0755     Q_Q(KoPathShape);
0756     KoSubpathList::const_iterator pathIt(q->m_subpaths.constBegin());
0757     for (; pathIt != q->m_subpaths.constEnd(); ++pathIt) {
0758         KoSubpath::const_iterator it((*pathIt)->constBegin());
0759         for (; it != (*pathIt)->constEnd(); ++it) {
0760             (*it)->map(matrix);
0761         }
0762     }
0763 }
0764 
0765 void KoPathShapePrivate::updateLast(KoPathPoint **lastPoint)
0766 {
0767     Q_Q(KoPathShape);
0768     // check if we are about to add a new point to a closed subpath
0769     if ((*lastPoint)->properties() & KoPathPoint::StopSubpath
0770             && (*lastPoint)->properties() & KoPathPoint::CloseSubpath) {
0771         // get the first point of the subpath
0772         KoPathPoint *subpathStart = q->m_subpaths.last()->first();
0773         // clone the first point of the subpath...
0774         KoPathPoint * newLastPoint = new KoPathPoint(*subpathStart);
0775         // ... and make it a normal point
0776         newLastPoint->setProperties(KoPathPoint::Normal);
0777         // now start a new subpath with the cloned start point
0778         KoSubpath *path = new KoSubpath;
0779         path->push_back(newLastPoint);
0780         q->m_subpaths.push_back(path);
0781         *lastPoint = newLastPoint;
0782     } else {
0783         // the subpath was not closed so the formerly last point
0784         // of the subpath is no end point anymore
0785         (*lastPoint)->unsetProperty(KoPathPoint::StopSubpath);
0786     }
0787     (*lastPoint)->unsetProperty(KoPathPoint::CloseSubpath);
0788 }
0789 
0790 QList<KoPathPoint*> KoPathShape::pointsAt(const QRectF &r) const
0791 {
0792     QList<KoPathPoint*> result;
0793 
0794     KoSubpathList::const_iterator pathIt(m_subpaths.constBegin());
0795     for (; pathIt != m_subpaths.constEnd(); ++pathIt) {
0796         KoSubpath::const_iterator it((*pathIt)->constBegin());
0797         for (; it != (*pathIt)->constEnd(); ++it) {
0798             if (r.contains((*it)->point()))
0799                 result.append(*it);
0800             else if ((*it)->activeControlPoint1() && r.contains((*it)->controlPoint1()))
0801                 result.append(*it);
0802             else if ((*it)->activeControlPoint2() && r.contains((*it)->controlPoint2()))
0803                 result.append(*it);
0804         }
0805     }
0806     return result;
0807 }
0808 
0809 QList<KoPathSegment> KoPathShape::segmentsAt(const QRectF &r) const
0810 {
0811     QList<KoPathSegment> segments;
0812     int subpathCount = m_subpaths.count();
0813     for (int subpathIndex = 0; subpathIndex < subpathCount; ++subpathIndex) {
0814         KoSubpath * subpath = m_subpaths[subpathIndex];
0815         int pointCount = subpath->count();
0816         bool subpathClosed = isClosedSubpath(subpathIndex);
0817         for (int pointIndex = 0; pointIndex < pointCount; ++pointIndex) {
0818             if (pointIndex == (pointCount - 1) && ! subpathClosed)
0819                 break;
0820             KoPathSegment s(subpath->at(pointIndex), subpath->at((pointIndex + 1) % pointCount));
0821             QRectF controlRect = s.controlPointRect();
0822             if (! r.intersects(controlRect) && ! controlRect.contains(r))
0823                 continue;
0824             QRectF bound = s.boundingRect();
0825             if (! r.intersects(bound) && ! bound.contains(r))
0826                 continue;
0827 
0828             segments.append(s);
0829         }
0830     }
0831     return segments;
0832 }
0833 
0834 KoPathPointIndex KoPathShape::pathPointIndex(const KoPathPoint *point) const
0835 {
0836     for (int subpathIndex = 0; subpathIndex < m_subpaths.size(); ++subpathIndex) {
0837         KoSubpath * subpath = m_subpaths.at(subpathIndex);
0838         for (int pointPos = 0; pointPos < subpath->size(); ++pointPos) {
0839             if (subpath->at(pointPos) == point) {
0840                 return KoPathPointIndex(subpathIndex, pointPos);
0841             }
0842         }
0843     }
0844     return KoPathPointIndex(-1, -1);
0845 }
0846 
0847 KoPathPoint * KoPathShape::pointByIndex(const KoPathPointIndex &pointIndex) const
0848 {
0849     Q_D(const KoPathShape);
0850     KoSubpath *subpath = d->subPath(pointIndex.first);
0851 
0852     if (subpath == 0 || pointIndex.second < 0 || pointIndex.second >= subpath->size())
0853         return 0;
0854 
0855     return subpath->at(pointIndex.second);
0856 }
0857 
0858 KoPathSegment KoPathShape::segmentByIndex(const KoPathPointIndex &pointIndex) const
0859 {
0860     Q_D(const KoPathShape);
0861     KoPathSegment segment(0, 0);
0862 
0863     KoSubpath *subpath = d->subPath(pointIndex.first);
0864 
0865     if (subpath != 0 && pointIndex.second >= 0 && pointIndex.second < subpath->size()) {
0866         KoPathPoint * point = subpath->at(pointIndex.second);
0867         int index = pointIndex.second;
0868         // check if we have a (closing) segment starting from the last point
0869         if ((index == subpath->size() - 1) && point->properties() & KoPathPoint::CloseSubpath)
0870             index = 0;
0871         else
0872             ++index;
0873 
0874         if (index < subpath->size()) {
0875             segment = KoPathSegment(point, subpath->at(index));
0876         }
0877     }
0878     return segment;
0879 }
0880 
0881 int KoPathShape::pointCount() const
0882 {
0883     int i = 0;
0884     KoSubpathList::const_iterator pathIt(m_subpaths.constBegin());
0885     for (; pathIt != m_subpaths.constEnd(); ++pathIt) {
0886         i += (*pathIt)->size();
0887     }
0888 
0889     return i;
0890 }
0891 
0892 int KoPathShape::subpathCount() const
0893 {
0894     return m_subpaths.count();
0895 }
0896 
0897 int KoPathShape::subpathPointCount(int subpathIndex) const
0898 {
0899     Q_D(const KoPathShape);
0900     KoSubpath *subpath = d->subPath(subpathIndex);
0901 
0902     if (subpath == 0)
0903         return -1;
0904 
0905     return subpath->size();
0906 }
0907 
0908 bool KoPathShape::isClosedSubpath(int subpathIndex) const
0909 {
0910     Q_D(const KoPathShape);
0911     KoSubpath *subpath = d->subPath(subpathIndex);
0912 
0913     if (subpath == 0)
0914         return false;
0915 
0916     const bool firstClosed = subpath->first()->properties() & KoPathPoint::CloseSubpath;
0917     const bool lastClosed = subpath->last()->properties() & KoPathPoint::CloseSubpath;
0918 
0919     return firstClosed && lastClosed;
0920 }
0921 
0922 bool KoPathShape::insertPoint(KoPathPoint* point, const KoPathPointIndex &pointIndex)
0923 {
0924     Q_D(KoPathShape);
0925     KoSubpath *subpath = d->subPath(pointIndex.first);
0926 
0927     if (subpath == 0 || pointIndex.second < 0 || pointIndex.second > subpath->size())
0928         return false;
0929 
0930     KoPathPoint::PointProperties properties = point->properties();
0931     properties &= ~KoPathPoint::StartSubpath;
0932     properties &= ~KoPathPoint::StopSubpath;
0933     properties &= ~KoPathPoint::CloseSubpath;
0934     // check if new point starts subpath
0935     if (pointIndex.second == 0) {
0936         properties |= KoPathPoint::StartSubpath;
0937         // subpath was closed
0938         if (subpath->last()->properties() & KoPathPoint::CloseSubpath) {
0939             // keep the path closed
0940             properties |= KoPathPoint::CloseSubpath;
0941         }
0942         // old first point does not start the subpath anymore
0943         subpath->first()->unsetProperty(KoPathPoint::StartSubpath);
0944     }
0945     // check if new point stops subpath
0946     else if (pointIndex.second == subpath->size()) {
0947         properties |= KoPathPoint::StopSubpath;
0948         // subpath was closed
0949         if (subpath->last()->properties() & KoPathPoint::CloseSubpath) {
0950             // keep the path closed
0951             properties = properties | KoPathPoint::CloseSubpath;
0952         }
0953         // old last point does not end subpath anymore
0954         subpath->last()->unsetProperty(KoPathPoint::StopSubpath);
0955     }
0956 
0957     point->setProperties(properties);
0958     point->setParent(this);
0959     subpath->insert(pointIndex.second , point);
0960     return true;
0961 }
0962 
0963 KoPathPoint * KoPathShape::removePoint(const KoPathPointIndex &pointIndex)
0964 {
0965     Q_D(KoPathShape);
0966     KoSubpath *subpath = d->subPath(pointIndex.first);
0967 
0968     if (subpath == 0 || pointIndex.second < 0 || pointIndex.second >= subpath->size())
0969         return 0;
0970 
0971     KoPathPoint * point = subpath->takeAt(pointIndex.second);
0972 
0973     //don't do anything (not even crash), if there was only one point
0974     if (pointCount()==0) {
0975         return point;
0976     }
0977     // check if we removed the first point
0978     else if (pointIndex.second == 0) {
0979         // first point removed, set new StartSubpath
0980         subpath->first()->setProperty(KoPathPoint::StartSubpath);
0981         // check if path was closed
0982         if (subpath->last()->properties() & KoPathPoint::CloseSubpath) {
0983             // keep path closed
0984             subpath->first()->setProperty(KoPathPoint::CloseSubpath);
0985         }
0986     }
0987     // check if we removed the last point
0988     else if (pointIndex.second == subpath->size()) { // use size as point is already removed
0989         // last point removed, set new StopSubpath
0990         subpath->last()->setProperty(KoPathPoint::StopSubpath);
0991         // check if path was closed
0992         if (point->properties() & KoPathPoint::CloseSubpath) {
0993             // keep path closed
0994             subpath->last()->setProperty(KoPathPoint::CloseSubpath);
0995         }
0996     }
0997 
0998     return point;
0999 }
1000 
1001 bool KoPathShape::breakAfter(const KoPathPointIndex &pointIndex)
1002 {
1003     Q_D(KoPathShape);
1004     KoSubpath *subpath = d->subPath(pointIndex.first);
1005 
1006     if (!subpath || pointIndex.second < 0 || pointIndex.second > subpath->size() - 2
1007         || isClosedSubpath(pointIndex.first))
1008         return false;
1009 
1010     KoSubpath * newSubpath = new KoSubpath;
1011 
1012     int size = subpath->size();
1013     for (int i = pointIndex.second + 1; i < size; ++i) {
1014         newSubpath->append(subpath->takeAt(pointIndex.second + 1));
1015     }
1016     // now make the first point of the new subpath a starting node
1017     newSubpath->first()->setProperty(KoPathPoint::StartSubpath);
1018     // the last point of the old subpath is now an ending node
1019     subpath->last()->setProperty(KoPathPoint::StopSubpath);
1020 
1021     // insert the new subpath after the broken one
1022     m_subpaths.insert(pointIndex.first + 1, newSubpath);
1023 
1024     return true;
1025 }
1026 
1027 bool KoPathShape::join(int subpathIndex)
1028 {
1029     Q_D(KoPathShape);
1030     KoSubpath *subpath = d->subPath(subpathIndex);
1031     KoSubpath *nextSubpath = d->subPath(subpathIndex + 1);
1032 
1033     if (!subpath || !nextSubpath || isClosedSubpath(subpathIndex)
1034             || isClosedSubpath(subpathIndex+1))
1035         return false;
1036 
1037     // the last point of the subpath does not end the subpath anymore
1038     subpath->last()->unsetProperty(KoPathPoint::StopSubpath);
1039     // the first point of the next subpath does not start a subpath anymore
1040     nextSubpath->first()->unsetProperty(KoPathPoint::StartSubpath);
1041 
1042     // append the second subpath to the first
1043     foreach(KoPathPoint * p, *nextSubpath)
1044         subpath->append(p);
1045 
1046     // remove the nextSubpath from path
1047     m_subpaths.removeAt(subpathIndex + 1);
1048 
1049     // delete it as it is no longer possible to use it
1050     delete nextSubpath;
1051 
1052     return true;
1053 }
1054 
1055 bool KoPathShape::moveSubpath(int oldSubpathIndex, int newSubpathIndex)
1056 {
1057     Q_D(KoPathShape);
1058     KoSubpath *subpath = d->subPath(oldSubpathIndex);
1059 
1060     if (subpath == 0 || newSubpathIndex >= m_subpaths.size())
1061         return false;
1062 
1063     if (oldSubpathIndex == newSubpathIndex)
1064         return true;
1065 
1066     m_subpaths.removeAt(oldSubpathIndex);
1067     m_subpaths.insert(newSubpathIndex, subpath);
1068 
1069     return true;
1070 }
1071 
1072 KoPathPointIndex KoPathShape::openSubpath(const KoPathPointIndex &pointIndex)
1073 {
1074     Q_D(KoPathShape);
1075     KoSubpath *subpath = d->subPath(pointIndex.first);
1076 
1077     if (!subpath || pointIndex.second < 0 || pointIndex.second >= subpath->size()
1078             || !isClosedSubpath(pointIndex.first))
1079         return KoPathPointIndex(-1, -1);
1080 
1081     KoPathPoint * oldStartPoint = subpath->first();
1082     // the old starting node no longer starts the subpath
1083     oldStartPoint->unsetProperty(KoPathPoint::StartSubpath);
1084     // the old end node no longer closes the subpath
1085     subpath->last()->unsetProperty(KoPathPoint::StopSubpath);
1086 
1087     // reorder the subpath
1088     for (int i = 0; i < pointIndex.second; ++i) {
1089         subpath->append(subpath->takeFirst());
1090     }
1091     // make the first point a start node
1092     subpath->first()->setProperty(KoPathPoint::StartSubpath);
1093     // make the last point an end node
1094     subpath->last()->setProperty(KoPathPoint::StopSubpath);
1095 
1096     return pathPointIndex(oldStartPoint);
1097 }
1098 
1099 KoPathPointIndex KoPathShape::closeSubpath(const KoPathPointIndex &pointIndex)
1100 {
1101     Q_D(KoPathShape);
1102     KoSubpath *subpath = d->subPath(pointIndex.first);
1103 
1104     if (!subpath || pointIndex.second < 0 || pointIndex.second >= subpath->size()
1105         || isClosedSubpath(pointIndex.first))
1106         return KoPathPointIndex(-1, -1);
1107 
1108     KoPathPoint * oldStartPoint = subpath->first();
1109     // the old starting node no longer starts the subpath
1110     oldStartPoint->unsetProperty(KoPathPoint::StartSubpath);
1111     // the old end node no longer ends the subpath
1112     subpath->last()->unsetProperty(KoPathPoint::StopSubpath);
1113 
1114     // reorder the subpath
1115     for (int i = 0; i < pointIndex.second; ++i) {
1116         subpath->append(subpath->takeFirst());
1117     }
1118     subpath->first()->setProperty(KoPathPoint::StartSubpath);
1119     subpath->last()->setProperty(KoPathPoint::StopSubpath);
1120 
1121     d->closeSubpath(subpath);
1122     return pathPointIndex(oldStartPoint);
1123 }
1124 
1125 bool KoPathShape::reverseSubpath(int subpathIndex)
1126 {
1127     Q_D(KoPathShape);
1128     KoSubpath *subpath = d->subPath(subpathIndex);
1129 
1130     if (subpath == 0)
1131         return false;
1132 
1133     int size = subpath->size();
1134     for (int i = 0; i < size; ++i) {
1135         KoPathPoint *p = subpath->takeAt(i);
1136         p->reverse();
1137         subpath->prepend(p);
1138     }
1139 
1140     // adjust the position dependent properties
1141     KoPathPoint *first = subpath->first();
1142     KoPathPoint *last = subpath->last();
1143 
1144     KoPathPoint::PointProperties firstProps = first->properties();
1145     KoPathPoint::PointProperties lastProps = last->properties();
1146 
1147     firstProps |= KoPathPoint::StartSubpath;
1148     firstProps &= ~KoPathPoint::StopSubpath;
1149     lastProps |= KoPathPoint::StopSubpath;
1150     lastProps &= ~KoPathPoint::StartSubpath;
1151     if (firstProps & KoPathPoint::CloseSubpath) {
1152         firstProps |= KoPathPoint::CloseSubpath;
1153         lastProps |= KoPathPoint::CloseSubpath;
1154     }
1155     first->setProperties(firstProps);
1156     last->setProperties(lastProps);
1157 
1158     return true;
1159 }
1160 
1161 KoSubpath * KoPathShape::removeSubpath(int subpathIndex)
1162 {
1163     Q_D(KoPathShape);
1164     KoSubpath *subpath = d->subPath(subpathIndex);
1165 
1166     if (subpath != 0)
1167         m_subpaths.removeAt(subpathIndex);
1168 
1169     return subpath;
1170 }
1171 
1172 bool KoPathShape::addSubpath(KoSubpath * subpath, int subpathIndex)
1173 {
1174     if (subpathIndex < 0 || subpathIndex > m_subpaths.size())
1175         return false;
1176 
1177     m_subpaths.insert(subpathIndex, subpath);
1178 
1179     return true;
1180 }
1181 
1182 bool KoPathShape::combine(KoPathShape *path)
1183 {
1184     if (! path)
1185         return false;
1186 
1187     QTransform pathMatrix = path->absoluteTransformation(0);
1188     QTransform myMatrix = absoluteTransformation(0).inverted();
1189 
1190     foreach(KoSubpath* subpath, path->m_subpaths) {
1191         KoSubpath *newSubpath = new KoSubpath();
1192 
1193         foreach(KoPathPoint* point, *subpath) {
1194             KoPathPoint *newPoint = new KoPathPoint(*point);
1195             newPoint->map(pathMatrix);
1196             newPoint->map(myMatrix);
1197             newPoint->setParent(this);
1198             newSubpath->append(newPoint);
1199         }
1200         m_subpaths.append(newSubpath);
1201     }
1202     normalize();
1203     return true;
1204 }
1205 
1206 bool KoPathShape::separate(QList<KoPathShape*> & separatedPaths)
1207 {
1208     if (! m_subpaths.size())
1209         return false;
1210 
1211     QTransform myMatrix = absoluteTransformation(0);
1212 
1213     foreach(KoSubpath* subpath, m_subpaths) {
1214         KoPathShape *shape = new KoPathShape();
1215         if (! shape) continue;
1216 
1217         shape->setStroke(stroke());
1218         shape->setShapeId(shapeId());
1219 
1220         KoSubpath *newSubpath = new KoSubpath();
1221 
1222         foreach(KoPathPoint* point, *subpath) {
1223             KoPathPoint *newPoint = new KoPathPoint(*point);
1224             newPoint->map(myMatrix);
1225             newSubpath->append(newPoint);
1226         }
1227         shape->m_subpaths.append(newSubpath);
1228         shape->normalize();
1229         separatedPaths.append(shape);
1230     }
1231     return true;
1232 }
1233 
1234 void KoPathShapePrivate::closeSubpath(KoSubpath *subpath)
1235 {
1236     if (! subpath)
1237         return;
1238 
1239     subpath->last()->setProperty(KoPathPoint::CloseSubpath);
1240     subpath->first()->setProperty(KoPathPoint::CloseSubpath);
1241 }
1242 
1243 void KoPathShapePrivate::closeMergeSubpath(KoSubpath *subpath)
1244 {
1245     if (! subpath || subpath->size() < 2)
1246         return;
1247 
1248     KoPathPoint * lastPoint = subpath->last();
1249     KoPathPoint * firstPoint = subpath->first();
1250 
1251     // check if first and last points are coincident
1252     if (lastPoint->point() == firstPoint->point()) {
1253         // we are removing the current last point and
1254         // reuse its first control point if active
1255         firstPoint->setProperty(KoPathPoint::StartSubpath);
1256         firstPoint->setProperty(KoPathPoint::CloseSubpath);
1257         if (lastPoint->activeControlPoint1())
1258             firstPoint->setControlPoint1(lastPoint->controlPoint1());
1259         // remove last point
1260         delete subpath->takeLast();
1261         // the new last point closes the subpath now
1262         lastPoint = subpath->last();
1263         lastPoint->setProperty(KoPathPoint::StopSubpath);
1264         lastPoint->setProperty(KoPathPoint::CloseSubpath);
1265     } else {
1266         closeSubpath(subpath);
1267     }
1268 }
1269 
1270 KoSubpath *KoPathShapePrivate::subPath(int subpathIndex) const
1271 {
1272     Q_Q(const KoPathShape);
1273     if (subpathIndex < 0 || subpathIndex >= q->m_subpaths.size())
1274         return 0;
1275 
1276     return q->m_subpaths.at(subpathIndex);
1277 }
1278 
1279 QString KoPathShape::pathShapeId() const
1280 {
1281     return KoPathShapeId;
1282 }
1283 
1284 QString KoPathShape::toString(const QTransform &matrix) const
1285 {
1286     QString d;
1287 
1288     // iterate over all subpaths
1289     KoSubpathList::const_iterator pathIt(m_subpaths.constBegin());
1290     for (; pathIt != m_subpaths.constEnd(); ++pathIt) {
1291         KoSubpath::const_iterator pointIt((*pathIt)->constBegin());
1292         // keep a pointer to the first point of the subpath
1293         KoPathPoint *firstPoint(*pointIt);
1294         // keep a pointer to the previous point of the subpath
1295         KoPathPoint *lastPoint = firstPoint;
1296         // keep track if the previous point has an active control point 2
1297         bool activeControlPoint2 = false;
1298 
1299         // iterate over all points of the current subpath
1300         for (; pointIt != (*pathIt)->constEnd(); ++pointIt) {
1301             KoPathPoint *currPoint(*pointIt);
1302             // first point of subpath ?
1303             if (currPoint == firstPoint) {
1304                 // are we starting a subpath ?
1305                 if (currPoint->properties() & KoPathPoint::StartSubpath) {
1306                     const QPointF p = matrix.map(currPoint->point());
1307                     d += QString("M%1 %2").arg(p.x()).arg(p.y());
1308                 }
1309             }
1310             // end point of curve segment ?
1311             else if (activeControlPoint2 || currPoint->activeControlPoint1()) {
1312                 // check if we have a cubic or quadratic curve
1313                 const bool isCubic = activeControlPoint2 && currPoint->activeControlPoint1();
1314                 KoPathSegment cubicSeg = isCubic ? KoPathSegment(lastPoint, currPoint)
1315                                                  : KoPathSegment(lastPoint, currPoint).toCubic();
1316                 const QPointF cp1 = matrix.map(cubicSeg.first()->controlPoint2());
1317                 const QPointF cp2 = matrix.map(cubicSeg.second()->controlPoint1());
1318                 const QPointF p = matrix.map(cubicSeg.second()->point());
1319                 d += QString("C%1 %2 %3 %4 %5 %6")
1320                      .arg(cp1.x()).arg(cp1.y())
1321                      .arg(cp2.x()).arg(cp2.y())
1322                      .arg(p.x()).arg(p.y());
1323             }
1324             // end point of line segment!
1325             else {
1326                 const QPointF p = matrix.map(currPoint->point());
1327                 d += QString("L%1 %2").arg(p.x()).arg(p.y());
1328             }
1329             // last point closes subpath ?
1330             if (currPoint->properties() & KoPathPoint::StopSubpath
1331                     && currPoint->properties() & KoPathPoint::CloseSubpath) {
1332                 // add curve when there is a curve on the way to the first point
1333                 if (currPoint->activeControlPoint2() || firstPoint->activeControlPoint1()) {
1334                     // check if we have a cubic or quadratic curve
1335                     const bool isCubic = currPoint->activeControlPoint2() && firstPoint->activeControlPoint1();
1336                     KoPathSegment cubicSeg = isCubic ? KoPathSegment(currPoint, firstPoint)
1337                                                      : KoPathSegment(currPoint, firstPoint).toCubic();
1338                     const QPointF cp1 = matrix.map(cubicSeg.first()->controlPoint2());
1339                     const QPointF cp2 = matrix.map(cubicSeg.second()->controlPoint1());
1340                     const QPointF p = matrix.map(cubicSeg.second()->point());
1341                     d += QString("C%1 %2 %3 %4 %5 %6")
1342                          .arg(cp1.x()).arg(cp1.y())
1343                          .arg(cp2.x()).arg(cp2.y())
1344                          .arg(p.x()).arg(p.y());
1345                 }
1346                 d += QString("Z");
1347             }
1348 
1349             activeControlPoint2 = currPoint->activeControlPoint2();
1350             lastPoint = currPoint;
1351         }
1352     }
1353 
1354     return d;
1355 }
1356 
1357 char nodeType(const KoPathPoint * point)
1358 {
1359     if (point->properties() & KoPathPoint::IsSmooth) {
1360         return 's';
1361     }
1362     else if (point->properties() & KoPathPoint::IsSymmetric) {
1363         return 'z';
1364     }
1365     else {
1366         return 'c';
1367     }
1368 }
1369 
1370 QString KoPathShapePrivate::nodeTypes() const
1371 {
1372     Q_Q(const KoPathShape);
1373     QString types;
1374     KoSubpathList::const_iterator pathIt(q->m_subpaths.constBegin());
1375     for (; pathIt != q->m_subpaths.constEnd(); ++pathIt) {
1376         KoSubpath::const_iterator it((*pathIt)->constBegin());
1377         for (; it != (*pathIt)->constEnd(); ++it) {
1378             if (it == (*pathIt)->constBegin()) {
1379                 types.append('c');
1380             }
1381             else {
1382                 types.append(nodeType(*it));
1383             }
1384 
1385             if ((*it)->properties() & KoPathPoint::StopSubpath
1386                 && (*it)->properties() & KoPathPoint::CloseSubpath) {
1387                 KoPathPoint * firstPoint = (*pathIt)->first();
1388                 types.append(nodeType(firstPoint));
1389             }
1390         }
1391     }
1392     return types;
1393 }
1394 
1395 void updateNodeType(KoPathPoint * point, const QChar & nodeType)
1396 {
1397     if (nodeType == 's') {
1398         point->setProperty(KoPathPoint::IsSmooth);
1399     }
1400     else if (nodeType == 'z') {
1401         point->setProperty(KoPathPoint::IsSymmetric);
1402     }
1403 }
1404 
1405 void KoPathShapePrivate::loadNodeTypes(const KoXmlElement &element)
1406 {
1407     Q_Q(KoPathShape);
1408     if (element.hasAttributeNS(KoXmlNS::calligra, "nodeTypes")) {
1409         QString nodeTypes = element.attributeNS(KoXmlNS::calligra, "nodeTypes");
1410         QString::const_iterator nIt(nodeTypes.constBegin());
1411         KoSubpathList::const_iterator pathIt(q->m_subpaths.constBegin());
1412         for (; pathIt != q->m_subpaths.constEnd(); ++pathIt) {
1413             KoSubpath::const_iterator it((*pathIt)->constBegin());
1414             for (; it != (*pathIt)->constEnd(); ++it, nIt++) {
1415                 // be sure not to crash if there are not enough nodes in nodeTypes
1416                 if (nIt == nodeTypes.constEnd()) {
1417                     warnFlake << "not enough nodes in calligra:nodeTypes";
1418                     return;
1419                 }
1420                 // the first node is always of type 'c'
1421                 if (it != (*pathIt)->constBegin()) {
1422                     updateNodeType(*it, *nIt);
1423                 }
1424 
1425                 if ((*it)->properties() & KoPathPoint::StopSubpath
1426                     && (*it)->properties() & KoPathPoint::CloseSubpath) {
1427                     ++nIt;
1428                     updateNodeType((*pathIt)->first(), *nIt);
1429                 }
1430             }
1431         }
1432     }
1433 }
1434 
1435 Qt::FillRule KoPathShape::fillRule() const
1436 {
1437     Q_D(const KoPathShape);
1438     return d->fillRule;
1439 }
1440 
1441 void KoPathShape::setFillRule(Qt::FillRule fillRule)
1442 {
1443     Q_D(KoPathShape);
1444     d->fillRule = fillRule;
1445 }
1446 
1447 KoPathShape * KoPathShape::createShapeFromPainterPath(const QPainterPath &path)
1448 {
1449     KoPathShape * shape = new KoPathShape();
1450 
1451     int elementCount = path.elementCount();
1452     for (int i = 0; i < elementCount; i++) {
1453         QPainterPath::Element element = path.elementAt(i);
1454         switch (element.type) {
1455         case QPainterPath::MoveToElement:
1456             shape->moveTo(QPointF(element.x, element.y));
1457             break;
1458         case QPainterPath::LineToElement:
1459             shape->lineTo(QPointF(element.x, element.y));
1460             break;
1461         case QPainterPath::CurveToElement:
1462             shape->curveTo(QPointF(element.x, element.y),
1463                            QPointF(path.elementAt(i + 1).x, path.elementAt(i + 1).y),
1464                            QPointF(path.elementAt(i + 2).x, path.elementAt(i + 2).y));
1465             break;
1466         default:
1467             continue;
1468         }
1469     }
1470 
1471     shape->normalize();
1472     return shape;
1473 }
1474 
1475 bool KoPathShape::hitTest(const QPointF &position) const
1476 {
1477     if (parent() && parent()->isClipped(this) && ! parent()->hitTest(position))
1478         return false;
1479 
1480     QPointF point = absoluteTransformation(0).inverted().map(position);
1481     const QPainterPath outlinePath = outline();
1482     if (stroke()) {
1483         KoInsets insets;
1484         stroke()->strokeInsets(this, insets);
1485         QRectF roi(QPointF(-insets.left, -insets.top), QPointF(insets.right, insets.bottom));
1486         roi.moveCenter(point);
1487         if (outlinePath.intersects(roi) || outlinePath.contains(roi))
1488             return true;
1489     } else {
1490         if (outlinePath.contains(point))
1491             return true;
1492     }
1493 
1494     // if there is no shadow we can as well just leave
1495     if (! shadow())
1496         return false;
1497 
1498     // the shadow has an offset to the shape, so we simply
1499     // check if the position minus the shadow offset hits the shape
1500     point = absoluteTransformation(0).inverted().map(position - shadow()->offset());
1501 
1502     return outlinePath.contains(point);
1503 }
1504 
1505 void KoPathShape::setMarker(const KoMarkerData &markerData)
1506 {
1507     Q_D(KoPathShape);
1508 
1509     if (markerData.position() == KoMarkerData::MarkerStart) {
1510         d->startMarker = markerData;
1511     }
1512     else {
1513         d->endMarker = markerData;
1514     }
1515 }
1516 
1517 void KoPathShape::setMarker(KoMarker *marker, KoMarkerData::MarkerPosition position)
1518 {
1519     Q_D(KoPathShape);
1520 
1521     if (position == KoMarkerData::MarkerStart) {
1522         if (!d->startMarker.marker()) {
1523             d->startMarker.setWidth(MM_TO_POINT(DefaultMarkerWidth), qreal(0.0));
1524         }
1525         d->startMarker.setMarker(marker);
1526     }
1527     else {
1528         if (!d->endMarker.marker()) {
1529             d->endMarker.setWidth(MM_TO_POINT(DefaultMarkerWidth), qreal(0.0));
1530         }
1531         d->endMarker.setMarker(marker);
1532     }
1533 }
1534 
1535 KoMarker *KoPathShape::marker(KoMarkerData::MarkerPosition position) const
1536 {
1537     Q_D(const KoPathShape);
1538 
1539     if (position == KoMarkerData::MarkerStart) {
1540         return d->startMarker.marker();
1541     }
1542     else {
1543         return d->endMarker.marker();
1544     }
1545 }
1546 
1547 KoMarkerData KoPathShape::markerData(KoMarkerData::MarkerPosition position) const
1548 {
1549     Q_D(const KoPathShape);
1550 
1551     if (position == KoMarkerData::MarkerStart) {
1552         return d->startMarker;
1553     }
1554     else {
1555         return d->endMarker;
1556     }
1557 }
1558 
1559 QPainterPath KoPathShape::pathStroke(const QPen &pen) const
1560 {
1561     if (m_subpaths.isEmpty()) {
1562         return QPainterPath();
1563     }
1564     QPainterPath pathOutline;
1565 
1566     QPainterPathStroker stroker;
1567     stroker.setWidth(0);
1568     stroker.setJoinStyle(Qt::MiterJoin);
1569 
1570     QPair<KoPathSegment, KoPathSegment> firstSegments;
1571     QPair<KoPathSegment, KoPathSegment> lastSegments;
1572 
1573     KoPathPoint *firstPoint = 0;
1574     KoPathPoint *lastPoint = 0;
1575     KoPathPoint *secondPoint = 0;
1576     KoPathPoint *preLastPoint = 0;
1577 
1578     KoSubpath *firstSubpath = m_subpaths.first();
1579     bool twoPointPath = subpathPointCount(0) == 2;
1580     bool closedPath = isClosedSubpath(0);
1581 
1582     /*
1583      * The geometry is horizontally centered. It is vertically positioned relative to an offset value which
1584      * is specified by a draw:marker-start-center attribute for markers referenced by a
1585      * draw:marker-start attribute, and by the draw:marker-end-center attribute for markers
1586      * referenced by a draw:marker-end attribute. The attribute value true defines an offset of 0.5
1587      * and the attribute value false defines an offset of 0.3, which is also the default value. The offset
1588      * specifies the marker's vertical position in a range from 0.0 to 1.0, where the value 0.0 means the
1589      * geometry's bottom bound is aligned to the X axis of the local coordinate system of the marker
1590      * geometry, and where the value 1.0 means the top bound to be aligned to the X axis of the local
1591      * coordinate system of the marker geometry.
1592      *
1593      * The shorten factor to use results of the 0.3 which means we need to start at 0.7 * height of the marker
1594      */
1595     static const qreal shortenFactor = 0.7;
1596 
1597     KoMarkerData mdStart = markerData(KoMarkerData::MarkerStart);
1598     KoMarkerData mdEnd = markerData(KoMarkerData::MarkerEnd);
1599     if (mdStart.marker() && !closedPath) {
1600         QPainterPath markerPath = mdStart.marker()->path(mdStart.width(pen.widthF()));
1601 
1602         KoPathSegment firstSegment = segmentByIndex(KoPathPointIndex(0, 0));
1603         if (firstSegment.isValid()) {
1604             QRectF pathBoundingRect = markerPath.boundingRect();
1605             qreal shortenLength = pathBoundingRect.height() * shortenFactor;
1606             debugFlake << "length" << firstSegment.length() << shortenLength;
1607             qreal t = firstSegment.paramAtLength(shortenLength);
1608             firstSegments = firstSegment.splitAt(t);
1609             // transform the marker so that it goes from the first point of the first segment to the second point of the first segment
1610             QPointF startPoint = firstSegments.first.first()->point();
1611             QPointF newStartPoint = firstSegments.first.second()->point();
1612             QLineF vector(newStartPoint, startPoint);
1613             qreal angle = -vector.angle() + 90;
1614             QTransform transform;
1615             transform.translate(startPoint.x(), startPoint.y())
1616                      .rotate(angle)
1617                      .translate(-pathBoundingRect.width() / 2.0, 0);
1618 
1619             markerPath = transform.map(markerPath);
1620             QPainterPath startOutline = stroker.createStroke(markerPath);
1621             startOutline = startOutline.united(markerPath);
1622             pathOutline.addPath(startOutline);
1623             firstPoint = firstSubpath->first();
1624             if (firstPoint->properties() & KoPathPoint::StartSubpath) {
1625                 firstSegments.second.first()->setProperty(KoPathPoint::StartSubpath);
1626             }
1627             debugFlake << "start marker" << angle << startPoint << newStartPoint << firstPoint->point();
1628 
1629             if (!twoPointPath) {
1630                 if (firstSegment.second()->activeControlPoint2()) {
1631                     firstSegments.second.second()->setControlPoint2(firstSegment.second()->controlPoint2());
1632                 }
1633                 secondPoint = (*firstSubpath)[1];
1634             }
1635             else if (!mdEnd.marker()) {
1636                 // in case it is two point path with no end marker we need to modify the last point via the secondPoint
1637                 secondPoint = (*firstSubpath)[1];
1638             }
1639         }
1640     }
1641     if (mdEnd.marker() && !closedPath) {
1642         QPainterPath markerPath = mdEnd.marker()->path(mdEnd.width(pen.widthF()));
1643 
1644         KoPathSegment lastSegment;
1645 
1646         /*
1647          * if the path consists only of 2 point and it it has an marker on both ends
1648          * use the firstSegments.second as that is the path that needs to be shortened
1649          */
1650         if (twoPointPath && firstPoint) {
1651             lastSegment = firstSegments.second;
1652         }
1653         else {
1654             lastSegment = segmentByIndex(KoPathPointIndex(0, firstSubpath->count() - 2));
1655         }
1656 
1657         if (lastSegment.isValid()) {
1658             QRectF pathBoundingRect = markerPath.boundingRect();
1659             qreal shortenLength = lastSegment.length() - pathBoundingRect.height() * shortenFactor;
1660             qreal t = lastSegment.paramAtLength(shortenLength);
1661             lastSegments = lastSegment.splitAt(t);
1662             // transform the marker so that it goes from the last point of the first segment to the previous point of the last segment
1663             QPointF startPoint = lastSegments.second.second()->point();
1664             QPointF newStartPoint = lastSegments.second.first()->point();
1665             QLineF vector(newStartPoint, startPoint);
1666             qreal angle = -vector.angle() + 90;
1667             QTransform transform;
1668             transform.translate(startPoint.x(), startPoint.y()).rotate(angle).translate(-pathBoundingRect.width() / 2.0, 0);
1669 
1670             markerPath = transform.map(markerPath);
1671             QPainterPath endOutline = stroker.createStroke(markerPath);
1672             endOutline = endOutline.united(markerPath);
1673             pathOutline.addPath(endOutline);
1674             lastPoint = firstSubpath->last();
1675             debugFlake << "end marker" << angle << startPoint << newStartPoint << lastPoint->point();
1676             if (twoPointPath) {
1677                 if (firstSegments.second.isValid()) {
1678                     if (lastSegments.first.first()->activeControlPoint2()) {
1679                         firstSegments.second.first()->setControlPoint2(lastSegments.first.first()->controlPoint2());
1680                     }
1681                 }
1682                 else {
1683                     // if there is no start marker we need the first point needs to be changed via the preLastPoint
1684                     // the flag needs to be set so the moveTo is done
1685                     lastSegments.first.first()->setProperty(KoPathPoint::StartSubpath);
1686                     preLastPoint = (*firstSubpath)[firstSubpath->count()-2];
1687                 }
1688             }
1689             else {
1690                 if (lastSegment.first()->activeControlPoint1()) {
1691                     lastSegments.first.first()->setControlPoint1(lastSegment.first()->controlPoint1());
1692                 }
1693                 preLastPoint = (*firstSubpath)[firstSubpath->count()-2];
1694             }
1695         }
1696     }
1697 
1698 
1699     stroker.setWidth(pen.widthF());
1700     stroker.setJoinStyle(pen.joinStyle());
1701     stroker.setMiterLimit(pen.miterLimit());
1702     stroker.setCapStyle(pen.capStyle());
1703     stroker.setDashOffset(pen.dashOffset());
1704     stroker.setDashPattern(pen.dashPattern());
1705 
1706     // shortent the path to make it look nice
1707     // replace the point temporarily in case there is an arrow
1708     // BE AWARE: this changes the content of the path so that outline give the correct values.
1709     if (firstPoint) {
1710         firstSubpath->first() = firstSegments.second.first();
1711         if (secondPoint) {
1712             (*firstSubpath)[1] = firstSegments.second.second();
1713         }
1714     }
1715     if (lastPoint) {
1716         if (preLastPoint) {
1717             (*firstSubpath)[firstSubpath->count() - 2] = lastSegments.first.first();
1718         }
1719         firstSubpath->last() = lastSegments.first.second();
1720     }
1721 
1722     QPainterPath path = stroker.createStroke(outline());
1723 
1724     if (firstPoint) {
1725         firstSubpath->first() = firstPoint;
1726         if (secondPoint) {
1727             (*firstSubpath)[1] = secondPoint;
1728         }
1729     }
1730     if (lastPoint) {
1731         if (preLastPoint) {
1732             (*firstSubpath)[firstSubpath->count() - 2] = preLastPoint;
1733         }
1734         firstSubpath->last() = lastPoint;
1735     }
1736 
1737     pathOutline.addPath(path);
1738     pathOutline.setFillRule(Qt::WindingFill);
1739 
1740     return pathOutline;
1741 }