From 3470de989b91462e61c74baa73c62ae414e22b08 Mon Sep 17 00:00:00 2001 From: Kurt Zenker Date: Tue, 24 Jun 2008 10:54:24 +0000 Subject: INTEGRATION: CWS canvas05 (1.9.4); FILE MERGED 2008/06/03 15:54:00 thb 1.9.4.1: Upstreaming elliptical arc import for svg:d parser --- basegfx/source/polygon/b2dsvgpolypolygon.cxx | 232 +++++++++++++++++++++++++-- 1 file changed, 219 insertions(+), 13 deletions(-) diff --git a/basegfx/source/polygon/b2dsvgpolypolygon.cxx b/basegfx/source/polygon/b2dsvgpolypolygon.cxx index 257eb2ca79b8..2247c237d90f 100644 --- a/basegfx/source/polygon/b2dsvgpolypolygon.cxx +++ b/basegfx/source/polygon/b2dsvgpolypolygon.cxx @@ -7,7 +7,7 @@ * OpenOffice.org - a multi-platform office productivity suite * * $RCSfile: b2dsvgpolypolygon.cxx,v $ - * $Revision: 1.9 $ + * $Revision: 1.10 $ * * This file is part of OpenOffice.org. * @@ -33,7 +33,9 @@ #include #include +#include #include +#include #include #include @@ -65,7 +67,7 @@ namespace basegfx } } - bool lcl_isOnNumberChar(const ::rtl::OUString& rStr, const sal_Int32 nPos, bool bSignAllowed = true) + inline bool lcl_isOnNumberChar(const ::rtl::OUString& rStr, const sal_Int32 nPos, bool bSignAllowed = true) { const sal_Unicode aChar(rStr[nPos]); @@ -142,6 +144,37 @@ namespace basegfx return true; } + bool lcl_importNumberAndSpaces(sal_Int32& o_nRetval, + sal_Int32& io_rPos, + const ::rtl::OUString& rStr, + const sal_Int32 nLen) + { + sal_Unicode aChar( rStr[io_rPos] ); + ::rtl::OUStringBuffer sNumberString; + + if(sal_Unicode('+') == aChar || sal_Unicode('-') == aChar) + { + sNumberString.append(rStr[io_rPos]); + aChar = rStr[++io_rPos]; + } + + while(sal_Unicode('0') <= aChar && sal_Unicode('9') >= aChar) + { + sNumberString.append(rStr[io_rPos]); + aChar = rStr[++io_rPos]; + } + + if(sNumberString.getLength()) + { + o_nRetval = sNumberString.makeStringAndClear().toInt32(); + lcl_skipSpacesAndCommas(io_rPos, rStr, nLen); + + return true; + } + + return false; + } + void lcl_skipNumber(sal_Int32& io_rPos, const ::rtl::OUString& rStr, const sal_Int32 nLen) @@ -621,24 +654,171 @@ namespace basegfx break; } - // #100617# not yet supported: elliptical arc - case 'A' : - // FALLTHROUGH intended case 'a' : { - OSL_ENSURE(false, "importFromSvgD(): non-interpreted tags in svg:d element (elliptical arc)!"); + bRelative = true; + // FALLTHROUGH intended + } + case 'A' : + { nPos++; lcl_skipSpaces(nPos, rSvgDStatement, nLen); while(nPos < nLen && lcl_isOnNumberChar(rSvgDStatement, nPos)) { - lcl_skipDoubleAndSpacesAndCommas(nPos, rSvgDStatement, nLen); - lcl_skipDoubleAndSpacesAndCommas(nPos, rSvgDStatement, nLen); - lcl_skipDoubleAndSpacesAndCommas(nPos, rSvgDStatement, nLen); - lcl_skipNumberAndSpacesAndCommas(nPos, rSvgDStatement, nLen); - lcl_skipNumberAndSpacesAndCommas(nPos, rSvgDStatement, nLen); - lcl_skipDoubleAndSpacesAndCommas(nPos, rSvgDStatement, nLen); - lcl_skipDoubleAndSpacesAndCommas(nPos, rSvgDStatement, nLen); + double nX, nY; + double fRX, fRY, fPhi; + sal_Int32 bLargeArcFlag, bSweepFlag; + + if(!lcl_importDoubleAndSpaces(fRX, nPos, rSvgDStatement, nLen)) return false; + if(!lcl_importDoubleAndSpaces(fRY, nPos, rSvgDStatement, nLen)) return false; + if(!lcl_importDoubleAndSpaces(fPhi, nPos, rSvgDStatement, nLen)) return false; + if(!lcl_importNumberAndSpaces(bLargeArcFlag, nPos, rSvgDStatement, nLen)) return false; + if(!lcl_importNumberAndSpaces(bSweepFlag, nPos, rSvgDStatement, nLen)) return false; + if(!lcl_importDoubleAndSpaces(nX, nPos, rSvgDStatement, nLen)) return false; + if(!lcl_importDoubleAndSpaces(nY, nPos, rSvgDStatement, nLen)) return false; + + if(bRelative) + { + nX += nLastX; + nY += nLastY; + } + + const B2DPoint aPrevPoint(aCurrPoly.getB2DPoint(aCurrPoly.count() - 1)); + + if( nX == nLastX && nY == nLastY ) + continue; // start==end -> skip according to SVG spec + + if( fRX == 0.0 || fRY == 0.0 ) + { + // straight line segment according to SVG spec + aCurrPoly.append(B2DPoint(nX, nY)); + } + else + { + // normalize according to SVG spec + fRX=fabs(fRX); fRY=fabs(fRY); + + // from the SVG spec, appendix F.6.4 + + // |x1'| |cos phi sin phi| |(x1 - x2)/2| + // |y1'| = |-sin phi cos phi| |(y1 - y2)/2| + const B2DPoint p1(nLastX, nLastY); + const B2DPoint p2(nX, nY); + B2DHomMatrix aTransform; aTransform.rotate(-fPhi*M_PI/180); + + const B2DPoint p1_prime( aTransform * B2DPoint(((p1-p2)/2.0)) ); + + // ______________________________________ rx y1' + // |cx'| + / rx^2 ry^2 - rx^2 y1'^2 - ry^2 x1^2 ry + // |cy'| =-/ rx^2y1'^2 + ry^2 x1'^2 - ry x1' + // rx + // chose + if f_A != f_S + // chose - if f_A = f_S + B2DPoint aCenter_prime; + const double fRadicant( + (fRX*fRX*fRY*fRY - fRX*fRX*p1_prime.getY()*p1_prime.getY() - fRY*fRY*p1_prime.getX()*p1_prime.getX())/ + (fRX*fRX*p1_prime.getY()*p1_prime.getY() + fRY*fRY*p1_prime.getX()*p1_prime.getX())); + if( fRadicant < 0.0 ) + { + // no solution - according to SVG + // spec, scale up ellipse + // uniformly such that it passes + // through end points (denominator + // of radicant solved for fRY, + // with s=fRX/fRY) + const double fRatio(fRX/fRY); + const double fRadicant2( + p1_prime.getY()*p1_prime.getY() + + p1_prime.getX()*p1_prime.getX()/(fRatio*fRatio)); + if( fRadicant2 < 0.0 ) + { + // only trivial solution, one + // of the axes 0 -> straight + // line segment according to + // SVG spec + aCurrPoly.append(B2DPoint(nX, nY)); + continue; + } + + fRY=sqrt(fRadicant2); + fRX=fRatio*fRY; + + // keep center_prime forced to (0,0) + } + else + { + const double fFactor( + (bLargeArcFlag==bSweepFlag ? -1.0 : 1.0) * + sqrt(fRadicant)); + + // actually calculate center_prime + aCenter_prime = B2DPoint( + fFactor*fRX*p1_prime.getY()/fRY, + -fFactor*fRY*p1_prime.getX()/fRX); + } + + // + u - v + // angle(u,v) = arccos( ------------ ) (take the sign of (ux vy - uy vx)) + // - ||u|| ||v|| + + // 1 | (x1' - cx')/rx | + // theta1 = angle(( ), | | ) + // 0 | (y1' - cy')/ry | + const B2DPoint aRadii(fRX,fRY); + double fTheta1( + B2DVector(1.0,0.0).angle( + (p1_prime-aCenter_prime)/aRadii)); + + // |1| | (-x1' - cx')/rx | + // theta2 = angle( | | , | | ) + // |0| | (-y1' - cy')/ry | + double fTheta2( + B2DVector(1.0,0.0).angle( + (-p1_prime-aCenter_prime)/aRadii)); + + // map both angles to [0,2pi) + fTheta1 = fmod(2*M_PI+fTheta1,2*M_PI); + fTheta2 = fmod(2*M_PI+fTheta2,2*M_PI); + + // make sure the large arc is taken + // (since + // createPolygonFromEllipseSegment() + // normalizes to e.g. cw arc) + const bool bFlipSegment( (bLargeArcFlag!=0) == + (fmod(fTheta2+2*M_PI-fTheta1, + 2*M_PI)