Add escape direction support for glue points in the preset shapes

Change-Id: I6727def5dd42ecd5dae3ddd27d2af733b5883e09
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/170006
Tested-by: Jenkins
Reviewed-by: Nagy Tibor <tibor.nagy.extern@allotropia.de>
This commit is contained in:
Tibor Nagy 2024-07-05 07:19:15 +02:00 committed by Nagy Tibor
parent a14531e413
commit 74b312d6f2
7 changed files with 152 additions and 16 deletions

View file

@ -118,6 +118,7 @@ class SVXCORE_DLLPUBLIC EnhancedCustomShape2d final : public SfxItemSet
css::uno::Sequence< css::drawing::EnhancedCustomShapeAdjustmentValue > m_seqAdjustmentValues;
css::uno::Sequence< css::beans::PropertyValues > m_seqHandles;
css::uno::Sequence< css::awt::Size > m_seqSubViewSize;
css::uno::Sequence< double > m_seqGluePointLeavingDirections;
bool m_bFilled : 1;
bool m_bStroked : 1;

View file

@ -162,6 +162,18 @@ awt::Rectangle lcl_parseRectangle(std::string_view rValue)
return aRectangle;
}
sal_Int32 lcl_parseDirection(std::string_view rValue)
{
sal_Int32 aDirection;
// We expect the following here: Direction
static const char aExpectedWidthPrefix[] = "Dir = (long) ";
assert(o3tl::starts_with(rValue, aExpectedWidthPrefix));
sal_Int32 nIndex = strlen(aExpectedWidthPrefix);
aDirection = o3tl::toInt32(rValue.substr(nIndex));
return aDirection;
}
awt::Size lcl_parseSize(std::string_view rValue)
{
awt::Size aSize;
@ -582,6 +594,73 @@ void lcl_parsePathGluePoints(std::vector<beans::PropertyValue>& rPath, std::stri
}
}
void lcl_parsePathGluePointLeavingDirectionsValues(std::vector<beans::PropertyValue>& rPath,
std::string_view rValue)
{
std::vector<double> aDirection;
sal_Int32 nLevel = 0;
sal_Int32 nStart = 0;
for (size_t i = 0; i < rValue.size(); ++i)
{
if (rValue[i] == '{')
{
if (!nLevel)
nStart = i;
nLevel++;
}
else if (rValue[i] == '}')
{
nLevel--;
if (!nLevel)
aDirection.push_back(lcl_parseDirection(
rValue.substr(nStart + strlen("{ "), i - nStart - strlen(" },"))));
}
}
beans::PropertyValue aPropertyValue;
aPropertyValue.Name = "GluePointLeavingDirections";
aPropertyValue.Value <<= comphelper::containerToSequence(aDirection);
rPath.push_back(aPropertyValue);
}
void lcl_parsePathGluePointLeavingDirections(std::vector<beans::PropertyValue>& rPath,
std::string_view rValue)
{
sal_Int32 nLevel = 0;
bool bIgnore = false;
sal_Int32 nStart = 0;
for (size_t i = 0; i < rValue.size(); ++i)
{
if (rValue[i] == '{')
{
if (!nLevel)
bIgnore = true;
nLevel++;
}
else if (rValue[i] == '}')
{
nLevel--;
if (!nLevel)
bIgnore = false;
}
else if (rValue[i] == ',' && !bIgnore)
{
std::string_view aToken = rValue.substr(nStart, i - nStart);
static const char aExpectedPrefix[] = "Value = (any) { ([]long) { ";
if (o3tl::starts_with(aToken, aExpectedPrefix))
{
aToken = aToken.substr(strlen(aExpectedPrefix),
aToken.size() - strlen(aExpectedPrefix) - strlen(" } }"));
lcl_parsePathGluePointLeavingDirectionsValues(rPath, aToken);
}
else if (!o3tl::starts_with(aToken, "Name =") && !o3tl::starts_with(aToken, "Handle ="))
SAL_WARN("oox",
"lcl_parsePathGluePointLeavingDirections: unexpected token: " << aToken);
nStart = i + strlen(", ");
}
}
}
void lcl_parsePathSegmentValues(std::vector<beans::PropertyValue>& rPath, std::string_view rValue)
{
std::vector<drawing::EnhancedCustomShapeSegment> aSegments;
@ -804,6 +883,8 @@ void lcl_parsePath(std::vector<beans::PropertyValue>& rPath, std::string_view rV
lcl_parsePathCoordinates(rPath, aToken);
else if (o3tl::starts_with(aToken, "Name = \"GluePoints\""))
lcl_parsePathGluePoints(rPath, aToken);
else if (o3tl::starts_with(aToken, "Name = \"GluePointLeavingDirections\""))
lcl_parsePathGluePointLeavingDirections(rPath, aToken);
else if (o3tl::starts_with(aToken, "Name = \"Segments\""))
lcl_parsePathSegments(rPath, aToken);
else if (o3tl::starts_with(aToken, "Name = \"TextFrames\""))

File diff suppressed because one or more lines are too long

View file

@ -128,6 +128,7 @@
</Coordinates>
</PropertyValue>
<PropertyValue name="GluePoints" handle="0" propertyState="DIRECT_VALUE"/>
<PropertyValue name="GluePointLeavingDirections" handle="0" propertyState="DIRECT_VALUE"/>
<PropertyValue name="Segments">
<Segments>
<EnhancedCustomShapeSegment command="1" count="1"/>
@ -211,6 +212,7 @@
</Coordinates>
</PropertyValue>
<PropertyValue name="GluePoints" handle="0" propertyState="DIRECT_VALUE"/>
<PropertyValue name="GluePointLeavingDirections" handle="0" propertyState="DIRECT_VALUE"/>
<PropertyValue name="Segments">
<Segments>
<EnhancedCustomShapeSegment command="1" count="1"/>

View file

@ -546,6 +546,34 @@ CPPUNIT_TEST_FIXTURE(SdImportTest, testTdf89449)
CPPUNIT_ASSERT_EQUAL(sal_Int32(2), nEndGlueId);
}
CPPUNIT_TEST_FIXTURE(SdImportTest, testGluePointLeavingDirections)
{
createSdImpressDoc("pptx/glue_point_leaving_directions.pptx");
uno::Reference<beans::XPropertySet> xEllipseShape(getShapeFromPage(0, 0));
uno::Sequence<beans::PropertyValue> aProps;
xEllipseShape->getPropertyValue(u"CustomShapeGeometry"_ustr) >>= aProps;
uno::Sequence<beans::PropertyValue> aPathProps;
for (beans::PropertyValue const& rProp : aProps)
{
if (rProp.Name == "Path")
aPathProps = rProp.Value.get<uno::Sequence<beans::PropertyValue>>();
}
uno::Sequence<double> seqGluePointLeavingDirections;
for (beans::PropertyValue const& rProp : aPathProps)
{
if (rProp.Name == "GluePointLeavingDirections")
{
rProp.Value >>= seqGluePointLeavingDirections;
}
}
sal_Int32 nCountGluePointLeavingDirections = seqGluePointLeavingDirections.getLength();
// The ellipse has 8 glue point leaving directions
CPPUNIT_ASSERT_EQUAL(sal_Int32(8), nCountGluePointLeavingDirections);
}
CPPUNIT_TEST_FIXTURE(SdImportTest, testTdf147459)
{
createSdImpressDoc("pptx/tdf147459.pptx");

View file

@ -565,6 +565,7 @@ void EnhancedCustomShape2d::ApplyShapeAttributes( const SdrCustomShapeGeometryIt
static constexpr OUString sPath( u"Path"_ustr );
static constexpr OUStringLiteral sCoordinates( u"Coordinates" );
static constexpr OUStringLiteral sGluePoints( u"GluePoints" );
static constexpr OUStringLiteral sGluePointLeavingDirections( u"GluePointLeavingDirections" );
static constexpr OUStringLiteral sSegments( u"Segments" );
static constexpr OUStringLiteral sSubViewSize( u"SubViewSize" );
static constexpr OUStringLiteral sStretchX( u"StretchX" );
@ -585,6 +586,10 @@ void EnhancedCustomShape2d::ApplyShapeAttributes( const SdrCustomShapeGeometryIt
if ( pAny )
*pAny >>= m_seqGluePoints;
// Path/GluePointLeavingDirections
pAny = const_cast<SdrCustomShapeGeometryItem&>(rGeometryItem).GetPropertyValueByName(sPath, sGluePointLeavingDirections);
if (pAny)
*pAny >>= m_seqGluePointLeavingDirections;
// Path/Segments
pAny = const_cast<SdrCustomShapeGeometryItem&>(rGeometryItem).GetPropertyValueByName( sPath, sSegments );
@ -3064,19 +3069,38 @@ rtl::Reference<SdrObject> EnhancedCustomShape2d::CreateObject( bool bLineGeometr
return pRet;
}
static SdrEscapeDirection lcl_GetEscapeDirection(sal_Int32 nDirection)
{
switch (nDirection)
{
case 1: return SdrEscapeDirection::LEFT;
case 2: return SdrEscapeDirection::RIGHT;
case 3: return SdrEscapeDirection::TOP;
case 4: return SdrEscapeDirection::BOTTOM;
default: return SdrEscapeDirection::SMART;
}
}
void EnhancedCustomShape2d::ApplyGluePoints(SdrObject* pObj)
{
if ( !pObj )
return;
for (const auto& rGluePoint : m_seqGluePoints)
SdrEscapeDirection aDirection = SdrEscapeDirection::SMART;
for (size_t i = 0; i < m_seqGluePoints.size(); i++)
{
SdrGluePoint aGluePoint;
EnhancedCustomShapeParameterPair aGluePointPair = m_seqGluePoints[i];
if (m_seqGluePointLeavingDirections.hasElements())
{
sal_Int32 aGluePointLeavingDirection = m_seqGluePointLeavingDirections[i];
aDirection = lcl_GetEscapeDirection(aGluePointLeavingDirection);
}
aGluePoint.SetPos( GetPoint( rGluePoint, !m_bOOXMLShape, true ) );
SdrGluePoint aGluePoint;
aGluePoint.SetPos( GetPoint( aGluePointPair, !m_bOOXMLShape, true ) );
aGluePoint.SetPercent( false );
aGluePoint.SetAlign( SdrAlign::VERT_TOP | SdrAlign::HORZ_LEFT );
aGluePoint.SetEscDir( SdrEscapeDirection::SMART );
aGluePoint.SetEscDir( aDirection );
SdrGluePointList* pList = pObj->ForceGluePointList();
if( pList )
/* sal_uInt16 nId = */ pList->Insert( aGluePoint );