oox/source/drawingml/transform2dcontext.cxx | 88 +++++++++++--- sd/qa/unit/data/pptx/tdf132302_SmartArt_rightArrow.pptx |binary sd/qa/unit/data/pptx/tdf135953_SmartArt_textposition.pptx |binary sd/qa/unit/import-tests-smartart.cxx | 58 +++++++++ 4 files changed, 129 insertions(+), 17 deletions(-)
New commits: commit 5df1bb4b1b222be00d25097660c4ee33542896ea Author: Regina Henschel <rb.hensc...@t-online.de> AuthorDate: Sun Jul 24 01:23:32 2022 +0200 Commit: Regina Henschel <rb.hensc...@t-online.de> CommitDate: Mon Jul 25 23:07:28 2022 +0200 tdf#135953 move txXfrm to compensate TextRotateAngle The text rectangle specified by the txXfrm element rotates around its center. We not directly use this rectangle but we use the text area rectangle of the preset geometry and crop it by setting distances so, that the result equals the txXfrm rectangle. But the text area rectangle still rotates at its original center. So we shift the txXfrm rectangle so, that it would be at it original place, when rotated with TextRotateAngle. We then can use the shifted one to calculate the needed distance values. This patch contains the calculation of the text area rectangle for type 'rightArrow' in addition. That fixes tdf#132302. Change-Id: I59d9aecaaa7ce2ef4502651088ff9576b90873f7 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/137396 Tested-by: Jenkins Reviewed-by: Regina Henschel <rb.hensc...@t-online.de> diff --git a/oox/source/drawingml/transform2dcontext.cxx b/oox/source/drawingml/transform2dcontext.cxx index 4c98db7592b5..da4ad10a168b 100644 --- a/oox/source/drawingml/transform2dcontext.cxx +++ b/oox/source/drawingml/transform2dcontext.cxx @@ -20,10 +20,14 @@ #include <cmath> #include <drawingml/transform2dcontext.hxx> -#include <oox/helper/attributelist.hxx> -#include <oox/drawingml/shape.hxx> + +#include <basegfx/matrix/b2dhommatrixtools.hxx> +#include <basegfx/numeric/ftools.hxx> +#include <basegfx/point/b2dpoint.hxx> #include <drawingml/customshapeproperties.hxx> #include <drawingml/textbody.hxx> +#include <oox/drawingml/shape.hxx> +#include <oox/helper/attributelist.hxx> #include <oox/token/namespaces.hxx> #include <com/sun/star/awt/Rectangle.hpp> @@ -190,7 +194,7 @@ bool ConstructPresetTextRectangle(Shape& rShape, awt::Rectangle& rRect) return false; double fMaxAdj = 50000.0 * nWidth / std::min(nWidth, nHeight); fAdj = std::clamp<double>(fAdj, 0, fMaxAdj); - double fFactor = fAdj/fMaxAdj/6.0 + 1.0/12.0; + double fFactor = fAdj / fMaxAdj / 6.0 + 1.0 / 12.0; sal_Int32 nTextLeft = nWidth * fFactor; sal_Int32 nTextTop = nHeight * fFactor; rRect.X = rShape.getPosition().X + nTextLeft; @@ -215,15 +219,51 @@ bool ConstructPresetTextRectangle(Shape& rShape, awt::Rectangle& rRect) rRect.Height = nHeight; return true; } + case XML_rightArrow: + { + // The identifiers here reflect the guides name value in presetShapeDefinitions.xml + sal_Int32 nWidth = rShape.getSize().Width; + sal_Int32 nHeight = rShape.getSize().Height; + if (nWidth == 0 || nHeight == 0) + return false; + double a1(50000.0); + double a2(50000.0); + auto aAdjGdList = rShape.getCustomShapeProperties()->getAdjustmentGuideList(); + if (aAdjGdList.size() == 2) + { + a1 = aAdjGdList[0].maFormula.toDouble(); + a2 = aAdjGdList[1].maFormula.toDouble(); + a1 = std::clamp<double>(a1, 0, 100000); + } + double maxAdj2 = 100000.0 * nWidth / std::min(nWidth, nHeight); + a2 = std::clamp<double>(a2, 0, maxAdj2); + double dx1 = std::min(nWidth, nHeight) * a2 / 100000.0; + double x1 = nWidth - dx1; + double dy1 = nHeight * a1 / 200000.0; + double y1 = nHeight / 2.0 - dy1; // top + double y2 = nHeight / 2.0 + dy1; // bottom + double dx2 = y1 * dx1 / (nHeight / 2.0); + double x2 = x1 + dx2; // right + rRect.X = rShape.getPosition().X; // left = 0 + rRect.Y = rShape.getPosition().Y + y1; + rRect.Width = x2; + rRect.Height = y2 - y1; + return true; + } default: return false; } } + +basegfx::B2DPoint getCenter(const awt::Rectangle& rRect) +{ + return basegfx::B2DPoint(rRect.X + rRect.Width / 2.0, rRect.Y + rRect.Height / 2.0); } +} // end namespace ContextHandlerRef Transform2DContext::onCreateContext( sal_Int32 aElementToken, const AttributeList& rAttribs ) { - if( mbtxXfrm ) + if (mbtxXfrm) { // The child elements <a:off> and <a:ext> of a <dsp:txXfrm> element describe the position and // size of the text area rectangle. We cannot change the text area rectangle directly, because @@ -232,7 +272,7 @@ ContextHandlerRef Transform2DContext::onCreateContext( sal_Int32 aElementToken, // and used in TextBodyProperties::pushTextDistances(). awt::Rectangle aPresetTextRectangle; if (!ConstructPresetTextRectangle(mrShape, aPresetTextRectangle)) - return nullptr; // faulty shape or corrections from txXfrm not needed. + return nullptr; // faulty shape or text area calculation not implemented switch (aElementToken) { @@ -254,26 +294,40 @@ ContextHandlerRef Transform2DContext::onCreateContext( sal_Int32 aElementToken, awt::Rectangle aUnrotatedTxXfrm = aPresetTextRectangle; // dummy initialize const OUString sCXValue = rAttribs.getStringDefaulted(XML_cx); const OUString sCYValue = rAttribs.getStringDefaulted(XML_cy); - if (!sCXValue.isEmpty() && !sCYValue.isEmpty() && mno_txXfrmOffX.has_value() - && mno_txXfrmOffY.has_value()) + if (!sCXValue.isEmpty() && !sCYValue.isEmpty()) { - sal_Int32 ntxXfrmWidth = sCXValue.toInt32(); - sal_Int32 ntxXfrmHeight = sCYValue.toInt32(); - aUnrotatedTxXfrm.X = mno_txXfrmOffX.value(); - aUnrotatedTxXfrm.Y = mno_txXfrmOffY.value(); - aUnrotatedTxXfrm.Width = ntxXfrmWidth; - aUnrotatedTxXfrm.Height = ntxXfrmHeight; + aUnrotatedTxXfrm.Width = sCXValue.toInt32(); + aUnrotatedTxXfrm.Height = sCYValue.toInt32(); } - else if (mno_txXfrmOffX.has_value() && mno_txXfrmOffY.has_value()) // can it happen? + if (mno_txXfrmOffX.has_value() && mno_txXfrmOffY.has_value()) { aUnrotatedTxXfrm.X = mno_txXfrmOffX.value(); aUnrotatedTxXfrm.Y = mno_txXfrmOffY.value(); } - else if (!sCXValue.isEmpty() && !sCYValue.isEmpty()) // can it happen? + + // Has the txXfrm an own rotation beyond compensation of the shape rotation? + // Happens e.g. in diagram type 'Detailed Process'. + sal_Int32 nAngleDiff + = (mrShape.getRotation() + mno_txXfrmRot.value_or(0)) % 21600000; + if (nAngleDiff != 0) { - aUnrotatedTxXfrm.Width = sCXValue.toInt32(); - aUnrotatedTxXfrm.Height = sCYValue.toInt32(); + // Rectangle aUnrotatedTxXfrm rotates around its center not around text area + // center from preset. We shift aUnrotatedTxXfrm so that it is at the original + // position after rotation of text area rectangle from preset. + basegfx::B2DPoint aXfrmCenter(getCenter(aUnrotatedTxXfrm)); + basegfx::B2DPoint aPresetCenter(getCenter(aPresetTextRectangle)); + + if (!aXfrmCenter.equal(aPresetCenter)) + { + double fAngleRad = basegfx::deg2rad(nAngleDiff / 60000.0); + basegfx::B2DHomMatrix aRotMatrix( + basegfx::utils::createRotateAroundPoint(aPresetCenter, -fAngleRad)); + basegfx::B2DPoint aNewCenter(aRotMatrix * aXfrmCenter); + aUnrotatedTxXfrm.X += aNewCenter.getX() - aXfrmCenter.getX(); + aUnrotatedTxXfrm.Y += aNewCenter.getY() - aXfrmCenter.getY(); + } } + // Calculate indent offsets sal_Int32 nOffsetLeft = aUnrotatedTxXfrm.X - aPresetTextRectangle.X; sal_Int32 nOffsetTop = aUnrotatedTxXfrm.Y - aPresetTextRectangle.Y; diff --git a/sd/qa/unit/data/pptx/tdf132302_SmartArt_rightArrow.pptx b/sd/qa/unit/data/pptx/tdf132302_SmartArt_rightArrow.pptx new file mode 100644 index 000000000000..7e50ff4005ba Binary files /dev/null and b/sd/qa/unit/data/pptx/tdf132302_SmartArt_rightArrow.pptx differ diff --git a/sd/qa/unit/data/pptx/tdf135953_SmartArt_textposition.pptx b/sd/qa/unit/data/pptx/tdf135953_SmartArt_textposition.pptx new file mode 100644 index 000000000000..c76e4c7db0cf Binary files /dev/null and b/sd/qa/unit/data/pptx/tdf135953_SmartArt_textposition.pptx differ diff --git a/sd/qa/unit/import-tests-smartart.cxx b/sd/qa/unit/import-tests-smartart.cxx index 1d2728b5bee4..9dda19f31a65 100644 --- a/sd/qa/unit/import-tests-smartart.cxx +++ b/sd/qa/unit/import-tests-smartart.cxx @@ -125,6 +125,8 @@ public: void testTdf149551Venn(); void testTdf149551Gear(); void testTdf145528Matrix(); + void testTdf135953TextPosition(); + void testTdf132302RightArrow(); CPPUNIT_TEST_SUITE(SdImportTestSmartArt); @@ -185,6 +187,8 @@ public: CPPUNIT_TEST(testTdf149551Venn); CPPUNIT_TEST(testTdf149551Gear); CPPUNIT_TEST(testTdf145528Matrix); + CPPUNIT_TEST(testTdf135953TextPosition); + CPPUNIT_TEST(testTdf132302RightArrow); CPPUNIT_TEST_SUITE_END(); }; @@ -1986,6 +1990,60 @@ void SdImportTestSmartArt::testTdf145528Matrix() xDocShRef->DoClose(); } +void SdImportTestSmartArt::testTdf135953TextPosition() +{ + // The file contains a diagram of type "Detailed Process". There the text area rectangle + // is at the left edge of the shape and rotated there. + sd::DrawDocShellRef xDocShRef = loadURL( + m_directories.getURLFromSrc(u"/sd/qa/unit/data/pptx/tdf135953_SmartArt_textposition.pptx"), + PPTX); + uno::Reference<drawing::XShape> xGroup(getShapeFromPage(0, 0, xDocShRef), uno::UNO_QUERY); + // shape at index 0 is the background shape + uno::Reference<drawing::XShape> xShape = getChildShape(xGroup, 1); + CPPUNIT_ASSERT(xShape.is()); + auto pCustomShape = dynamic_cast<SdrObjCustomShape*>(SdrObject::getSdrObjectFromXShape(xShape)); + CPPUNIT_ASSERT(pCustomShape); + tools::Rectangle aTextRect; + pCustomShape->TakeTextAnchorRect(aTextRect); + // without fix the text area rectangle had LT[-2213,2336] and RB[4123,4575]. It was not + // considered that the txXfrm and the preset text area rectangle have different centers in this + // case and thus the text was far off. + // The used tolerance is estimated. + CPPUNIT_ASSERT_DOUBLES_EQUAL(sal_Int32(3223), sal_Int32(aTextRect.Left()), 4); + CPPUNIT_ASSERT_DOUBLES_EQUAL(sal_Int32(-1661), sal_Int32(aTextRect.Top()), 4); + CPPUNIT_ASSERT_DOUBLES_EQUAL(sal_Int32(9559), sal_Int32(aTextRect.Right()), 4); + CPPUNIT_ASSERT_DOUBLES_EQUAL(sal_Int32(578), sal_Int32(aTextRect.Bottom()), 4); + + xDocShRef->DoClose(); +} + +void SdImportTestSmartArt::testTdf132302RightArrow() +{ + // The file contains a diagram of type "Process Arrows". It uses shapes of type "rightArrow". + // The text starts not at the left edge but in the middle to have space for a circle. Error was + // that the text starts left and so was hidden by the circle. + sd::DrawDocShellRef xDocShRef = loadURL( + m_directories.getURLFromSrc(u"/sd/qa/unit/data/pptx/tdf132302_SmartArt_rightArrow.pptx"), + PPTX); + uno::Reference<drawing::XShape> xGroup(getShapeFromPage(0, 0, xDocShRef), uno::UNO_QUERY); + // shape at index 0 is the background shape + uno::Reference<drawing::XShape> xShape = getChildShape(xGroup, 1); + CPPUNIT_ASSERT(xShape.is()); + auto pCustomShape = dynamic_cast<SdrObjCustomShape*>(SdrObject::getSdrObjectFromXShape(xShape)); + CPPUNIT_ASSERT(pCustomShape); + tools::Rectangle aTextRect; + pCustomShape->TakeTextAnchorRect(aTextRect); + // without fix the text area rectangle had LT[2790,59] and RB[11591,7940]. Position and size + // given in txXfrm in drawing.xml were not considered. + // The used tolerance is estimated. + CPPUNIT_ASSERT_DOUBLES_EQUAL(sal_Int32(5078), sal_Int32(aTextRect.Left()), 4); + CPPUNIT_ASSERT_DOUBLES_EQUAL(sal_Int32(58), sal_Int32(aTextRect.Top()), 4); + CPPUNIT_ASSERT_DOUBLES_EQUAL(sal_Int32(11989), sal_Int32(aTextRect.Right()), 4); + CPPUNIT_ASSERT_DOUBLES_EQUAL(sal_Int32(7940), sal_Int32(aTextRect.Bottom()), 4); + + xDocShRef->DoClose(); +} + CPPUNIT_TEST_SUITE_REGISTRATION(SdImportTestSmartArt); CPPUNIT_PLUGIN_IMPLEMENT();