/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include <vcl/bitmap/BitmapShadowFilter.hxx>
#include <svx/svdoashp.hxx>
#include <svx/unoapi.hxx>
#include <com/sun/star/loader/CannotActivateFactoryException.hpp>
#include <com/sun/star/drawing/XShape.hpp>
#include <com/sun/star/drawing/XCustomShapeEngine.hpp>
#include <com/sun/star/drawing/PolyPolygonBezierCoords.hpp>
#include <com/sun/star/beans/PropertyValue.hpp>
#include <com/sun/star/awt/Rectangle.hpp>
#include <com/sun/star/uno/XComponentContext.hpp>
#include <comphelper/processfactory.hxx>
#include <comphelper/propertyvalue.hxx>
#include <comphelper/sequenceashashmap.hxx>
#include <com/sun/star/uno/Sequence.h>
#include <tools/helpers.hxx>
#include <svx/svddrag.hxx>
#include <svx/svddrgmt.hxx>
#include <svx/svdmodel.hxx>
#include <svx/svdpage.hxx>
#include <svx/svditer.hxx>
#include <svx/svdobj.hxx>
#include <svx/svdtrans.hxx>
#include <svx/dialmgr.hxx>
#include <svx/strings.hrc>
#include <editeng/eeitem.hxx>
#include <editeng/editstat.hxx>
#include <editeng/adjustitem.hxx>
#include <svx/svdoutl.hxx>
#include <editeng/outlobj.hxx>
#include <svx/sdtfchim.hxx>
#include <svx/EnhancedCustomShapeGeometry.hxx>
#include <svx/EnhancedCustomShapeTypeNames.hxx>
#include <svx/EnhancedCustomShape2d.hxx>
#include <com/sun/star/beans/PropertyValues.hpp>
#include <com/sun/star/drawing/EnhancedCustomShapeAdjustmentValue.hpp>
#include <com/sun/star/drawing/EnhancedCustomShapeParameterPair.hpp>
#include <com/sun/star/drawing/EnhancedCustomShapeTextFrame.hpp>
#include <com/sun/star/drawing/EnhancedCustomShapeSegment.hpp>
#include <com/sun/star/drawing/EnhancedCustomShapeSegmentCommand.hpp>
#include <editeng/writingmodeitem.hxx>
#include <svx/xlineit0.hxx>
#include <svx/xlnclit.hxx>
#include <sdr/properties/customshapeproperties.hxx>
#include <sdr/contact/viewcontactofsdrobjcustomshape.hxx>
#include <svx/xlntrit.hxx>
#include <svx/xfillit0.hxx>
#include <svx/xfltrit.hxx>
#include <svx/xflclit.hxx>
#include <svx/xflgrit.hxx>
#include <svx/xflhtit.hxx>
#include <svx/xbtmpit.hxx>
#include <vcl/virdev.hxx>
#include <svx/svdview.hxx>
#include <svx/sdmetitm.hxx>
#include <svx/sdprcitm.hxx>
#include <svx/sdshitm.hxx>
#include <svx/sdsxyitm.hxx>
#include <svx/sdtmfitm.hxx>
#include <svx/sdasitm.hxx>
#include <basegfx/polygon/b2dpolypolygontools.hxx>
#include <basegfx/matrix/b2dhommatrix.hxx>
#include <basegfx/matrix/b2dhommatrixtools.hxx>
#include <basegfx/polygon/b2dpolygon.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
#include <basegfx/range/b2drange.hxx>
#include <svdobjplusdata.hxx>
#include <sal/log.hxx>
#include <o3tl/string_view.hxx>
#include "presetooxhandleadjustmentrelations.hxx"
#include <editeng/frmdiritem.hxx>
using namespace ::com::sun::star;
static void lcl_ShapeSegmentFromBinary( drawing::EnhancedCustomShapeSegment& rSe
gInfo, sal_uInt16 nSDat )
{
switch ( nSDat >> 8 )
{
case 0x00 :
rSegInfo.Command = drawing::EnhancedCustomShapeSegmentCommand::LINETO;
rSegInfo.Count = nSDat & 0xff;
if ( !rSegInfo.Count )
rSegInfo.Count = 1;
break ;
case 0x20 :
rSegInfo.Command = drawing::EnhancedCustomShapeSegmentCommand::CURVETO;
rSegInfo.Count = nSDat & 0xff;
if ( !rSegInfo.Count )
rSegInfo.Count = 1;
break ;
case 0x40 :
rSegInfo.Command = drawing::EnhancedCustomShapeSegmentCommand::MOVETO;
rSegInfo.Count = nSDat & 0xff;
if ( !rSegInfo.Count )
rSegInfo.Count = 1;
break ;
case 0x60 :
rSegInfo.Command = drawing::EnhancedCustomShapeSegmentCommand::CLOSESUBPATH;
rSegInfo.Count = 0;
break ;
case 0x80 :
rSegInfo.Command = drawing::EnhancedCustomShapeSegmentCommand::ENDSUBPATH;
rSegInfo.Count = 0;
break ;
case 0xa1 :
rSegInfo.Command = drawing::EnhancedCustomShapeSegmentCommand::ANGLEELLIPSETO;
rSegInfo.Count = ( nSDat & 0xff ) / 3;
break ;
case 0xa2 :
rSegInfo.Command = drawing::EnhancedCustomShapeSegmentCommand::ANGLEELLIPSE;
rSegInfo.Count = ( nSDat & 0xff ) / 3;
break ;
case 0xa3 :
rSegInfo.Command = drawing::EnhancedCustomShapeSegmentCommand::ARCTO;
rSegInfo.Count = ( nSDat & 0xff ) >> 2;
break ;
case 0xa4 :
rSegInfo.Command = drawing::EnhancedCustomShapeSegmentCommand::ARC;
rSegInfo.Count = ( nSDat & 0xff ) >> 2;
break ;
case 0xa5 :
rSegInfo.Command = drawing::EnhancedCustomShapeSegmentCommand::CLOCKWISEARCTO;
rSegInfo.Count = ( nSDat & 0xff ) >> 2;
break ;
case 0xa6 :
rSegInfo.Command = drawing::EnhancedCustomShapeSegmentCommand::CLOCKWISEARC;
rSegInfo.Count = ( nSDat & 0xff ) >> 2;
break ;
case 0xa7 :
rSegInfo.Command = drawing::EnhancedCustomShapeSegmentCommand::ELLIPTICALQUADRANTX;
rSegInfo.Count = nSDat & 0xff;
break ;
case 0xa8 :
rSegInfo.Command = drawing::EnhancedCustomShapeSegmentCommand::ELLIPTICALQUADRANTY;
rSegInfo.Count = nSDat & 0xff;
break ;
case 0xaa :
rSegInfo.Command = drawing::EnhancedCustomShapeSegmentCommand::NOFILL;
rSegInfo.Count = 0;
break ;
case 0xab :
rSegInfo.Command = drawing::EnhancedCustomShapeSegmentCommand::NOSTROKE;
rSegInfo.Count = 0;
break ;
default :
case 0xf8 :
rSegInfo.Command = drawing::EnhancedCustomShapeSegmentCommand::UNKNOWN;
rSegInfo.Count = nSDat;
break ;
}
}
static MSO_SPT ImpGetCustomShapeType( const SdrObjCustomShape& rCustoShape )
{
MSO_SPT eRetValue = mso_sptNil;
OUString aEngine( rCustoShape.GetMergedItem( SDRATTR_CUSTOMSHAPE_ENGINE ).GetValue() );
if ( aEngine.isEmpty() || aEngine == "com.sun.star.drawing.EnhancedCustomShapeEngine" )
{
OUString sShapeType;
const SdrCustomShapeGeometryItem& rGeometryItem( rCustoShape.GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
const uno::Any* pAny = rGeometryItem.GetPropertyValueByName( u"Type" _ustr );
if ( pAny && ( *pAny >>= sShapeType ) )
eRetValue = EnhancedCustomShapeTypeNames::Get( sShapeType );
}
return eRetValue;
};
static bool ImpVerticalSwitch( const SdrObjCustomShape& rCustoShape )
{
bool bRet = false ;
MSO_SPT eShapeType( ImpGetCustomShapeType( rCustoShape ) );
switch ( eShapeType )
{
case mso_sptAccentBorderCallout90 : // 2 ortho
case mso_sptBorderCallout1 : // 2 diag
case mso_sptBorderCallout2 : // 3
{
bRet = true ;
}
break ;
default : break ;
}
return bRet;
}
// #i37011# create a clone with all attributes changed to shadow attributes
// and translation executed, too.
static rtl::Reference<SdrObject> ImpCreateShadowObjectClone(const SdrObject& rOriginal, const SfxItemSet& rOriginalSet)
{
rtl::Reference<SdrObject> pRetval;
const bool bShadow(rOriginalSet.Get(SDRATTR_SHADOW).GetValue());
if (bShadow)
{
// create a shadow representing object
const sal_Int32 nXDist(rOriginalSet.Get(SDRATTR_SHADOWXDIST).GetValue());
const sal_Int32 nYDist(rOriginalSet.Get(SDRATTR_SHADOWYDIST).GetValue());
const ::Color aShadowColor(rOriginalSet.Get(SDRATTR_SHADOWCOLOR).GetColorValue());
const sal_uInt16 nShadowTransparence(rOriginalSet.Get(SDRATTR_SHADOWTRANSPARENCE).GetValue());
pRetval = rOriginal.CloneSdrObject(rOriginal.getSdrModelFromSdrObject());
DBG_ASSERT(pRetval, "ImpCreateShadowObjectClone: Could not clone object (!)" );
// look for used stuff
SdrObjListIter aIterator(rOriginal);
bool bLineUsed(false );
bool bAllFillUsed(false );
bool bSolidFillUsed(false );
bool bGradientFillUsed(false );
bool bHatchFillUsed(false );
bool bBitmapFillUsed(false );
while (aIterator.IsMore())
{
SdrObject* pObj = aIterator.Next();
drawing::FillStyle eFillStyle = pObj->GetMergedItem(XATTR_FILLSTYLE).GetValue();
if (!bLineUsed)
{
drawing::LineStyle eLineStyle = pObj->GetMergedItem(XATTR_LINESTYLE).GetValue();
if (drawing::LineStyle_NONE != eLineStyle)
{
bLineUsed = true ;
}
}
if (!bAllFillUsed)
{
if (!bSolidFillUsed && drawing::FillStyle_SOLID == eFillStyle)
{
bSolidFillUsed = true ;
bAllFillUsed = (bSolidFillUsed && bGradientFillUsed && bHatchFillUsed && bBitmapFillUsed);
}
if (!bGradientFillUsed && drawing::FillStyle_GRADIENT == eFillStyle)
{
bGradientFillUsed = true ;
bAllFillUsed = (bSolidFillUsed && bGradientFillUsed && bHatchFillUsed && bBitmapFillUsed);
}
if (!bHatchFillUsed && drawing::FillStyle_HATCH == eFillStyle)
{
bHatchFillUsed = true ;
bAllFillUsed = (bSolidFillUsed && bGradientFillUsed && bHatchFillUsed && bBitmapFillUsed);
}
if (!bBitmapFillUsed && drawing::FillStyle_BITMAP == eFillStyle)
{
bBitmapFillUsed = true ;
bAllFillUsed = (bSolidFillUsed && bGradientFillUsed && bHatchFillUsed && bBitmapFillUsed);
}
}
}
// translate to shadow coordinates
pRetval->NbcMove(Size(nXDist, nYDist));
// set items as needed
SfxItemSet aTempSet(rOriginalSet);
// if a SvxWritingModeItem (Top->Bottom) is set the text object
// is creating a paraobject, but paraobjects can not be created without model. So
// we are preventing the crash by setting the writing mode always left to right,
// this is not bad since our shadow geometry does not contain text.
aTempSet.Put(SvxWritingModeItem(text::WritingMode_LR_TB, SDRATTR_TEXTDIRECTION));
// no shadow
aTempSet.Put(makeSdrShadowItem(false ));
aTempSet.Put(makeSdrShadowXDistItem(0));
aTempSet.Put(makeSdrShadowYDistItem(0));
// line color and transparency like shadow
if (bLineUsed)
{
aTempSet.Put(XLineColorItem(OUString(), aShadowColor));
aTempSet.Put(XLineTransparenceItem(nShadowTransparence));
}
// fill color and transparency like shadow
if (bSolidFillUsed)
{
aTempSet.Put(XFillColorItem(OUString(), aShadowColor));
aTempSet.Put(XFillTransparenceItem(nShadowTransparence));
}
// gradient and transparency like shadow
if (bGradientFillUsed)
{
basegfx::BGradient aGradient(rOriginalSet.Get(XATTR_FILLGRADIENT).GetGradientValue());
sal_uInt8 nStartLuminance(Color(aGradient.GetColorStops().front().getStopColor()).GetLuminance());
sal_uInt8 nEndLuminance(Color(aGradient.GetColorStops().back().getStopColor()).GetLuminance());
if (aGradient.GetStartIntens() != 100)
{
nStartLuminance = static_cast <sal_uInt8>(nStartLuminance * (static_cast <double >(aGradient.GetStartIntens()) / 100.0));
}
if (aGradient.GetEndIntens() != 100)
{
nEndLuminance = static_cast <sal_uInt8>(nEndLuminance * (static_cast <double >(aGradient.GetEndIntens()) / 100.0));
}
::Color aStartColor(
static_cast <sal_uInt8>((nStartLuminance * aShadowColor.GetRed()) / 256),
static_cast <sal_uInt8>((nStartLuminance * aShadowColor.GetGreen()) / 256),
static_cast <sal_uInt8>((nStartLuminance * aShadowColor.GetBlue()) / 256));
::Color aEndColor(
static_cast <sal_uInt8>((nEndLuminance * aShadowColor.GetRed()) / 256),
static_cast <sal_uInt8>((nEndLuminance * aShadowColor.GetGreen()) / 256),
static_cast <sal_uInt8>((nEndLuminance * aShadowColor.GetBlue()) / 256));
aGradient.SetColorStops(
basegfx::BColorStops(
aStartColor.getBColor(),
aEndColor.getBColor()));
aTempSet.Put(XFillGradientItem(aGradient));
aTempSet.Put(XFillTransparenceItem(nShadowTransparence));
}
// hatch and transparency like shadow
if (bHatchFillUsed)
{
XHatch aHatch(rOriginalSet.Get(XATTR_FILLHATCH).GetHatchValue());
aHatch.SetColor(aShadowColor);
aTempSet.Put(XFillHatchItem(aHatch));
aTempSet.Put(XFillTransparenceItem(nShadowTransparence));
}
// bitmap and transparency like shadow
if (bBitmapFillUsed)
{
GraphicObject aGraphicObject(rOriginalSet.Get(XATTR_FILLBITMAP).GetGraphicObject());
BitmapEx aBitmapEx(aGraphicObject.GetGraphic().GetBitmapEx());
if (!aBitmapEx.IsEmpty())
{
ScopedVclPtr<VirtualDevice> pVirDev(VclPtr<VirtualDevice>::Create());
pVirDev->SetOutputSizePixel(aBitmapEx.GetSizePixel());
BitmapFilter::Filter(aBitmapEx, BitmapShadowFilter(aShadowColor));
pVirDev->DrawBitmapEx(Point(), aBitmapEx);
aGraphicObject.SetGraphic(Graphic(pVirDev->GetBitmapEx(Point(0,0), aBitmapEx.GetSizePixel())));
}
aTempSet.Put(XFillBitmapItem(aGraphicObject));
aTempSet.Put(XFillTransparenceItem(nShadowTransparence));
}
// set attributes and paint shadow object
pRetval->SetMergedItemSet( aTempSet );
}
return pRetval;
}
uno::Reference<drawing::XCustomShapeEngine> const & SdrObjCustomShape::GetCustomShapeEngine() const
{
if (mxCustomShapeEngine.is())
return mxCustomShapeEngine;
uno::Reference<drawing::XShape> aXShape = GetXShapeForSdrObject(const_cast <SdrObjCustomShape*>(this ));
if ( !aXShape )
return mxCustomShapeEngine;
const uno::Reference<uno::XComponentContext>& xContext( ::comphelper::getProcessComponentContext() );
OUString aEngine(GetMergedItem( SDRATTR_CUSTOMSHAPE_ENGINE ).GetValue());
static constexpr OUStringLiteral sEnhancedCustomShapeEngine = u"com.sun.star.drawing.EnhancedCustomShapeEngine" ;
if ( aEngine.isEmpty() )
aEngine = sEnhancedCustomShapeEngine;
{
static constexpr OUString sCustomShape = u"CustomShape" _ustr;
uno::Sequence<beans::PropertyValue> aPropValues{ comphelper::makePropertyValue(sCustomShape,
aXShape) };
uno::Sequence<uno::Any> aArgument{ uno::Any(aPropValues) };
try
{
uno::Reference<uno::XInterface> xInterface(xContext->getServiceManager()->createInstanceWithArgumentsAndContext(aEngine, aArgument, xContext));
if (xInterface.is())
mxCustomShapeEngine.set(xInterface, uno::UNO_QUERY);
}
catch (const loader::CannotActivateFactoryException&)
{
}
}
return mxCustomShapeEngine;
}
const SdrObject* SdrObjCustomShape::GetSdrObjectFromCustomShape() const
{
if ( !mXRenderedCustomShape.is() )
{
uno::Reference<drawing::XCustomShapeEngine> xCustomShapeEngine( GetCustomShapeEngine() );
if ( xCustomShapeEngine.is() )
const_cast <SdrObjCustomShape*>(this )->mXRenderedCustomShape = xCustomShapeEngine->render();
}
SdrObject* pRenderedCustomShape = mXRenderedCustomShape.is()
? SdrObject::getSdrObjectFromXShape( mXRenderedCustomShape )
: nullptr;
return pRenderedCustomShape;
}
// #i37011# Shadow geometry creation
const SdrObject* SdrObjCustomShape::GetSdrObjectShadowFromCustomShape() const
{
if (!mpLastShadowGeometry)
{
const SdrObject* pSdrObject = GetSdrObjectFromCustomShape();
if (pSdrObject)
{
const SfxItemSet& rOriginalSet = GetObjectItemSet();
const bool bShadow(rOriginalSet.Get( SDRATTR_SHADOW ).GetValue());
if (bShadow)
{
// create a clone with all attributes changed to shadow attributes
// and translation executed, too.
const_cast <SdrObjCustomShape*>(this )->mpLastShadowGeometry =
ImpCreateShadowObjectClone(*pSdrObject, rOriginalSet);
}
}
}
return mpLastShadowGeometry.get();
}
bool SdrObjCustomShape::IsTextPath() const
{
static constexpr OUString sTextPath( u"TextPath" _ustr );
bool bTextPathOn = false ;
const SdrCustomShapeGeometryItem& rGeometryItem = GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY );
const uno::Any* pAny = rGeometryItem.GetPropertyValueByName( sTextPath, sTextPath );
if ( pAny )
*pAny >>= bTextPathOn;
return bTextPathOn;
}
bool SdrObjCustomShape::UseNoFillStyle() const
{
bool bRet = false ;
OUString sShapeType;
static constexpr OUString sType( u"Type" _ustr );
const SdrCustomShapeGeometryItem& rGeometryItem( GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
const uno::Any* pAny = rGeometryItem.GetPropertyValueByName( sType );
if ( pAny )
*pAny >>= sShapeType;
bRet = !IsCustomShapeFilledByDefault( EnhancedCustomShapeTypeNames::Get( sType ) );
return bRet;
}
bool SdrObjCustomShape::IsMirroredX() const
{
bool bMirroredX = false ;
const SdrCustomShapeGeometryItem & rGeometryItem( GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
const uno::Any* pAny = rGeometryItem.GetPropertyValueByName( u"MirroredX" _ustr );
if ( pAny )
*pAny >>= bMirroredX;
return bMirroredX;
}
bool SdrObjCustomShape::IsMirroredY() const
{
bool bMirroredY = false ;
const SdrCustomShapeGeometryItem & rGeometryItem( GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
const uno::Any* pAny = rGeometryItem.GetPropertyValueByName( u"MirroredY" _ustr );
if ( pAny )
*pAny >>= bMirroredY;
return bMirroredY;
}
void SdrObjCustomShape::SetMirroredX( const bool bMirrorX )
{
SdrCustomShapeGeometryItem aGeometryItem( GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
beans::PropertyValue aPropVal;
aPropVal.Name = "MirroredX" ;
aPropVal.Value <<= bMirrorX;
aGeometryItem.SetPropertyValue( aPropVal );
SetMergedItem( aGeometryItem );
}
void SdrObjCustomShape::SetMirroredY( const bool bMirrorY )
{
SdrCustomShapeGeometryItem aGeometryItem( GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
beans::PropertyValue aPropVal;
aPropVal.Name = "MirroredY" ;
aPropVal.Value <<= bMirrorY;
aGeometryItem.SetPropertyValue( aPropVal );
SetMergedItem( aGeometryItem );
}
double SdrObjCustomShape::GetExtraTextRotation( const bool bPreRotation ) const
{
double fExtraTextRotateAngle = 0.0;
if (bPreRotation)
{
// textPreRotateAngle might be set by macro or diagram (SmartArt) import
const uno::Any* pAny;
const SdrCustomShapeGeometryItem& rGeometryItem = GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY );
pAny = rGeometryItem.GetPropertyValueByName(u"TextPreRotateAngle" _ustr);
if ( pAny )
*pAny >>= fExtraTextRotateAngle;
// As long as the edit engine is not able to render these text directions we
// emulate them by setting a suitable text pre-rotation.
const SvxFrameDirectionItem& rDirectionItem = GetMergedItem(SDRATTR_WRITINGMODE2);
if (rDirectionItem.GetValue() == SvxFrameDirection::Vertical_RL_TB90)
fExtraTextRotateAngle -= 90;
else if (rDirectionItem.GetValue() == SvxFrameDirection::Vertical_LR_BT)
fExtraTextRotateAngle -=270;
}
else
{
const uno::Any* pAny;
const SdrCustomShapeGeometryItem& rGeometryItem = GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY );
pAny = rGeometryItem.GetPropertyValueByName(u"TextRotateAngle" _ustr);
if ( pAny )
*pAny >>= fExtraTextRotateAngle;
}
return fExtraTextRotateAngle;
}
bool SdrObjCustomShape::GetTextBounds( tools::Rectangle& rTextBound ) const
{
bool bRet = false ;
uno::Reference<drawing::XCustomShapeEngine> xCustomShapeEngine( GetCustomShapeEngine() );
if ( xCustomShapeEngine.is() )
{
awt::Rectangle aR( xCustomShapeEngine->getTextBounds() );
if ( aR.Width > 1 && aR.Height > 1 )
{
rTextBound = tools::Rectangle( Point( aR.X, aR.Y ), Size( aR.Width, aR.Height ) );
bRet = true ;
}
}
return bRet;
}
basegfx::B2DPolyPolygon SdrObjCustomShape::GetLineGeometry( const bool bBezierAllowed ) const
{
basegfx::B2DPolyPolygon aRetval;
uno::Reference<drawing::XCustomShapeEngine> xCustomShapeEngine( GetCustomShapeEngine() );
if ( xCustomShapeEngine.is() )
{
drawing::PolyPolygonBezierCoords aBezierCoords = xCustomShapeEngine->getLineGeometry();
try
{
aRetval = basegfx::utils::UnoPolyPolygonBezierCoordsToB2DPolyPolygon( aBezierCoords );
if ( !bBezierAllowed && aRetval.areControlPointsUsed())
{
aRetval = basegfx::utils::adaptiveSubdivideByAngle(aRetval);
}
}
catch ( const lang::IllegalArgumentException & )
{
}
}
return aRetval;
}
std::vector< SdrCustomShapeInteraction > SdrObjCustomShape::GetInteractionHandles() const
{
std::vector< SdrCustomShapeInteraction > aRet;
try
{
uno::Reference<drawing::XCustomShapeEngine> xCustomShapeEngine( GetCustomShapeEngine() );
if ( xCustomShapeEngine.is() )
{
int i;
uno::Sequence<uno::Reference<drawing::XCustomShapeHandle>> xInteractionHandles( xCustomShapeEngine->getInteraction() );
for ( i = 0; i < xInteractionHandles.getLength(); i++ )
{
if ( xInteractionHandles[ i ].is() )
{
SdrCustomShapeInteraction aSdrCustomShapeInteraction;
aSdrCustomShapeInteraction.xInteraction = xInteractionHandles[ i ];
aSdrCustomShapeInteraction.aPosition = xInteractionHandles[ i ]->getPosition();
CustomShapeHandleModes nMode = CustomShapeHandleModes::NONE;
switch ( ImpGetCustomShapeType( *this ) )
{
case mso_sptAccentBorderCallout90 : // 2 ortho
{
if (i == 0)
nMode |= CustomShapeHandleModes::RESIZE_FIXED | CustomShapeHandleModes::CREATE_FIXED;
else if (i == 1)
nMode |= CustomShapeHandleModes::RESIZE_ABSOLUTE_X | CustomShapeHandleModes::RESIZE_ABSOLUTE_Y | CustomShapeHandleModes::MOVE_SHAPE | CustomShapeHandleModes::ORTHO4;
}
break ;
case mso_sptChevron :
case mso_sptHomePlate :
nMode |= CustomShapeHandleModes::RESIZE_ABSOLUTE_NEGX;
break ;
case mso_sptWedgeRectCallout :
case mso_sptWedgeRRectCallout :
case mso_sptCloudCallout :
case mso_sptWedgeEllipseCallout :
{
if (i == 0)
nMode |= CustomShapeHandleModes::RESIZE_FIXED;
}
break ;
case mso_sptBorderCallout1 : // 2 diag
{
if (i == 0)
nMode |= CustomShapeHandleModes::RESIZE_FIXED | CustomShapeHandleModes::CREATE_FIXED;
else if (i == 1)
nMode |= CustomShapeHandleModes::RESIZE_ABSOLUTE_X | CustomShapeHandleModes::RESIZE_ABSOLUTE_Y | CustomShapeHandleModes::MOVE_SHAPE;
}
break ;
case mso_sptBorderCallout2 : // 3
{
if (i == 0)
nMode |= CustomShapeHandleModes::RESIZE_FIXED | CustomShapeHandleModes::CREATE_FIXED;
else if (i == 2)
nMode |= CustomShapeHandleModes::RESIZE_ABSOLUTE_X | CustomShapeHandleModes::RESIZE_ABSOLUTE_Y | CustomShapeHandleModes::MOVE_SHAPE;
}
break ;
case mso_sptCallout90 :
case mso_sptAccentCallout90 :
case mso_sptBorderCallout90 :
case mso_sptCallout1 :
case mso_sptCallout2 :
case mso_sptCallout3 :
case mso_sptAccentCallout1 :
case mso_sptAccentCallout2 :
case mso_sptAccentCallout3 :
case mso_sptBorderCallout3 :
case mso_sptAccentBorderCallout1 :
case mso_sptAccentBorderCallout2 :
case mso_sptAccentBorderCallout3 :
{
if (i == 0)
nMode |= CustomShapeHandleModes::RESIZE_FIXED | CustomShapeHandleModes::CREATE_FIXED;
}
break ;
default : break ;
}
aSdrCustomShapeInteraction.nMode = nMode;
aRet.push_back( aSdrCustomShapeInteraction );
}
}
}
}
catch ( const uno::RuntimeException& )
{
}
return aRet;
}
// BaseProperties section
#define DEFAULT_MINIMUM_SIGNED_COMPARE (sal_Int32(0x80000000))
#define DEFAULT_MAXIMUM_SIGNED_COMPARE (sal_Int32(0x7fffffff))
static sal_Int32 GetNumberOfProperties ( const SvxMSDffHandle* pData )
{
sal_Int32 nPropertiesNeeded=1; // position is always needed
SvxMSDffHandleFlags nFlags = pData->nFlags;
if ( nFlags & SvxMSDffHandleFlags::MIRRORED_X )
nPropertiesNeeded++;
if ( nFlags & SvxMSDffHandleFlags::MIRRORED_Y )
nPropertiesNeeded++;
if ( nFlags & SvxMSDffHandleFlags::SWITCHED )
nPropertiesNeeded++;
if ( nFlags & SvxMSDffHandleFlags::POLAR )
{
nPropertiesNeeded++;
if ( nFlags & SvxMSDffHandleFlags::RADIUS_RANGE )
{
if ( pData->nRangeXMin != DEFAULT_MINIMUM_SIGNED_COMPARE )
nPropertiesNeeded++;
if ( pData->nRangeXMax != DEFAULT_MAXIMUM_SIGNED_COMPARE )
nPropertiesNeeded++;
}
}
else if ( nFlags & SvxMSDffHandleFlags::RANGE )
{
if ( pData->nRangeXMin != DEFAULT_MINIMUM_SIGNED_COMPARE )
nPropertiesNeeded++;
if ( pData->nRangeXMax != DEFAULT_MAXIMUM_SIGNED_COMPARE )
nPropertiesNeeded++;
if ( pData->nRangeYMin != DEFAULT_MINIMUM_SIGNED_COMPARE )
nPropertiesNeeded++;
if ( pData->nRangeYMax != DEFAULT_MAXIMUM_SIGNED_COMPARE )
nPropertiesNeeded++;
}
return nPropertiesNeeded;
}
static void lcl_ShapePropertiesFromDFF( const SvxMSDffHandle* pData, beans::PropertyValues& rPropValues )
{
SvxMSDffHandleFlags nFlags = pData->nFlags;
sal_Int32 n=0;
auto pPropValues = rPropValues.getArray();
// POSITION
{
drawing::EnhancedCustomShapeParameterPair aPosition;
EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aPosition.First, pData->nPositionX, true , true );
EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aPosition.Second, pData->nPositionY, true , false );
pPropValues[ n ].Name = "Position" ;
pPropValues[ n++ ].Value <<= aPosition;
}
if ( nFlags & SvxMSDffHandleFlags::MIRRORED_X )
{
pPropValues[ n ].Name = "MirroredX" ;
pPropValues[ n++ ].Value <<= true ;
}
if ( nFlags & SvxMSDffHandleFlags::MIRRORED_Y )
{
pPropValues[ n ].Name = "MirroredY" ;
pPropValues[ n++ ].Value <<= true ;
}
if ( nFlags & SvxMSDffHandleFlags::SWITCHED )
{
pPropValues[ n ].Name = "Switched" ;
pPropValues[ n++ ].Value <<= true ;
}
if ( nFlags & SvxMSDffHandleFlags::POLAR )
{
drawing::EnhancedCustomShapeParameterPair aCenter;
EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aCenter.First, pData->nCenterX,
bool ( nFlags & SvxMSDffHandleFlags::CENTER_X_IS_SPECIAL ), true );
EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aCenter.Second, pData->nCenterY,
bool ( nFlags & SvxMSDffHandleFlags::CENTER_Y_IS_SPECIAL ), false );
pPropValues[ n ].Name = "Polar" ;
pPropValues[ n++ ].Value <<= aCenter;
if ( nFlags & SvxMSDffHandleFlags::RADIUS_RANGE )
{
if ( pData->nRangeXMin != DEFAULT_MINIMUM_SIGNED_COMPARE )
{
drawing::EnhancedCustomShapeParameter aRadiusRangeMinimum;
EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aRadiusRangeMinimum, pData->nRangeXMin,
bool ( nFlags & SvxMSDffHandleFlags::RANGE_X_MIN_IS_SPECIAL ), true );
pPropValues[ n ].Name = "RadiusRangeMinimum" ;
pPropValues[ n++ ].Value <<= aRadiusRangeMinimum;
}
if ( pData->nRangeXMax != DEFAULT_MAXIMUM_SIGNED_COMPARE )
{
drawing::EnhancedCustomShapeParameter aRadiusRangeMaximum;
EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aRadiusRangeMaximum, pData->nRangeXMax,
bool ( nFlags & SvxMSDffHandleFlags::RANGE_X_MAX_IS_SPECIAL ), false );
pPropValues[ n ].Name = "RadiusRangeMaximum" ;
pPropValues[ n++ ].Value <<= aRadiusRangeMaximum;
}
}
}
else if ( nFlags & SvxMSDffHandleFlags::RANGE )
{
if ( pData->nRangeXMin != DEFAULT_MINIMUM_SIGNED_COMPARE )
{
drawing::EnhancedCustomShapeParameter aRangeXMinimum;
EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aRangeXMinimum, pData->nRangeXMin,
bool ( nFlags & SvxMSDffHandleFlags::RANGE_X_MIN_IS_SPECIAL ), true );
pPropValues[ n ].Name = "RangeXMinimum" ;
pPropValues[ n++ ].Value <<= aRangeXMinimum;
}
if ( pData->nRangeXMax != DEFAULT_MAXIMUM_SIGNED_COMPARE )
{
drawing::EnhancedCustomShapeParameter aRangeXMaximum;
EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aRangeXMaximum, pData->nRangeXMax,
bool ( nFlags & SvxMSDffHandleFlags::RANGE_X_MAX_IS_SPECIAL ), false );
pPropValues[ n ].Name = "RangeXMaximum" ;
pPropValues[ n++ ].Value <<= aRangeXMaximum;
}
if ( pData->nRangeYMin != DEFAULT_MINIMUM_SIGNED_COMPARE )
{
drawing::EnhancedCustomShapeParameter aRangeYMinimum;
EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aRangeYMinimum, pData->nRangeYMin,
bool ( nFlags & SvxMSDffHandleFlags::RANGE_Y_MIN_IS_SPECIAL ), true );
pPropValues[ n ].Name = "RangeYMinimum" ;
pPropValues[ n++ ].Value <<= aRangeYMinimum;
}
if ( pData->nRangeYMax != DEFAULT_MAXIMUM_SIGNED_COMPARE )
{
drawing::EnhancedCustomShapeParameter aRangeYMaximum;
EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aRangeYMaximum, pData->nRangeYMax,
bool ( nFlags & SvxMSDffHandleFlags::RANGE_Y_MAX_IS_SPECIAL ), false );
pPropValues[ n ].Name = "RangeYMaximum" ;
pPropValues[ n++ ].Value <<= aRangeYMaximum;
}
}
}
std::unique_ptr<sdr::properties::BaseProperties> SdrObjCustomShape::CreateObjectSpecificProperties()
{
return std::make_unique<sdr::properties::CustomShapeProperties>(*this );
}
SdrObjCustomShape::SdrObjCustomShape(SdrModel& rSdrModel)
: SdrTextObj(rSdrModel)
, m_fObjectRotation(0.0)
, mbAdjustingTextFrameWidthAndHeight(false )
{
m_bClosedObj = true ; // custom shapes may be filled
mbTextFrame = true ;
}
SdrObjCustomShape::SdrObjCustomShape(SdrModel& rSdrModel, SdrObjCustomShape const & rSource)
: SdrTextObj(rSdrModel, rSource)
, m_fObjectRotation(0.0)
, mbAdjustingTextFrameWidthAndHeight(false )
{
m_bClosedObj = true ; // custom shapes may be filled
mbTextFrame = true ;
m_fObjectRotation = rSource.m_fObjectRotation;
mbAdjustingTextFrameWidthAndHeight = rSource.mbAdjustingTextFrameWidthAndHeight;
assert(!mbAdjustingTextFrameWidthAndHeight);
InvalidateRenderGeometry();
}
SdrObjCustomShape::~SdrObjCustomShape()
{
// delete buffered display geometry
InvalidateRenderGeometry();
}
void SdrObjCustomShape::MergeDefaultAttributes( const OUString* pType )
{
beans::PropertyValue aPropVal;
OUString sShapeType;
static constexpr OUString sType( u"Type" _ustr );
SdrCustomShapeGeometryItem aGeometryItem( GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
if ( pType && !pType->isEmpty() )
{
sal_Int32 nType = pType->toInt32();
if ( nType )
sShapeType = EnhancedCustomShapeTypeNames::Get( static_cast < MSO_SPT >( nType ) );
else
sShapeType = *pType;
aPropVal.Name = sType;
aPropVal.Value <<= sShapeType;
aGeometryItem.SetPropertyValue( aPropVal );
}
else
{
uno::Any *pAny = aGeometryItem.GetPropertyValueByName( sType );
if ( pAny )
*pAny >>= sShapeType;
}
MSO_SPT eSpType = EnhancedCustomShapeTypeNames::Get( sShapeType );
const sal_Int32* pDefData = nullptr;
const mso_CustomShape* pDefCustomShape = GetCustomShapeContent( eSpType );
if ( pDefCustomShape )
pDefData = pDefCustomShape->pDefData;
uno::Sequence<drawing::EnhancedCustomShapeAdjustmentValue> seqAdjustmentValues;
// AdjustmentValues
static constexpr OUString sAdjustmentValues( u"AdjustmentValues" _ustr );
const uno::Any* pAny = aGeometryItem.GetPropertyValueByName( sAdjustmentValues );
if ( pAny )
*pAny >>= seqAdjustmentValues;
if ( pDefCustomShape && pDefData ) // now check if we have to default some adjustment values
{
// first check if there are adjustment values are to be appended
sal_Int32 i, nAdjustmentValues = seqAdjustmentValues.getLength();
sal_Int32 nAdjustmentDefaults = *pDefData++;
if ( nAdjustmentDefaults > nAdjustmentValues )
seqAdjustmentValues.realloc( nAdjustmentDefaults );
auto pseqAdjustmentValues = seqAdjustmentValues.getArray();
for ( i = nAdjustmentValues; i < nAdjustmentDefaults; i++ )
{
pseqAdjustmentValues[ i ].Value <<= pDefData[ i ];
pseqAdjustmentValues[ i ].State = beans::PropertyState_DIRECT_VALUE;
}
// check if there are defaulted adjustment values that should be filled the hard coded defaults (pDefValue)
sal_Int32 nCount = std::min(nAdjustmentValues, nAdjustmentDefaults);
for ( i = 0; i < nCount; i++ )
{
if ( seqAdjustmentValues[ i ].State != beans::PropertyState_DIRECT_VALUE )
{
pseqAdjustmentValues[ i ].Value <<= pDefData[ i ];
pseqAdjustmentValues[ i ].State = beans::PropertyState_DIRECT_VALUE;
}
}
}
aPropVal.Name = sAdjustmentValues;
aPropVal.Value <<= seqAdjustmentValues;
aGeometryItem.SetPropertyValue( aPropVal );
// Coordsize
static constexpr OUString sViewBox( u"ViewBox" _ustr );
const uno::Any* pViewBox = aGeometryItem.GetPropertyValueByName( sViewBox );
awt::Rectangle aViewBox;
if ( !pViewBox || !(*pViewBox >>= aViewBox ) )
{
if ( pDefCustomShape )
{
aViewBox.X = 0;
aViewBox.Y = 0;
aViewBox.Width = pDefCustomShape->nCoordWidth;
aViewBox.Height= pDefCustomShape->nCoordHeight;
aPropVal.Name = sViewBox;
aPropVal.Value <<= aViewBox;
aGeometryItem.SetPropertyValue( aPropVal );
}
}
static constexpr OUString sPath( u"Path" _ustr );
// Path/Coordinates
static constexpr OUString sCoordinates( u"Coordinates" _ustr );
pAny = aGeometryItem.GetPropertyValueByName( sPath, sCoordinates );
if (!pAny && pDefCustomShape && !pDefCustomShape->pVertices.empty())
{
sal_Int32 i, nCount = pDefCustomShape->pVertices.size();
uno::Sequence<drawing::EnhancedCustomShapeParameterPair> seqCoordinates( nCount );
auto pseqCoordinates = seqCoordinates.getArray();
for ( i = 0; i < nCount; i++ )
{
EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqCoordinates[ i ].First, pDefCustomShape->pVertices[ i ].nValA );
EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqCoordinates[ i ].Second, pDefCustomShape->pVertices[ i ].nValB );
}
aPropVal.Name = sCoordinates;
aPropVal.Value <<= seqCoordinates;
aGeometryItem.SetPropertyValue( sPath, aPropVal );
}
// Path/GluePoints
static constexpr OUString sGluePoints( u"GluePoints" _ustr );
pAny = aGeometryItem.GetPropertyValueByName( sPath, sGluePoints );
if (!pAny && pDefCustomShape && !pDefCustomShape->pGluePoints.empty())
{
sal_Int32 i, nCount = pDefCustomShape->pGluePoints.size();
uno::Sequence<drawing::EnhancedCustomShapeParameterPair> seqGluePoints( nCount );
auto pseqGluePoints = seqGluePoints.getArray();
for ( i = 0; i < nCount; i++ )
{
EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqGluePoints[ i ].First, pDefCustomShape->pGluePoints[ i ].nValA );
EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqGluePoints[ i ].Second, pDefCustomShape->pGluePoints[ i ].nValB );
}
aPropVal.Name = sGluePoints;
aPropVal.Value <<= seqGluePoints;
aGeometryItem.SetPropertyValue( sPath, aPropVal );
}
// Path/Segments
static constexpr OUString sSegments( u"Segments" _ustr );
pAny = aGeometryItem.GetPropertyValueByName( sPath, sSegments );
if ( !pAny && pDefCustomShape && pDefCustomShape->nElements && pDefCustomShape->pElements )
{
sal_Int32 i, nCount = pDefCustomShape->nElements;
uno::Sequence<drawing::EnhancedCustomShapeSegment> seqSegments( nCount );
auto pseqSegments = seqSegments.getArray();
for ( i = 0; i < nCount; i++ )
{
drawing::EnhancedCustomShapeSegment& rSegInfo = pseqSegments[ i ];
sal_uInt16 nSDat = pDefCustomShape->pElements[ i ];
lcl_ShapeSegmentFromBinary( rSegInfo, nSDat );
}
aPropVal.Name = sSegments;
aPropVal.Value <<= seqSegments;
aGeometryItem.SetPropertyValue( sPath, aPropVal );
}
// Path/StretchX
static constexpr OUString sStretchX( u"StretchX" _ustr );
pAny = aGeometryItem.GetPropertyValueByName( sPath, sStretchX );
if ( !pAny && pDefCustomShape )
{
sal_Int32 nXRef = pDefCustomShape->nXRef;
if ( nXRef != DEFAULT_MINIMUM_SIGNED_COMPARE )
{
aPropVal.Name = sStretchX;
aPropVal.Value <<= nXRef;
aGeometryItem.SetPropertyValue( sPath, aPropVal );
}
}
// Path/StretchY
static constexpr OUString sStretchY( u"StretchY" _ustr );
pAny = aGeometryItem.GetPropertyValueByName( sPath, sStretchY );
if ( !pAny && pDefCustomShape )
{
sal_Int32 nYRef = pDefCustomShape->nYRef;
if ( nYRef != DEFAULT_MINIMUM_SIGNED_COMPARE )
{
aPropVal.Name = sStretchY;
aPropVal.Value <<= nYRef;
aGeometryItem.SetPropertyValue( sPath, aPropVal );
}
}
// Path/TextFrames
static constexpr OUString sTextFrames( u"TextFrames" _ustr );
pAny = aGeometryItem.GetPropertyValueByName( sPath, sTextFrames );
if (!pAny && pDefCustomShape && !pDefCustomShape->pTextRect.empty())
{
sal_Int32 i, nCount = pDefCustomShape->pTextRect.size();
uno::Sequence<drawing::EnhancedCustomShapeTextFrame> seqTextFrames( nCount );
auto pseqTextFrames = seqTextFrames.getArray();
for (i = 0; i < nCount; i++)
{
const SvxMSDffTextRectangles* pRectangles = &pDefCustomShape->pTextRect[i];
EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqTextFrames[ i ].TopLeft.First, pRectangles->nPairA.nValA );
EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqTextFrames[ i ].TopLeft.Second, pRectangles->nPairA.nValB );
EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqTextFrames[ i ].BottomRight.First, pRectangles->nPairB.nValA );
EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqTextFrames[ i ].BottomRight.Second, pRectangles->nPairB.nValB );
}
aPropVal.Name = sTextFrames;
aPropVal.Value <<= seqTextFrames;
aGeometryItem.SetPropertyValue( sPath, aPropVal );
}
// Equations
static constexpr OUString sEquations( u"Equations" _ustr );
pAny = aGeometryItem.GetPropertyValueByName( sEquations );
if (!pAny && pDefCustomShape && !pDefCustomShape->pCalculation.empty() )
{
sal_Int32 i, nCount = pDefCustomShape->pCalculation.size();
uno::Sequence< OUString > seqEquations( nCount );
auto pseqEquations = seqEquations.getArray();
for (i = 0; i < nCount; i++)
{
const SvxMSDffCalculationData* pData = &pDefCustomShape->pCalculation[i];
pseqEquations[ i ] = EnhancedCustomShape2d::GetEquation( pData->nFlags, pData->nVal[ 0 ], pData->nVal[ 1 ], pData->nVal[ 2 ] );
}
aPropVal.Name = sEquations;
aPropVal.Value <<= seqEquations;
aGeometryItem.SetPropertyValue( aPropVal );
}
// Handles
static constexpr OUString sHandles( u"Handles" _ustr );
pAny = aGeometryItem.GetPropertyValueByName( sHandles );
if (!pAny && pDefCustomShape && !pDefCustomShape->pHandles.empty())
{
sal_Int32 i, nCount = pDefCustomShape->pHandles.size();
uno::Sequence<beans::PropertyValues> seqHandles( nCount );
auto pseqHandles = seqHandles.getArray();
for (i = 0; i < nCount; i++)
{
const SvxMSDffHandle* pData = &pDefCustomShape->pHandles[i];
sal_Int32 nPropertiesNeeded;
beans::PropertyValues& rPropValues = pseqHandles[ i ];
nPropertiesNeeded = GetNumberOfProperties( pData );
rPropValues.realloc( nPropertiesNeeded );
lcl_ShapePropertiesFromDFF( pData, rPropValues );
}
aPropVal.Name = sHandles;
aPropVal.Value <<= seqHandles;
aGeometryItem.SetPropertyValue( aPropVal );
}
else if (pAny && sShapeType.startsWith("ooxml-" ) && sShapeType != "ooxml-non-primitive" )
{
// ODF is not able to store the ooxml way of connecting handle to an adjustment
// value by name, e.g. attribute RefX="adj". So the information is lost, when exporting
// a pptx to odp, for example. This part reconstructs this information for the
// ooxml preset shapes from their definition.
uno::Sequence<beans::PropertyValues> seqHandles;
*pAny >>= seqHandles;
auto seqHandlesRange = asNonConstRange(seqHandles);
bool bChanged(false );
for (sal_Int32 i = 0; i < seqHandles.getLength(); i++)
{
comphelper::SequenceAsHashMap aHandleProps(seqHandles[i]);
OUString sFirstRefType;
sal_Int32 nFirstAdjRef;
OUString sSecondRefType;
sal_Int32 nSecondAdjRef;
PresetOOXHandleAdj::GetOOXHandleAdjRelation(sShapeType, i, sFirstRefType, nFirstAdjRef,
sSecondRefType, nSecondAdjRef);
if (sFirstRefType != "na" && 0 <= nFirstAdjRef
&& nFirstAdjRef < seqAdjustmentValues.getLength())
{
bChanged |= aHandleProps.createItemIfMissing(sFirstRefType, nFirstAdjRef);
}
if (sSecondRefType != "na" && 0 <= nSecondAdjRef
&& nSecondAdjRef < seqAdjustmentValues.getLength())
{
bChanged |= aHandleProps.createItemIfMissing(sSecondRefType, nSecondAdjRef);
}
aHandleProps >> seqHandlesRange[i];
}
if (bChanged)
{
aPropVal.Name = sHandles;
aPropVal.Value <<= seqHandles;
aGeometryItem.SetPropertyValue(aPropVal);
}
}
SetMergedItem( aGeometryItem );
}
bool SdrObjCustomShape::IsDefaultGeometry( const DefaultType eDefaultType ) const
{
bool bIsDefaultGeometry = false ;
OUString sShapeType;
const SdrCustomShapeGeometryItem & rGeometryItem( GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
const uno::Any *pAny = rGeometryItem.GetPropertyValueByName( u"Type" _ustr );
if ( pAny )
*pAny >>= sShapeType;
MSO_SPT eSpType = EnhancedCustomShapeTypeNames::Get( sShapeType );
const mso_CustomShape* pDefCustomShape = GetCustomShapeContent( eSpType );
static constexpr OUString sPath( u"Path" _ustr );
switch ( eDefaultType )
{
case DefaultType::Viewbox :
{
const uno::Any* pViewBox = rGeometryItem.GetPropertyValueByName( u"ViewBox" _ustr );
awt::Rectangle aViewBox;
if (pViewBox && (*pViewBox >>= aViewBox) && pDefCustomShape)
{
if ( ( aViewBox.Width == pDefCustomShape->nCoordWidth )
&& ( aViewBox.Height == pDefCustomShape->nCoordHeight ) )
bIsDefaultGeometry = true ;
}
}
break ;
case DefaultType::Path :
{
pAny = rGeometryItem.GetPropertyValueByName( sPath, u"Coordinates" _ustr );
if (pAny && pDefCustomShape && !pDefCustomShape->pVertices.empty())
{
uno::Sequence<drawing::EnhancedCustomShapeParameterPair> seqCoordinates1;
if ( *pAny >>= seqCoordinates1 )
{
sal_Int32 i, nCount = pDefCustomShape->pVertices.size();
uno::Sequence<drawing::EnhancedCustomShapeParameterPair> seqCoordinates2( nCount );
auto pseqCoordinates2 = seqCoordinates2.getArray();
for ( i = 0; i < nCount; i++ )
{
EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqCoordinates2[ i ].First, pDefCustomShape->pVertices[ i ].nValA );
EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqCoordinates2[ i ].Second, pDefCustomShape->pVertices[ i ].nValB );
}
if ( seqCoordinates1 == seqCoordinates2 )
bIsDefaultGeometry = true ;
}
}
else if (pDefCustomShape && pDefCustomShape->pVertices.empty())
bIsDefaultGeometry = true ;
}
break ;
case DefaultType::Gluepoints :
{
pAny = rGeometryItem.GetPropertyValueByName( sPath, u"GluePoints" _ustr );
if (pAny && pDefCustomShape && !pDefCustomShape->pGluePoints.empty())
{
uno::Sequence<drawing::EnhancedCustomShapeParameterPair> seqGluePoints1;
if ( *pAny >>= seqGluePoints1 )
{
sal_Int32 i, nCount = pDefCustomShape->pGluePoints.size();
uno::Sequence<drawing::EnhancedCustomShapeParameterPair> seqGluePoints2( nCount );
auto pseqGluePoints2 = seqGluePoints2.getArray();
for ( i = 0; i < nCount; i++ )
{
EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqGluePoints2[ i ].First, pDefCustomShape->pGluePoints[ i ].nValA );
EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqGluePoints2[ i ].Second, pDefCustomShape->pGluePoints[ i ].nValB );
}
if ( seqGluePoints1 == seqGluePoints2 )
bIsDefaultGeometry = true ;
}
}
else if (pDefCustomShape && pDefCustomShape->pGluePoints.empty())
bIsDefaultGeometry = true ;
}
break ;
case DefaultType::Segments :
{
// Path/Segments
pAny = rGeometryItem.GetPropertyValueByName( sPath, u"Segments" _ustr );
if ( pAny )
{
uno::Sequence<drawing::EnhancedCustomShapeSegment> seqSegments1;
if ( *pAny >>= seqSegments1 )
{
if ( pDefCustomShape && pDefCustomShape->nElements && pDefCustomShape->pElements )
{
sal_Int32 i, nCount = pDefCustomShape->nElements;
if ( nCount )
{
uno::Sequence<drawing::EnhancedCustomShapeSegment> seqSegments2( nCount );
auto pseqSegments2 = seqSegments2.getArray();
for ( i = 0; i < nCount; i++ )
{
drawing::EnhancedCustomShapeSegment& rSegInfo = pseqSegments2[ i ];
sal_uInt16 nSDat = pDefCustomShape->pElements[ i ];
lcl_ShapeSegmentFromBinary( rSegInfo, nSDat );
}
if ( seqSegments1 == seqSegments2 )
bIsDefaultGeometry = true ;
}
}
else
{
// check if it's the default segment description ( M L Z N )
if ( seqSegments1.getLength() == 4 )
{
if ( ( seqSegments1[ 0 ].Command == drawing::EnhancedCustomShapeSegmentCommand::MOVETO )
&& ( seqSegments1[ 1 ].Command == drawing::EnhancedCustomShapeSegmentCommand::LINETO )
&& ( seqSegments1[ 2 ].Command == drawing::EnhancedCustomShapeSegmentCommand::CLOSESUBPATH )
&& ( seqSegments1[ 3 ].Command == drawing::EnhancedCustomShapeSegmentCommand::ENDSUBPATH ) )
bIsDefaultGeometry = true ;
}
}
}
}
else if ( pDefCustomShape && ( ( pDefCustomShape->nElements == 0 ) || ( pDefCustomShape->pElements == nullptr ) ) )
bIsDefaultGeometry = true ;
}
break ;
case DefaultType::StretchX :
{
pAny = rGeometryItem.GetPropertyValueByName( sPath, u"StretchX" _ustr );
if ( pAny && pDefCustomShape )
{
sal_Int32 nStretchX = 0;
if ( *pAny >>= nStretchX )
{
if ( pDefCustomShape->nXRef == nStretchX )
bIsDefaultGeometry = true ;
}
}
else if ( pDefCustomShape && ( pDefCustomShape->nXRef == DEFAULT_MINIMUM_SIGNED_COMPARE ) )
bIsDefaultGeometry = true ;
}
break ;
case DefaultType::StretchY :
{
pAny = rGeometryItem.GetPropertyValueByName( sPath, u"StretchY" _ustr );
if ( pAny && pDefCustomShape )
{
sal_Int32 nStretchY = 0;
if ( *pAny >>= nStretchY )
{
if ( pDefCustomShape->nYRef == nStretchY )
bIsDefaultGeometry = true ;
}
}
else if ( pDefCustomShape && ( pDefCustomShape->nYRef == DEFAULT_MINIMUM_SIGNED_COMPARE ) )
bIsDefaultGeometry = true ;
}
break ;
case DefaultType::Equations :
{
pAny = rGeometryItem.GetPropertyValueByName( u"Equations" _ustr );
if (pAny && pDefCustomShape && !pDefCustomShape->pCalculation.empty())
{
uno::Sequence<OUString> seqEquations1;
if ( *pAny >>= seqEquations1 )
{
sal_Int32 i, nCount = pDefCustomShape->pCalculation.size();
uno::Sequence<OUString> seqEquations2( nCount );
auto pseqEquations2 = seqEquations2.getArray();
for (i = 0; i < nCount; i++)
{
const SvxMSDffCalculationData* pData = &pDefCustomShape->pCalculation[i];
pseqEquations2[ i ] = EnhancedCustomShape2d::GetEquation( pData->nFlags, pData->nVal[ 0 ], pData->nVal[ 1 ], pData->nVal[ 2 ] );
}
if ( seqEquations1 == seqEquations2 )
bIsDefaultGeometry = true ;
}
}
else if (pDefCustomShape && pDefCustomShape->pCalculation.empty())
bIsDefaultGeometry = true ;
}
break ;
case DefaultType::TextFrames :
{
pAny = rGeometryItem.GetPropertyValueByName( sPath, u"TextFrames" _ustr );
if (pAny && pDefCustomShape && !pDefCustomShape->pTextRect.empty())
{
uno::Sequence<drawing::EnhancedCustomShapeTextFrame> seqTextFrames1;
if ( *pAny >>= seqTextFrames1 )
{
sal_Int32 i, nCount = pDefCustomShape->pTextRect.size();
uno::Sequence<drawing::EnhancedCustomShapeTextFrame> seqTextFrames2( nCount );
auto pseqTextFrames2 = seqTextFrames2.getArray();
for (i = 0; i < nCount; i++)
{
const SvxMSDffTextRectangles* pRectangles = &pDefCustomShape->pTextRect[i];
EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqTextFrames2[ i ].TopLeft.First, pRectangles->nPairA.nValA );
EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqTextFrames2[ i ].TopLeft.Second, pRectangles->nPairA.nValB );
EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqTextFrames2[ i ].BottomRight.First, pRectangles->nPairB.nValA );
EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqTextFrames2[ i ].BottomRight.Second, pRectangles->nPairB.nValB );
}
if ( seqTextFrames1 == seqTextFrames2 )
bIsDefaultGeometry = true ;
}
}
else if (pDefCustomShape && pDefCustomShape->pTextRect.empty())
bIsDefaultGeometry = true ;
}
break ;
}
return bIsDefaultGeometry;
}
void SdrObjCustomShape::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
{
rInfo.bResizeFreeAllowed=m_fObjectRotation == 0.0;
rInfo.bResizePropAllowed=true ;
rInfo.bRotateFreeAllowed=true ;
rInfo.bRotate90Allowed =true ;
rInfo.bMirrorFreeAllowed=true ;
rInfo.bMirror45Allowed =true ;
rInfo.bMirror90Allowed =true ;
rInfo.bTransparenceAllowed = false ;
rInfo.bShearAllowed =true ;
rInfo.bEdgeRadiusAllowed=false ;
rInfo.bNoContortion =true ;
// #i37011#
if ( !mXRenderedCustomShape.is() )
return ;
const SdrObject* pRenderedCustomShape = SdrObject::getSdrObjectFromXShape( mXRenderedCustomShape );
if ( !pRenderedCustomShape )
return ;
// #i37262#
// Iterate self over the contained objects, since there are combinations of
// polygon and curve objects. In that case, aInfo.bCanConvToPath and
// aInfo.bCanConvToPoly would be false. What is needed here is an or, not an and.
SdrObjListIter aIterator(*pRenderedCustomShape);
while (aIterator.IsMore())
{
SdrObject* pCandidate = aIterator.Next();
SdrObjTransformInfoRec aInfo;
pCandidate->TakeObjInfo(aInfo);
// set path and poly conversion if one is possible since
// this object will first be broken
const bool bCanConvToPathOrPoly(aInfo.bCanConvToPath || aInfo.bCanConvToPoly);
if (rInfo.bCanConvToPath != bCanConvToPathOrPoly)
{
rInfo.bCanConvToPath = bCanConvToPathOrPoly;
}
if (rInfo.bCanConvToPoly != bCanConvToPathOrPoly)
{
rInfo.bCanConvToPoly = bCanConvToPathOrPoly;
}
if (rInfo.bCanConvToContour != aInfo.bCanConvToContour)
{
rInfo.bCanConvToContour = aInfo.bCanConvToContour;
}
if (rInfo.bShearAllowed != aInfo.bShearAllowed)
{
rInfo.bShearAllowed = aInfo.bShearAllowed;
}
}
}
SdrObjKind SdrObjCustomShape::GetObjIdentifier() const
{
return SdrObjKind::CustomShape;
}
// #115391# This implementation is based on the TextFrame size of the CustomShape and the
// state of the ResizeShapeToFitText flag to correctly set TextMinFrameWidth/Height
void SdrObjCustomShape::AdaptTextMinSize()
{
if (getSdrModelFromSdrObject().IsCreatingDataObj() || getSdrModelFromSdrObject().IsPasteResize())
return ;
// check if we need to change anything before creating an SfxItemSet, because that is expensive
const bool bResizeShapeToFitText(GetObjectItem(SDRATTR_TEXT_AUTOGROWHEIGHT).GetValue());
tools::Rectangle aTextBound(getRectangle());
bool bChanged(false );
if (bResizeShapeToFitText)
bChanged = true ;
else if (GetTextBounds(aTextBound))
bChanged = true ;
if (!bChanged)
return ;
SfxItemSetFixed<SDRATTR_TEXT_MINFRAMEHEIGHT, SDRATTR_TEXT_AUTOGROWHEIGHT,
SDRATTR_TEXT_MINFRAMEWIDTH, SDRATTR_TEXT_AUTOGROWWIDTH> // contains SDRATTR_TEXT_MAXFRAMEWIDTH
aSet(*GetObjectItemSet().GetPool());
if (bResizeShapeToFitText)
{
// always reset MinWidthHeight to zero to only rely on text size and frame size
// to allow resizing being completely dependent on text size only
aSet.Put(makeSdrTextMinFrameWidthItem(0));
aSet.Put(makeSdrTextMinFrameHeightItem(0));
}
else
{
// recreate from CustomShape-specific TextBounds
const tools::Long nHDist(GetTextLeftDistance() + GetTextRightDistance());
const tools::Long nVDist(GetTextUpperDistance() + GetTextLowerDistance());
const tools::Long nTWdt(std::max(tools::Long (0), static_cast <tools::Long >(aTextBound.GetWidth() - 1 - nHDist)));
const tools::Long nTHgt(std::max(tools::Long (0), static_cast <tools::Long >(aTextBound.GetHeight() - 1 - nVDist)));
aSet.Put(makeSdrTextMinFrameWidthItem(nTWdt));
aSet.Put(makeSdrTextMinFrameHeightItem(nTHgt));
}
SetObjectItemSet(aSet);
}
void SdrObjCustomShape::NbcSetSnapRect(const tools::Rectangle& rRectangle)
{
tools::Rectangle aRectangle(rRectangle);
ImpJustifyRect(aRectangle);
setRectangle(aRectangle);
InvalidateRenderGeometry();
AdaptTextMinSize();
ImpCheckShear();
SetBoundAndSnapRectsDirty();
SetChanged();
}
void SdrObjCustomShape::SetSnapRect( const tools::Rectangle& rRect )
{
tools::Rectangle aBoundRect0;
if ( m_pUserCall )
aBoundRect0 = GetLastBoundRect();
NbcSetSnapRect( rRect );
BroadcastObjectChange();
SendUserCall(SdrUserCallType::Resize,aBoundRect0);
}
void SdrObjCustomShape::NbcSetLogicRect(const tools::Rectangle& rRectangle, bool bAdaptTextMinSize)
{
tools::Rectangle aRectangle(rRectangle);
ImpJustifyRect(aRectangle);
setRectangle(aRectangle);
InvalidateRenderGeometry();
if (bAdaptTextMinSize)
AdaptTextMinSize();
SetBoundAndSnapRectsDirty();
SetChanged();
}
void SdrObjCustomShape::SetLogicRect( const tools::Rectangle& rRect )
{
tools::Rectangle aBoundRect0;
if ( m_pUserCall )
aBoundRect0 = GetLastBoundRect();
NbcSetLogicRect(rRect);
BroadcastObjectChange();
SendUserCall(SdrUserCallType::Resize,aBoundRect0);
}
void SdrObjCustomShape::Move( const Size& rSiz )
{
if ( rSiz.Width() || rSiz.Height() )
{
tools::Rectangle aBoundRect0;
if ( m_pUserCall )
aBoundRect0 = GetLastBoundRect();
NbcMove(rSiz);
SetChanged();
BroadcastObjectChange();
SendUserCall(SdrUserCallType::MoveOnly,aBoundRect0);
}
}
void SdrObjCustomShape::NbcMove( const Size& rSiz )
{
SdrTextObj::NbcMove( rSiz );
if ( mXRenderedCustomShape.is() )
{
SdrObject* pRenderedCustomShape = SdrObject::getSdrObjectFromXShape(mXRenderedCustomShape);
if ( pRenderedCustomShape )
{
// #i97149# the visualisation shape needs to be informed
// about change, too
pRenderedCustomShape->ActionChanged();
pRenderedCustomShape->NbcMove( rSiz );
}
}
// #i37011# adapt geometry shadow
if (mpLastShadowGeometry)
{
mpLastShadowGeometry->NbcMove( rSiz );
}
}
void SdrObjCustomShape::NbcResize( const Point& rRef, const Fraction& rxFact, const Fraction& ryFact )
{
// taking care of handles that should not been changed
tools::Rectangle aOld(getRectangle());
std::vector< SdrCustomShapeInteraction > aInteractionHandles( GetInteractionHandles() );
SdrTextObj::NbcResize( rRef, rxFact, ryFact );
if ( ( rxFact.GetNumerator() != rxFact.GetDenominator() )
|| ( ryFact.GetNumerator()!= ryFact.GetDenominator() ) )
{
if ( ( ( rxFact.GetNumerator() < 0 ) && ( rxFact.GetDenominator() > 0 ) ) ||
( ( rxFact.GetNumerator() > 0 ) && ( rxFact.GetDenominator() < 0 ) ) )
{
SetMirroredX( !IsMirroredX() );
}
if ( ( ( ryFact.GetNumerator() < 0 ) && ( ryFact.GetDenominator() > 0 ) ) ||
( ( ryFact.GetNumerator() > 0 ) && ( ryFact.GetDenominator() < 0 ) ) )
{
SetMirroredY( !IsMirroredY() );
}
}
for (const auto & rInteraction : aInteractionHandles)
{
try
{
if ( rInteraction.nMode & CustomShapeHandleModes::RESIZE_FIXED )
rInteraction.xInteraction->setControllerPosition( rInteraction.aPosition );
if ( rInteraction.nMode & CustomShapeHandleModes::RESIZE_ABSOLUTE_X )
{
sal_Int32 nX = ( rInteraction.aPosition.X - aOld.Left() ) + getRectangle().Left();
rInteraction.xInteraction->setControllerPosition(awt::Point(nX, rInteraction.xInteraction->getPosition().Y));
}
else if ( rInteraction.nMode & CustomShapeHandleModes::RESIZE_ABSOLUTE_NEGX )
{
sal_Int32 nX = getRectangle().Right() - (aOld.Right() - rInteraction.aPosition.X);
rInteraction.xInteraction->setControllerPosition(awt::Point(nX, rInteraction.xInteraction->getPosition().Y));
}
if ( rInteraction.nMode & CustomShapeHandleModes::RESIZE_ABSOLUTE_Y )
{
sal_Int32 nY = ( rInteraction.aPosition.Y - aOld.Top() ) + getRectangle().Top();
rInteraction.xInteraction->setControllerPosition(awt::Point(rInteraction.xInteraction->getPosition().X, nY));
}
}
catch ( const uno::RuntimeException& )
{
}
}
// updating fObjectRotation
Degree100 nTextObjRotation = maGeo.m_nRotationAngle;
double fAngle = toDegrees(nTextObjRotation);
if (IsMirroredX())
{
if (IsMirroredY())
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5 C=93 H=95 G=93
¤ Dauer der Verarbeitung: 0.20 Sekunden
(vorverarbeitet)
¤
*© Formatika GbR, Deutschland