lib Library API Documentation

svgpathparser.cc

00001 /* This file is part of the KDE project
00002    Copyright (C) 2002, The Karbon Developers
00003 
00004    This library is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Library General Public
00006    License as published by the Free Software Foundation; either
00007    version 2 of the License, or (at your option) any later version.
00008 
00009    This library is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Library General Public License for more details.
00013 
00014    You should have received a copy of the GNU Library General Public License
00015    along with this library; see the file COPYING.LIB.  If not, write to
00016    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00017    Boston, MA 02111-1307, USA.
00018 */
00019 
00020 #include "svgpathparser.h"
00021 #include <qstring.h>
00022 #include <math.h>
00023 
00024 // parses the coord into number and forwards to the next token
00025 const char *
00026 SVGPathParser::getCoord( const char *ptr, double &number )
00027 {
00028     int integer, exponent;
00029     double decimal, frac;
00030     int sign, expsign;
00031 
00032     exponent = 0;
00033     integer = 0;
00034     frac = 1.0;
00035     decimal = 0;
00036     sign = 1;
00037     expsign = 1;
00038 
00039     // read the sign
00040     if(*ptr == '+')
00041         ptr++;
00042     else if(*ptr == '-')
00043     {
00044         ptr++;
00045         sign = -1;
00046     }
00047 
00048     // read the integer part
00049     while(*ptr != '\0' && *ptr >= '0' && *ptr <= '9')
00050         integer = (integer * 10) + *(ptr++) - '0';
00051     if(*ptr == '.') // read the decimals
00052     {
00053         ptr++;
00054         while(*ptr != '\0' && *ptr >= '0' && *ptr <= '9')
00055             decimal += (*(ptr++) - '0') * (frac *= 0.1);
00056     }
00057 
00058     if(*ptr == 'e' || *ptr == 'E') // read the exponent part
00059     {
00060         ptr++;
00061 
00062         // read the sign of the exponent
00063         if(*ptr == '+')
00064             ptr++;
00065         else if(*ptr == '-')
00066         {
00067             ptr++;
00068             expsign = -1;
00069         }
00070 
00071         exponent = 0;
00072         while(*ptr != '\0' && *ptr >= '0' && *ptr <= '9')
00073         {
00074             exponent *= 10;
00075             exponent += *ptr - '0';
00076             ptr++;
00077         }
00078     }
00079     number = integer + decimal;
00080     number *= sign * pow( (double)10, double( expsign * exponent ) );
00081 
00082     // skip the following space
00083     if(*ptr == ' ')
00084         ptr++;
00085 
00086     return ptr;
00087 }
00088 
00089 void
00090 SVGPathParser::parseSVG( const QString &s, bool process )
00091 {
00092     if( !s.isEmpty() )
00093     {
00094         QString d = s;
00095         d = d.replace( ',', ' ' );
00096         d = d.simplifyWhiteSpace();
00097 
00098         const char *ptr = d.latin1();
00099         const char *end = d.latin1() + d.length() + 1;
00100 
00101         double contrlx, contrly, curx, cury, subpathx, subpathy, tox, toy, x1, y1, x2, y2, xc, yc;
00102         double px1, py1, px2, py2, px3, py3;
00103         bool relative;
00104         char command = *(ptr++), lastCommand = ' ';
00105 
00106         subpathx = subpathy = curx = cury = contrlx = contrly = 0.0;
00107         while( ptr < end )
00108         {
00109             if( *ptr == ' ' )
00110                 ptr++;
00111 
00112             relative = false;
00113 
00114             //std::cout << "Command : " << command << std::endl;
00115             switch( command )
00116             {
00117                 case 'm':
00118                     relative = true;
00119                 case 'M':
00120                 {
00121                     ptr = getCoord( ptr, tox );
00122                     ptr = getCoord( ptr, toy );
00123 
00124                     if( process )
00125                     {
00126                         subpathx = curx = relative ? curx + tox : tox;
00127                         subpathy = cury = relative ? cury + toy : toy;
00128 
00129                         svgMoveTo( curx, cury );
00130                     }
00131                     else
00132                         svgMoveTo( tox, toy, !relative );
00133                     break;
00134                 }
00135                 case 'l':
00136                     relative = true;
00137                 case 'L':
00138                 {
00139                     ptr = getCoord( ptr, tox );
00140                     ptr = getCoord( ptr, toy );
00141 
00142                     if( process )
00143                     {
00144                         curx = relative ? curx + tox : tox;
00145                         cury = relative ? cury + toy : toy;
00146 
00147                         svgLineTo( curx, cury );
00148                     }
00149                     else
00150                         svgLineTo( tox, toy, !relative );
00151                     break;
00152                 }
00153                 case 'h':
00154                 {
00155                     ptr = getCoord( ptr, tox );
00156                     if( process )
00157                     {
00158                         curx = curx + tox;
00159                         svgLineTo( curx, cury );
00160                     }
00161                     else
00162                         svgLineToHorizontal( tox, false );
00163                     break;
00164                 }
00165                 case 'H':
00166                 {
00167                     ptr = getCoord( ptr, tox );
00168                     if( process )
00169                     {
00170                         curx = tox;
00171                         svgLineTo( curx, cury );
00172                     }
00173                     else
00174                         svgLineToHorizontal( tox );
00175                     break;
00176                 }
00177                 case 'v':
00178                 {
00179                     ptr = getCoord( ptr, toy );
00180                     if( process )
00181                     {
00182                         cury = cury + toy;
00183                         svgLineTo( curx, cury );
00184                     }
00185                     else
00186                         svgLineToVertical( toy, false );
00187                     break;
00188                 }
00189                 case 'V':
00190                 {
00191                     ptr = getCoord( ptr, toy );
00192                     if( process )
00193                     {
00194                         cury = toy;
00195                         svgLineTo( curx, cury );
00196                     }
00197                     else
00198                         svgLineToVertical( toy );
00199                     break;
00200                 }
00201                 case 'z':
00202                 case 'Z':
00203                 {
00204                     // reset curx, cury for next path
00205                     if( process )
00206                     {
00207                         curx = subpathx;
00208                         cury = subpathy;
00209                     }
00210                     svgClosePath();
00211                     break;
00212                 }
00213                 case 'c':
00214                     relative = true;
00215                 case 'C':
00216                 {
00217                     ptr = getCoord( ptr, x1 );
00218                     ptr = getCoord( ptr, y1 );
00219                     ptr = getCoord( ptr, x2 );
00220                     ptr = getCoord( ptr, y2 );
00221                     ptr = getCoord( ptr, tox );
00222                     ptr = getCoord( ptr, toy );
00223 
00224                     if( process )
00225                     {
00226                         px1 = relative ? curx + x1 : x1;
00227                         py1 = relative ? cury + y1 : y1;
00228                         px2 = relative ? curx + x2 : x2;
00229                         py2 = relative ? cury + y2 : y2;
00230                         px3 = relative ? curx + tox : tox;
00231                         py3 = relative ? cury + toy : toy;
00232 
00233                         svgCurveToCubic( px1, py1, px2, py2, px3, py3 );
00234 
00235                         contrlx = relative ? curx + x2 : x2;
00236                         contrly = relative ? cury + y2 : y2;
00237                         curx = relative ? curx + tox : tox;
00238                         cury = relative ? cury + toy : toy;
00239                     }
00240                     else
00241                         svgCurveToCubic( x1, y1, x2, y2, tox, toy, !relative );
00242 
00243                     break;
00244                 }
00245                 case 's':
00246                     relative = true;
00247                 case 'S':
00248                 {
00249                     ptr = getCoord( ptr, x2 );
00250                     ptr = getCoord( ptr, y2 );
00251                     ptr = getCoord( ptr, tox );
00252                     ptr = getCoord( ptr, toy );
00253 
00254                     if( process )
00255                     {
00256                         px1 = 2 * curx - contrlx;
00257                         py1 = 2 * cury - contrly;
00258                         px2 = relative ? curx + x2 : x2;
00259                         py2 = relative ? cury + y2 : y2;
00260                         px3 = relative ? curx + tox : tox;
00261                         py3 = relative ? cury + toy : toy;
00262 
00263                         svgCurveToCubic( px1, py1, px2, py2, px3, py3 );
00264 
00265                         contrlx = relative ? curx + x2 : x2;
00266                         contrly = relative ? cury + y2 : y2;
00267                         curx = relative ? curx + tox : tox;
00268                         cury = relative ? cury + toy : toy;
00269                     }
00270                     else
00271                         svgCurveToCubicSmooth( x2, y2, tox, toy, !relative );
00272                     break;
00273                 }
00274                 case 'q':
00275                     relative = true;
00276                 case 'Q':
00277                 {
00278                     ptr = getCoord( ptr, x1 );
00279                     ptr = getCoord( ptr, y1 );
00280                     ptr = getCoord( ptr, tox );
00281                     ptr = getCoord( ptr, toy );
00282 
00283                     if( process )
00284                     {
00285                         px1 = relative ? (curx + 2 * (x1 + curx)) * (1.0 / 3.0) : (curx + 2 * x1) * (1.0 / 3.0);
00286                         py1 = relative ? (cury + 2 * (y1 + cury)) * (1.0 / 3.0) : (cury + 2 * y1) * (1.0 / 3.0);
00287                         px2 = relative ? ((curx + tox) + 2 * (x1 + curx)) * (1.0 / 3.0) : (tox + 2 * x1) * (1.0 / 3.0);
00288                         py2 = relative ? ((cury + toy) + 2 * (y1 + cury)) * (1.0 / 3.0) : (toy + 2 * y1) * (1.0 / 3.0);
00289                         px3 = relative ? curx + tox : tox;
00290                         py3 = relative ? cury + toy : toy;
00291 
00292                         svgCurveToCubic( px1, py1, px2, py2, px3, py3 );
00293 
00294                         contrlx = relative ? curx + x1 : (tox + 2 * x1) * (1.0 / 3.0);
00295                         contrly = relative ? cury + y1 : (toy + 2 * y1) * (1.0 / 3.0);
00296                         curx = relative ? curx + tox : tox;
00297                         cury = relative ? cury + toy : toy;
00298                     }
00299                     else
00300                         svgCurveToQuadratic( x1, y1, tox, toy, !relative );
00301                     break;
00302                 }
00303                 case 't':
00304                     relative = true;
00305                 case 'T':
00306                 {
00307                     ptr = getCoord(ptr, tox);
00308                     ptr = getCoord(ptr, toy);
00309 
00310                     if( process )
00311                     {
00312                         xc = 2 * curx - contrlx;
00313                         yc = 2 * cury - contrly;
00314 
00315                         px1 = relative ? (curx + 2 * xc) * (1.0 / 3.0) : (curx + 2 * xc) * (1.0 / 3.0);
00316                         py1 = relative ? (cury + 2 * yc) * (1.0 / 3.0) : (cury + 2 * yc) * (1.0 / 3.0);
00317                         px2 = relative ? ((curx + tox) + 2 * xc) * (1.0 / 3.0) : (tox + 2 * xc) * (1.0 / 3.0);
00318                         py2 = relative ? ((cury + toy) + 2 * yc) * (1.0 / 3.0) : (toy + 2 * yc) * (1.0 / 3.0);
00319                         px3 = relative ? curx + tox : tox;
00320                         py3 = relative ? cury + toy : toy;
00321 
00322                         svgCurveToCubic( px1, py1, px2, py2, px3, py3 );
00323 
00324                         contrlx = xc;
00325                         contrly = yc;
00326                         curx = relative ? curx + tox : tox;
00327                         cury = relative ? cury + toy : toy;
00328                     }
00329                     else
00330                         svgCurveToQuadraticSmooth( tox, toy, !relative );
00331                     break;
00332                 }
00333                 case 'a':
00334                     relative = true;
00335                 case 'A':
00336                 {
00337                     bool largeArc, sweep;
00338                     double angle, rx, ry;
00339                     ptr = getCoord( ptr, rx );
00340                     ptr = getCoord( ptr, ry );
00341                     ptr = getCoord( ptr, angle );
00342                     ptr = getCoord( ptr, tox );
00343                     largeArc = tox == 1;
00344                     ptr = getCoord( ptr, tox );
00345                     sweep = tox == 1;
00346                     ptr = getCoord( ptr, tox );
00347                     ptr = getCoord( ptr, toy );
00348 
00349                     // Spec: radii are nonnegative numbers
00350                     rx = fabs(rx);
00351                     ry = fabs(ry);
00352 
00353                     if( process )
00354                         calculateArc( relative, curx, cury, angle, tox, toy, rx, ry, largeArc, sweep );
00355                     else
00356                         svgArcTo( tox, toy, rx, ry, angle, largeArc, sweep, !relative );
00357                 }
00358             }
00359 
00360             lastCommand = command;
00361 
00362             if(*ptr == '+' || *ptr == '-' || (*ptr >= '0' && *ptr <= '9'))
00363             {
00364                 // there are still coords in this command
00365                 if(command == 'M')
00366                     command = 'L';
00367                 else if(command == 'm')
00368                     command = 'l';
00369             }
00370             else
00371                 command = *(ptr++);
00372 
00373             if( lastCommand != 'C' && lastCommand != 'c' &&
00374                 lastCommand != 'S' && lastCommand != 's' &&
00375                 lastCommand != 'Q' && lastCommand != 'q' &&
00376                 lastCommand != 'T' && lastCommand != 't')
00377             {
00378                 contrlx = curx;
00379                 contrly = cury;
00380             }
00381         }
00382     }
00383 }
00384 
00385 // This works by converting the SVG arc to "simple" beziers.
00386 // For each bezier found a svgToCurve call is done.
00387 // Adapted from Niko's code in kdelibs/kdecore/svgicons.
00388 // Maybe this can serve in some shared lib? (Rob)
00389 void
00390 SVGPathParser::calculateArc(bool relative, double &curx, double &cury, double angle, double x, double y, double r1, double r2, bool largeArcFlag, bool sweepFlag)
00391 {
00392     double sin_th, cos_th;
00393     double a00, a01, a10, a11;
00394     double x0, y0, x1, y1, xc, yc;
00395     double d, sfactor, sfactor_sq;
00396     double th0, th1, th_arc;
00397     int i, n_segs;
00398 
00399     sin_th = sin(angle * (M_PI / 180.0));
00400     cos_th = cos(angle * (M_PI / 180.0));
00401 
00402     double dx;
00403 
00404     if(!relative)
00405         dx = (curx - x) / 2.0;
00406     else
00407         dx = -x / 2.0;
00408 
00409     double dy;
00410         
00411     if(!relative)
00412         dy = (cury - y) / 2.0;
00413     else
00414         dy = -y / 2.0;
00415         
00416     double _x1 =  cos_th * dx + sin_th * dy;
00417     double _y1 = -sin_th * dx + cos_th * dy;
00418     double Pr1 = r1 * r1;
00419     double Pr2 = r2 * r2;
00420     double Px = _x1 * _x1;
00421     double Py = _y1 * _y1;
00422 
00423     // Spec : check if radii are large enough
00424     double check = Px / Pr1 + Py / Pr2;
00425     if(check > 1)
00426     {
00427         r1 = r1 * sqrt(check);
00428         r2 = r2 * sqrt(check);
00429     }
00430 
00431     a00 = cos_th / r1;
00432     a01 = sin_th / r1;
00433     a10 = -sin_th / r2;
00434     a11 = cos_th / r2;
00435 
00436     x0 = a00 * curx + a01 * cury;
00437     y0 = a10 * curx + a11 * cury;
00438 
00439     if(!relative)
00440         x1 = a00 * x + a01 * y;
00441     else
00442         x1 = a00 * (curx + x) + a01 * (cury + y);
00443         
00444     if(!relative)
00445         y1 = a10 * x + a11 * y;
00446     else
00447         y1 = a10 * (curx + x) + a11 * (cury + y);
00448 
00449     /* (x0, y0) is current point in transformed coordinate space.
00450        (x1, y1) is new point in transformed coordinate space.
00451 
00452        The arc fits a unit-radius circle in this space.
00453      */
00454 
00455     d = (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0);
00456 
00457     sfactor_sq = 1.0 / d - 0.25;
00458 
00459     if(sfactor_sq < 0)
00460         sfactor_sq = 0;
00461 
00462     sfactor = sqrt(sfactor_sq);
00463 
00464     if(sweepFlag == largeArcFlag)
00465         sfactor = -sfactor;
00466 
00467     xc = 0.5 * (x0 + x1) - sfactor * (y1 - y0);
00468     yc = 0.5 * (y0 + y1) + sfactor * (x1 - x0);
00469 
00470     /* (xc, yc) is center of the circle. */
00471     th0 = atan2(y0 - yc, x0 - xc);
00472     th1 = atan2(y1 - yc, x1 - xc);
00473 
00474     th_arc = th1 - th0;
00475     if(th_arc < 0 && sweepFlag)
00476         th_arc += 2 * M_PI;
00477     else if(th_arc > 0 && !sweepFlag)
00478         th_arc -= 2 * M_PI;
00479 
00480     n_segs = (int) (int) ceil(fabs(th_arc / (M_PI * 0.5 + 0.001)));
00481 
00482     for(i = 0; i < n_segs; i++)
00483     {
00484         {
00485             double sin_th, cos_th;
00486             double a00, a01, a10, a11;
00487             double x1, y1, x2, y2, x3, y3;
00488             double t;
00489             double th_half;
00490 
00491             double _th0 = th0 + i * th_arc / n_segs;
00492             double _th1 = th0 + (i + 1) * th_arc / n_segs;
00493 
00494             sin_th = sin(angle * (M_PI / 180.0));
00495             cos_th = cos(angle * (M_PI / 180.0));
00496 
00497             /* inverse transform compared with rsvg_path_arc */
00498             a00 = cos_th * r1;
00499             a01 = -sin_th * r2;
00500             a10 = sin_th * r1;
00501             a11 = cos_th * r2;
00502 
00503             th_half = 0.5 * (_th1 - _th0);
00504             t = (8.0 / 3.0) * sin(th_half * 0.5) * sin(th_half * 0.5) / sin(th_half);
00505             x1 = xc + cos(_th0) - t * sin(_th0);
00506             y1 = yc + sin(_th0) + t * cos(_th0);
00507             x3 = xc + cos(_th1);
00508             y3 = yc + sin(_th1);
00509             x2 = x3 + t * sin(_th1);
00510             y2 = y3 - t * cos(_th1);
00511 
00512             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 );
00513         }
00514     }
00515 
00516     if(!relative)
00517         curx = x;
00518     else
00519         curx += x;
00520 
00521     if(!relative)
00522         cury = y;
00523     else
00524         cury += y;  
00525 }
00526 
00527 void
00528 SVGPathParser::svgLineToHorizontal( double, bool )
00529 {
00530 }
00531 
00532 void
00533 SVGPathParser::svgLineToVertical( double, bool )
00534 {
00535 }
00536 
00537 void
00538 SVGPathParser::svgCurveToCubicSmooth( double, double, double, double, bool )
00539 {
00540 }
00541 
00542 void
00543 SVGPathParser::svgCurveToQuadratic( double, double, double, double, bool )
00544 {
00545 }
00546 
00547 void
00548 SVGPathParser::svgCurveToQuadraticSmooth( double, double, bool )
00549 {
00550 }
00551 
00552 void
00553 SVGPathParser::svgArcTo( double, double, double, double, double, bool, bool, bool )
00554 {
00555 }
00556 
KDE Logo
This file is part of the documentation for lib Library Version 1.3.5.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Fri Mar 11 11:47:46 2005 by doxygen 1.3.9.1 written by Dimitri van Heesch, © 1997-2003