LCOV - code coverage report
Current view: top level - libreoffice/basegfx/source/polygon - b2dsvgpolypolygon.cxx (source / functions) Hit Total Coverage
Test: libreoffice_filtered.info Lines: 295 429 68.8 %
Date: 2012-12-27 Functions: 11 13 84.6 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.10