F:/KPlato/koffice/libs/kopainter/svgpathparser.cc

Aller à la documentation de ce fichier.
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>
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.simplified();
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                                         kDebug() << "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 

Généré le Wed Nov 22 23:41:06 2006 pour KPlato par  doxygen 1.5.1-p1