File indexing completed on 2024-05-12 15:56:44

0001 /* This file is part of the KDE project
0002  * SPDX-FileCopyrightText: 2007 Jan Hambrecht <jaham@gmx.net>
0003  *
0004  * SPDX-License-Identifier: LGPL-2.0-or-later
0005  */
0006 
0007 #include "KoPathShapeLoader.h"
0008 #include "KoPathShape.h"
0009 #include <math.h>
0010 #include <FlakeDebug.h>
0011 #include <kis_algebra_2d.h>
0012 
0013 class KoPathShapeLoaderPrivate
0014 {
0015 public:
0016     KoPathShapeLoaderPrivate(KoPathShape * p) : path(p) {
0017         Q_ASSERT(path);
0018         path->clear();
0019     }
0020 
0021     void parseSvg(const QString &svgInputData, bool process = false);
0022 
0023     void svgMoveTo(qreal x1, qreal y1, bool abs = true);
0024     void svgLineTo(qreal x1, qreal y1, bool abs = true);
0025     void svgLineToHorizontal(qreal x, bool abs = true);
0026     void svgLineToVertical(qreal y, bool abs = true);
0027     void svgCurveToCubic(qreal x1, qreal y1, qreal x2, qreal y2, qreal x, qreal y, bool abs = true);
0028     void svgCurveToCubicSmooth(qreal x, qreal y, qreal x2, qreal y2, bool abs = true);
0029     void svgCurveToQuadratic(qreal x, qreal y, qreal x1, qreal y1, bool abs = true);
0030     void svgCurveToQuadraticSmooth(qreal x, qreal y, bool abs = true);
0031     void svgArcTo(qreal x, qreal y, qreal r1, qreal r2, qreal angle, bool largeArcFlag, bool sweepFlag, bool abs = true);
0032     void svgClosePath();
0033 
0034     const char *getCoord(const char *, qreal &);
0035     const char *getFlag(const char *ptr, bool &flag);
0036     void calculateArc(bool relative, qreal &curx, qreal &cury, qreal angle, qreal x, qreal y, qreal r1, qreal r2, bool largeArcFlag, bool sweepFlag);
0037 
0038     KoPathShape * path; ///< the path shape to work on
0039     QPointF lastPoint;
0040 };
0041 
0042 void KoPathShapeLoaderPrivate::parseSvg(const QString &s, bool process)
0043 {
0044     if (!s.isEmpty()) {
0045         QString d = s;
0046         d.replace(',', ' ');
0047         d = d.simplified();
0048 
0049         const QByteArray buffer = d.toLatin1();
0050         const char *ptr = buffer.constData();
0051         const char *end = buffer.constData() + buffer.length() + 1;
0052 
0053         qreal curx = 0.0;
0054         qreal cury = 0.0;
0055         qreal contrlx, contrly, subpathx, subpathy, tox, toy, x1, y1, x2, y2, xc, yc;
0056         qreal px1, py1, px2, py2, px3, py3;
0057         bool relative;
0058         char command = *(ptr++), lastCommand = ' ';
0059 
0060         subpathx = subpathy = curx = cury = contrlx = contrly = 0.0;
0061         while (ptr < end) {
0062             if (*ptr == ' ')
0063                 ++ptr;
0064 
0065             relative = false;
0066 
0067             switch (command) {
0068             case 'm':
0069                 relative = true;
0070                 Q_FALLTHROUGH();
0071             case 'M': {
0072                 ptr = getCoord(ptr, tox);
0073                 ptr = getCoord(ptr, toy);
0074 
0075                 if (process) {
0076                     subpathx = curx = relative ? curx + tox : tox;
0077                     subpathy = cury = relative ? cury + toy : toy;
0078 
0079                     svgMoveTo(curx, cury);
0080                 } else
0081                     svgMoveTo(tox, toy, !relative);
0082                 break;
0083             }
0084             case 'l':
0085                 relative = true;
0086                 Q_FALLTHROUGH();
0087             case 'L': {
0088                 ptr = getCoord(ptr, tox);
0089                 ptr = getCoord(ptr, toy);
0090 
0091                 if (process) {
0092                     curx = relative ? curx + tox : tox;
0093                     cury = relative ? cury + toy : toy;
0094 
0095                     svgLineTo(curx, cury);
0096                 } else
0097                     svgLineTo(tox, toy, !relative);
0098                 break;
0099             }
0100             case 'h': {
0101                 ptr = getCoord(ptr, tox);
0102                 if (process) {
0103                     curx = curx + tox;
0104                     svgLineTo(curx, cury);
0105                 } else
0106                     svgLineToHorizontal(tox, false);
0107                 break;
0108             }
0109             case 'H': {
0110                 ptr = getCoord(ptr, tox);
0111                 if (process) {
0112                     curx = tox;
0113                     svgLineTo(curx, cury);
0114                 } else
0115                     svgLineToHorizontal(tox);
0116                 break;
0117             }
0118             case 'v': {
0119                 ptr = getCoord(ptr, toy);
0120                 if (process) {
0121                     cury = cury + toy;
0122                     svgLineTo(curx, cury);
0123                 } else
0124                     svgLineToVertical(toy, false);
0125                 break;
0126             }
0127             case 'V': {
0128                 ptr = getCoord(ptr, toy);
0129                 if (process) {
0130                     cury = toy;
0131                     svgLineTo(curx, cury);
0132                 } else
0133                     svgLineToVertical(toy);
0134                 break;
0135             }
0136             case 'z':
0137                 Q_FALLTHROUGH();
0138             case 'Z': {
0139                 // reset curx, cury for next path
0140                 if (process) {
0141                     curx = subpathx;
0142                     cury = subpathy;
0143                 }
0144                 svgClosePath();
0145                 break;
0146             }
0147             case 'c':
0148                 relative = true;
0149                 Q_FALLTHROUGH();
0150             case 'C': {
0151                 ptr = getCoord(ptr, x1);
0152                 ptr = getCoord(ptr, y1);
0153                 ptr = getCoord(ptr, x2);
0154                 ptr = getCoord(ptr, y2);
0155                 ptr = getCoord(ptr, tox);
0156                 ptr = getCoord(ptr, toy);
0157 
0158                 if (process) {
0159                     px1 = relative ? curx + x1 : x1;
0160                     py1 = relative ? cury + y1 : y1;
0161                     px2 = relative ? curx + x2 : x2;
0162                     py2 = relative ? cury + y2 : y2;
0163                     px3 = relative ? curx + tox : tox;
0164                     py3 = relative ? cury + toy : toy;
0165 
0166                     svgCurveToCubic(px1, py1, px2, py2, px3, py3);
0167 
0168                     contrlx = relative ? curx + x2 : x2;
0169                     contrly = relative ? cury + y2 : y2;
0170                     curx = relative ? curx + tox : tox;
0171                     cury = relative ? cury + toy : toy;
0172                 } else
0173                     svgCurveToCubic(x1, y1, x2, y2, tox, toy, !relative);
0174 
0175                 break;
0176             }
0177             case 's':
0178                 relative = true;
0179                 Q_FALLTHROUGH();
0180             case 'S': {
0181                 ptr = getCoord(ptr, x2);
0182                 ptr = getCoord(ptr, y2);
0183                 ptr = getCoord(ptr, tox);
0184                 ptr = getCoord(ptr, toy);
0185                 if (!(lastCommand == 'c' || lastCommand == 'C' ||
0186                         lastCommand == 's' || lastCommand == 'S')) {
0187                     contrlx = curx;
0188                     contrly = cury;
0189                 }
0190 
0191                 if (process) {
0192                     px1 = 2 * curx - contrlx;
0193                     py1 = 2 * cury - contrly;
0194                     px2 = relative ? curx + x2 : x2;
0195                     py2 = relative ? cury + y2 : y2;
0196                     px3 = relative ? curx + tox : tox;
0197                     py3 = relative ? cury + toy : toy;
0198 
0199                     svgCurveToCubic(px1, py1, px2, py2, px3, py3);
0200 
0201                     contrlx = relative ? curx + x2 : x2;
0202                     contrly = relative ? cury + y2 : y2;
0203                     curx = relative ? curx + tox : tox;
0204                     cury = relative ? cury + toy : toy;
0205                 } else
0206                     svgCurveToCubicSmooth(x2, y2, tox, toy, !relative);
0207                 break;
0208             }
0209             case 'q':
0210                 relative = true;
0211                 Q_FALLTHROUGH();
0212             case 'Q': {
0213                 ptr = getCoord(ptr, x1);
0214                 ptr = getCoord(ptr, y1);
0215                 ptr = getCoord(ptr, tox);
0216                 ptr = getCoord(ptr, toy);
0217 
0218                 if (process) {
0219                     px1 = relative ? (curx + 2 * (x1 + curx)) * (1.0 / 3.0) : (curx + 2 * x1) * (1.0 / 3.0);
0220                     py1 = relative ? (cury + 2 * (y1 + cury)) * (1.0 / 3.0) : (cury + 2 * y1) * (1.0 / 3.0);
0221                     px2 = relative ? ((curx + tox) + 2 * (x1 + curx)) * (1.0 / 3.0) : (tox + 2 * x1) * (1.0 / 3.0);
0222                     py2 = relative ? ((cury + toy) + 2 * (y1 + cury)) * (1.0 / 3.0) : (toy + 2 * y1) * (1.0 / 3.0);
0223                     px3 = relative ? curx + tox : tox;
0224                     py3 = relative ? cury + toy : toy;
0225 
0226                     svgCurveToCubic(px1, py1, px2, py2, px3, py3);
0227 
0228                     contrlx = relative ? curx + x1 : x1;
0229                     contrly = relative ? cury + y1 : y1;
0230                     curx = relative ? curx + tox : tox;
0231                     cury = relative ? cury + toy : toy;
0232                 } else
0233                     svgCurveToQuadratic(x1, y1, tox, toy, !relative);
0234                 break;
0235             }
0236             case 't':
0237                 relative = true;
0238                 Q_FALLTHROUGH();
0239             case 'T': {
0240                 ptr = getCoord(ptr, tox);
0241                 ptr = getCoord(ptr, toy);
0242                 if (!(lastCommand == 'q' || lastCommand == 'Q' ||
0243                         lastCommand == 't' || lastCommand == 'T')) {
0244                     contrlx = curx;
0245                     contrly = cury;
0246                 }
0247 
0248                 if (process) {
0249                     xc = 2 * curx - contrlx;
0250                     yc = 2 * cury - contrly;
0251 
0252                     px1 = (curx + 2 * xc) * (1.0 / 3.0);
0253                     py1 = (cury + 2 * yc) * (1.0 / 3.0);
0254                     px2 = relative ? ((curx + tox) + 2 * xc) * (1.0 / 3.0) : (tox + 2 * xc) * (1.0 / 3.0);
0255                     py2 = relative ? ((cury + toy) + 2 * yc) * (1.0 / 3.0) : (toy + 2 * yc) * (1.0 / 3.0);
0256                     px3 = relative ? curx + tox : tox;
0257                     py3 = relative ? cury + toy : toy;
0258 
0259                     svgCurveToCubic(px1, py1, px2, py2, px3, py3);
0260 
0261                     contrlx = xc;
0262                     contrly = yc;
0263                     curx = relative ? curx + tox : tox;
0264                     cury = relative ? cury + toy : toy;
0265                 } else
0266                     svgCurveToQuadraticSmooth(tox, toy, !relative);
0267                 break;
0268             }
0269             case 'a':
0270                 relative = true;
0271                 Q_FALLTHROUGH();
0272             case 'A': {
0273                 bool largeArc = false;
0274                 bool sweep = false;
0275                 qreal angle, rx, ry;
0276                 ptr = getCoord(ptr, rx);
0277                 ptr = getCoord(ptr, ry);
0278                 ptr = getCoord(ptr, angle);
0279                 ptr = getFlag(ptr, largeArc);
0280                 ptr = getFlag(ptr, sweep);
0281                 ptr = getCoord(ptr, tox);
0282                 ptr = getCoord(ptr, toy);
0283 
0284                 // Spec: radii are nonnegative numbers
0285                 rx = fabs(rx);
0286                 ry = fabs(ry);
0287 
0288                 if (process)
0289                     calculateArc(relative, curx, cury, angle, tox, toy, rx, ry, largeArc, sweep);
0290                 else
0291                     svgArcTo(tox, toy, rx, ry, angle, largeArc, sweep, !relative);
0292                 break;
0293             }
0294             default: {
0295                 // when svg parser is used for a parsing an odf path an unknown command
0296                 // can be encountered, so we stop parsing here
0297                 debugFlake << "KoSvgPathParser::parseSVG(): unknown command \"" << command << "\"";
0298                 return;
0299             }
0300             }
0301 
0302             lastCommand = command;
0303 
0304             if (*ptr == '+' || *ptr == '-' || *ptr == '.' || (*ptr >= '0' && *ptr <= '9')) {
0305                 // there are still coords in this command
0306                 if (command == 'M')
0307                     command = 'L';
0308                 else if (command == 'm')
0309                     command = 'l';
0310             } else
0311                 command = *(ptr++);
0312 
0313             if (lastCommand != 'C' && lastCommand != 'c' &&
0314                     lastCommand != 'S' && lastCommand != 's' &&
0315                     lastCommand != 'Q' && lastCommand != 'q' &&
0316                     lastCommand != 'T' && lastCommand != 't') {
0317                 contrlx = curx;
0318                 contrly = cury;
0319             }
0320         }
0321     }
0322 }
0323 
0324 // parses the coord into number and forwards to the next token
0325 const char * KoPathShapeLoaderPrivate::getCoord(const char *ptr, qreal &number)
0326 {
0327     int integer, exponent;
0328     qreal decimal, frac;
0329     int sign, expsign;
0330 
0331     exponent = 0;
0332     integer = 0;
0333     frac = 1.0;
0334     decimal = 0;
0335     sign = 1;
0336     expsign = 1;
0337 
0338     // read the sign
0339     if (*ptr == '+')
0340         ++ptr;
0341     else if (*ptr == '-') {
0342         ++ptr;
0343         sign = -1;
0344     }
0345 
0346     // read the integer part
0347     while (*ptr != '\0' && *ptr >= '0' && *ptr <= '9')
0348         integer = (integer * 10) + *(ptr++) - '0';
0349     if (*ptr == '.') { // read the decimals
0350         ++ptr;
0351         while (*ptr != '\0' && *ptr >= '0' && *ptr <= '9')
0352             decimal += (*(ptr++) - '0') * (frac *= 0.1);
0353     }
0354 
0355     if (*ptr == 'e' || *ptr == 'E') { // read the exponent part
0356         ++ptr;
0357 
0358         // read the sign of the exponent
0359         if (*ptr == '+')
0360             ++ptr;
0361         else if (*ptr == '-') {
0362             ++ptr;
0363             expsign = -1;
0364         }
0365 
0366         exponent = 0;
0367         while (*ptr != '\0' && *ptr >= '0' && *ptr <= '9') {
0368             exponent *= 10;
0369             exponent += *ptr - '0';
0370             ++ptr;
0371         }
0372     }
0373     number = integer + decimal;
0374     number *= sign * pow((qreal)10, qreal(expsign * exponent));
0375 
0376     // skip the following space
0377     if (*ptr == ' ')
0378         ++ptr;
0379 
0380     return ptr;
0381 }
0382 
0383 const char *KoPathShapeLoaderPrivate::getFlag(const char *ptr, bool &flag)
0384 {
0385     // check for '0' or '1'
0386     if (*ptr != '0' && *ptr != '1') {
0387         return ptr;
0388     }
0389     flag = (*ptr == '1');
0390     ++ptr;
0391 
0392     if (*ptr == ' ') {
0393         ++ptr;
0394     }
0395     return ptr;
0396 }
0397 
0398 // This works by converting the SVG arc to "simple" beziers.
0399 // For each bezier found a svgToCurve call is done.
0400 void KoPathShapeLoaderPrivate::calculateArc(bool relative, qreal &curx, qreal &cury, qreal angle, qreal x, qreal y, qreal rx, qreal ry, bool largeArcFlag, bool sweepFlag)
0401 {
0402     // if radii are zero or the endpoints of ellipse are same as its start point, then use lineTo/don't do
0403     // anything.
0404     if (qFuzzyCompare(rx, 0.0) || qFuzzyCompare(ry, 0.0)
0405         || (!relative && qFuzzyCompare(curx - x, 0) && qFuzzyCompare(cury - y, 0))
0406         || (relative && qFuzzyCompare(x, 0) && qFuzzyCompare(y, 0))) {
0407         qreal x2 = x;
0408         qreal y2 = y;
0409 
0410         if (relative) {
0411             x2 += curx;
0412             y2 += cury;
0413         }
0414         svgLineTo(x2, y2);
0415         return;
0416     }
0417 
0418     const qreal angleRadians = angle * (M_PI / 180.0);
0419     const qreal sin_th = sin(angleRadians);
0420     const qreal cos_th = cos(angleRadians);
0421 
0422     qreal dx;
0423     qreal x2 = x;
0424     if (!relative) {
0425         dx = (curx - x) / 2.0;
0426     } else {
0427         dx = -(x / 2.0);
0428         x2 = curx + x;
0429     }
0430 
0431     qreal dy;
0432     qreal y2 = y;
0433     if (!relative) {
0434         dy = (cury - y) / 2.0;
0435     } else {
0436         dy = -(y / 2.0);
0437         y2 = cury + y;
0438     }
0439 
0440     // From SVG spec
0441 
0442     // Step 1: Compute (x1_prime, y1_prime)
0443     const qreal x1Prime = cos_th * dx + sin_th * dy;
0444     const qreal y1Prime = -sin_th * dx + cos_th * dy;
0445 
0446     // eq. 5.1
0447     const qreal x1PrimeSq = x1Prime * x1Prime;
0448     const qreal y1PrimeSq = y1Prime * y1Prime;
0449 
0450     // Step 2: Compute (c_x_prime, c_y_prime)
0451     qreal rxSq = rx * rx;
0452     qreal rySq = ry * ry;
0453 
0454     // Spec : check if radii are large enough
0455     // eq. 6.2
0456     const qreal check = x1PrimeSq / rxSq + y1PrimeSq / rySq;
0457     if (check > 1) {
0458         // eq. 6.3
0459         rx = rx * sqrt(check);
0460         ry = ry * sqrt(check);
0461 
0462         rxSq = rx * rx;
0463         rySq = ry * ry;
0464     }
0465 
0466     // Step 2: Compute (c_x_prime, c_y_prime)
0467     const qreal radiiSq = rxSq * rySq;
0468     const qreal ellipseValue = rxSq * y1PrimeSq + rySq * x1PrimeSq;
0469 
0470     qreal coef = sqrt(std::fabs((radiiSq - ellipseValue) / ellipseValue));
0471     if (sweepFlag == largeArcFlag) {
0472         coef = -coef;
0473     }
0474     // eq. 5.2
0475     const qreal cxPrime = coef * (rx * y1Prime) / ry;
0476     const qreal cyPrime = coef * -(ry * x1Prime) / rx;
0477 
0478     // Step 3: Compute (c_x, c_y) from (c_x_prime, c_y_prime)
0479     // eq. 5.3
0480     const qreal cx = cos_th * cxPrime - sin_th * cyPrime + (curx + x2) * 0.5;
0481     const qreal cy = sin_th * cxPrime + cos_th * cyPrime + (cury + y2) * 0.5;
0482 
0483     // Step 4: Compute angle and delta
0484     const QPointF v = {(x1Prime - cxPrime) / rx, (y1Prime - cyPrime) / ry};
0485     // eq. 5.5
0486     const qreal theta = KisAlgebra2D::angleBetweenVectors({1.0, 0.0}, v);
0487     // eq. 5.6
0488     qreal delta = std::fmod(
0489         KisAlgebra2D::angleBetweenVectors(v, {(-x1Prime - cxPrime) / rx, (-y1Prime - cyPrime) / ry}),
0490         M_PI * 2);
0491 
0492     if (sweepFlag && delta < 0) {
0493         delta += (M_PI * 2);
0494     } else if (!sweepFlag && delta > 0) {
0495         delta -= (M_PI * 2);
0496     }
0497 
0498     int n_segs = (int)ceil(fabs(delta / (M_PI * 0.25)));
0499 
0500     // From: http://www.spaceroots.org/documents/ellipse/elliptical-arc.pdf
0501     for (int i = 0; i < n_segs; ++i) {
0502         const qreal eta1 = theta + i * delta / n_segs;
0503         const qreal eta2 = theta + (i + 1) * delta / n_segs;
0504 
0505         const qreal etaHalf = 0.5 * (eta2 - eta1);
0506 
0507         const qreal cosAngle = cos(angleRadians);
0508         const qreal sinAngle = sin(angleRadians);
0509 
0510         auto ellipseArcToPoint = [sinAngle, cosAngle](qreal cx, qreal cy, qreal eta, qreal rx, qreal ry) {
0511             qreal x = cx + (rx * cosAngle * cos(eta)) - (ry * sinAngle * sin(eta));
0512             qreal y = cy + (rx * sinAngle * cos(eta)) + (ry * cosAngle * sin(eta));
0513             return QPointF(x, y);
0514         };
0515         auto ellipseDerivativeArcToPoint = [sinAngle, cosAngle](qreal eta, qreal rx, qreal ry) {
0516             qreal x = -(rx * cosAngle * sin(eta)) - (ry * sinAngle * cos(eta));
0517             qreal y = -(rx * sinAngle * sin(eta)) + (ry * cosAngle * cos(eta));
0518             return QPointF(x, y);
0519         };
0520 
0521         // bezier control points
0522         const QPointF p1 = ellipseArcToPoint(cx, cy, eta1, rx, ry);
0523         const QPointF p2 = ellipseArcToPoint(cx, cy, eta2, rx, ry);
0524 
0525         const qreal alpha = sin(eta2 - eta1) * (sqrt(4 + 3 * tan(etaHalf) * tan(etaHalf)) - 1) / 3;
0526 
0527         const QPointF q1 = p1 + alpha * ellipseDerivativeArcToPoint(eta1, rx, ry);
0528         const QPointF q2 = p2 - alpha * ellipseDerivativeArcToPoint(eta2, rx, ry);
0529 
0530         svgCurveToCubic(q1.x(), q1.y(), q2.x(), q2.y(), p2.x(), p2.y());
0531     }
0532 
0533     if (!relative)
0534         curx = x;
0535     else
0536         curx += x;
0537 
0538     if (!relative)
0539         cury = y;
0540     else
0541         cury += y;
0542 }
0543 
0544 void KoPathShapeLoaderPrivate::svgMoveTo(qreal x1, qreal y1, bool abs)
0545 {
0546     if (abs)
0547         lastPoint = QPointF(x1, y1);
0548     else
0549         lastPoint += QPointF(x1, y1);
0550     path->moveTo(lastPoint);
0551 }
0552 
0553 void KoPathShapeLoaderPrivate::svgLineTo(qreal x1, qreal y1, bool abs)
0554 {
0555     if (abs)
0556         lastPoint = QPointF(x1, y1);
0557     else
0558         lastPoint += QPointF(x1, y1);
0559 
0560     path->lineTo(lastPoint);
0561 }
0562 
0563 void KoPathShapeLoaderPrivate::svgLineToHorizontal(qreal x, bool abs)
0564 {
0565     if (abs)
0566         lastPoint.setX(x);
0567     else
0568         lastPoint.rx() += x;
0569 
0570     path->lineTo(lastPoint);
0571 }
0572 
0573 void KoPathShapeLoaderPrivate::svgLineToVertical(qreal y, bool abs)
0574 {
0575     if (abs)
0576         lastPoint.setY(y);
0577     else
0578         lastPoint.ry() += y;
0579 
0580     path->lineTo(lastPoint);
0581 }
0582 
0583 void KoPathShapeLoaderPrivate::svgCurveToCubic(qreal x1, qreal y1, qreal x2, qreal y2, qreal x, qreal y, bool abs)
0584 {
0585     QPointF p1, p2;
0586     if (abs) {
0587         p1 = QPointF(x1, y1);
0588         p2 = QPointF(x2, y2);
0589         lastPoint = QPointF(x, y);
0590     } else {
0591         p1 = lastPoint + QPointF(x1, y1);
0592         p2 = lastPoint + QPointF(x2, y2);
0593         lastPoint += QPointF(x, y);
0594     }
0595 
0596     path->curveTo(p1, p2, lastPoint);
0597 }
0598 
0599 void KoPathShapeLoaderPrivate::svgCurveToCubicSmooth(qreal x, qreal y, qreal x2, qreal y2, bool abs)
0600 {
0601     Q_UNUSED(x);
0602     Q_UNUSED(y);
0603     Q_UNUSED(x2);
0604     Q_UNUSED(y2);
0605     Q_UNUSED(abs);
0606     // TODO implement
0607 }
0608 
0609 void KoPathShapeLoaderPrivate::svgCurveToQuadratic(qreal x, qreal y, qreal x1, qreal y1, bool abs)
0610 {
0611     Q_UNUSED(x);
0612     Q_UNUSED(y);
0613     Q_UNUSED(x1);
0614     Q_UNUSED(y1);
0615     Q_UNUSED(abs);
0616     // TODO implement
0617 }
0618 
0619 void KoPathShapeLoaderPrivate::svgCurveToQuadraticSmooth(qreal x, qreal y, bool abs)
0620 {
0621     Q_UNUSED(x);
0622     Q_UNUSED(y);
0623     Q_UNUSED(abs);
0624     // TODO implement
0625 }
0626 
0627 void KoPathShapeLoaderPrivate::svgArcTo(qreal x, qreal y, qreal r1, qreal r2, qreal angle, bool largeArcFlag, bool sweepFlag, bool abs)
0628 {
0629     Q_UNUSED(x);
0630     Q_UNUSED(y);
0631     Q_UNUSED(r1);
0632     Q_UNUSED(r2);
0633     Q_UNUSED(angle);
0634     Q_UNUSED(largeArcFlag);
0635     Q_UNUSED(sweepFlag);
0636     Q_UNUSED(abs);
0637     // TODO implement
0638 }
0639 
0640 void KoPathShapeLoaderPrivate::svgClosePath()
0641 {
0642     path->closeMerge();
0643 }
0644 
0645 KoPathShapeLoader::KoPathShapeLoader(KoPathShape *path)
0646     : d(new KoPathShapeLoaderPrivate(path))
0647 {
0648 }
0649 
0650 KoPathShapeLoader::~KoPathShapeLoader()
0651 {
0652     delete d;
0653 }
0654 
0655 void KoPathShapeLoader::parseSvg(const QString &s, bool process)
0656 {
0657     d->parseSvg(s, process);
0658 }