lib

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