/* -*- 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 .
*/
// 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(bShadow)
{ // create a clone with all attributes changed to shadow attributes // and translation executed, too. const_cast<SdrObjCustomShape*>(this)->mpLastShadowGeometry =
ImpCreateShadowObjectClone(*pSdrObject, rOriginalSet);
}
}
}
double SdrObjCustomShape::GetExtraTextRotation( constbool 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; elseif (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;
}
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; elseif (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; elseif (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;
}
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 );
// #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 constbool bCanConvToPathOrPoly(aInfo.bCanConvToPath || aInfo.bCanConvToPoly); if(rInfo.bCanConvToPath != bCanConvToPathOrPoly)
{
rInfo.bCanConvToPath = bCanConvToPathOrPoly;
}
// #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 constbool bResizeShapeToFitText(GetObjectItem(SDRATTR_TEXT_AUTOGROWHEIGHT).GetValue());
tools::Rectangle aTextBound(getRectangle()); bool bChanged(false); if(bResizeShapeToFitText)
bChanged = true; elseif(GetTextBounds(aTextBound))
bChanged = true; if (!bChanged) return;
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)));
// the rotation angle for ashapes is stored in fObjectRotation, this rotation // has to be applied to the text object (which is internally using maGeo.nAngle).
SdrTextObj::NbcRotate( getRectangle().TopLeft(), -maGeo.m_nRotationAngle, // retrieving the unrotated text object
-maGeo.mfSinRotationAngle,
maGeo.mfCosRotationAngle );
maGeo.m_nRotationAngle = 0_deg100; // resetting aGeo data
maGeo.RecalcSinCos();
void SdrObjCustomShape::NbcMirror( const Point& rRef1, const Point& rRef2 )
{ // TTTT: Fix for old mirroring, can be removed again in aw080 // storing horizontal and vertical flipping without modifying the rotate angle // decompose other flipping to rotation and MirrorX.
tools::Long ndx = rRef2.X()-rRef1.X();
tools::Long ndy = rRef2.Y()-rRef1.Y();
// copy new list to local. This is NOT very convenient behavior, the local // GluePointList should not be set, but we delivered by using GetGluePointList(), // maybe on demand. Since the local object is changed here, this is assumed to // be a result of GetGluePointList and thus the list is copied if(m_pPlusData)
{
m_pPlusData->SetGluePoints(aNewList);
}
}
switch( eHdl )
{ case SdrHdlKind::UpperLeft : case SdrHdlKind::Upper : case SdrHdlKind::UpperRight : case SdrHdlKind::Left : case SdrHdlKind::Right : case SdrHdlKind::LowerLeft : case SdrHdlKind::Lower : case SdrHdlKind::LowerRight : case SdrHdlKind::Move :
{ break;
} default:
{ returnfalse;
}
}
}
case SdrHdlKind::UpperLeft : case SdrHdlKind::Upper : case SdrHdlKind::UpperRight : case SdrHdlKind::Left : case SdrHdlKind::Right : case SdrHdlKind::LowerLeft : case SdrHdlKind::Lower : case SdrHdlKind::LowerRight :
{
DragResizeCustomShape( ImpDragCalcRect(rDrag) ); break;
} case SdrHdlKind::Move :
{
Move(Size(rDrag.GetDX(), rDrag.GetDY())); break;
} default: break;
}
// in context with the SdrObjCustomShape the SdrTextAutoGrowHeightItem == true -> Resize Shape to fit text, // the SdrTextAutoGrowWidthItem == true -> Word wrap text in Shape bool SdrObjCustomShape::IsAutoGrowHeight() const
{ const SfxItemSet& rSet = GetMergedItemSet(); bool bIsAutoGrowHeight = rSet.Get(SDRATTR_TEXT_AUTOGROWHEIGHT).GetValue(); if ( bIsAutoGrowHeight && IsVerticalWriting() )
bIsAutoGrowHeight = !rSet.Get(SDRATTR_TEXT_WORDWRAP).GetValue(); return bIsAutoGrowHeight;
} bool SdrObjCustomShape::IsAutoGrowWidth() const
{ const SfxItemSet& rSet = GetMergedItemSet(); bool bIsAutoGrowWidth = rSet.Get(SDRATTR_TEXT_AUTOGROWHEIGHT).GetValue(); if ( bIsAutoGrowWidth && !IsVerticalWriting() )
bIsAutoGrowWidth = !rSet.Get(SDRATTR_TEXT_WORDWRAP).GetValue(); return bIsAutoGrowWidth;
}
/* The following method is identical to the SdrTextObj::SetVerticalWriting method, the only difference is that the SdrAutoGrowWidthItem and SdrAutoGrowHeightItem are not exchanged if the vertical writing
mode has been changed */
// prepare ItemSet to set exchanged width and height items
SfxItemSetFixed<SDRATTR_TEXT_AUTOGROWHEIGHT, SDRATTR_TEXT_AUTOGROWHEIGHT, // Expanded item ranges to also support horizontal and vertical adjust.
SDRATTR_TEXT_VERTADJUST, SDRATTR_TEXT_VERTADJUST,
SDRATTR_TEXT_AUTOGROWWIDTH, SDRATTR_TEXT_HORZADJUST> aNewSet(*rSet.GetPool());
aNewSet.Put(rSet);
// Exchange horizontal and vertical adjusts switch(eVert)
{ case SDRTEXTVERTADJUST_TOP: aNewSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_RIGHT)); break; case SDRTEXTVERTADJUST_CENTER: aNewSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_CENTER)); break; case SDRTEXTVERTADJUST_BOTTOM: aNewSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_LEFT)); break; case SDRTEXTVERTADJUST_BLOCK: aNewSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_BLOCK)); break;
} switch(eHorz)
{ case SDRTEXTHORZADJUST_LEFT: aNewSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_BOTTOM)); break; case SDRTEXTHORZADJUST_CENTER: aNewSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_CENTER)); break; case SDRTEXTHORZADJUST_RIGHT: aNewSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_TOP)); break; case SDRTEXTHORZADJUST_BLOCK: aNewSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_BLOCK)); break;
}
bool SdrObjCustomShape::AdjustTextFrameWidthAndHeight(tools::Rectangle& rR, bool bHgt, bool bWdt) const
{ // Either we have text or the application has native text and suggested its size to us. bool bHasText = HasText() || !m_aSuggestedTextFrameSize.IsEmpty(); if ( bHasText && !rR.IsEmpty() )
{ bool bWdtGrow=bWdt && IsAutoGrowWidth(); bool bHgtGrow=bHgt && IsAutoGrowHeight(); if ( bWdtGrow || bHgtGrow )
{
tools::Rectangle aR0(rR);
tools::Long nHgt=0,nMinHgt=0,nMaxHgt=0;
tools::Long nWdt=0,nMinWdt=0,nMaxWdt=0;
Size aSiz(rR.GetSize()); aSiz.AdjustWidth( -1 ); aSiz.AdjustHeight( -1 );
Size aMaxSiz(100000,100000);
Size aTmpSiz(getSdrModelFromSdrObject().GetMaxObjSize()); if (aTmpSiz.Width()!=0) aMaxSiz.setWidth(aTmpSiz.Width() ); if (aTmpSiz.Height()!=0) aMaxSiz.setHeight(aTmpSiz.Height() ); if (bWdtGrow)
{
nMinWdt=GetMinTextFrameWidth();
nMaxWdt=GetMaxTextFrameWidth(); if (nMaxWdt==0 || nMaxWdt>aMaxSiz.Width()) nMaxWdt=aMaxSiz.Width(); if (nMinWdt<=0) nMinWdt=1;
aSiz.setWidth(nMaxWdt );
} if (bHgtGrow)
{
nMinHgt=GetMinTextFrameHeight();
nMaxHgt=GetMaxTextFrameHeight(); if (nMaxHgt==0 || nMaxHgt>aMaxSiz.Height()) nMaxHgt=aMaxSiz.Height(); if (nMinHgt<=0) nMinHgt=1;
aSiz.setHeight(nMaxHgt );
}
tools::Long nHDist=GetTextLeftDistance()+GetTextRightDistance();
tools::Long nVDist=GetTextUpperDistance()+GetTextLowerDistance();
aSiz.AdjustWidth( -nHDist );
aSiz.AdjustHeight( -nVDist ); if ( aSiz.Width() < 2 )
aSiz.setWidth( 2 ); // minimum size=2 if ( aSiz.Height() < 2 )
aSiz.setHeight( 2 ); // minimum size=2
if (HasText())
{ if(mpEditingOutliner)
{
mpEditingOutliner->SetMaxAutoPaperSize( aSiz ); if (bWdtGrow)
{
Size aSiz2(mpEditingOutliner->CalcTextSize());
nWdt=aSiz2.Width()+1; // a little more tolerance if (bHgtGrow) nHgt=aSiz2.Height()+1; // a little more tolerance
} else
{
nHgt=mpEditingOutliner->GetTextHeight()+1; // a little more tolerance
}
} else
{
Outliner& rOutliner=ImpGetDrawOutliner();
rOutliner.SetPaperSize(aSiz); // TODO: add the optimization with bPortionInfoChecked again.
OutlinerParaObject* pOutlinerParaObject = GetOutlinerParaObject(); if( pOutlinerParaObject != nullptr )
{
rOutliner.SetFixedCellHeight(
GetMergedItem(SDRATTR_TEXT_USEFIXEDCELLHEIGHT).GetValue());
rOutliner.SetText(*pOutlinerParaObject);
}
rOutliner.SetUpdateLayout(true); if ( bWdtGrow )
{
Size aSiz2(rOutliner.CalcTextSize());
nWdt=aSiz2.Width()+1; // a little more tolerance if ( bHgtGrow )
nHgt=aSiz2.Height()+1; // a little more tolerance
} else
{
nHgt = rOutliner.GetTextHeight()+1; // a little more tolerance
sal_Int16 nColumns = GetMergedItem(SDRATTR_TEXTCOLUMNS_NUMBER).GetValue(); if (bHgtGrow && nColumns > 1)
{ // Both 'resize shape to fix text' and multiple columns are enabled. The // first means a dynamic height, the second expects a fixed height. // Resolve this conflict by going with the original height.
nHgt = rR.getOpenHeight();
}
} // cleanup outliner
rOutliner.Clear();
rOutliner.SetFixedCellHeight(false);
}
} else
{
nHgt = m_aSuggestedTextFrameSize.Height();
nWdt = m_aSuggestedTextFrameSize.Width();
} if ( nWdt < nMinWdt )
nWdt = nMinWdt; if ( nWdt > nMaxWdt )
nWdt = nMaxWdt;
nWdt += nHDist; if ( nWdt < 1 )
nWdt = 1; // nHDist may also be negative if ( nHgt < nMinHgt )
nHgt = nMinHgt; if ( nHgt > nMaxHgt )
nHgt = nMaxHgt;
nHgt+=nVDist; if ( nHgt < 1 )
nHgt = 1; // nVDist may also be negative
tools::Long nWdtGrow = nWdt-(rR.Right()-rR.Left());
tools::Long nHgtGrow = nHgt-(rR.Bottom()-rR.Top()); if ( nWdtGrow == 0 )
bWdtGrow = false; if ( nHgtGrow == 0 )
bHgtGrow=false; if ( bWdtGrow || bHgtGrow || !m_aSuggestedTextFrameSize.IsEmpty())
{ if ( bWdtGrow || m_aSuggestedTextFrameSize.Width() )
{
SdrTextHorzAdjust eHAdj=GetTextHorizontalAdjust(); if (m_aSuggestedTextFrameSize.Width())
{
rR.SetRight(rR.Left() + m_aSuggestedTextFrameSize.Width());
} elseif ( eHAdj == SDRTEXTHORZADJUST_LEFT )
rR.AdjustRight(nWdtGrow ); elseif ( eHAdj == SDRTEXTHORZADJUST_RIGHT )
rR.AdjustLeft( -nWdtGrow ); else
{
tools::Long nWdtGrow2=nWdtGrow/2;
rR.AdjustLeft( -nWdtGrow2 );
rR.SetRight(rR.Left()+nWdt );
}
} if ( bHgtGrow || m_aSuggestedTextFrameSize.Height() )
{
SdrTextVertAdjust eVAdj=GetTextVerticalAdjust(); if (m_aSuggestedTextFrameSize.Height())
{
rR.SetBottom(rR.Top() + m_aSuggestedTextFrameSize.Height());
} elseif ( eVAdj == SDRTEXTVERTADJUST_TOP )
rR.AdjustBottom(nHgtGrow ); elseif ( eVAdj == SDRTEXTVERTADJUST_BOTTOM )
rR.AdjustTop( -nHgtGrow ); else
{
tools::Long nHgtGrow2=nHgtGrow/2;
rR.AdjustTop( -nHgtGrow2 );
rR.SetBottom(rR.Top()+nHgt );
}
} if ( maGeo.m_nRotationAngle )
{
Point aD1(rR.TopLeft());
aD1-=aR0.TopLeft();
Point aD2(aD1);
RotatePoint(aD2,Point(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
aD2-=aD1;
rR.Move(aD2.X(),aD2.Y());
} returntrue;
}
}
} returnfalse;
}
tools::Rectangle aOldTextRect(getRectangle()); // <- initial text rectangle
tools::Rectangle aNewTextRect(getRectangle()); // <- new text rectangle returned from the custom shape renderer,
GetTextBounds( aNewTextRect ); // it depends to the current logical shape size
tools::Rectangle aAdjustedTextRect( aNewTextRect ); // <- new text rectangle is being tested by AdjustTextFrameWidthAndHeight to ensure if ( AdjustTextFrameWidthAndHeight( aAdjustedTextRect, bHgt, bWdt ) ) // that the new text rectangle is matching the current text size from the outliner
{ if (aAdjustedTextRect != aNewTextRect && aOldTextRect != aAdjustedTextRect &&
aNewTextRect.GetWidth() && aNewTextRect.GetHeight())
{
aReturnValue = getRectangle(); double fXScale = static_cast<double>(aOldTextRect.GetWidth()) / static_cast<double>(aNewTextRect.GetWidth()); double fYScale = static_cast<double>(aOldTextRect.GetHeight()) / static_cast<double>(aNewTextRect.GetHeight()); double fRightDiff = static_cast<double>( aAdjustedTextRect.Right() - aNewTextRect.Right() ) * fXScale; double fLeftDiff = static_cast<double>( aAdjustedTextRect.Left() - aNewTextRect.Left() ) * fXScale; double fTopDiff = static_cast<double>( aAdjustedTextRect.Top() - aNewTextRect.Top() ) * fYScale; double fBottomDiff= static_cast<double>( aAdjustedTextRect.Bottom()- aNewTextRect.Bottom()) * fYScale;
aReturnValue.AdjustLeft(static_cast<sal_Int32>(fLeftDiff) );
aReturnValue.AdjustRight(static_cast<sal_Int32>(fRightDiff) );
aReturnValue.AdjustTop(static_cast<sal_Int32>(fTopDiff) );
aReturnValue.AdjustBottom(static_cast<sal_Int32>(fBottomDiff) );
}
} return aReturnValue;
}
// put text into the Outliner - if necessary the use the text from the EditOutliner
std::optional<OutlinerParaObject> pPara; if (GetOutlinerParaObject())
pPara = *GetOutlinerParaObject(); if (mpEditingOutliner && !bNoEditText)
pPara=mpEditingOutliner->CreateParaObject();
Point aTextPos(aAnkRect.TopLeft());
Size aTextSiz(rOutliner.GetPaperSize()); // GetPaperSize() has a little added tolerance, no?
// For draw objects containing text correct horizontal/vertical alignment if text is bigger // than the object itself. Without that correction, the text would always be // formatted to the left edge (or top edge when vertical) of the draw object.
if( !IsTextFrame() )
{ if(aAnkRect.GetWidth() < aTextSiz.Width() && !IsVerticalWriting())
{ // Horizontal case here. Correct only if eHAdj == SDRTEXTHORZADJUST_BLOCK, // else the alignment is wanted. if(SDRTEXTHORZADJUST_BLOCK == eHAdj)
{
SvxAdjust eAdjust = GetObjectItemSet().Get(EE_PARA_JUST).GetAdjust(); switch (eAdjust)
{ case SvxAdjust::Left: eHAdj = SDRTEXTHORZADJUST_LEFT; break; case SvxAdjust::Right: eHAdj = SDRTEXTHORZADJUST_RIGHT; break; case SvxAdjust::Center: eHAdj = SDRTEXTHORZADJUST_CENTER; break; default: break;
}
}
}
if(aAnkRect.GetHeight() < aTextSiz.Height() && IsVerticalWriting())
{ // Vertical case here. Correct only if eHAdj == SDRTEXTVERTADJUST_BLOCK, // else the alignment is wanted. if(SDRTEXTVERTADJUST_BLOCK == eVAdj)
{
eVAdj = SDRTEXTVERTADJUST_CENTER;
}
}
}
if (eHAdj==SDRTEXTHORZADJUST_CENTER || eHAdj==SDRTEXTHORZADJUST_RIGHT)
{
tools::Long nFreeWdt=aAnkRect.GetWidth()-aTextSiz.Width(); if (eHAdj==SDRTEXTHORZADJUST_CENTER)
aTextPos.AdjustX(nFreeWdt/2 ); if (eHAdj==SDRTEXTHORZADJUST_RIGHT)
aTextPos.AdjustX(nFreeWdt );
} if (eVAdj==SDRTEXTVERTADJUST_CENTER || eVAdj==SDRTEXTVERTADJUST_BOTTOM)
{
tools::Long nFreeHgt=aAnkRect.GetHeight()-aTextSiz.Height(); if (eVAdj==SDRTEXTVERTADJUST_CENTER)
aTextPos.AdjustY(nFreeHgt/2 ); if (eVAdj==SDRTEXTVERTADJUST_BOTTOM)
aTextPos.AdjustY(nFreeHgt );
} if (maGeo.m_nRotationAngle != 0_deg100)
RotatePoint(aTextPos,aAnkRect.TopLeft(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
if (pAnchorRect)
*pAnchorRect=aAnkRect;
// using rTextRect together with ContourFrame doesn't always work correctly
rTextRect=tools::Rectangle(aTextPos,aTextSiz);
}
if ( !mXRenderedCustomShape.is() )
{ // force CustomShape
GetSdrObjectFromCustomShape();
}
if ( mXRenderedCustomShape.is() )
{
pRenderedCustomShape = SdrObject::getSdrObjectFromXShape(mXRenderedCustomShape);
}
if ( pRenderedCustomShape )
{ // Clone to same SdrModel
rtl::Reference<SdrObject> pCandidate(pRenderedCustomShape->CloneSdrObject(pRenderedCustomShape->getSdrModelFromSdrObject()));
DBG_ASSERT(pCandidate, "SdrObjCustomShape::DoConvertToPolyObj: Could not clone SdrObject (!)");
pRetval = pCandidate->DoConvertToPolyObj(bBezier, bAddText);
pCandidate.clear();
if(nullptr != pNewPage)
{ // invalidating rectangles by SetRectsDirty is not sufficient, // AdjustTextFrameWidthAndHeight() also has to be made, both // actions are done by NbcSetSnapRect
tools::Rectangle aRectangle(getRectangle()); //creating temporary rectangle #i61108#
NbcSetSnapRect(aRectangle);
}
}
if (rMaxRect.IsEmpty() || rMaxRect == GetSnapRect()) return;
// Get a matrix, that would produce the existing shape, when applied to a unit square
basegfx::B2DPolyPolygon aPolyPolygon; //not used, but formal needed
basegfx::B2DHomMatrix aMatrix;
TRGetBaseGeometry(aMatrix, aPolyPolygon); // Using TRSetBaseGeometry(aMatrix, aPolyPolygon) would regenerate the current shape. But // applying aMatrix to a unit square will not generate the current shape. Scaling, // rotation and translation are correct, but shear angle has wrong sign. So break up // matrix and create a mathematically correct new one.
basegfx::B2DTuple aScale;
basegfx::B2DTuple aTranslate; double fRotate, fShearX;
aMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
basegfx::B2DHomMatrix aMathMatrix;
aMathMatrix = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
aScale,
-fShearX,
fRotate,
aTranslate);
// Calculate scaling factors from size of the transformed unit polygon as ersatz for the not // usable current snap rectangle.
basegfx::B2DPolygon aB2DPolygon(basegfx::utils::createUnitPolygon());
aB2DPolygon.transform(aMathMatrix);
basegfx::B2DRange aB2DRange(aB2DPolygon.getB2DRange()); double fPolygonWidth = aB2DRange.getWidth(); if (fPolygonWidth == 0)
fPolygonWidth = 1; double fPolygonHeight = aB2DRange.getHeight(); if (fPolygonHeight == 0)
fPolygonHeight = 1; constdouble aFactorX = static_cast<double>(rMaxRect.GetWidth()) / fPolygonWidth; constdouble aFactorY = static_cast<double>(rMaxRect.GetHeight()) / fPolygonHeight;
// Generate matrix, that would produce the desired rMaxRect when applied to unit square
aMathMatrix.scale(aFactorX, aFactorY);
aB2DPolygon = basegfx::utils::createUnitPolygon();
aB2DPolygon.transform(aMathMatrix);
aB2DRange = aB2DPolygon.getB2DRange(); constdouble fPolygonLeft = aB2DRange.getMinX(); constdouble fPolygonTop = aB2DRange.getMinY();
aMathMatrix.translate(rMaxRect.Left() - fPolygonLeft, rMaxRect.Top() - fPolygonTop);
// Create a Matrix from aMathMatrix, which is usable with TRSetBaseGeometry
aMathMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
aMatrix = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
aScale,
-fShearX,
fRotate,
aTranslate);
// Now use TRSetBaseGeometry to actually perform scale, shear, rotate and translate // on the shape. That considers gluepoints, interaction handles and text area, and includes // setting rectangles dirty and broadcast.
TRSetBaseGeometry(aMatrix, aPolyPolygon);
}
void SdrObjCustomShape::TRSetBaseGeometry(const basegfx::B2DHomMatrix& rMatrix, const basegfx::B2DPolyPolygon& /*rPolyPolygon*/)
{ // The shape might have already flipping in its enhanced geometry. LibreOffice applies // such after all transformations. We remove it, but remember it to apply them later. bool bIsMirroredX = IsMirroredX(); bool bIsMirroredY = IsMirroredY(); if (bIsMirroredX || bIsMirroredY)
{
Point aCurrentCenter = GetSnapRect().Center(); if (bIsMirroredX) // mirror on the y-axis
{
Mirror(aCurrentCenter, Point(aCurrentCenter.X(), aCurrentCenter.Y() + 1000));
} if (bIsMirroredY) // mirror on the x-axis
{
Mirror(aCurrentCenter, Point(aCurrentCenter.X() + 1000, aCurrentCenter.Y()));
}
}
// if anchor is used, make position relative to it if(getSdrModelFromSdrObject().IsWriter())
{ if(GetAnchorPos().X() || GetAnchorPos().Y())
{
aTranslate += basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
}
}
// scale
Size aSize(basegfx::fround<tools::Long>(fabs(aScale.getX())),
basegfx::fround<tools::Long>(fabs(aScale.getY()))); // fdo#47434 We need a valid rectangle here if( !aSize.Height() ) aSize.setHeight( 1 ); if( !aSize.Width() ) aSize.setWidth( 1 );
tools::Rectangle aBaseRect(Point(), aSize);
SetLogicRect(aBaseRect);
// Apply flipping from Matrix, which is a transformation relative to origin if (aScale.getX() < 0.0)
Mirror(Point(0, 0), Point(0, 1000)); // mirror on the y-axis if (aScale.getY() < 0.0)
Mirror(Point(0, 0), Point(1000, 0)); // mirror on the x-axis
// shear? if(!basegfx::fTools::equalZero(fShearX))
{
GeoStat aGeoStat; // #i123181# The fix for #121932# here was wrong, the trunk version does not correct the // mirrored shear values, neither at the object level, nor on the API or XML level. Taking // back the mirroring of the shear angle
aGeoStat.m_nShearAngle = Degree100(basegfx::fround(basegfx::rad2deg<100>(atan(fShearX))));
aGeoStat.RecalcTan();
Shear(Point(), aGeoStat.m_nShearAngle, aGeoStat.mfTanShearAngle, false);
}
// #i78696# // fRotate is mathematically correct, but aGeoStat.nRotationAngle is // mirrored -> mirror value here
aGeoStat.m_nRotationAngle = NormAngle36000(Degree100(basegfx::fround(-basegfx::rad2deg<100>(fRotate))));
aGeoStat.RecalcSinCos();
Rotate(Point(), aGeoStat.m_nRotationAngle, aGeoStat.mfSinRotationAngle, aGeoStat.mfCosRotationAngle);
}
// Apply flipping from enhanced geometry at center of the shape. if (!(bIsMirroredX || bIsMirroredY)) return;
// create mathematically matrix for the applied transformations // aScale was in most cases built from a rectangle including edge // and is therefore mathematically too large by 1 if (aScale.getX() > 2.0 && aScale.getY() > 2.0)
aScale -= basegfx::B2DTuple(1.0, 1.0);
basegfx::B2DHomMatrix aMathMat = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
aScale, -fShearX, fRotate,
aTranslate); // Use matrix to get current center
basegfx::B2DPoint aCenter(0.5,0.5);
aCenter = aMathMat * aCenter; double fCenterX = aCenter.getX(); double fCenterY = aCenter.getY(); if (bIsMirroredX) // vertical axis
Mirror(Point(basegfx::fround<tools::Long>(fCenterX), basegfx::fround<tools::Long>(fCenterY)),
Point(basegfx::fround<tools::Long>(fCenterX), basegfx::fround<tools::Long>(fCenterY + 1000.0))); if (bIsMirroredY) // horizontal axis
Mirror(Point(basegfx::fround<tools::Long>(fCenterX), basegfx::fround<tools::Long>(fCenterY)),
Point(basegfx::fround<tools::Long>(fCenterX + 1000.0), basegfx::fround<tools::Long>(fCenterY)));
}
// taking fObjectRotation instead of aGeo.nAngle bool SdrObjCustomShape::TRGetBaseGeometry(basegfx::B2DHomMatrix& rMatrix, basegfx::B2DPolyPolygon& /*rPolyPolygon*/) const
{ // get turn and shear double fRotate = basegfx::deg2rad(m_fObjectRotation); double fShearX = toRadians(maGeo.m_nShearAngle);
// get aRectangle, this is the unrotated snaprect
tools::Rectangle aRectangle(getRectangle());
bool bMirroredX = IsMirroredX(); bool bMirroredY = IsMirroredY(); if ( bMirroredX || bMirroredY )
{ // we have to retrieve the unmirrored rect
Point aRef1( ( aBoundRect.Left() + aBoundRect.Right() ) >> 1, aBoundRect.Top() );
Point aRef2( aRef1.X(), aRef1.Y() + 1000 );
sal_uInt16 i;
sal_uInt16 nPointCount=aPol.GetSize(); for (i=0; i<nPointCount; i++)
{
MirrorPoint(aPol[i],aRef1,aRef2);
} // mirror polygon and move it a bit
tools::Polygon aPol0(aPol);
aPol[0]=aPol0[1];
aPol[1]=aPol0[0];
aPol[2]=aPol0[3];
aPol[3]=aPol0[2];
aPol[4]=aPol0[1];
aRectangle = svx::polygonToRectangle(aPol, aNewGeo);
} if ( bMirroredY )
{
fShearX = -fShearX;
tools::Polygon aPol( Rect2Poly( aRectangle, aNewGeo ) );
tools::Rectangle aBoundRect( aPol.GetBoundRect() );
Point aRef1( aBoundRect.Left(), ( aBoundRect.Top() + aBoundRect.Bottom() ) >> 1 );
Point aRef2( aRef1.X() + 1000, aRef1.Y() );
sal_uInt16 i;
sal_uInt16 nPointCount=aPol.GetSize(); for (i=0; i<nPointCount; i++)
{
MirrorPoint(aPol[i],aRef1,aRef2);
} // mirror polygon and move it a bit
tools::Polygon aPol0(aPol);
aPol[0]=aPol0[1]; // This was WRONG for vertical (!)
aPol[1]=aPol0[0]; // #i121932# Despite my own comment above
aPol[2]=aPol0[3]; // it was *not* wrong even when the reordering
aPol[3]=aPol0[2]; // *seems* to be specific for X-Mirrorings. Oh
aPol[4]=aPol0[1]; // will I be happy when this old stuff is |gone| with aw080 (!)
aRectangle = svx::polygonToRectangle(aPol, aNewGeo);
}
}
// fill other values
basegfx::B2DTuple aScale(aRectangle.GetWidth(), aRectangle.GetHeight());
basegfx::B2DTuple aTranslate(aRectangle.Left(), aRectangle.Top());
// position may be relative to anchorpos, convert if(getSdrModelFromSdrObject().IsWriter())
{ if(GetAnchorPos().X() || GetAnchorPos().Y())
{
aTranslate -= basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.