[Libreoffice-commits] core.git: Branch 'libreoffice-7-0' - svx/qa svx/source

Regina Henschel (via logerrit) logerrit at kemper.freedesktop.org
Tue Jun 16 11:15:46 UTC 2020


 svx/qa/unit/customshapes.cxx                            |   53 +++++++
 svx/qa/unit/data/tdf103474_commandG_CaseZeroHeight.odp  |binary
 svx/qa/unit/data/tdf122323_swingAngle_larger360deg.pptx |binary
 svx/source/customshapes/EnhancedCustomShape2d.cxx       |  112 +++++++++-------
 4 files changed, 120 insertions(+), 45 deletions(-)

New commits:
commit 6eaa80f5c40619d8d27c1e5dc180bd2289bd3559
Author:     Regina Henschel <rb.henschel at t-online.de>
AuthorDate: Wed Jun 10 20:58:54 2020 +0200
Commit:     Xisco Fauli <xiscofauli at libreoffice.org>
CommitDate: Tue Jun 16 13:15:16 2020 +0200

    tdf#103474 handle edge cases in ARCANGLETO
    
    The arc-polygon generation in tools does not consider edge cases with
    zero height or width. That leads to wrong rendering in some ooxml
    shapes, when the handle is dragged to its extrem position, e.g. in
    left/right braces/bracket and can.
    
    I have switched from tools to basegfx in case ARCANGLETO and added
    handling for edge cases. Switching to basegfx has the additional
    advantage, that Bezier curves are used and not polylines. You see
    the difference, if you convert the shape to curve.
    ARCANGLETO is not used from our shapes or from import from binary
    MS Office, but only from OOXML or user-defined custom shapes.
    
    tdf#122323 MS Office restricts the swing angle to [-360°,360] in
    rendering. Such restriction is not in OOXML and not in ODF.
    Nevertheless, I have added a clamp for ooxml-foo shapes for better
    interoperability.
    
    Change-Id: Ib3233ce14dab950cc521cb8cbac6809a1d3e34a7
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/96068
    Tested-by: Jenkins
    Reviewed-by: Regina Henschel <rb.henschel at t-online.de>
    (cherry picked from commit 6de8d3109dffa7d4d0cc06f319cca70134f0a8f3)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/96335
    Reviewed-by: Xisco Fauli <xiscofauli at libreoffice.org>

diff --git a/svx/qa/unit/customshapes.cxx b/svx/qa/unit/customshapes.cxx
index d8f7fb8a5bf9..309c5155d8da 100644
--- a/svx/qa/unit/customshapes.cxx
+++ b/svx/qa/unit/customshapes.cxx
@@ -704,6 +704,59 @@ CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf103474_commandT_CaseZeroHeight)
     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("aStart y-coordinate", 9999.0, aStart.getY(), 1.0);
     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("aEnd y-coordinate", 1999.0, aEnd.getY(), 1.0);
 }
+
+CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf103474_commandG_CaseZeroHeight)
+{
+    // Some as above, but with shape with command G.
+    OUString sURL
+        = m_directories.getURLFromSrc(sDataDirectory) + "tdf103474_commandG_CaseZeroHeight.odp";
+    mxComponent = loadFromDesktop(sURL, "com.sun.star.comp.presentation.PresentationDocument");
+    CPPUNIT_ASSERT_MESSAGE("Could not load document", mxComponent.is());
+    uno::Reference<drawing::XShape> xShape(getShape(0));
+    // The end points of the straight line segment should have the same x-coordinate of left
+    // of shape, and different y-coordinates, one top and the other bottom of the shape.
+    SdrObjCustomShape& rSdrObjCustomShape(
+        static_cast<SdrObjCustomShape&>(*GetSdrObjectFromXShape(xShape)));
+    EnhancedCustomShape2d aCustomShape2d(rSdrObjCustomShape);
+    SdrPathObj* pPathObj = static_cast<SdrPathObj*>(aCustomShape2d.CreateLineGeometry());
+    CPPUNIT_ASSERT_MESSAGE("Could not convert to SdrPathObj", pPathObj);
+    const basegfx::B2DPolyPolygon aPolyPolygon(pPathObj->GetPathPoly());
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("count polygons", static_cast<sal_uInt32>(1),
+                                 aPolyPolygon.count());
+    const basegfx::B2DPolygon aPolygon(aPolyPolygon.getB2DPolygon(0));
+    // Get the middle points of the polygon. They are the endpoints of the
+    // straight line segment regardless of the quarter ellipse parts, because
+    // the shape is symmetric.
+    const basegfx::B2DPoint aStart(aPolygon.getB2DPoint(aPolygon.count() / 2 - 1));
+    const basegfx::B2DPoint aEnd(aPolygon.getB2DPoint(aPolygon.count() / 2));
+    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("aStart x-coordinate", 1999.0, aStart.getX(), 1.0);
+    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("aEnd x-coordinate", 1999.0, aEnd.getX(), 1.0);
+    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("aStart y-coordinate", 9999.0, aStart.getY(), 1.0);
+    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("aEnd y-coordinate", 1999.0, aEnd.getY(), 1.0);
+}
+
+CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf122323_largeSwingAngle)
+{
+    // SwingAngles are clamped to [-360;360] in MS Office. Error was, that LO calculated
+    // the end angle and used it modulo 360, no full ellipse was drawn.
+    OUString sURL
+        = m_directories.getURLFromSrc(sDataDirectory) + "tdf122323_swingAngle_larger360deg.pptx";
+    mxComponent = loadFromDesktop(sURL, "com.sun.star.comp.presentation.PresentationDocument");
+    CPPUNIT_ASSERT_MESSAGE("Could not load document", mxComponent.is());
+    uno::Reference<drawing::XShape> xShape(getShape(0));
+    uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
+    SdrObjCustomShape& rSdrObjCustomShape(
+        static_cast<SdrObjCustomShape&>(*GetSdrObjectFromXShape(xShape)));
+    EnhancedCustomShape2d aCustomShape2d(rSdrObjCustomShape);
+    SdrPathObj* pPathObj = static_cast<SdrPathObj*>(aCustomShape2d.CreateLineGeometry());
+    CPPUNIT_ASSERT_MESSAGE("Could not convert to SdrPathObj", pPathObj);
+    const basegfx::B2DPolyPolygon aPolyPolygon(pPathObj->GetPathPoly());
+    const basegfx::B2DPolygon aPolygon(aPolyPolygon.getB2DPolygon(0));
+    const basegfx::B2DPoint aStart(aPolygon.getB2DPoint(0));
+    // last point comes from line to center, therefore -2 instead of -1
+    const basegfx::B2DPoint aEnd(aPolygon.getB2DPoint(aPolygon.count() - 2));
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Start <> End", aStart, aEnd);
+}
 }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/qa/unit/data/tdf103474_commandG_CaseZeroHeight.odp b/svx/qa/unit/data/tdf103474_commandG_CaseZeroHeight.odp
new file mode 100644
index 000000000000..9b36d45eed6a
Binary files /dev/null and b/svx/qa/unit/data/tdf103474_commandG_CaseZeroHeight.odp differ
diff --git a/svx/qa/unit/data/tdf122323_swingAngle_larger360deg.pptx b/svx/qa/unit/data/tdf122323_swingAngle_larger360deg.pptx
new file mode 100644
index 000000000000..919675ef9d27
Binary files /dev/null and b/svx/qa/unit/data/tdf122323_swingAngle_larger360deg.pptx differ
diff --git a/svx/source/customshapes/EnhancedCustomShape2d.cxx b/svx/source/customshapes/EnhancedCustomShape2d.cxx
index 8b570edd809e..89360b62fc70 100644
--- a/svx/source/customshapes/EnhancedCustomShape2d.cxx
+++ b/svx/source/customshapes/EnhancedCustomShape2d.cxx
@@ -2356,58 +2356,80 @@ void EnhancedCustomShape2d::CreateSubPath(
 
                 case ARCANGLETO :
                 {
-                    double fWR, fHR, fStartAngle, fSwingAngle;
+                    double fWR, fHR; // in Shape coordinate system
+                    double fStartAngle, fSwingAngle; // in deg
 
                     for ( sal_uInt16 i = 0; ( i < nPntCount ) && ( rSrcPt + 1 < nCoordSize ); i++ )
                     {
-                        GetParameter ( fWR, seqCoordinates[ static_cast<sal_uInt16>(rSrcPt) ].First, true, false );
-                        GetParameter ( fHR, seqCoordinates[ static_cast<sal_uInt16>(rSrcPt) ].Second, false, true );
-
-                        GetParameter ( fStartAngle, seqCoordinates[ static_cast<sal_uInt16>( rSrcPt + 1) ].First, false, false );
-                        GetParameter ( fSwingAngle, seqCoordinates[ static_cast<sal_uInt16>( rSrcPt + 1 ) ].Second, false, false );
-
-                        // Convert angles to radians, but don't do any scaling / translation yet.
-
-                        fStartAngle = basegfx::deg2rad(fStartAngle);
-                        fSwingAngle = basegfx::deg2rad(fSwingAngle);
+                        basegfx::B2DPoint aTempPair;
+                        aTempPair = GetPointAsB2DPoint(seqCoordinates[static_cast<sal_uInt16>(rSrcPt)], false /*bScale*/, false /*bReplaceGeoSize*/);
+                        fWR = aTempPair.getX();
+                        fHR = aTempPair.getY();
+                        aTempPair = GetPointAsB2DPoint(seqCoordinates[static_cast<sal_uInt16>(rSrcPt + 1)], false /*bScale*/, false /*bReplaceGeoSize*/);
+                        fStartAngle = aTempPair.getX();
+                        fSwingAngle = aTempPair.getY();
+
+                        // tdf#122323 MS Office clamps the swing angle to [-360,360]. Such restriction
+                        // is neither in OOXML nor in ODF. Nevertheless, to be compatible we do it for
+                        // "ooxml-foo" shapes. Those shapes have their origin in MS Office.
+                        if (bOOXMLShape)
+                        {
+                            fSwingAngle = std::clamp(fSwingAngle, -360.0, 360.0);
+                        }
 
                         SAL_INFO("svx", "ARCANGLETO scale: " << fWR << "x" << fHR << " angles: " << fStartAngle << "," << fSwingAngle);
 
-                        bool bClockwise = fSwingAngle >= 0.0;
-
-                        if (aNewB2DPolygon.count() > 0)
+                        if (aNewB2DPolygon.count() > 0) // otherwise no "current point"
                         {
-                            basegfx::B2DPoint aStartPointB2D( aNewB2DPolygon.getB2DPoint(aNewB2DPolygon.count() - 1 ) );
-                            Point aStartPoint( 0, 0 );
-
-                            double fT = atan2((fWR*sin(fStartAngle)), (fHR*cos(fStartAngle)));
-                            double fTE = atan2((fWR*sin(fStartAngle + fSwingAngle)), fHR*cos(fStartAngle + fSwingAngle));
-
-                            SAL_INFO("svx", "ARCANGLETO angles: " << fStartAngle << ", " << fSwingAngle
-                                             << " --> parameters: " << fT <<", " << fTE );
-
-                            fWR *= fXScale;
-                            fHR *= fYScale;
-
-                            tools::Rectangle aRect ( Point ( aStartPoint.getX() - fWR*cos(fT) - fWR, aStartPoint.getY() - fHR*sin(fT) - fHR ),
-                                              Point ( aStartPoint.getX() - fWR*cos(fT) + fWR, aStartPoint.getY() - fHR*sin(fT) + fHR) );
-
-                            Point aEndPoint ( aStartPoint.getX() - fWR*(cos(fT) - cos(fTE)), aStartPoint.getY() - fHR*(sin(fT) - sin(fTE)) );
-
-                            SAL_INFO(
-                                "svx",
-                                "ARCANGLETO rect: " << aRect.Left() << ", "
-                                    << aRect.Top() << "   x   " << aRect.Right()
-                                    << ", " << aRect.Bottom() << "   start: "
-                                    << aStartPoint.X() << ", "
-                                    << aStartPoint.Y() << " end: "
-                                    << aEndPoint.X() << ", " << aEndPoint.Y()
-                                    << " clockwise: " << int(bClockwise));
-                            basegfx::B2DPolygon aArc = CreateArc( aRect, bClockwise ? aEndPoint : aStartPoint, bClockwise ? aStartPoint : aEndPoint, bClockwise, aStartPoint == aEndPoint && ((bClockwise && fSwingAngle > F_PI) || (!bClockwise && fSwingAngle < -F_PI)));
-                            // Now that we have the arc, move it to aStartPointB2D.
-                            basegfx::B2DHomMatrix aMatrix = basegfx::utils::createTranslateB2DHomMatrix(aStartPointB2D.getX(), aStartPointB2D.getY());
-                            aArc.transform(aMatrix);
-                            aNewB2DPolygon.append(aArc);
+                            // use similar methods as in command U
+                            basegfx::B2DPolygon aTempB2DPolygon;
+
+                            if (fWR == 0.0 && fHR == 0.0)
+                            {
+                                // degenerated ellipse, add this one point
+                                aTempB2DPolygon.append(basegfx::B2DPoint(0.0, 0.0));
+                            }
+                            else
+                            {
+                                double fEndAngle = fStartAngle + fSwingAngle;
+                                // Generate arc with ellipse left|top = 0|0.
+                                basegfx::B2DPoint aCenter(fWR, fHR);
+                                if (fSwingAngle < 0.0)
+                                    std::swap(fStartAngle, fEndAngle);
+                                double fS; // fFrom in radians in [0..2Pi[
+                                double fE; // fTo or fEndAngle in radians in [0..2PI[
+                                double fFrom(fStartAngle);
+                                // createPolygonFromEllipseSegment expects angles in [0..2PI[.
+                                if (fSwingAngle >= 360.0 || fSwingAngle <= -360.0)
+                                {
+                                    double fTo(fFrom + 180.0);
+                                    while (fTo < fEndAngle)
+                                    {
+                                        fS = lcl_getNormalizedCircleAngleRad(fWR, fHR, fFrom);
+                                        fE = lcl_getNormalizedCircleAngleRad(fWR, fHR, fTo);
+                                        aTempB2DPolygon.append(basegfx::utils::createPolygonFromEllipseSegment(aCenter, fWR, fHR, fS,fE));
+                                        fFrom = fTo;
+                                        fTo += 180.0;
+                                    }
+                                }
+                                fS = lcl_getNormalizedCircleAngleRad(fWR, fHR, fFrom);
+                                fE = lcl_getNormalizedCircleAngleRad(fWR, fHR, fEndAngle);
+                                aTempB2DPolygon.append(basegfx::utils::createPolygonFromEllipseSegment(aCenter, fWR, fHR,fS, fE));
+                                if (fSwingAngle < 0)
+                                    aTempB2DPolygon.flip();
+                                aTempB2DPolygon.removeDoublePoints();
+                            }
+                            // Scale arc to 1/100mm
+                            basegfx::B2DHomMatrix aMatrix = basegfx::utils::createScaleB2DHomMatrix(fXScale, fYScale);
+                            aTempB2DPolygon.transform(aMatrix);
+
+                            // Now that we have the arc, move it to the "current point".
+                            basegfx::B2DPoint aCurrentPointB2D( aNewB2DPolygon.getB2DPoint(aNewB2DPolygon.count() - 1 ) );
+                            const double fDx(aCurrentPointB2D.getX() - aTempB2DPolygon.getB2DPoint(0).getX());
+                            const double fDy(aCurrentPointB2D.getY() - aTempB2DPolygon.getB2DPoint(0).getY());
+                            aMatrix = basegfx::utils::createTranslateB2DHomMatrix(fDx, fDy);
+                            aTempB2DPolygon.transform(aMatrix);
+                            aNewB2DPolygon.append(aTempB2DPolygon);
                         }
 
                         rSrcPt += 2;


More information about the Libreoffice-commits mailing list