1*cdf0e10cSrcweir /************************************************************************* 2*cdf0e10cSrcweir * 3*cdf0e10cSrcweir * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4*cdf0e10cSrcweir * 5*cdf0e10cSrcweir * Copyright 2000, 2010 Oracle and/or its affiliates. 6*cdf0e10cSrcweir * 7*cdf0e10cSrcweir * OpenOffice.org - a multi-platform office productivity suite 8*cdf0e10cSrcweir * 9*cdf0e10cSrcweir * This file is part of OpenOffice.org. 10*cdf0e10cSrcweir * 11*cdf0e10cSrcweir * OpenOffice.org is free software: you can redistribute it and/or modify 12*cdf0e10cSrcweir * it under the terms of the GNU Lesser General Public License version 3 13*cdf0e10cSrcweir * only, as published by the Free Software Foundation. 14*cdf0e10cSrcweir * 15*cdf0e10cSrcweir * OpenOffice.org is distributed in the hope that it will be useful, 16*cdf0e10cSrcweir * but WITHOUT ANY WARRANTY; without even the implied warranty of 17*cdf0e10cSrcweir * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18*cdf0e10cSrcweir * GNU Lesser General Public License version 3 for more details 19*cdf0e10cSrcweir * (a copy is included in the LICENSE file that accompanied this code). 20*cdf0e10cSrcweir * 21*cdf0e10cSrcweir * You should have received a copy of the GNU Lesser General Public License 22*cdf0e10cSrcweir * version 3 along with OpenOffice.org. If not, see 23*cdf0e10cSrcweir * <http://www.openoffice.org/license.html> 24*cdf0e10cSrcweir * for a copy of the LGPLv3 License. 25*cdf0e10cSrcweir * 26*cdf0e10cSrcweir ************************************************************************/ 27*cdf0e10cSrcweir 28*cdf0e10cSrcweir // MARKER(update_precomp.py): autogen include statement, do not remove 29*cdf0e10cSrcweir #include "precompiled_basegfx.hxx" 30*cdf0e10cSrcweir 31*cdf0e10cSrcweir #include <basegfx/polygon/b2dpolygontools.hxx> 32*cdf0e10cSrcweir #include <basegfx/polygon/b2dpolypolygontools.hxx> 33*cdf0e10cSrcweir #include <basegfx/polygon/b2dpolygontools.hxx> 34*cdf0e10cSrcweir #include <basegfx/polygon/b2dpolypolygon.hxx> 35*cdf0e10cSrcweir #include <basegfx/matrix/b2dhommatrix.hxx> 36*cdf0e10cSrcweir #include <basegfx/matrix/b2dhommatrixtools.hxx> 37*cdf0e10cSrcweir #include <rtl/ustring.hxx> 38*cdf0e10cSrcweir #include <rtl/math.hxx> 39*cdf0e10cSrcweir 40*cdf0e10cSrcweir namespace basegfx 41*cdf0e10cSrcweir { 42*cdf0e10cSrcweir namespace tools 43*cdf0e10cSrcweir { 44*cdf0e10cSrcweir namespace 45*cdf0e10cSrcweir { 46*cdf0e10cSrcweir void lcl_skipSpaces(sal_Int32& io_rPos, 47*cdf0e10cSrcweir const ::rtl::OUString& rStr, 48*cdf0e10cSrcweir const sal_Int32 nLen) 49*cdf0e10cSrcweir { 50*cdf0e10cSrcweir while( io_rPos < nLen && 51*cdf0e10cSrcweir sal_Unicode(' ') == rStr[io_rPos] ) 52*cdf0e10cSrcweir { 53*cdf0e10cSrcweir ++io_rPos; 54*cdf0e10cSrcweir } 55*cdf0e10cSrcweir } 56*cdf0e10cSrcweir 57*cdf0e10cSrcweir void lcl_skipSpacesAndCommas(sal_Int32& io_rPos, 58*cdf0e10cSrcweir const ::rtl::OUString& rStr, 59*cdf0e10cSrcweir const sal_Int32 nLen) 60*cdf0e10cSrcweir { 61*cdf0e10cSrcweir while(io_rPos < nLen 62*cdf0e10cSrcweir && (sal_Unicode(' ') == rStr[io_rPos] || sal_Unicode(',') == rStr[io_rPos])) 63*cdf0e10cSrcweir { 64*cdf0e10cSrcweir ++io_rPos; 65*cdf0e10cSrcweir } 66*cdf0e10cSrcweir } 67*cdf0e10cSrcweir 68*cdf0e10cSrcweir inline bool lcl_isOnNumberChar(const sal_Unicode aChar, bool bSignAllowed = true) 69*cdf0e10cSrcweir { 70*cdf0e10cSrcweir const bool bPredicate( (sal_Unicode('0') <= aChar && sal_Unicode('9') >= aChar) 71*cdf0e10cSrcweir || (bSignAllowed && sal_Unicode('+') == aChar) 72*cdf0e10cSrcweir || (bSignAllowed && sal_Unicode('-') == aChar) ); 73*cdf0e10cSrcweir 74*cdf0e10cSrcweir return bPredicate; 75*cdf0e10cSrcweir } 76*cdf0e10cSrcweir 77*cdf0e10cSrcweir inline bool lcl_isOnNumberChar(const ::rtl::OUString& rStr, const sal_Int32 nPos, bool bSignAllowed = true) 78*cdf0e10cSrcweir { 79*cdf0e10cSrcweir return lcl_isOnNumberChar(rStr[nPos], 80*cdf0e10cSrcweir bSignAllowed); 81*cdf0e10cSrcweir } 82*cdf0e10cSrcweir 83*cdf0e10cSrcweir bool lcl_getDoubleChar(double& o_fRetval, 84*cdf0e10cSrcweir sal_Int32& io_rPos, 85*cdf0e10cSrcweir const ::rtl::OUString& rStr, 86*cdf0e10cSrcweir const sal_Int32 /*nLen*/) 87*cdf0e10cSrcweir { 88*cdf0e10cSrcweir sal_Unicode aChar( rStr[io_rPos] ); 89*cdf0e10cSrcweir ::rtl::OUStringBuffer sNumberString; 90*cdf0e10cSrcweir 91*cdf0e10cSrcweir if(sal_Unicode('+') == aChar || sal_Unicode('-') == aChar) 92*cdf0e10cSrcweir { 93*cdf0e10cSrcweir sNumberString.append(rStr[io_rPos]); 94*cdf0e10cSrcweir aChar = rStr[++io_rPos]; 95*cdf0e10cSrcweir } 96*cdf0e10cSrcweir 97*cdf0e10cSrcweir while((sal_Unicode('0') <= aChar && sal_Unicode('9') >= aChar) 98*cdf0e10cSrcweir || sal_Unicode('.') == aChar) 99*cdf0e10cSrcweir { 100*cdf0e10cSrcweir sNumberString.append(rStr[io_rPos]); 101*cdf0e10cSrcweir aChar = rStr[++io_rPos]; 102*cdf0e10cSrcweir } 103*cdf0e10cSrcweir 104*cdf0e10cSrcweir if(sal_Unicode('e') == aChar || sal_Unicode('E') == aChar) 105*cdf0e10cSrcweir { 106*cdf0e10cSrcweir sNumberString.append(rStr[io_rPos]); 107*cdf0e10cSrcweir aChar = rStr[++io_rPos]; 108*cdf0e10cSrcweir 109*cdf0e10cSrcweir if(sal_Unicode('+') == aChar || sal_Unicode('-') == aChar) 110*cdf0e10cSrcweir { 111*cdf0e10cSrcweir sNumberString.append(rStr[io_rPos]); 112*cdf0e10cSrcweir aChar = rStr[++io_rPos]; 113*cdf0e10cSrcweir } 114*cdf0e10cSrcweir 115*cdf0e10cSrcweir while(sal_Unicode('0') <= aChar && sal_Unicode('9') >= aChar) 116*cdf0e10cSrcweir { 117*cdf0e10cSrcweir sNumberString.append(rStr[io_rPos]); 118*cdf0e10cSrcweir aChar = rStr[++io_rPos]; 119*cdf0e10cSrcweir } 120*cdf0e10cSrcweir } 121*cdf0e10cSrcweir 122*cdf0e10cSrcweir if(sNumberString.getLength()) 123*cdf0e10cSrcweir { 124*cdf0e10cSrcweir rtl_math_ConversionStatus eStatus; 125*cdf0e10cSrcweir o_fRetval = ::rtl::math::stringToDouble( sNumberString.makeStringAndClear(), 126*cdf0e10cSrcweir (sal_Unicode)('.'), 127*cdf0e10cSrcweir (sal_Unicode)(','), 128*cdf0e10cSrcweir &eStatus, 129*cdf0e10cSrcweir NULL ); 130*cdf0e10cSrcweir return ( eStatus == rtl_math_ConversionStatus_Ok ); 131*cdf0e10cSrcweir } 132*cdf0e10cSrcweir 133*cdf0e10cSrcweir return false; 134*cdf0e10cSrcweir } 135*cdf0e10cSrcweir 136*cdf0e10cSrcweir bool lcl_importDoubleAndSpaces( double& o_fRetval, 137*cdf0e10cSrcweir sal_Int32& io_rPos, 138*cdf0e10cSrcweir const ::rtl::OUString& rStr, 139*cdf0e10cSrcweir const sal_Int32 nLen ) 140*cdf0e10cSrcweir { 141*cdf0e10cSrcweir if( !lcl_getDoubleChar(o_fRetval, io_rPos, rStr, nLen) ) 142*cdf0e10cSrcweir return false; 143*cdf0e10cSrcweir 144*cdf0e10cSrcweir lcl_skipSpacesAndCommas(io_rPos, rStr, nLen); 145*cdf0e10cSrcweir 146*cdf0e10cSrcweir return true; 147*cdf0e10cSrcweir } 148*cdf0e10cSrcweir 149*cdf0e10cSrcweir bool lcl_importNumberAndSpaces(sal_Int32& o_nRetval, 150*cdf0e10cSrcweir sal_Int32& io_rPos, 151*cdf0e10cSrcweir const ::rtl::OUString& rStr, 152*cdf0e10cSrcweir const sal_Int32 nLen) 153*cdf0e10cSrcweir { 154*cdf0e10cSrcweir sal_Unicode aChar( rStr[io_rPos] ); 155*cdf0e10cSrcweir ::rtl::OUStringBuffer sNumberString; 156*cdf0e10cSrcweir 157*cdf0e10cSrcweir if(sal_Unicode('+') == aChar || sal_Unicode('-') == aChar) 158*cdf0e10cSrcweir { 159*cdf0e10cSrcweir sNumberString.append(rStr[io_rPos]); 160*cdf0e10cSrcweir aChar = rStr[++io_rPos]; 161*cdf0e10cSrcweir } 162*cdf0e10cSrcweir 163*cdf0e10cSrcweir while(sal_Unicode('0') <= aChar && sal_Unicode('9') >= aChar) 164*cdf0e10cSrcweir { 165*cdf0e10cSrcweir sNumberString.append(rStr[io_rPos]); 166*cdf0e10cSrcweir aChar = rStr[++io_rPos]; 167*cdf0e10cSrcweir } 168*cdf0e10cSrcweir 169*cdf0e10cSrcweir if(sNumberString.getLength()) 170*cdf0e10cSrcweir { 171*cdf0e10cSrcweir o_nRetval = sNumberString.makeStringAndClear().toInt32(); 172*cdf0e10cSrcweir lcl_skipSpacesAndCommas(io_rPos, rStr, nLen); 173*cdf0e10cSrcweir 174*cdf0e10cSrcweir return true; 175*cdf0e10cSrcweir } 176*cdf0e10cSrcweir 177*cdf0e10cSrcweir return false; 178*cdf0e10cSrcweir } 179*cdf0e10cSrcweir 180*cdf0e10cSrcweir void lcl_skipNumber(sal_Int32& io_rPos, 181*cdf0e10cSrcweir const ::rtl::OUString& rStr, 182*cdf0e10cSrcweir const sal_Int32 nLen) 183*cdf0e10cSrcweir { 184*cdf0e10cSrcweir bool bSignAllowed(true); 185*cdf0e10cSrcweir 186*cdf0e10cSrcweir while(io_rPos < nLen && lcl_isOnNumberChar(rStr, io_rPos, bSignAllowed)) 187*cdf0e10cSrcweir { 188*cdf0e10cSrcweir bSignAllowed = false; 189*cdf0e10cSrcweir ++io_rPos; 190*cdf0e10cSrcweir } 191*cdf0e10cSrcweir } 192*cdf0e10cSrcweir 193*cdf0e10cSrcweir void lcl_skipDouble(sal_Int32& io_rPos, 194*cdf0e10cSrcweir const ::rtl::OUString& rStr, 195*cdf0e10cSrcweir const sal_Int32 /*nLen*/) 196*cdf0e10cSrcweir { 197*cdf0e10cSrcweir sal_Unicode aChar( rStr[io_rPos] ); 198*cdf0e10cSrcweir 199*cdf0e10cSrcweir if(sal_Unicode('+') == aChar || sal_Unicode('-') == aChar) 200*cdf0e10cSrcweir aChar = rStr[++io_rPos]; 201*cdf0e10cSrcweir 202*cdf0e10cSrcweir while((sal_Unicode('0') <= aChar && sal_Unicode('9') >= aChar) 203*cdf0e10cSrcweir || sal_Unicode('.') == aChar) 204*cdf0e10cSrcweir { 205*cdf0e10cSrcweir aChar = rStr[++io_rPos]; 206*cdf0e10cSrcweir } 207*cdf0e10cSrcweir 208*cdf0e10cSrcweir if(sal_Unicode('e') == aChar || sal_Unicode('E') == aChar) 209*cdf0e10cSrcweir { 210*cdf0e10cSrcweir aChar = rStr[++io_rPos]; 211*cdf0e10cSrcweir 212*cdf0e10cSrcweir if(sal_Unicode('+') == aChar || sal_Unicode('-') == aChar) 213*cdf0e10cSrcweir aChar = rStr[++io_rPos]; 214*cdf0e10cSrcweir 215*cdf0e10cSrcweir while(sal_Unicode('0') <= aChar && sal_Unicode('9') >= aChar) 216*cdf0e10cSrcweir { 217*cdf0e10cSrcweir aChar = rStr[++io_rPos]; 218*cdf0e10cSrcweir } 219*cdf0e10cSrcweir } 220*cdf0e10cSrcweir } 221*cdf0e10cSrcweir void lcl_skipNumberAndSpacesAndCommas(sal_Int32& io_rPos, 222*cdf0e10cSrcweir const ::rtl::OUString& rStr, 223*cdf0e10cSrcweir const sal_Int32 nLen) 224*cdf0e10cSrcweir { 225*cdf0e10cSrcweir lcl_skipNumber(io_rPos, rStr, nLen); 226*cdf0e10cSrcweir lcl_skipSpacesAndCommas(io_rPos, rStr, nLen); 227*cdf0e10cSrcweir } 228*cdf0e10cSrcweir 229*cdf0e10cSrcweir // #100617# Allow to skip doubles, too. 230*cdf0e10cSrcweir void lcl_skipDoubleAndSpacesAndCommas(sal_Int32& io_rPos, 231*cdf0e10cSrcweir const ::rtl::OUString& rStr, 232*cdf0e10cSrcweir const sal_Int32 nLen) 233*cdf0e10cSrcweir { 234*cdf0e10cSrcweir lcl_skipDouble(io_rPos, rStr, nLen); 235*cdf0e10cSrcweir lcl_skipSpacesAndCommas(io_rPos, rStr, nLen); 236*cdf0e10cSrcweir } 237*cdf0e10cSrcweir 238*cdf0e10cSrcweir void lcl_putNumberChar( ::rtl::OUStringBuffer& rStr, 239*cdf0e10cSrcweir double fValue ) 240*cdf0e10cSrcweir { 241*cdf0e10cSrcweir rStr.append( fValue ); 242*cdf0e10cSrcweir } 243*cdf0e10cSrcweir 244*cdf0e10cSrcweir void lcl_putNumberCharWithSpace( ::rtl::OUStringBuffer& rStr, 245*cdf0e10cSrcweir double fValue, 246*cdf0e10cSrcweir double fOldValue, 247*cdf0e10cSrcweir bool bUseRelativeCoordinates ) 248*cdf0e10cSrcweir { 249*cdf0e10cSrcweir if( bUseRelativeCoordinates ) 250*cdf0e10cSrcweir fValue -= fOldValue; 251*cdf0e10cSrcweir 252*cdf0e10cSrcweir const sal_Int32 aLen( rStr.getLength() ); 253*cdf0e10cSrcweir if(aLen) 254*cdf0e10cSrcweir { 255*cdf0e10cSrcweir if( lcl_isOnNumberChar(rStr.charAt(aLen - 1), false) && 256*cdf0e10cSrcweir fValue >= 0.0 ) 257*cdf0e10cSrcweir { 258*cdf0e10cSrcweir rStr.append( sal_Unicode(' ') ); 259*cdf0e10cSrcweir } 260*cdf0e10cSrcweir } 261*cdf0e10cSrcweir 262*cdf0e10cSrcweir lcl_putNumberChar(rStr, fValue); 263*cdf0e10cSrcweir } 264*cdf0e10cSrcweir 265*cdf0e10cSrcweir inline sal_Unicode lcl_getCommand( sal_Char cUpperCaseCommand, 266*cdf0e10cSrcweir sal_Char cLowerCaseCommand, 267*cdf0e10cSrcweir bool bUseRelativeCoordinates ) 268*cdf0e10cSrcweir { 269*cdf0e10cSrcweir return bUseRelativeCoordinates ? cLowerCaseCommand : cUpperCaseCommand; 270*cdf0e10cSrcweir } 271*cdf0e10cSrcweir } 272*cdf0e10cSrcweir 273*cdf0e10cSrcweir bool importFromSvgD(B2DPolyPolygon& o_rPolyPolygon, const ::rtl::OUString& rSvgDStatement) 274*cdf0e10cSrcweir { 275*cdf0e10cSrcweir o_rPolyPolygon.clear(); 276*cdf0e10cSrcweir const sal_Int32 nLen(rSvgDStatement.getLength()); 277*cdf0e10cSrcweir sal_Int32 nPos(0); 278*cdf0e10cSrcweir bool bIsClosed(false); 279*cdf0e10cSrcweir double nLastX( 0.0 ); 280*cdf0e10cSrcweir double nLastY( 0.0 ); 281*cdf0e10cSrcweir B2DPolygon aCurrPoly; 282*cdf0e10cSrcweir 283*cdf0e10cSrcweir // skip initial whitespace 284*cdf0e10cSrcweir lcl_skipSpaces(nPos, rSvgDStatement, nLen); 285*cdf0e10cSrcweir 286*cdf0e10cSrcweir while(nPos < nLen) 287*cdf0e10cSrcweir { 288*cdf0e10cSrcweir bool bRelative(false); 289*cdf0e10cSrcweir bool bMoveTo(false); 290*cdf0e10cSrcweir const sal_Unicode aCurrChar(rSvgDStatement[nPos]); 291*cdf0e10cSrcweir 292*cdf0e10cSrcweir switch(aCurrChar) 293*cdf0e10cSrcweir { 294*cdf0e10cSrcweir case 'z' : 295*cdf0e10cSrcweir case 'Z' : 296*cdf0e10cSrcweir { 297*cdf0e10cSrcweir nPos++; 298*cdf0e10cSrcweir lcl_skipSpaces(nPos, rSvgDStatement, nLen); 299*cdf0e10cSrcweir 300*cdf0e10cSrcweir // remember closed state of current polygon 301*cdf0e10cSrcweir bIsClosed = true; 302*cdf0e10cSrcweir break; 303*cdf0e10cSrcweir } 304*cdf0e10cSrcweir 305*cdf0e10cSrcweir case 'm' : 306*cdf0e10cSrcweir case 'M' : 307*cdf0e10cSrcweir { 308*cdf0e10cSrcweir bMoveTo = true; 309*cdf0e10cSrcweir // FALLTHROUGH intended 310*cdf0e10cSrcweir } 311*cdf0e10cSrcweir case 'l' : 312*cdf0e10cSrcweir case 'L' : 313*cdf0e10cSrcweir { 314*cdf0e10cSrcweir if('m' == aCurrChar || 'l' == aCurrChar) 315*cdf0e10cSrcweir { 316*cdf0e10cSrcweir bRelative = true; 317*cdf0e10cSrcweir } 318*cdf0e10cSrcweir 319*cdf0e10cSrcweir if(bMoveTo) 320*cdf0e10cSrcweir { 321*cdf0e10cSrcweir // new polygon start, finish old one 322*cdf0e10cSrcweir if(aCurrPoly.count()) 323*cdf0e10cSrcweir { 324*cdf0e10cSrcweir // add current polygon 325*cdf0e10cSrcweir if(bIsClosed) 326*cdf0e10cSrcweir { 327*cdf0e10cSrcweir closeWithGeometryChange(aCurrPoly); 328*cdf0e10cSrcweir } 329*cdf0e10cSrcweir 330*cdf0e10cSrcweir o_rPolyPolygon.append(aCurrPoly); 331*cdf0e10cSrcweir 332*cdf0e10cSrcweir // reset import values 333*cdf0e10cSrcweir bIsClosed = false; 334*cdf0e10cSrcweir aCurrPoly.clear(); 335*cdf0e10cSrcweir } 336*cdf0e10cSrcweir } 337*cdf0e10cSrcweir 338*cdf0e10cSrcweir nPos++; 339*cdf0e10cSrcweir lcl_skipSpaces(nPos, rSvgDStatement, nLen); 340*cdf0e10cSrcweir 341*cdf0e10cSrcweir while(nPos < nLen && lcl_isOnNumberChar(rSvgDStatement, nPos)) 342*cdf0e10cSrcweir { 343*cdf0e10cSrcweir double nX, nY; 344*cdf0e10cSrcweir 345*cdf0e10cSrcweir if(!lcl_importDoubleAndSpaces(nX, nPos, rSvgDStatement, nLen)) return false; 346*cdf0e10cSrcweir if(!lcl_importDoubleAndSpaces(nY, nPos, rSvgDStatement, nLen)) return false; 347*cdf0e10cSrcweir 348*cdf0e10cSrcweir if(bRelative) 349*cdf0e10cSrcweir { 350*cdf0e10cSrcweir nX += nLastX; 351*cdf0e10cSrcweir nY += nLastY; 352*cdf0e10cSrcweir } 353*cdf0e10cSrcweir 354*cdf0e10cSrcweir // set last position 355*cdf0e10cSrcweir nLastX = nX; 356*cdf0e10cSrcweir nLastY = nY; 357*cdf0e10cSrcweir 358*cdf0e10cSrcweir // add point 359*cdf0e10cSrcweir aCurrPoly.append(B2DPoint(nX, nY)); 360*cdf0e10cSrcweir } 361*cdf0e10cSrcweir break; 362*cdf0e10cSrcweir } 363*cdf0e10cSrcweir 364*cdf0e10cSrcweir case 'h' : 365*cdf0e10cSrcweir { 366*cdf0e10cSrcweir bRelative = true; 367*cdf0e10cSrcweir // FALLTHROUGH intended 368*cdf0e10cSrcweir } 369*cdf0e10cSrcweir case 'H' : 370*cdf0e10cSrcweir { 371*cdf0e10cSrcweir nPos++; 372*cdf0e10cSrcweir lcl_skipSpaces(nPos, rSvgDStatement, nLen); 373*cdf0e10cSrcweir 374*cdf0e10cSrcweir while(nPos < nLen && lcl_isOnNumberChar(rSvgDStatement, nPos)) 375*cdf0e10cSrcweir { 376*cdf0e10cSrcweir double nX, nY(nLastY); 377*cdf0e10cSrcweir 378*cdf0e10cSrcweir if(!lcl_importDoubleAndSpaces(nX, nPos, rSvgDStatement, nLen)) return false; 379*cdf0e10cSrcweir 380*cdf0e10cSrcweir if(bRelative) 381*cdf0e10cSrcweir { 382*cdf0e10cSrcweir nX += nLastX; 383*cdf0e10cSrcweir } 384*cdf0e10cSrcweir 385*cdf0e10cSrcweir // set last position 386*cdf0e10cSrcweir nLastX = nX; 387*cdf0e10cSrcweir 388*cdf0e10cSrcweir // add point 389*cdf0e10cSrcweir aCurrPoly.append(B2DPoint(nX, nY)); 390*cdf0e10cSrcweir } 391*cdf0e10cSrcweir break; 392*cdf0e10cSrcweir } 393*cdf0e10cSrcweir 394*cdf0e10cSrcweir case 'v' : 395*cdf0e10cSrcweir { 396*cdf0e10cSrcweir bRelative = true; 397*cdf0e10cSrcweir // FALLTHROUGH intended 398*cdf0e10cSrcweir } 399*cdf0e10cSrcweir case 'V' : 400*cdf0e10cSrcweir { 401*cdf0e10cSrcweir nPos++; 402*cdf0e10cSrcweir lcl_skipSpaces(nPos, rSvgDStatement, nLen); 403*cdf0e10cSrcweir 404*cdf0e10cSrcweir while(nPos < nLen && lcl_isOnNumberChar(rSvgDStatement, nPos)) 405*cdf0e10cSrcweir { 406*cdf0e10cSrcweir double nX(nLastX), nY; 407*cdf0e10cSrcweir 408*cdf0e10cSrcweir if(!lcl_importDoubleAndSpaces(nY, nPos, rSvgDStatement, nLen)) return false; 409*cdf0e10cSrcweir 410*cdf0e10cSrcweir if(bRelative) 411*cdf0e10cSrcweir { 412*cdf0e10cSrcweir nY += nLastY; 413*cdf0e10cSrcweir } 414*cdf0e10cSrcweir 415*cdf0e10cSrcweir // set last position 416*cdf0e10cSrcweir nLastY = nY; 417*cdf0e10cSrcweir 418*cdf0e10cSrcweir // add point 419*cdf0e10cSrcweir aCurrPoly.append(B2DPoint(nX, nY)); 420*cdf0e10cSrcweir } 421*cdf0e10cSrcweir break; 422*cdf0e10cSrcweir } 423*cdf0e10cSrcweir 424*cdf0e10cSrcweir case 's' : 425*cdf0e10cSrcweir { 426*cdf0e10cSrcweir bRelative = true; 427*cdf0e10cSrcweir // FALLTHROUGH intended 428*cdf0e10cSrcweir } 429*cdf0e10cSrcweir case 'S' : 430*cdf0e10cSrcweir { 431*cdf0e10cSrcweir nPos++; 432*cdf0e10cSrcweir lcl_skipSpaces(nPos, rSvgDStatement, nLen); 433*cdf0e10cSrcweir 434*cdf0e10cSrcweir while(nPos < nLen && lcl_isOnNumberChar(rSvgDStatement, nPos)) 435*cdf0e10cSrcweir { 436*cdf0e10cSrcweir double nX, nY; 437*cdf0e10cSrcweir double nX2, nY2; 438*cdf0e10cSrcweir 439*cdf0e10cSrcweir if(!lcl_importDoubleAndSpaces(nX2, nPos, rSvgDStatement, nLen)) return false; 440*cdf0e10cSrcweir if(!lcl_importDoubleAndSpaces(nY2, nPos, rSvgDStatement, nLen)) return false; 441*cdf0e10cSrcweir if(!lcl_importDoubleAndSpaces(nX, nPos, rSvgDStatement, nLen)) return false; 442*cdf0e10cSrcweir if(!lcl_importDoubleAndSpaces(nY, nPos, rSvgDStatement, nLen)) return false; 443*cdf0e10cSrcweir 444*cdf0e10cSrcweir if(bRelative) 445*cdf0e10cSrcweir { 446*cdf0e10cSrcweir nX2 += nLastX; 447*cdf0e10cSrcweir nY2 += nLastY; 448*cdf0e10cSrcweir nX += nLastX; 449*cdf0e10cSrcweir nY += nLastY; 450*cdf0e10cSrcweir } 451*cdf0e10cSrcweir 452*cdf0e10cSrcweir // ensure existance of start point 453*cdf0e10cSrcweir if(!aCurrPoly.count()) 454*cdf0e10cSrcweir { 455*cdf0e10cSrcweir aCurrPoly.append(B2DPoint(nLastX, nLastY)); 456*cdf0e10cSrcweir } 457*cdf0e10cSrcweir 458*cdf0e10cSrcweir // get first control point. It's the reflection of the PrevControlPoint 459*cdf0e10cSrcweir // of the last point. If not existent, use current point (see SVG) 460*cdf0e10cSrcweir B2DPoint aPrevControl(B2DPoint(nLastX, nLastY)); 461*cdf0e10cSrcweir const sal_uInt32 nIndex(aCurrPoly.count() - 1); 462*cdf0e10cSrcweir 463*cdf0e10cSrcweir if(aCurrPoly.areControlPointsUsed() && aCurrPoly.isPrevControlPointUsed(nIndex)) 464*cdf0e10cSrcweir { 465*cdf0e10cSrcweir const B2DPoint aPrevPoint(aCurrPoly.getB2DPoint(nIndex)); 466*cdf0e10cSrcweir const B2DPoint aPrevControlPoint(aCurrPoly.getPrevControlPoint(nIndex)); 467*cdf0e10cSrcweir 468*cdf0e10cSrcweir // use mirrored previous control point 469*cdf0e10cSrcweir aPrevControl.setX((2.0 * aPrevPoint.getX()) - aPrevControlPoint.getX()); 470*cdf0e10cSrcweir aPrevControl.setY((2.0 * aPrevPoint.getY()) - aPrevControlPoint.getY()); 471*cdf0e10cSrcweir } 472*cdf0e10cSrcweir 473*cdf0e10cSrcweir // append curved edge 474*cdf0e10cSrcweir aCurrPoly.appendBezierSegment(aPrevControl, B2DPoint(nX2, nY2), B2DPoint(nX, nY)); 475*cdf0e10cSrcweir 476*cdf0e10cSrcweir // set last position 477*cdf0e10cSrcweir nLastX = nX; 478*cdf0e10cSrcweir nLastY = nY; 479*cdf0e10cSrcweir } 480*cdf0e10cSrcweir break; 481*cdf0e10cSrcweir } 482*cdf0e10cSrcweir 483*cdf0e10cSrcweir case 'c' : 484*cdf0e10cSrcweir { 485*cdf0e10cSrcweir bRelative = true; 486*cdf0e10cSrcweir // FALLTHROUGH intended 487*cdf0e10cSrcweir } 488*cdf0e10cSrcweir case 'C' : 489*cdf0e10cSrcweir { 490*cdf0e10cSrcweir nPos++; 491*cdf0e10cSrcweir lcl_skipSpaces(nPos, rSvgDStatement, nLen); 492*cdf0e10cSrcweir 493*cdf0e10cSrcweir while(nPos < nLen && lcl_isOnNumberChar(rSvgDStatement, nPos)) 494*cdf0e10cSrcweir { 495*cdf0e10cSrcweir double nX, nY; 496*cdf0e10cSrcweir double nX1, nY1; 497*cdf0e10cSrcweir double nX2, nY2; 498*cdf0e10cSrcweir 499*cdf0e10cSrcweir if(!lcl_importDoubleAndSpaces(nX1, nPos, rSvgDStatement, nLen)) return false; 500*cdf0e10cSrcweir if(!lcl_importDoubleAndSpaces(nY1, nPos, rSvgDStatement, nLen)) return false; 501*cdf0e10cSrcweir if(!lcl_importDoubleAndSpaces(nX2, nPos, rSvgDStatement, nLen)) return false; 502*cdf0e10cSrcweir if(!lcl_importDoubleAndSpaces(nY2, nPos, rSvgDStatement, nLen)) return false; 503*cdf0e10cSrcweir if(!lcl_importDoubleAndSpaces(nX, nPos, rSvgDStatement, nLen)) return false; 504*cdf0e10cSrcweir if(!lcl_importDoubleAndSpaces(nY, nPos, rSvgDStatement, nLen)) return false; 505*cdf0e10cSrcweir 506*cdf0e10cSrcweir if(bRelative) 507*cdf0e10cSrcweir { 508*cdf0e10cSrcweir nX1 += nLastX; 509*cdf0e10cSrcweir nY1 += nLastY; 510*cdf0e10cSrcweir nX2 += nLastX; 511*cdf0e10cSrcweir nY2 += nLastY; 512*cdf0e10cSrcweir nX += nLastX; 513*cdf0e10cSrcweir nY += nLastY; 514*cdf0e10cSrcweir } 515*cdf0e10cSrcweir 516*cdf0e10cSrcweir // ensure existance of start point 517*cdf0e10cSrcweir if(!aCurrPoly.count()) 518*cdf0e10cSrcweir { 519*cdf0e10cSrcweir aCurrPoly.append(B2DPoint(nLastX, nLastY)); 520*cdf0e10cSrcweir } 521*cdf0e10cSrcweir 522*cdf0e10cSrcweir // append curved edge 523*cdf0e10cSrcweir aCurrPoly.appendBezierSegment(B2DPoint(nX1, nY1), B2DPoint(nX2, nY2), B2DPoint(nX, nY)); 524*cdf0e10cSrcweir 525*cdf0e10cSrcweir // set last position 526*cdf0e10cSrcweir nLastX = nX; 527*cdf0e10cSrcweir nLastY = nY; 528*cdf0e10cSrcweir } 529*cdf0e10cSrcweir break; 530*cdf0e10cSrcweir } 531*cdf0e10cSrcweir 532*cdf0e10cSrcweir // #100617# quadratic beziers are imported as cubic ones 533*cdf0e10cSrcweir case 'q' : 534*cdf0e10cSrcweir { 535*cdf0e10cSrcweir bRelative = true; 536*cdf0e10cSrcweir // FALLTHROUGH intended 537*cdf0e10cSrcweir } 538*cdf0e10cSrcweir case 'Q' : 539*cdf0e10cSrcweir { 540*cdf0e10cSrcweir nPos++; 541*cdf0e10cSrcweir lcl_skipSpaces(nPos, rSvgDStatement, nLen); 542*cdf0e10cSrcweir 543*cdf0e10cSrcweir while(nPos < nLen && lcl_isOnNumberChar(rSvgDStatement, nPos)) 544*cdf0e10cSrcweir { 545*cdf0e10cSrcweir double nX, nY; 546*cdf0e10cSrcweir double nX1, nY1; 547*cdf0e10cSrcweir 548*cdf0e10cSrcweir if(!lcl_importDoubleAndSpaces(nX1, nPos, rSvgDStatement, nLen)) return false; 549*cdf0e10cSrcweir if(!lcl_importDoubleAndSpaces(nY1, nPos, rSvgDStatement, nLen)) return false; 550*cdf0e10cSrcweir if(!lcl_importDoubleAndSpaces(nX, nPos, rSvgDStatement, nLen)) return false; 551*cdf0e10cSrcweir if(!lcl_importDoubleAndSpaces(nY, nPos, rSvgDStatement, nLen)) return false; 552*cdf0e10cSrcweir 553*cdf0e10cSrcweir if(bRelative) 554*cdf0e10cSrcweir { 555*cdf0e10cSrcweir nX1 += nLastX; 556*cdf0e10cSrcweir nY1 += nLastY; 557*cdf0e10cSrcweir nX += nLastX; 558*cdf0e10cSrcweir nY += nLastY; 559*cdf0e10cSrcweir } 560*cdf0e10cSrcweir 561*cdf0e10cSrcweir // calculate the cubic bezier coefficients from the quadratic ones 562*cdf0e10cSrcweir const double nX1Prime((nX1 * 2.0 + nLastX) / 3.0); 563*cdf0e10cSrcweir const double nY1Prime((nY1 * 2.0 + nLastY) / 3.0); 564*cdf0e10cSrcweir const double nX2Prime((nX1 * 2.0 + nX) / 3.0); 565*cdf0e10cSrcweir const double nY2Prime((nY1 * 2.0 + nY) / 3.0); 566*cdf0e10cSrcweir 567*cdf0e10cSrcweir // ensure existance of start point 568*cdf0e10cSrcweir if(!aCurrPoly.count()) 569*cdf0e10cSrcweir { 570*cdf0e10cSrcweir aCurrPoly.append(B2DPoint(nLastX, nLastY)); 571*cdf0e10cSrcweir } 572*cdf0e10cSrcweir 573*cdf0e10cSrcweir // append curved edge 574*cdf0e10cSrcweir aCurrPoly.appendBezierSegment(B2DPoint(nX1Prime, nY1Prime), B2DPoint(nX2Prime, nY2Prime), B2DPoint(nX, nY)); 575*cdf0e10cSrcweir 576*cdf0e10cSrcweir // set last position 577*cdf0e10cSrcweir nLastX = nX; 578*cdf0e10cSrcweir nLastY = nY; 579*cdf0e10cSrcweir } 580*cdf0e10cSrcweir break; 581*cdf0e10cSrcweir } 582*cdf0e10cSrcweir 583*cdf0e10cSrcweir // #100617# relative quadratic beziers are imported as cubic 584*cdf0e10cSrcweir case 't' : 585*cdf0e10cSrcweir { 586*cdf0e10cSrcweir bRelative = true; 587*cdf0e10cSrcweir // FALLTHROUGH intended 588*cdf0e10cSrcweir } 589*cdf0e10cSrcweir case 'T' : 590*cdf0e10cSrcweir { 591*cdf0e10cSrcweir nPos++; 592*cdf0e10cSrcweir lcl_skipSpaces(nPos, rSvgDStatement, nLen); 593*cdf0e10cSrcweir 594*cdf0e10cSrcweir while(nPos < nLen && lcl_isOnNumberChar(rSvgDStatement, nPos)) 595*cdf0e10cSrcweir { 596*cdf0e10cSrcweir double nX, nY; 597*cdf0e10cSrcweir 598*cdf0e10cSrcweir if(!lcl_importDoubleAndSpaces(nX, nPos, rSvgDStatement, nLen)) return false; 599*cdf0e10cSrcweir if(!lcl_importDoubleAndSpaces(nY, nPos, rSvgDStatement, nLen)) return false; 600*cdf0e10cSrcweir 601*cdf0e10cSrcweir if(bRelative) 602*cdf0e10cSrcweir { 603*cdf0e10cSrcweir nX += nLastX; 604*cdf0e10cSrcweir nY += nLastY; 605*cdf0e10cSrcweir } 606*cdf0e10cSrcweir 607*cdf0e10cSrcweir // ensure existance of start point 608*cdf0e10cSrcweir if(!aCurrPoly.count()) 609*cdf0e10cSrcweir { 610*cdf0e10cSrcweir aCurrPoly.append(B2DPoint(nLastX, nLastY)); 611*cdf0e10cSrcweir } 612*cdf0e10cSrcweir 613*cdf0e10cSrcweir // get first control point. It's the reflection of the PrevControlPoint 614*cdf0e10cSrcweir // of the last point. If not existent, use current point (see SVG) 615*cdf0e10cSrcweir B2DPoint aPrevControl(B2DPoint(nLastX, nLastY)); 616*cdf0e10cSrcweir const sal_uInt32 nIndex(aCurrPoly.count() - 1); 617*cdf0e10cSrcweir const B2DPoint aPrevPoint(aCurrPoly.getB2DPoint(nIndex)); 618*cdf0e10cSrcweir 619*cdf0e10cSrcweir if(aCurrPoly.areControlPointsUsed() && aCurrPoly.isPrevControlPointUsed(nIndex)) 620*cdf0e10cSrcweir { 621*cdf0e10cSrcweir const B2DPoint aPrevControlPoint(aCurrPoly.getPrevControlPoint(nIndex)); 622*cdf0e10cSrcweir 623*cdf0e10cSrcweir // use mirrored previous control point 624*cdf0e10cSrcweir aPrevControl.setX((2.0 * aPrevPoint.getX()) - aPrevControlPoint.getX()); 625*cdf0e10cSrcweir aPrevControl.setY((2.0 * aPrevPoint.getY()) - aPrevControlPoint.getY()); 626*cdf0e10cSrcweir } 627*cdf0e10cSrcweir 628*cdf0e10cSrcweir if(!aPrevControl.equal(aPrevPoint)) 629*cdf0e10cSrcweir { 630*cdf0e10cSrcweir // there is a prev control point, and we have the already mirrored one 631*cdf0e10cSrcweir // in aPrevControl. We also need the quadratic control point for this 632*cdf0e10cSrcweir // new quadratic segment to calculate the 2nd cubic control point 633*cdf0e10cSrcweir const B2DPoint aQuadControlPoint( 634*cdf0e10cSrcweir ((3.0 * aPrevControl.getX()) - aPrevPoint.getX()) / 2.0, 635*cdf0e10cSrcweir ((3.0 * aPrevControl.getY()) - aPrevPoint.getY()) / 2.0); 636*cdf0e10cSrcweir 637*cdf0e10cSrcweir // calculate the cubic bezier coefficients from the quadratic ones. 638*cdf0e10cSrcweir const double nX2Prime((aQuadControlPoint.getX() * 2.0 + nX) / 3.0); 639*cdf0e10cSrcweir const double nY2Prime((aQuadControlPoint.getY() * 2.0 + nY) / 3.0); 640*cdf0e10cSrcweir 641*cdf0e10cSrcweir // append curved edge, use mirrored cubic control point directly 642*cdf0e10cSrcweir aCurrPoly.appendBezierSegment(aPrevControl, B2DPoint(nX2Prime, nY2Prime), B2DPoint(nX, nY)); 643*cdf0e10cSrcweir } 644*cdf0e10cSrcweir else 645*cdf0e10cSrcweir { 646*cdf0e10cSrcweir // when no previous control, SVG says to use current point -> straight line. 647*cdf0e10cSrcweir // Just add end point 648*cdf0e10cSrcweir aCurrPoly.append(B2DPoint(nX, nY)); 649*cdf0e10cSrcweir } 650*cdf0e10cSrcweir 651*cdf0e10cSrcweir // set last position 652*cdf0e10cSrcweir nLastX = nX; 653*cdf0e10cSrcweir nLastY = nY; 654*cdf0e10cSrcweir } 655*cdf0e10cSrcweir break; 656*cdf0e10cSrcweir } 657*cdf0e10cSrcweir 658*cdf0e10cSrcweir case 'a' : 659*cdf0e10cSrcweir { 660*cdf0e10cSrcweir bRelative = true; 661*cdf0e10cSrcweir // FALLTHROUGH intended 662*cdf0e10cSrcweir } 663*cdf0e10cSrcweir case 'A' : 664*cdf0e10cSrcweir { 665*cdf0e10cSrcweir nPos++; 666*cdf0e10cSrcweir lcl_skipSpaces(nPos, rSvgDStatement, nLen); 667*cdf0e10cSrcweir 668*cdf0e10cSrcweir while(nPos < nLen && lcl_isOnNumberChar(rSvgDStatement, nPos)) 669*cdf0e10cSrcweir { 670*cdf0e10cSrcweir double nX, nY; 671*cdf0e10cSrcweir double fRX, fRY, fPhi; 672*cdf0e10cSrcweir sal_Int32 bLargeArcFlag, bSweepFlag; 673*cdf0e10cSrcweir 674*cdf0e10cSrcweir if(!lcl_importDoubleAndSpaces(fRX, nPos, rSvgDStatement, nLen)) return false; 675*cdf0e10cSrcweir if(!lcl_importDoubleAndSpaces(fRY, nPos, rSvgDStatement, nLen)) return false; 676*cdf0e10cSrcweir if(!lcl_importDoubleAndSpaces(fPhi, nPos, rSvgDStatement, nLen)) return false; 677*cdf0e10cSrcweir if(!lcl_importNumberAndSpaces(bLargeArcFlag, nPos, rSvgDStatement, nLen)) return false; 678*cdf0e10cSrcweir if(!lcl_importNumberAndSpaces(bSweepFlag, nPos, rSvgDStatement, nLen)) return false; 679*cdf0e10cSrcweir if(!lcl_importDoubleAndSpaces(nX, nPos, rSvgDStatement, nLen)) return false; 680*cdf0e10cSrcweir if(!lcl_importDoubleAndSpaces(nY, nPos, rSvgDStatement, nLen)) return false; 681*cdf0e10cSrcweir 682*cdf0e10cSrcweir if(bRelative) 683*cdf0e10cSrcweir { 684*cdf0e10cSrcweir nX += nLastX; 685*cdf0e10cSrcweir nY += nLastY; 686*cdf0e10cSrcweir } 687*cdf0e10cSrcweir 688*cdf0e10cSrcweir const B2DPoint aPrevPoint(aCurrPoly.getB2DPoint(aCurrPoly.count() - 1)); 689*cdf0e10cSrcweir 690*cdf0e10cSrcweir if( nX == nLastX && nY == nLastY ) 691*cdf0e10cSrcweir continue; // start==end -> skip according to SVG spec 692*cdf0e10cSrcweir 693*cdf0e10cSrcweir if( fRX == 0.0 || fRY == 0.0 ) 694*cdf0e10cSrcweir { 695*cdf0e10cSrcweir // straight line segment according to SVG spec 696*cdf0e10cSrcweir aCurrPoly.append(B2DPoint(nX, nY)); 697*cdf0e10cSrcweir } 698*cdf0e10cSrcweir else 699*cdf0e10cSrcweir { 700*cdf0e10cSrcweir // normalize according to SVG spec 701*cdf0e10cSrcweir fRX=fabs(fRX); fRY=fabs(fRY); 702*cdf0e10cSrcweir 703*cdf0e10cSrcweir // from the SVG spec, appendix F.6.4 704*cdf0e10cSrcweir 705*cdf0e10cSrcweir // |x1'| |cos phi sin phi| |(x1 - x2)/2| 706*cdf0e10cSrcweir // |y1'| = |-sin phi cos phi| |(y1 - y2)/2| 707*cdf0e10cSrcweir const B2DPoint p1(nLastX, nLastY); 708*cdf0e10cSrcweir const B2DPoint p2(nX, nY); 709*cdf0e10cSrcweir B2DHomMatrix aTransform(basegfx::tools::createRotateB2DHomMatrix(-fPhi*M_PI/180)); 710*cdf0e10cSrcweir 711*cdf0e10cSrcweir const B2DPoint p1_prime( aTransform * B2DPoint(((p1-p2)/2.0)) ); 712*cdf0e10cSrcweir 713*cdf0e10cSrcweir // ______________________________________ rx y1' 714*cdf0e10cSrcweir // |cx'| + / rx^2 ry^2 - rx^2 y1'^2 - ry^2 x1^2 ry 715*cdf0e10cSrcweir // |cy'| =-/ rx^2y1'^2 + ry^2 x1'^2 - ry x1' 716*cdf0e10cSrcweir // rx 717*cdf0e10cSrcweir // chose + if f_A != f_S 718*cdf0e10cSrcweir // chose - if f_A = f_S 719*cdf0e10cSrcweir B2DPoint aCenter_prime; 720*cdf0e10cSrcweir const double fRadicant( 721*cdf0e10cSrcweir (fRX*fRX*fRY*fRY - fRX*fRX*p1_prime.getY()*p1_prime.getY() - fRY*fRY*p1_prime.getX()*p1_prime.getX())/ 722*cdf0e10cSrcweir (fRX*fRX*p1_prime.getY()*p1_prime.getY() + fRY*fRY*p1_prime.getX()*p1_prime.getX())); 723*cdf0e10cSrcweir if( fRadicant < 0.0 ) 724*cdf0e10cSrcweir { 725*cdf0e10cSrcweir // no solution - according to SVG 726*cdf0e10cSrcweir // spec, scale up ellipse 727*cdf0e10cSrcweir // uniformly such that it passes 728*cdf0e10cSrcweir // through end points (denominator 729*cdf0e10cSrcweir // of radicant solved for fRY, 730*cdf0e10cSrcweir // with s=fRX/fRY) 731*cdf0e10cSrcweir const double fRatio(fRX/fRY); 732*cdf0e10cSrcweir const double fRadicant2( 733*cdf0e10cSrcweir p1_prime.getY()*p1_prime.getY() + 734*cdf0e10cSrcweir p1_prime.getX()*p1_prime.getX()/(fRatio*fRatio)); 735*cdf0e10cSrcweir if( fRadicant2 < 0.0 ) 736*cdf0e10cSrcweir { 737*cdf0e10cSrcweir // only trivial solution, one 738*cdf0e10cSrcweir // of the axes 0 -> straight 739*cdf0e10cSrcweir // line segment according to 740*cdf0e10cSrcweir // SVG spec 741*cdf0e10cSrcweir aCurrPoly.append(B2DPoint(nX, nY)); 742*cdf0e10cSrcweir continue; 743*cdf0e10cSrcweir } 744*cdf0e10cSrcweir 745*cdf0e10cSrcweir fRY=sqrt(fRadicant2); 746*cdf0e10cSrcweir fRX=fRatio*fRY; 747*cdf0e10cSrcweir 748*cdf0e10cSrcweir // keep center_prime forced to (0,0) 749*cdf0e10cSrcweir } 750*cdf0e10cSrcweir else 751*cdf0e10cSrcweir { 752*cdf0e10cSrcweir const double fFactor( 753*cdf0e10cSrcweir (bLargeArcFlag==bSweepFlag ? -1.0 : 1.0) * 754*cdf0e10cSrcweir sqrt(fRadicant)); 755*cdf0e10cSrcweir 756*cdf0e10cSrcweir // actually calculate center_prime 757*cdf0e10cSrcweir aCenter_prime = B2DPoint( 758*cdf0e10cSrcweir fFactor*fRX*p1_prime.getY()/fRY, 759*cdf0e10cSrcweir -fFactor*fRY*p1_prime.getX()/fRX); 760*cdf0e10cSrcweir } 761*cdf0e10cSrcweir 762*cdf0e10cSrcweir // + u - v 763*cdf0e10cSrcweir // angle(u,v) = arccos( ------------ ) (take the sign of (ux vy - uy vx)) 764*cdf0e10cSrcweir // - ||u|| ||v|| 765*cdf0e10cSrcweir 766*cdf0e10cSrcweir // 1 | (x1' - cx')/rx | 767*cdf0e10cSrcweir // theta1 = angle(( ), | | ) 768*cdf0e10cSrcweir // 0 | (y1' - cy')/ry | 769*cdf0e10cSrcweir const B2DPoint aRadii(fRX,fRY); 770*cdf0e10cSrcweir double fTheta1( 771*cdf0e10cSrcweir B2DVector(1.0,0.0).angle( 772*cdf0e10cSrcweir (p1_prime-aCenter_prime)/aRadii)); 773*cdf0e10cSrcweir 774*cdf0e10cSrcweir // |1| | (-x1' - cx')/rx | 775*cdf0e10cSrcweir // theta2 = angle( | | , | | ) 776*cdf0e10cSrcweir // |0| | (-y1' - cy')/ry | 777*cdf0e10cSrcweir double fTheta2( 778*cdf0e10cSrcweir B2DVector(1.0,0.0).angle( 779*cdf0e10cSrcweir (-p1_prime-aCenter_prime)/aRadii)); 780*cdf0e10cSrcweir 781*cdf0e10cSrcweir // map both angles to [0,2pi) 782*cdf0e10cSrcweir fTheta1 = fmod(2*M_PI+fTheta1,2*M_PI); 783*cdf0e10cSrcweir fTheta2 = fmod(2*M_PI+fTheta2,2*M_PI); 784*cdf0e10cSrcweir 785*cdf0e10cSrcweir // make sure the large arc is taken 786*cdf0e10cSrcweir // (since 787*cdf0e10cSrcweir // createPolygonFromEllipseSegment() 788*cdf0e10cSrcweir // normalizes to e.g. cw arc) 789*cdf0e10cSrcweir const bool bFlipSegment( (bLargeArcFlag!=0) == 790*cdf0e10cSrcweir (fmod(fTheta2+2*M_PI-fTheta1, 791*cdf0e10cSrcweir 2*M_PI)<M_PI) ); 792*cdf0e10cSrcweir if( bFlipSegment ) 793*cdf0e10cSrcweir std::swap(fTheta1,fTheta2); 794*cdf0e10cSrcweir 795*cdf0e10cSrcweir // finally, create bezier polygon from this 796*cdf0e10cSrcweir B2DPolygon aSegment( 797*cdf0e10cSrcweir tools::createPolygonFromUnitEllipseSegment( 798*cdf0e10cSrcweir fTheta1, fTheta2 )); 799*cdf0e10cSrcweir 800*cdf0e10cSrcweir // transform ellipse by rotation & move to final center 801*cdf0e10cSrcweir aTransform = basegfx::tools::createScaleB2DHomMatrix(fRX, fRY); 802*cdf0e10cSrcweir aTransform.translate(aCenter_prime.getX(), 803*cdf0e10cSrcweir aCenter_prime.getY()); 804*cdf0e10cSrcweir aTransform.rotate(fPhi*M_PI/180); 805*cdf0e10cSrcweir const B2DPoint aOffset((p1+p2)/2.0); 806*cdf0e10cSrcweir aTransform.translate(aOffset.getX(), 807*cdf0e10cSrcweir aOffset.getY()); 808*cdf0e10cSrcweir aSegment.transform(aTransform); 809*cdf0e10cSrcweir 810*cdf0e10cSrcweir // createPolygonFromEllipseSegment() 811*cdf0e10cSrcweir // always creates arcs that are 812*cdf0e10cSrcweir // positively oriented - flip polygon 813*cdf0e10cSrcweir // if we swapped angles above 814*cdf0e10cSrcweir if( bFlipSegment ) 815*cdf0e10cSrcweir aSegment.flip(); 816*cdf0e10cSrcweir aCurrPoly.append(aSegment); 817*cdf0e10cSrcweir } 818*cdf0e10cSrcweir 819*cdf0e10cSrcweir // set last position 820*cdf0e10cSrcweir nLastX = nX; 821*cdf0e10cSrcweir nLastY = nY; 822*cdf0e10cSrcweir } 823*cdf0e10cSrcweir break; 824*cdf0e10cSrcweir } 825*cdf0e10cSrcweir 826*cdf0e10cSrcweir default: 827*cdf0e10cSrcweir { 828*cdf0e10cSrcweir OSL_ENSURE(false, "importFromSvgD(): skipping tags in svg:d element (unknown)!"); 829*cdf0e10cSrcweir OSL_TRACE("importFromSvgD(): skipping tags in svg:d element (unknown: \"%c\")!", aCurrChar); 830*cdf0e10cSrcweir ++nPos; 831*cdf0e10cSrcweir break; 832*cdf0e10cSrcweir } 833*cdf0e10cSrcweir } 834*cdf0e10cSrcweir } 835*cdf0e10cSrcweir 836*cdf0e10cSrcweir if(aCurrPoly.count()) 837*cdf0e10cSrcweir { 838*cdf0e10cSrcweir // end-process last poly 839*cdf0e10cSrcweir if(bIsClosed) 840*cdf0e10cSrcweir { 841*cdf0e10cSrcweir closeWithGeometryChange(aCurrPoly); 842*cdf0e10cSrcweir } 843*cdf0e10cSrcweir 844*cdf0e10cSrcweir o_rPolyPolygon.append(aCurrPoly); 845*cdf0e10cSrcweir } 846*cdf0e10cSrcweir 847*cdf0e10cSrcweir return true; 848*cdf0e10cSrcweir } 849*cdf0e10cSrcweir 850*cdf0e10cSrcweir bool importFromSvgPoints( B2DPolygon& o_rPoly, 851*cdf0e10cSrcweir const ::rtl::OUString& rSvgPointsAttribute ) 852*cdf0e10cSrcweir { 853*cdf0e10cSrcweir o_rPoly.clear(); 854*cdf0e10cSrcweir const sal_Int32 nLen(rSvgPointsAttribute.getLength()); 855*cdf0e10cSrcweir sal_Int32 nPos(0); 856*cdf0e10cSrcweir double nX, nY; 857*cdf0e10cSrcweir 858*cdf0e10cSrcweir // skip initial whitespace 859*cdf0e10cSrcweir lcl_skipSpaces(nPos, rSvgPointsAttribute, nLen); 860*cdf0e10cSrcweir 861*cdf0e10cSrcweir while(nPos < nLen) 862*cdf0e10cSrcweir { 863*cdf0e10cSrcweir if(!lcl_importDoubleAndSpaces(nX, nPos, rSvgPointsAttribute, nLen)) return false; 864*cdf0e10cSrcweir if(!lcl_importDoubleAndSpaces(nY, nPos, rSvgPointsAttribute, nLen)) return false; 865*cdf0e10cSrcweir 866*cdf0e10cSrcweir // add point 867*cdf0e10cSrcweir o_rPoly.append(B2DPoint(nX, nY)); 868*cdf0e10cSrcweir 869*cdf0e10cSrcweir // skip to next number, or finish 870*cdf0e10cSrcweir lcl_skipSpaces(nPos, rSvgPointsAttribute, nLen); 871*cdf0e10cSrcweir } 872*cdf0e10cSrcweir 873*cdf0e10cSrcweir return true; 874*cdf0e10cSrcweir } 875*cdf0e10cSrcweir 876*cdf0e10cSrcweir ::rtl::OUString exportToSvgD( 877*cdf0e10cSrcweir const B2DPolyPolygon& rPolyPolygon, 878*cdf0e10cSrcweir bool bUseRelativeCoordinates, 879*cdf0e10cSrcweir bool bDetectQuadraticBeziers) 880*cdf0e10cSrcweir { 881*cdf0e10cSrcweir const sal_uInt32 nCount(rPolyPolygon.count()); 882*cdf0e10cSrcweir ::rtl::OUStringBuffer aResult; 883*cdf0e10cSrcweir B2DPoint aCurrentSVGPosition(0.0, 0.0); // SVG assumes (0,0) as the initial current point 884*cdf0e10cSrcweir 885*cdf0e10cSrcweir for(sal_uInt32 i(0); i < nCount; i++) 886*cdf0e10cSrcweir { 887*cdf0e10cSrcweir const B2DPolygon aPolygon(rPolyPolygon.getB2DPolygon(i)); 888*cdf0e10cSrcweir const sal_uInt32 nPointCount(aPolygon.count()); 889*cdf0e10cSrcweir 890*cdf0e10cSrcweir if(nPointCount) 891*cdf0e10cSrcweir { 892*cdf0e10cSrcweir const bool bPolyUsesControlPoints(aPolygon.areControlPointsUsed()); 893*cdf0e10cSrcweir const sal_uInt32 nEdgeCount(aPolygon.isClosed() ? nPointCount : nPointCount - 1); 894*cdf0e10cSrcweir sal_Unicode aLastSVGCommand(' '); // last SVG command char 895*cdf0e10cSrcweir B2DPoint aLeft, aRight; // for quadratic bezier test 896*cdf0e10cSrcweir 897*cdf0e10cSrcweir // handle polygon start point 898*cdf0e10cSrcweir B2DPoint aEdgeStart(aPolygon.getB2DPoint(0)); 899*cdf0e10cSrcweir aResult.append(lcl_getCommand('M', 'm', bUseRelativeCoordinates)); 900*cdf0e10cSrcweir lcl_putNumberCharWithSpace(aResult, aEdgeStart.getX(), aCurrentSVGPosition.getX(), bUseRelativeCoordinates); 901*cdf0e10cSrcweir lcl_putNumberCharWithSpace(aResult, aEdgeStart.getY(), aCurrentSVGPosition.getY(), bUseRelativeCoordinates); 902*cdf0e10cSrcweir aLastSVGCommand = lcl_getCommand('L', 'l', bUseRelativeCoordinates); 903*cdf0e10cSrcweir aCurrentSVGPosition = aEdgeStart; 904*cdf0e10cSrcweir 905*cdf0e10cSrcweir for(sal_uInt32 nIndex(0); nIndex < nEdgeCount; nIndex++) 906*cdf0e10cSrcweir { 907*cdf0e10cSrcweir // prepare access to next point 908*cdf0e10cSrcweir const sal_uInt32 nNextIndex((nIndex + 1) % nPointCount); 909*cdf0e10cSrcweir const B2DPoint aEdgeEnd(aPolygon.getB2DPoint(nNextIndex)); 910*cdf0e10cSrcweir 911*cdf0e10cSrcweir // handle edge from (aEdgeStart, aEdgeEnd) using indices (nIndex, nNextIndex) 912*cdf0e10cSrcweir const bool bEdgeIsBezier(bPolyUsesControlPoints 913*cdf0e10cSrcweir && (aPolygon.isNextControlPointUsed(nIndex) || aPolygon.isPrevControlPointUsed(nNextIndex))); 914*cdf0e10cSrcweir 915*cdf0e10cSrcweir if(bEdgeIsBezier) 916*cdf0e10cSrcweir { 917*cdf0e10cSrcweir // handle bezier edge 918*cdf0e10cSrcweir const B2DPoint aControlEdgeStart(aPolygon.getNextControlPoint(nIndex)); 919*cdf0e10cSrcweir const B2DPoint aControlEdgeEnd(aPolygon.getPrevControlPoint(nNextIndex)); 920*cdf0e10cSrcweir bool bIsQuadraticBezier(false); 921*cdf0e10cSrcweir 922*cdf0e10cSrcweir // check continuity at current edge's start point. For SVG, do NOT use an 923*cdf0e10cSrcweir // existing continuity since no 'S' or 's' statement should be written. At 924*cdf0e10cSrcweir // import, that 'previous' control vector is not available. SVG documentation 925*cdf0e10cSrcweir // says for interpretation: 926*cdf0e10cSrcweir // 927*cdf0e10cSrcweir // "(If there is no previous command or if the previous command was 928*cdf0e10cSrcweir // not an C, c, S or s, assume the first control point is coincident 929*cdf0e10cSrcweir // with the current point.)" 930*cdf0e10cSrcweir // 931*cdf0e10cSrcweir // That's what is done from our import, so avoid exporting it as first statement 932*cdf0e10cSrcweir // is necessary. 933*cdf0e10cSrcweir const bool bSymmetricAtEdgeStart( 934*cdf0e10cSrcweir 0 != nIndex 935*cdf0e10cSrcweir && CONTINUITY_C2 == aPolygon.getContinuityInPoint(nIndex)); 936*cdf0e10cSrcweir 937*cdf0e10cSrcweir if(bDetectQuadraticBeziers) 938*cdf0e10cSrcweir { 939*cdf0e10cSrcweir // check for quadratic beziers - that's 940*cdf0e10cSrcweir // the case if both control points are in 941*cdf0e10cSrcweir // the same place when they are prolonged 942*cdf0e10cSrcweir // to the common quadratic control point 943*cdf0e10cSrcweir // 944*cdf0e10cSrcweir // Left: P = (3P1 - P0) / 2 945*cdf0e10cSrcweir // Right: P = (3P2 - P3) / 2 946*cdf0e10cSrcweir aLeft = B2DPoint((3.0 * aControlEdgeStart - aEdgeStart) / 2.0); 947*cdf0e10cSrcweir aRight= B2DPoint((3.0 * aControlEdgeEnd - aEdgeEnd) / 2.0); 948*cdf0e10cSrcweir bIsQuadraticBezier = aLeft.equal(aRight); 949*cdf0e10cSrcweir } 950*cdf0e10cSrcweir 951*cdf0e10cSrcweir if(bIsQuadraticBezier) 952*cdf0e10cSrcweir { 953*cdf0e10cSrcweir // approximately equal, export as quadratic bezier 954*cdf0e10cSrcweir if(bSymmetricAtEdgeStart) 955*cdf0e10cSrcweir { 956*cdf0e10cSrcweir const sal_Unicode aCommand(lcl_getCommand('T', 't', bUseRelativeCoordinates)); 957*cdf0e10cSrcweir 958*cdf0e10cSrcweir if(aLastSVGCommand != aCommand) 959*cdf0e10cSrcweir { 960*cdf0e10cSrcweir aResult.append(aCommand); 961*cdf0e10cSrcweir aLastSVGCommand = aCommand; 962*cdf0e10cSrcweir } 963*cdf0e10cSrcweir 964*cdf0e10cSrcweir lcl_putNumberCharWithSpace(aResult, aEdgeEnd.getX(), aCurrentSVGPosition.getX(), bUseRelativeCoordinates); 965*cdf0e10cSrcweir lcl_putNumberCharWithSpace(aResult, aEdgeEnd.getY(), aCurrentSVGPosition.getY(), bUseRelativeCoordinates); 966*cdf0e10cSrcweir aLastSVGCommand = aCommand; 967*cdf0e10cSrcweir aCurrentSVGPosition = aEdgeEnd; 968*cdf0e10cSrcweir } 969*cdf0e10cSrcweir else 970*cdf0e10cSrcweir { 971*cdf0e10cSrcweir const sal_Unicode aCommand(lcl_getCommand('Q', 'q', bUseRelativeCoordinates)); 972*cdf0e10cSrcweir 973*cdf0e10cSrcweir if(aLastSVGCommand != aCommand) 974*cdf0e10cSrcweir { 975*cdf0e10cSrcweir aResult.append(aCommand); 976*cdf0e10cSrcweir aLastSVGCommand = aCommand; 977*cdf0e10cSrcweir } 978*cdf0e10cSrcweir 979*cdf0e10cSrcweir lcl_putNumberCharWithSpace(aResult, aLeft.getX(), aCurrentSVGPosition.getX(), bUseRelativeCoordinates); 980*cdf0e10cSrcweir lcl_putNumberCharWithSpace(aResult, aLeft.getY(), aCurrentSVGPosition.getY(), bUseRelativeCoordinates); 981*cdf0e10cSrcweir lcl_putNumberCharWithSpace(aResult, aEdgeEnd.getX(), aCurrentSVGPosition.getX(), bUseRelativeCoordinates); 982*cdf0e10cSrcweir lcl_putNumberCharWithSpace(aResult, aEdgeEnd.getY(), aCurrentSVGPosition.getY(), bUseRelativeCoordinates); 983*cdf0e10cSrcweir aLastSVGCommand = aCommand; 984*cdf0e10cSrcweir aCurrentSVGPosition = aEdgeEnd; 985*cdf0e10cSrcweir } 986*cdf0e10cSrcweir } 987*cdf0e10cSrcweir else 988*cdf0e10cSrcweir { 989*cdf0e10cSrcweir // export as cubic bezier 990*cdf0e10cSrcweir if(bSymmetricAtEdgeStart) 991*cdf0e10cSrcweir { 992*cdf0e10cSrcweir const sal_Unicode aCommand(lcl_getCommand('S', 's', bUseRelativeCoordinates)); 993*cdf0e10cSrcweir 994*cdf0e10cSrcweir if(aLastSVGCommand != aCommand) 995*cdf0e10cSrcweir { 996*cdf0e10cSrcweir aResult.append(aCommand); 997*cdf0e10cSrcweir aLastSVGCommand = aCommand; 998*cdf0e10cSrcweir } 999*cdf0e10cSrcweir 1000*cdf0e10cSrcweir lcl_putNumberCharWithSpace(aResult, aControlEdgeEnd.getX(), aCurrentSVGPosition.getX(), bUseRelativeCoordinates); 1001*cdf0e10cSrcweir lcl_putNumberCharWithSpace(aResult, aControlEdgeEnd.getY(), aCurrentSVGPosition.getY(), bUseRelativeCoordinates); 1002*cdf0e10cSrcweir lcl_putNumberCharWithSpace(aResult, aEdgeEnd.getX(), aCurrentSVGPosition.getX(), bUseRelativeCoordinates); 1003*cdf0e10cSrcweir lcl_putNumberCharWithSpace(aResult, aEdgeEnd.getY(), aCurrentSVGPosition.getY(), bUseRelativeCoordinates); 1004*cdf0e10cSrcweir aLastSVGCommand = aCommand; 1005*cdf0e10cSrcweir aCurrentSVGPosition = aEdgeEnd; 1006*cdf0e10cSrcweir } 1007*cdf0e10cSrcweir else 1008*cdf0e10cSrcweir { 1009*cdf0e10cSrcweir const sal_Unicode aCommand(lcl_getCommand('C', 'c', bUseRelativeCoordinates)); 1010*cdf0e10cSrcweir 1011*cdf0e10cSrcweir if(aLastSVGCommand != aCommand) 1012*cdf0e10cSrcweir { 1013*cdf0e10cSrcweir aResult.append(aCommand); 1014*cdf0e10cSrcweir aLastSVGCommand = aCommand; 1015*cdf0e10cSrcweir } 1016*cdf0e10cSrcweir 1017*cdf0e10cSrcweir lcl_putNumberCharWithSpace(aResult, aControlEdgeStart.getX(), aCurrentSVGPosition.getX(), bUseRelativeCoordinates); 1018*cdf0e10cSrcweir lcl_putNumberCharWithSpace(aResult, aControlEdgeStart.getY(), aCurrentSVGPosition.getY(), bUseRelativeCoordinates); 1019*cdf0e10cSrcweir lcl_putNumberCharWithSpace(aResult, aControlEdgeEnd.getX(), aCurrentSVGPosition.getX(), bUseRelativeCoordinates); 1020*cdf0e10cSrcweir lcl_putNumberCharWithSpace(aResult, aControlEdgeEnd.getY(), aCurrentSVGPosition.getY(), bUseRelativeCoordinates); 1021*cdf0e10cSrcweir lcl_putNumberCharWithSpace(aResult, aEdgeEnd.getX(), aCurrentSVGPosition.getX(), bUseRelativeCoordinates); 1022*cdf0e10cSrcweir lcl_putNumberCharWithSpace(aResult, aEdgeEnd.getY(), aCurrentSVGPosition.getY(), bUseRelativeCoordinates); 1023*cdf0e10cSrcweir aLastSVGCommand = aCommand; 1024*cdf0e10cSrcweir aCurrentSVGPosition = aEdgeEnd; 1025*cdf0e10cSrcweir } 1026*cdf0e10cSrcweir } 1027*cdf0e10cSrcweir } 1028*cdf0e10cSrcweir else 1029*cdf0e10cSrcweir { 1030*cdf0e10cSrcweir // straight edge 1031*cdf0e10cSrcweir if(0 == nNextIndex) 1032*cdf0e10cSrcweir { 1033*cdf0e10cSrcweir // it's a closed polygon's last edge and it's not a bezier edge, so there is 1034*cdf0e10cSrcweir // no need to write it 1035*cdf0e10cSrcweir } 1036*cdf0e10cSrcweir else 1037*cdf0e10cSrcweir { 1038*cdf0e10cSrcweir const bool bXEqual(aEdgeStart.getX() == aEdgeEnd.getX()); 1039*cdf0e10cSrcweir const bool bYEqual(aEdgeStart.getY() == aEdgeEnd.getY()); 1040*cdf0e10cSrcweir 1041*cdf0e10cSrcweir if(bXEqual && bYEqual) 1042*cdf0e10cSrcweir { 1043*cdf0e10cSrcweir // point is a double point; do not export at all 1044*cdf0e10cSrcweir } 1045*cdf0e10cSrcweir else if(bXEqual) 1046*cdf0e10cSrcweir { 1047*cdf0e10cSrcweir // export as vertical line 1048*cdf0e10cSrcweir const sal_Unicode aCommand(lcl_getCommand('V', 'v', bUseRelativeCoordinates)); 1049*cdf0e10cSrcweir 1050*cdf0e10cSrcweir if(aLastSVGCommand != aCommand) 1051*cdf0e10cSrcweir { 1052*cdf0e10cSrcweir aResult.append(aCommand); 1053*cdf0e10cSrcweir aLastSVGCommand = aCommand; 1054*cdf0e10cSrcweir } 1055*cdf0e10cSrcweir 1056*cdf0e10cSrcweir lcl_putNumberCharWithSpace(aResult, aEdgeEnd.getY(), aCurrentSVGPosition.getY(), bUseRelativeCoordinates); 1057*cdf0e10cSrcweir aCurrentSVGPosition = aEdgeEnd; 1058*cdf0e10cSrcweir } 1059*cdf0e10cSrcweir else if(bYEqual) 1060*cdf0e10cSrcweir { 1061*cdf0e10cSrcweir // export as horizontal line 1062*cdf0e10cSrcweir const sal_Unicode aCommand(lcl_getCommand('H', 'h', bUseRelativeCoordinates)); 1063*cdf0e10cSrcweir 1064*cdf0e10cSrcweir if(aLastSVGCommand != aCommand) 1065*cdf0e10cSrcweir { 1066*cdf0e10cSrcweir aResult.append(aCommand); 1067*cdf0e10cSrcweir aLastSVGCommand = aCommand; 1068*cdf0e10cSrcweir } 1069*cdf0e10cSrcweir 1070*cdf0e10cSrcweir lcl_putNumberCharWithSpace(aResult, aEdgeEnd.getX(), aCurrentSVGPosition.getX(), bUseRelativeCoordinates); 1071*cdf0e10cSrcweir aCurrentSVGPosition = aEdgeEnd; 1072*cdf0e10cSrcweir } 1073*cdf0e10cSrcweir else 1074*cdf0e10cSrcweir { 1075*cdf0e10cSrcweir // export as line 1076*cdf0e10cSrcweir const sal_Unicode aCommand(lcl_getCommand('L', 'l', bUseRelativeCoordinates)); 1077*cdf0e10cSrcweir 1078*cdf0e10cSrcweir if(aLastSVGCommand != aCommand) 1079*cdf0e10cSrcweir { 1080*cdf0e10cSrcweir aResult.append(aCommand); 1081*cdf0e10cSrcweir aLastSVGCommand = aCommand; 1082*cdf0e10cSrcweir } 1083*cdf0e10cSrcweir 1084*cdf0e10cSrcweir lcl_putNumberCharWithSpace(aResult, aEdgeEnd.getX(), aCurrentSVGPosition.getX(), bUseRelativeCoordinates); 1085*cdf0e10cSrcweir lcl_putNumberCharWithSpace(aResult, aEdgeEnd.getY(), aCurrentSVGPosition.getY(), bUseRelativeCoordinates); 1086*cdf0e10cSrcweir aCurrentSVGPosition = aEdgeEnd; 1087*cdf0e10cSrcweir } 1088*cdf0e10cSrcweir } 1089*cdf0e10cSrcweir } 1090*cdf0e10cSrcweir 1091*cdf0e10cSrcweir // prepare edge start for next loop step 1092*cdf0e10cSrcweir aEdgeStart = aEdgeEnd; 1093*cdf0e10cSrcweir } 1094*cdf0e10cSrcweir 1095*cdf0e10cSrcweir // close path if closed poly (Z and z are equivalent here, but looks nicer when case is matched) 1096*cdf0e10cSrcweir if(aPolygon.isClosed()) 1097*cdf0e10cSrcweir { 1098*cdf0e10cSrcweir aResult.append(lcl_getCommand('Z', 'z', bUseRelativeCoordinates)); 1099*cdf0e10cSrcweir } 1100*cdf0e10cSrcweir } 1101*cdf0e10cSrcweir } 1102*cdf0e10cSrcweir 1103*cdf0e10cSrcweir return aResult.makeStringAndClear(); 1104*cdf0e10cSrcweir } 1105*cdf0e10cSrcweir } 1106*cdf0e10cSrcweir } 1107*cdf0e10cSrcweir 1108*cdf0e10cSrcweir // eof 1109