Warning, file /office/calligra/libs/flake/KoPathShapeLoader.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) 2007 Jan Hambrecht <jaham@gmx.net> 0003 * 0004 * This library is free software; you can redistribute it and/or 0005 * modify it under the terms of the GNU Library General Public 0006 * License as published by the Free Software Foundation; either 0007 * version 2 of the License, or (at your option) any later version. 0008 * 0009 * This library is distributed in the hope that it will be useful, 0010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0012 * Library General Public License for more details. 0013 * 0014 * You should have received a copy of the GNU Library General Public License 0015 * along with this library; see the file COPYING.LIB. If not, write to 0016 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0017 * Boston, MA 02110-1301, USA. 0018 */ 0019 0020 #include "KoPathShapeLoader.h" 0021 #include "KoPathShape.h" 0022 #include <math.h> 0023 #include <FlakeDebug.h> 0024 0025 class KoPathShapeLoaderPrivate 0026 { 0027 public: 0028 KoPathShapeLoaderPrivate(KoPathShape * p) : path(p) { 0029 Q_ASSERT(path); 0030 path->clear(); 0031 } 0032 0033 void parseSvg(const QString &svgInputData, bool process = false); 0034 0035 void svgMoveTo(qreal x1, qreal y1, bool abs = true); 0036 void svgLineTo(qreal x1, qreal y1, bool abs = true); 0037 void svgLineToHorizontal(qreal x, bool abs = true); 0038 void svgLineToVertical(qreal y, bool abs = true); 0039 void svgCurveToCubic(qreal x1, qreal y1, qreal x2, qreal y2, qreal x, qreal y, bool abs = true); 0040 void svgCurveToCubicSmooth(qreal x, qreal y, qreal x2, qreal y2, bool abs = true); 0041 void svgCurveToQuadratic(qreal x, qreal y, qreal x1, qreal y1, bool abs = true); 0042 void svgCurveToQuadraticSmooth(qreal x, qreal y, bool abs = true); 0043 void svgArcTo(qreal x, qreal y, qreal r1, qreal r2, qreal angle, bool largeArcFlag, bool sweepFlag, bool abs = true); 0044 void svgClosePath(); 0045 0046 const char *getCoord(const char *, qreal &); 0047 void calculateArc(bool relative, qreal &curx, qreal &cury, qreal angle, qreal x, qreal y, qreal r1, qreal r2, bool largeArcFlag, bool sweepFlag); 0048 0049 KoPathShape * path; ///< the path shape to work on 0050 QPointF lastPoint; 0051 }; 0052 0053 void KoPathShapeLoaderPrivate::parseSvg(const QString &s, bool process) 0054 { 0055 if (!s.isEmpty()) { 0056 QString d = s; 0057 d.replace(',', ' '); 0058 d = d.simplified(); 0059 0060 const QByteArray buffer = d.toLatin1(); 0061 const char *ptr = buffer.constData(); 0062 const char *end = buffer.constData() + buffer.length() + 1; 0063 0064 qreal curx = 0.0; 0065 qreal cury = 0.0; 0066 qreal contrlx, contrly, subpathx, subpathy, tox, toy, x1, y1, x2, y2, xc, yc; 0067 qreal px1, py1, px2, py2, px3, py3; 0068 bool relative; 0069 char command = *(ptr++), lastCommand = ' '; 0070 0071 subpathx = subpathy = curx = cury = contrlx = contrly = 0.0; 0072 while (ptr < end) { 0073 if (*ptr == ' ') 0074 ++ptr; 0075 0076 relative = false; 0077 0078 switch (command) { 0079 case 'm': 0080 relative = true; 0081 case 'M': { 0082 ptr = getCoord(ptr, tox); 0083 ptr = getCoord(ptr, toy); 0084 0085 if (process) { 0086 subpathx = curx = relative ? curx + tox : tox; 0087 subpathy = cury = relative ? cury + toy : toy; 0088 0089 svgMoveTo(curx, cury); 0090 } else 0091 svgMoveTo(tox, toy, !relative); 0092 break; 0093 } 0094 case 'l': 0095 relative = true; 0096 case 'L': { 0097 ptr = getCoord(ptr, tox); 0098 ptr = getCoord(ptr, toy); 0099 0100 if (process) { 0101 curx = relative ? curx + tox : tox; 0102 cury = relative ? cury + toy : toy; 0103 0104 svgLineTo(curx, cury); 0105 } else 0106 svgLineTo(tox, toy, !relative); 0107 break; 0108 } 0109 case 'h': { 0110 ptr = getCoord(ptr, tox); 0111 if (process) { 0112 curx = curx + tox; 0113 svgLineTo(curx, cury); 0114 } else 0115 svgLineToHorizontal(tox, false); 0116 break; 0117 } 0118 case 'H': { 0119 ptr = getCoord(ptr, tox); 0120 if (process) { 0121 curx = tox; 0122 svgLineTo(curx, cury); 0123 } else 0124 svgLineToHorizontal(tox); 0125 break; 0126 } 0127 case 'v': { 0128 ptr = getCoord(ptr, toy); 0129 if (process) { 0130 cury = cury + toy; 0131 svgLineTo(curx, cury); 0132 } else 0133 svgLineToVertical(toy, false); 0134 break; 0135 } 0136 case 'V': { 0137 ptr = getCoord(ptr, toy); 0138 if (process) { 0139 cury = toy; 0140 svgLineTo(curx, cury); 0141 } else 0142 svgLineToVertical(toy); 0143 break; 0144 } 0145 case 'z': 0146 case 'Z': { 0147 // reset curx, cury for next path 0148 if (process) { 0149 curx = subpathx; 0150 cury = subpathy; 0151 } 0152 svgClosePath(); 0153 break; 0154 } 0155 case 'c': 0156 relative = true; 0157 case 'C': { 0158 ptr = getCoord(ptr, x1); 0159 ptr = getCoord(ptr, y1); 0160 ptr = getCoord(ptr, x2); 0161 ptr = getCoord(ptr, y2); 0162 ptr = getCoord(ptr, tox); 0163 ptr = getCoord(ptr, toy); 0164 0165 if (process) { 0166 px1 = relative ? curx + x1 : x1; 0167 py1 = relative ? cury + y1 : y1; 0168 px2 = relative ? curx + x2 : x2; 0169 py2 = relative ? cury + y2 : y2; 0170 px3 = relative ? curx + tox : tox; 0171 py3 = relative ? cury + toy : toy; 0172 0173 svgCurveToCubic(px1, py1, px2, py2, px3, py3); 0174 0175 contrlx = relative ? curx + x2 : x2; 0176 contrly = relative ? cury + y2 : y2; 0177 curx = relative ? curx + tox : tox; 0178 cury = relative ? cury + toy : toy; 0179 } else 0180 svgCurveToCubic(x1, y1, x2, y2, tox, toy, !relative); 0181 0182 break; 0183 } 0184 case 's': 0185 relative = true; 0186 case 'S': { 0187 ptr = getCoord(ptr, x2); 0188 ptr = getCoord(ptr, y2); 0189 ptr = getCoord(ptr, tox); 0190 ptr = getCoord(ptr, toy); 0191 if (!(lastCommand == 'c' || lastCommand == 'C' || 0192 lastCommand == 's' || lastCommand == 'S')) { 0193 contrlx = curx; 0194 contrly = cury; 0195 } 0196 0197 if (process) { 0198 px1 = 2 * curx - contrlx; 0199 py1 = 2 * cury - contrly; 0200 px2 = relative ? curx + x2 : x2; 0201 py2 = relative ? cury + y2 : y2; 0202 px3 = relative ? curx + tox : tox; 0203 py3 = relative ? cury + toy : toy; 0204 0205 svgCurveToCubic(px1, py1, px2, py2, px3, py3); 0206 0207 contrlx = relative ? curx + x2 : x2; 0208 contrly = relative ? cury + y2 : y2; 0209 curx = relative ? curx + tox : tox; 0210 cury = relative ? cury + toy : toy; 0211 } else 0212 svgCurveToCubicSmooth(x2, y2, tox, toy, !relative); 0213 break; 0214 } 0215 case 'q': 0216 relative = true; 0217 case 'Q': { 0218 ptr = getCoord(ptr, x1); 0219 ptr = getCoord(ptr, y1); 0220 ptr = getCoord(ptr, tox); 0221 ptr = getCoord(ptr, toy); 0222 0223 if (process) { 0224 px1 = relative ? (curx + 2 * (x1 + curx)) * (1.0 / 3.0) : (curx + 2 * x1) * (1.0 / 3.0); 0225 py1 = relative ? (cury + 2 * (y1 + cury)) * (1.0 / 3.0) : (cury + 2 * y1) * (1.0 / 3.0); 0226 px2 = relative ? ((curx + tox) + 2 * (x1 + curx)) * (1.0 / 3.0) : (tox + 2 * x1) * (1.0 / 3.0); 0227 py2 = relative ? ((cury + toy) + 2 * (y1 + cury)) * (1.0 / 3.0) : (toy + 2 * y1) * (1.0 / 3.0); 0228 px3 = relative ? curx + tox : tox; 0229 py3 = relative ? cury + toy : toy; 0230 0231 svgCurveToCubic(px1, py1, px2, py2, px3, py3); 0232 0233 contrlx = relative ? curx + x1 : x1; 0234 contrly = relative ? cury + y1 : y1; 0235 curx = relative ? curx + tox : tox; 0236 cury = relative ? cury + toy : toy; 0237 } else 0238 svgCurveToQuadratic(x1, y1, tox, toy, !relative); 0239 break; 0240 } 0241 case 't': 0242 relative = true; 0243 case 'T': { 0244 ptr = getCoord(ptr, tox); 0245 ptr = getCoord(ptr, toy); 0246 if (!(lastCommand == 'q' || lastCommand == 'Q' || 0247 lastCommand == 't' || lastCommand == 'T')) { 0248 contrlx = curx; 0249 contrly = cury; 0250 } 0251 0252 if (process) { 0253 xc = 2 * curx - contrlx; 0254 yc = 2 * cury - contrly; 0255 0256 px1 = (curx + 2 * xc) * (1.0 / 3.0); 0257 py1 = (cury + 2 * yc) * (1.0 / 3.0); 0258 px2 = relative ? ((curx + tox) + 2 * xc) * (1.0 / 3.0) : (tox + 2 * xc) * (1.0 / 3.0); 0259 py2 = relative ? ((cury + toy) + 2 * yc) * (1.0 / 3.0) : (toy + 2 * yc) * (1.0 / 3.0); 0260 px3 = relative ? curx + tox : tox; 0261 py3 = relative ? cury + toy : toy; 0262 0263 svgCurveToCubic(px1, py1, px2, py2, px3, py3); 0264 0265 contrlx = xc; 0266 contrly = yc; 0267 curx = relative ? curx + tox : tox; 0268 cury = relative ? cury + toy : toy; 0269 } else 0270 svgCurveToQuadraticSmooth(tox, toy, !relative); 0271 break; 0272 } 0273 case 'a': 0274 relative = true; 0275 case 'A': { 0276 bool largeArc, sweep; 0277 qreal angle, rx, ry; 0278 ptr = getCoord(ptr, rx); 0279 ptr = getCoord(ptr, ry); 0280 ptr = getCoord(ptr, angle); 0281 ptr = getCoord(ptr, tox); 0282 largeArc = tox == 1; 0283 ptr = getCoord(ptr, tox); 0284 sweep = tox == 1; 0285 ptr = getCoord(ptr, tox); 0286 ptr = getCoord(ptr, toy); 0287 0288 // Spec: radii are nonnegative numbers 0289 rx = fabs(rx); 0290 ry = fabs(ry); 0291 0292 if (process) 0293 calculateArc(relative, curx, cury, angle, tox, toy, rx, ry, largeArc, sweep); 0294 else 0295 svgArcTo(tox, toy, rx, ry, angle, largeArc, sweep, !relative); 0296 break; 0297 } 0298 default: { 0299 // when svg parser is used for a parsing an odf path an unknown command 0300 // can be encountered, so we stop parsing here 0301 debugFlake << "KoSvgPathParser::parseSVG(): unknown command \"" << command << "\""; 0302 return; 0303 } 0304 } 0305 0306 lastCommand = command; 0307 0308 if (*ptr == '+' || *ptr == '-' || (*ptr >= '0' && *ptr <= '9')) { 0309 // there are still coords in this command 0310 if (command == 'M') 0311 command = 'L'; 0312 else if (command == 'm') 0313 command = 'l'; 0314 } else 0315 command = *(ptr++); 0316 0317 if (lastCommand != 'C' && lastCommand != 'c' && 0318 lastCommand != 'S' && lastCommand != 's' && 0319 lastCommand != 'Q' && lastCommand != 'q' && 0320 lastCommand != 'T' && lastCommand != 't') { 0321 contrlx = curx; 0322 contrly = cury; 0323 } 0324 } 0325 } 0326 } 0327 0328 // parses the coord into number and forwards to the next token 0329 const char * KoPathShapeLoaderPrivate::getCoord(const char *ptr, qreal &number) 0330 { 0331 int integer, exponent; 0332 qreal decimal, frac; 0333 int sign, expsign; 0334 0335 exponent = 0; 0336 integer = 0; 0337 frac = 1.0; 0338 decimal = 0; 0339 sign = 1; 0340 expsign = 1; 0341 0342 // read the sign 0343 if (*ptr == '+') 0344 ++ptr; 0345 else if (*ptr == '-') { 0346 ++ptr; 0347 sign = -1; 0348 } 0349 0350 // read the integer part 0351 while (*ptr != '\0' && *ptr >= '0' && *ptr <= '9') 0352 integer = (integer * 10) + *(ptr++) - '0'; 0353 if (*ptr == '.') { // read the decimals 0354 ++ptr; 0355 while (*ptr != '\0' && *ptr >= '0' && *ptr <= '9') 0356 decimal += (*(ptr++) - '0') * (frac *= 0.1); 0357 } 0358 0359 if (*ptr == 'e' || *ptr == 'E') { // read the exponent part 0360 ++ptr; 0361 0362 // read the sign of the exponent 0363 if (*ptr == '+') 0364 ++ptr; 0365 else if (*ptr == '-') { 0366 ++ptr; 0367 expsign = -1; 0368 } 0369 0370 exponent = 0; 0371 while (*ptr != '\0' && *ptr >= '0' && *ptr <= '9') { 0372 exponent *= 10; 0373 exponent += *ptr - '0'; 0374 ++ptr; 0375 } 0376 } 0377 number = integer + decimal; 0378 number *= sign * pow((qreal)10, qreal(expsign * exponent)); 0379 0380 // skip the following space 0381 if (*ptr == ' ') 0382 ++ptr; 0383 0384 return ptr; 0385 } 0386 0387 // This works by converting the SVG arc to "simple" beziers. 0388 // For each bezier found a svgToCurve call is done. 0389 // Adapted from Niko's code in kdelibs/kdecore/svgicons. 0390 // Maybe this can serve in some shared lib? (Rob) 0391 void KoPathShapeLoaderPrivate::calculateArc(bool relative, qreal &curx, qreal &cury, qreal angle, qreal x, qreal y, qreal r1, qreal r2, bool largeArcFlag, bool sweepFlag) 0392 { 0393 qreal sin_th, cos_th; 0394 qreal a00, a01, a10, a11; 0395 qreal x0, y0, x1, y1, xc, yc; 0396 qreal d, sfactor, sfactor_sq; 0397 qreal th0, th1, th_arc; 0398 int i, n_segs; 0399 0400 sin_th = sin(angle * (M_PI / 180.0)); 0401 cos_th = cos(angle * (M_PI / 180.0)); 0402 0403 qreal dx; 0404 0405 if (!relative) 0406 dx = (curx - x) / 2.0; 0407 else 0408 dx = -x / 2.0; 0409 0410 qreal dy; 0411 0412 if (!relative) 0413 dy = (cury - y) / 2.0; 0414 else 0415 dy = -y / 2.0; 0416 0417 qreal _x1 = cos_th * dx + sin_th * dy; 0418 qreal _y1 = -sin_th * dx + cos_th * dy; 0419 qreal Pr1 = r1 * r1; 0420 qreal Pr2 = r2 * r2; 0421 qreal Px = _x1 * _x1; 0422 qreal Py = _y1 * _y1; 0423 0424 // Spec : check if radii are large enough 0425 qreal check = Px / Pr1 + Py / Pr2; 0426 if (check > 1) { 0427 r1 = r1 * sqrt(check); 0428 r2 = r2 * sqrt(check); 0429 } 0430 0431 a00 = cos_th / r1; 0432 a01 = sin_th / r1; 0433 a10 = -sin_th / r2; 0434 a11 = cos_th / r2; 0435 0436 x0 = a00 * curx + a01 * cury; 0437 y0 = a10 * curx + a11 * cury; 0438 0439 if (!relative) 0440 x1 = a00 * x + a01 * y; 0441 else 0442 x1 = a00 * (curx + x) + a01 * (cury + y); 0443 0444 if (!relative) 0445 y1 = a10 * x + a11 * y; 0446 else 0447 y1 = a10 * (curx + x) + a11 * (cury + y); 0448 0449 /* (x0, y0) is current point in transformed coordinate space. 0450 (x1, y1) is new point in transformed coordinate space. 0451 0452 The arc fits a unit-radius circle in this space. 0453 */ 0454 0455 d = (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0); 0456 0457 sfactor_sq = 1.0 / d - 0.25; 0458 0459 if (sfactor_sq < 0) 0460 sfactor_sq = 0; 0461 0462 sfactor = sqrt(sfactor_sq); 0463 0464 if (sweepFlag == largeArcFlag) 0465 sfactor = -sfactor; 0466 0467 xc = 0.5 * (x0 + x1) - sfactor * (y1 - y0); 0468 yc = 0.5 * (y0 + y1) + sfactor * (x1 - x0); 0469 0470 /* (xc, yc) is center of the circle. */ 0471 th0 = atan2(y0 - yc, x0 - xc); 0472 th1 = atan2(y1 - yc, x1 - xc); 0473 0474 th_arc = th1 - th0; 0475 if (th_arc < 0 && sweepFlag) 0476 th_arc += 2 * M_PI; 0477 else if (th_arc > 0 && !sweepFlag) 0478 th_arc -= 2 * M_PI; 0479 0480 n_segs = (int)(int) ceil(fabs(th_arc / (M_PI * 0.5 + 0.001))); 0481 0482 for (i = 0; i < n_segs; ++i) { 0483 { 0484 qreal sin_th, cos_th; 0485 qreal a00, a01, a10, a11; 0486 qreal x1, y1, x2, y2, x3, y3; 0487 qreal t; 0488 qreal th_half; 0489 0490 qreal _th0 = th0 + i * th_arc / n_segs; 0491 qreal _th1 = th0 + (i + 1) * th_arc / n_segs; 0492 0493 sin_th = sin(angle * (M_PI / 180.0)); 0494 cos_th = cos(angle * (M_PI / 180.0)); 0495 0496 /* inverse transform compared with rsvg_path_arc */ 0497 a00 = cos_th * r1; 0498 a01 = -sin_th * r2; 0499 a10 = sin_th * r1; 0500 a11 = cos_th * r2; 0501 0502 th_half = 0.5 * (_th1 - _th0); 0503 t = (8.0 / 3.0) * sin(th_half * 0.5) * sin(th_half * 0.5) / sin(th_half); 0504 x1 = xc + cos(_th0) - t * sin(_th0); 0505 y1 = yc + sin(_th0) + t * cos(_th0); 0506 x3 = xc + cos(_th1); 0507 y3 = yc + sin(_th1); 0508 x2 = x3 + t * sin(_th1); 0509 y2 = y3 - t * cos(_th1); 0510 0511 svgCurveToCubic(a00 * x1 + a01 * y1, a10 * x1 + a11 * y1, a00 * x2 + a01 * y2, a10 * x2 + a11 * y2, a00 * x3 + a01 * y3, a10 * x3 + a11 * y3); 0512 } 0513 } 0514 0515 if (!relative) 0516 curx = x; 0517 else 0518 curx += x; 0519 0520 if (!relative) 0521 cury = y; 0522 else 0523 cury += y; 0524 } 0525 0526 void KoPathShapeLoaderPrivate::svgMoveTo(qreal x1, qreal y1, bool abs) 0527 { 0528 if (abs) 0529 lastPoint = QPointF(x1, y1); 0530 else 0531 lastPoint += QPointF(x1, y1); 0532 path->moveTo(lastPoint); 0533 } 0534 0535 void KoPathShapeLoaderPrivate::svgLineTo(qreal x1, qreal y1, bool abs) 0536 { 0537 if (abs) 0538 lastPoint = QPointF(x1, y1); 0539 else 0540 lastPoint += QPointF(x1, y1); 0541 0542 path->lineTo(lastPoint); 0543 } 0544 0545 void KoPathShapeLoaderPrivate::svgLineToHorizontal(qreal x, bool abs) 0546 { 0547 if (abs) 0548 lastPoint.setX(x); 0549 else 0550 lastPoint.rx() += x; 0551 0552 path->lineTo(lastPoint); 0553 } 0554 0555 void KoPathShapeLoaderPrivate::svgLineToVertical(qreal y, bool abs) 0556 { 0557 if (abs) 0558 lastPoint.setY(y); 0559 else 0560 lastPoint.ry() += y; 0561 0562 path->lineTo(lastPoint); 0563 } 0564 0565 void KoPathShapeLoaderPrivate::svgCurveToCubic(qreal x1, qreal y1, qreal x2, qreal y2, qreal x, qreal y, bool abs) 0566 { 0567 QPointF p1, p2; 0568 if (abs) { 0569 p1 = QPointF(x1, y1); 0570 p2 = QPointF(x2, y2); 0571 lastPoint = QPointF(x, y); 0572 } else { 0573 p1 = lastPoint + QPointF(x1, y1); 0574 p2 = lastPoint + QPointF(x2, y2); 0575 lastPoint += QPointF(x, y); 0576 } 0577 0578 path->curveTo(p1, p2, lastPoint); 0579 } 0580 0581 void KoPathShapeLoaderPrivate::svgCurveToCubicSmooth(qreal x, qreal y, qreal x2, qreal y2, bool abs) 0582 { 0583 Q_UNUSED(x); 0584 Q_UNUSED(y); 0585 Q_UNUSED(x2); 0586 Q_UNUSED(y2); 0587 Q_UNUSED(abs); 0588 // TODO implement 0589 } 0590 0591 void KoPathShapeLoaderPrivate::svgCurveToQuadratic(qreal x, qreal y, qreal x1, qreal y1, bool abs) 0592 { 0593 Q_UNUSED(x); 0594 Q_UNUSED(y); 0595 Q_UNUSED(x1); 0596 Q_UNUSED(y1); 0597 Q_UNUSED(abs); 0598 // TODO implement 0599 } 0600 0601 void KoPathShapeLoaderPrivate::svgCurveToQuadraticSmooth(qreal x, qreal y, bool abs) 0602 { 0603 Q_UNUSED(x); 0604 Q_UNUSED(y); 0605 Q_UNUSED(abs); 0606 // TODO implement 0607 } 0608 0609 void KoPathShapeLoaderPrivate::svgArcTo(qreal x, qreal y, qreal r1, qreal r2, qreal angle, bool largeArcFlag, bool sweepFlag, bool abs) 0610 { 0611 Q_UNUSED(x); 0612 Q_UNUSED(y); 0613 Q_UNUSED(r1); 0614 Q_UNUSED(r2); 0615 Q_UNUSED(angle); 0616 Q_UNUSED(largeArcFlag); 0617 Q_UNUSED(sweepFlag); 0618 Q_UNUSED(abs); 0619 // TODO implement 0620 } 0621 0622 void KoPathShapeLoaderPrivate::svgClosePath() 0623 { 0624 path->closeMerge(); 0625 } 0626 0627 KoPathShapeLoader::KoPathShapeLoader(KoPathShape *path) 0628 : d(new KoPathShapeLoaderPrivate(path)) 0629 { 0630 } 0631 0632 KoPathShapeLoader::~KoPathShapeLoader() 0633 { 0634 delete d; 0635 } 0636 0637 void KoPathShapeLoader::parseSvg(const QString &s, bool process) 0638 { 0639 d->parseSvg(s, process); 0640 }