Rebased ref, commits from common ancestor: commit d906e70d48d989f672af4b2398e28ea752976269 Author: Armin Le Grand <armin.le.gr...@cib.de> Date: Wed Oct 4 17:44:24 2017 +0200
RotGrfFlyFrame: Added interactive rotation mode The FlyFrames containing a graphic now support an interactive rotation mode. Added a rotation icon to the Toolbar close to right/left 90degree rotation. When activated, works as similar to draw object mode as possible. Shear and move of the rotation center is deactivated since not supported. It uses as much of the existing interaction stuff as possible. Change-Id: Ia1a4e5c064d8576b114c3fcf3a96ccb42c9372bb diff --git a/basegfx/source/matrix/b2dhommatrixtools.cxx b/basegfx/source/matrix/b2dhommatrixtools.cxx index 013bb7d23703..a2cdedf77b03 100644 --- a/basegfx/source/matrix/b2dhommatrixtools.cxx +++ b/basegfx/source/matrix/b2dhommatrixtools.cxx @@ -364,7 +364,8 @@ namespace basegfx { basegfx::B2DHomMatrix aRetval; - // RotGrfFlyFrame: Take rotation into account. Rotation is in 10th degrees + // RotGrfFlyFrame: Create a transformation that maps the range inside of itself + // so that it fits, takes as much space as possible and keeps the aspect ratio if(0.0 != fRotate) { // Fit rotated graphic to center of available space, keeping page ratio: diff --git a/include/svx/svdobj.hxx b/include/svx/svdobj.hxx index 3d7ded0a7fe9..cd6953312565 100644 --- a/include/svx/svdobj.hxx +++ b/include/svx/svdobj.hxx @@ -410,6 +410,10 @@ public: void SingleObjectPainter(OutputDevice& rOut) const; bool LineGeometryUsageIsNecessary() const; + // RotGrfFlyFrame: If true, this SdrObject supports only limited rotation, that + // means no change of the rotation point (only centered) and no shear allowed + virtual bool HasLimitedRotation() const; + // Returns a copy of the object. Every inherited class must reimplement this (in class Foo // it should be sufficient to do "virtual Foo* Clone() const { return CloneHelper< Foo >(); }". // Note that this function uses operator= internally. diff --git a/include/svx/svdovirt.hxx b/include/svx/svdovirt.hxx index a6223975572d..c6d626e1305f 100644 --- a/include/svx/svdovirt.hxx +++ b/include/svx/svdovirt.hxx @@ -68,6 +68,9 @@ public: virtual OUString TakeObjNameSingul() const override; virtual OUString TakeObjNamePlural() const override; + // RotGrfFlyFrame: If true, this SdrObject supports only limited rotation + virtual bool HasLimitedRotation() const override; + virtual basegfx::B2DPolyPolygon TakeXorPoly() const override; virtual sal_uInt32 GetHdlCount() const override; virtual SdrHdl* GetHdl(sal_uInt32 nHdlNum) const override; diff --git a/include/svx/svxids.hrc b/include/svx/svxids.hrc index 1c25b7e0b550..12fc7909cc3c 100644 --- a/include/svx/svxids.hrc +++ b/include/svx/svxids.hrc @@ -919,7 +919,7 @@ #define SID_ROTATE_GRAPHIC_LEFT ( SID_SVX_START + 1121 ) #define SID_ROTATE_GRAPHIC_RIGHT ( SID_SVX_START + 1122 ) #define SID_ROTATE_GRAPHIC_180 ( SID_SVX_START + 1123 ) -#define SID_ROTATE_GRAPHIC_RESET ( SID_SVX_START + 1092 ) /* RotGrfFlyFrame: new slot */ +#define SID_ROTATE_GRAPHIC_RESET ( SID_SVX_START + 1092 ) /* RotGrfFlyFrame: */ // new slots for panels #define SID_ATTR_FILL_TRANSPARENCE ( SID_SVX_START + 1124 ) diff --git a/svx/source/svdraw/svddrgmt.cxx b/svx/source/svdraw/svddrgmt.cxx index d7913114e7f0..dc1126fea867 100644 --- a/svx/source/svdraw/svddrgmt.cxx +++ b/svx/source/svdraw/svddrgmt.cxx @@ -2097,18 +2097,28 @@ bool SdrDragRotate::BeginSdrDrag() { SdrHdl* pH=GetHdlList().GetHdl(SdrHdlKind::Ref1); - if (pH!=nullptr) + if (nullptr != pH) { Show(); DragStat().Ref1()=pH->GetPos(); nAngle0=GetAngle(DragStat().GetStart()-DragStat().GetRef1()); return true; } - else + + // RotGrfFlyFrame: Support rotation around center *without* Ref1 (normally + // the rotation point) + const tools::Rectangle aLocalMarkRect(getSdrDragView().GetMarkedObjRect()); + + if(!aLocalMarkRect.IsEmpty()) { - OSL_FAIL("SdrDragRotate::BeginSdrDrag(): No reference point handle found."); - return false; + Show(); + DragStat().Ref1() = aLocalMarkRect.Center(); + nAngle0=GetAngle(DragStat().GetStart()-DragStat().GetRef1()); + return true; } + + OSL_FAIL("SdrDragRotate::BeginSdrDrag(): No reference point handle found."); + return false; } basegfx::B2DHomMatrix SdrDragRotate::getCurrentTransformation() diff --git a/svx/source/svdraw/svdmrkv.cxx b/svx/source/svdraw/svdmrkv.cxx index 73a79d4bb983..61286258cc0f 100644 --- a/svx/source/svdraw/svdmrkv.cxx +++ b/svx/source/svdraw/svdmrkv.cxx @@ -658,14 +658,22 @@ void SdrMarkView::SetMarkHandles(SfxViewShell* pOtherShell) const size_t nMarkCount=GetMarkedObjectCount(); bool bStdDrag=meDragMode==SdrDragMode::Move; bool bSingleTextObjMark=false; + bool bLimitedRotation(false); if (nMarkCount==1) { mpMarkedObj=GetMarkedObjectByIndex(0); - bSingleTextObjMark = - mpMarkedObj && - dynamic_cast<const SdrTextObj*>( mpMarkedObj) != nullptr && - static_cast<SdrTextObj*>(mpMarkedObj)->IsTextFrame(); + + if(nullptr != mpMarkedObj) + { + bSingleTextObjMark = + mpMarkedObj && + dynamic_cast<const SdrTextObj*>( mpMarkedObj) != nullptr && + static_cast<SdrTextObj*>(mpMarkedObj)->IsTextFrame(); + + // RotGrfFlyFrame: we may have limited rotation + bLimitedRotation = SdrDragMode::Rotate == meDragMode && mpMarkedObj->HasLimitedRotation(); + } } bool bFrmHdl=ImpIsFrameHandles(); @@ -831,27 +839,59 @@ void SdrMarkView::SetMarkHandles(SfxViewShell* pOtherShell) } else { - bool bWdt0=aRect.Left()==aRect.Right(); - bool bHgt0=aRect.Top()==aRect.Bottom(); + const bool bWdt0(aRect.Left() == aRect.Right()); + const bool bHgt0(aRect.Top() == aRect.Bottom()); + if (bWdt0 && bHgt0) { - maHdlList.AddHdl(new SdrHdl(aRect.TopLeft(),SdrHdlKind::UpperLeft)); + maHdlList.AddHdl(new SdrHdl(aRect.TopLeft(), SdrHdlKind::UpperLeft)); } else if (!bStdDrag && (bWdt0 || bHgt0)) { - maHdlList.AddHdl(new SdrHdl(aRect.TopLeft() ,SdrHdlKind::UpperLeft)); - maHdlList.AddHdl(new SdrHdl(aRect.BottomRight(),SdrHdlKind::LowerRight)); + maHdlList.AddHdl(new SdrHdl(aRect.TopLeft(), SdrHdlKind::UpperLeft)); + maHdlList.AddHdl(new SdrHdl(aRect.BottomRight(), SdrHdlKind::LowerRight)); } else { - if (!bWdt0 && !bHgt0) maHdlList.AddHdl(new SdrHdl(aRect.TopLeft() ,SdrHdlKind::UpperLeft)); - if ( !bHgt0) maHdlList.AddHdl(new SdrHdl(aRect.TopCenter() ,SdrHdlKind::Upper)); - if (!bWdt0 && !bHgt0) maHdlList.AddHdl(new SdrHdl(aRect.TopRight() ,SdrHdlKind::UpperRight)); - if (!bWdt0 ) maHdlList.AddHdl(new SdrHdl(aRect.LeftCenter() ,SdrHdlKind::Left )); - if (!bWdt0 ) maHdlList.AddHdl(new SdrHdl(aRect.RightCenter() ,SdrHdlKind::Right)); - if (!bWdt0 && !bHgt0) maHdlList.AddHdl(new SdrHdl(aRect.BottomLeft() ,SdrHdlKind::LowerLeft)); - if ( !bHgt0) maHdlList.AddHdl(new SdrHdl(aRect.BottomCenter(),SdrHdlKind::Lower)); - if (!bWdt0 && !bHgt0) maHdlList.AddHdl(new SdrHdl(aRect.BottomRight() ,SdrHdlKind::LowerRight)); + if (!bWdt0 && !bHgt0) + { + maHdlList.AddHdl(new SdrHdl(aRect.TopLeft(), SdrHdlKind::UpperLeft)); + } + + if (!bLimitedRotation && !bHgt0) + { + maHdlList.AddHdl(new SdrHdl(aRect.TopCenter(), SdrHdlKind::Upper)); + } + + if (!bWdt0 && !bHgt0) + { + maHdlList.AddHdl(new SdrHdl(aRect.TopRight(), SdrHdlKind::UpperRight)); + } + + if (!bLimitedRotation && !bWdt0) + { + maHdlList.AddHdl(new SdrHdl(aRect.LeftCenter(), SdrHdlKind::Left )); + } + + if (!bLimitedRotation && !bWdt0) + { + maHdlList.AddHdl(new SdrHdl(aRect.RightCenter(), SdrHdlKind::Right)); + } + + if (!bWdt0 && !bHgt0) + { + maHdlList.AddHdl(new SdrHdl(aRect.BottomLeft(), SdrHdlKind::LowerLeft)); + } + + if (!bLimitedRotation && !bHgt0) + { + maHdlList.AddHdl(new SdrHdl(aRect.BottomCenter(), SdrHdlKind::Lower)); + } + + if (!bWdt0 && !bHgt0) + { + maHdlList.AddHdl(new SdrHdl(aRect.BottomRight(), SdrHdlKind::LowerRight)); + } } } } @@ -945,7 +985,10 @@ void SdrMarkView::SetMarkHandles(SfxViewShell* pOtherShell) } // rotation point/axis of reflection - AddDragModeHdl(meDragMode); + if(!bLimitedRotation) + { + AddDragModeHdl(meDragMode); + } // sort handles maHdlList.Sort(); diff --git a/svx/source/svdraw/svdobj.cxx b/svx/source/svdraw/svdobj.cxx index e1257be84517..c829f4e8d75a 100644 --- a/svx/source/svdraw/svdobj.cxx +++ b/svx/source/svdraw/svdobj.cxx @@ -931,6 +931,12 @@ bool SdrObject::LineGeometryUsageIsNecessary() const return (eXLS != drawing::LineStyle_NONE); } +bool SdrObject::HasLimitedRotation() const +{ + // RotGrfFlyFrame: Default is false, support full rotation + return false; +} + SdrObject* SdrObject::Clone() const { return CloneHelper< SdrObject >(); diff --git a/svx/source/svdraw/svdovirt.cxx b/svx/source/svdraw/svdovirt.cxx index cbd89c4b69d5..dec33db72d2b 100644 --- a/svx/source/svdraw/svdovirt.cxx +++ b/svx/source/svdraw/svdovirt.cxx @@ -166,6 +166,12 @@ OUString SdrVirtObj::TakeObjNamePlural() const return sName.makeStringAndClear(); } +bool SdrVirtObj::HasLimitedRotation() const +{ + // RotGrfFlyFrame: If true, this SdrObject supports only limited rotation + return rRefObj.HasLimitedRotation(); +} + basegfx::B2DPolyPolygon SdrVirtObj::TakeXorPoly() const { basegfx::B2DPolyPolygon aPolyPolygon(rRefObj.TakeXorPoly()); diff --git a/sw/inc/fesh.hxx b/sw/inc/fesh.hxx index 0775d14cd852..1141a4ea9812 100644 --- a/sw/inc/fesh.hxx +++ b/sw/inc/fesh.hxx @@ -505,6 +505,9 @@ public: // Start cropping the selected image void StartCropImage(); + // RotGrfFlyFrame: check if RotationMode is possibe + bool IsRotationOfSwGrfNodePossible() const; + size_t IsObjSelected() const; ///< @return object count, but doesn't count the objects in groups. bool IsObjSelected( const SdrObject& rObj ) const; bool IsObjSameLevelWithMarked(const SdrObject* pObj) const; diff --git a/sw/sdi/grfsh.sdi b/sw/sdi/grfsh.sdi index 956a0c82b443..8451eae60eba 100644 --- a/sw/sdi/grfsh.sdi +++ b/sw/sdi/grfsh.sdi @@ -24,5 +24,14 @@ shell SwGrfShell : SwBaseShell { import TextGraphic; + + // RotGrfFlyFrame: need SID_OBJECT_ROTATE for FlyFrames with graphic + SID_OBJECT_ROTATE + [ + Export = FALSE; + ExecMethod = Execute ; + StateMethod = GetAttrState ; + DisableFlags="SfxDisableFlags::SwOnProtectedCursor"; + ] } diff --git a/sw/source/core/draw/dflyobj.cxx b/sw/source/core/draw/dflyobj.cxx index 32b59b036178..34e13c370fdb 100644 --- a/sw/source/core/draw/dflyobj.cxx +++ b/sw/source/core/draw/dflyobj.cxx @@ -49,6 +49,7 @@ #include "rootfrm.hxx" #include "wrtsh.hxx" #include <ndgrf.hxx> +#include <frmmgr.hxx> #include <svx/sdr/properties/defaultproperties.hxx> #include <basegfx/range/b2drange.hxx> @@ -354,6 +355,64 @@ basegfx::B2DRange SwVirtFlyDrawObj::getInnerBound() const return aInnerRange; } +bool SwVirtFlyDrawObj::ContainsSwGrfNode() const +{ + // RotGrfFlyFrame: Check if this is a SwGrfNode + const SwFlyFrame* pFlyFrame(GetFlyFrame()); + + if(nullptr != pFlyFrame && pFlyFrame->Lower() && pFlyFrame->Lower()->IsNoTextFrame()) + { + const SwContentFrame* pCntFr(static_cast<const SwContentFrame*>(pFlyFrame->Lower())); + + if(nullptr != pCntFr) + { + const SwGrfNode* pGrfNd(pCntFr->GetNode()->GetGrfNode()); + + return nullptr != pGrfNd; + } + } + + return false; +} + +bool SwVirtFlyDrawObj::HasLimitedRotation() const +{ + // RotGrfFlyFrame: If true, this SdrObject supports only limited rotation. + // This is the case for SwGrfNode instances + return ContainsSwGrfNode(); +} + +void SwVirtFlyDrawObj::Rotate(const Point& rRef, long nAngle, double sn, double cs) +{ + if(ContainsSwGrfNode()) + { + // RotGrfFlyFrame: Here is where the positively completed rotate interaction is executed. + // Rotation is in 1/100th degree and may be signed (!) + nAngle /= 10; + + while(nAngle < 0) + { + nAngle += 3600; + } + + if(0 != nAngle) + { + // RotGrfFlyFrame: Add transformation to placeholder object + Size aSize; + const sal_uInt16 nOldRot(SwVirtFlyDrawObj::getPossibleRotationFromFraphicFrame(aSize)); + SwWrtShell *pSh = dynamic_cast<SwWrtShell*>( GetFlyFrame()->getRootFrame()->GetCurrShell() ); + SwFlyFrameAttrMgr aMgr(false, pSh, Frmmgr_Type::NONE); + + aMgr.SetRotation(nOldRot, (nOldRot + static_cast<sal_uInt16>(nAngle)) % 3600, aSize); + } + } + else + { + // call parent + SdrVirtObj::Rotate(rRef, nAngle, sn, cs); + } +} + sdr::contact::ViewContact* SwVirtFlyDrawObj::CreateObjectSpecificViewContact() { // need an own ViewContact (VC) to allow creation of a specialized primitive @@ -477,7 +536,9 @@ void SwVirtFlyDrawObj::TakeObjInfo( SdrObjTransformInfoRec& rInfo ) const rInfo.bMoveAllowed = rInfo.bResizeFreeAllowed = rInfo.bResizePropAllowed = true; - rInfo.bRotateFreeAllowed = rInfo.bRotate90Allowed = + // RotGrfFlyFrame: Some rotation may be allowed + rInfo.bRotateFreeAllowed = rInfo.bRotate90Allowed = HasLimitedRotation(); + rInfo.bMirrorFreeAllowed = rInfo.bMirror45Allowed = rInfo.bMirror90Allowed = rInfo.bShearAllowed = rInfo.bCanConvToPath = rInfo.bCanConvToPoly = @@ -924,7 +985,7 @@ void SwVirtFlyDrawObj::Crop(const Point& rRef, const Fraction& xFact, const Frac } // RotGrfFlyFrame: Helper to access possible rotation of Graphic contained in FlyFrame -sal_uInt16 SwVirtFlyDrawObj::getPossibleRotationFromFraphicFrame() const +sal_uInt16 SwVirtFlyDrawObj::getPossibleRotationFromFraphicFrame(Size& rSize) const { sal_uInt16 nRetval(0); const SwNoTextFrame* pNoTx = dynamic_cast< const SwNoTextFrame* >(GetFlyFrame()->Lower()); @@ -939,6 +1000,7 @@ sal_uInt16 SwVirtFlyDrawObj::getPossibleRotationFromFraphicFrame() const const SwAttrSet& rSet = pGrfNd->GetSwAttrSet(); const SwRotationGrf& rRotation = rSet.GetRotationGrf(); + rSize = rRotation.GetUnrotatedSize(); nRetval = rRotation.GetValue(); } } @@ -951,25 +1013,19 @@ SdrObject* SwVirtFlyDrawObj::getFullDragClone() const // call parent SdrObject* pRetval = SdrVirtObj::getFullDragClone(); - if(pRetval) + if(pRetval && ContainsSwGrfNode()) { // RotGrfFlyFrame: Add transformation to placeholder object - const sal_uInt16 nRotation(SwVirtFlyDrawObj::getPossibleRotationFromFraphicFrame()); - - if(0 != nRotation) - { - const double fRotate(static_cast< double >(-nRotation) * (M_PI/1800.0)); - const tools::Rectangle aTmpOutRect(GetFlyFrame()->Frame().SVRect()); - const basegfx::B2DRange aTargetRange( - aTmpOutRect.Left(), aTmpOutRect.Top(), - aTmpOutRect.Right(), aTmpOutRect.Bottom()); - const basegfx::B2DHomMatrix aTargetTransform( - basegfx::tools::createRotateAroundCenterKeepAspectRatioStayInsideRange( - aTargetRange, - fRotate)); + Size aSize; + const sal_uInt16 nRotation(SwVirtFlyDrawObj::getPossibleRotationFromFraphicFrame(aSize)); + const double fRotate(static_cast< double >(-nRotation) * (M_PI/1800.0)); + const basegfx::B2DRange aTargetRange(getInnerBound()); + const basegfx::B2DHomMatrix aTargetTransform( + basegfx::tools::createRotateAroundCenterKeepAspectRatioStayInsideRange( + aTargetRange, + fRotate)); - pRetval->TRSetBaseGeometry(aTargetTransform, basegfx::B2DPolyPolygon()); - } + pRetval->TRSetBaseGeometry(aTargetTransform, basegfx::B2DPolyPolygon()); } return pRetval; @@ -987,7 +1043,8 @@ void SwVirtFlyDrawObj::addCropHandles(SdrHdlList& rTarget) const if(!aTargetRange.isEmpty()) { - const sal_uInt16 nRotation(SwVirtFlyDrawObj::getPossibleRotationFromFraphicFrame()); + Size aSize; + const sal_uInt16 nRotation(SwVirtFlyDrawObj::getPossibleRotationFromFraphicFrame(aSize)); const double fRotate(static_cast< double >(-nRotation) * (M_PI/1800.0)); const basegfx::B2DHomMatrix aTargetTransform( basegfx::tools::createRotateAroundCenterKeepAspectRatioStayInsideRange( diff --git a/sw/source/core/draw/dview.cxx b/sw/source/core/draw/dview.cxx index 5bf7304acf39..f437c4274f06 100644 --- a/sw/source/core/draw/dview.cxx +++ b/sw/source/core/draw/dview.cxx @@ -44,6 +44,7 @@ #include "doc.hxx" #include "mdiexp.hxx" #include <ndole.hxx> +#include <ndgrf.hxx> #include <fmtanchr.hxx> #include "shellres.hxx" #include <IDocumentUndoRedo.hxx> @@ -814,6 +815,8 @@ void SwDrawView::CheckPossibilities() const SdrMarkList &rMrkList = GetMarkedObjectList(); bool bProtect = false; bool bSzProtect = false; + bool bRotate(false); + for ( size_t i = 0; !bProtect && i < rMrkList.GetMarkCount(); ++i ) { const SdrObject *pObj = rMrkList.GetMark( i )->GetMarkedSdrObj(); @@ -826,10 +829,18 @@ void SwDrawView::CheckPossibilities() pFrame = pFly->GetAnchorFrame(); if ( pFly->Lower() && pFly->Lower()->IsNoTextFrame() ) { - SwOLENode *pNd = const_cast<SwContentFrame*>(static_cast<const SwContentFrame*>(pFly->Lower()))->GetNode()->GetOLENode(); - if ( pNd ) + // SwNoTextNode& rNoTNd = const_cast<SwNoTextNode&>(*static_cast<const SwNoTextNode*>((static_cast<const SwContentFrame*>(pFly->Lower()))->GetNode())); + // SwGrfNode* pGrfNd = rNoTNd.GetGrfNode(); + // SwOLENode* pOLENd = rNoTNd.GetOLENode(); + + const SwContentFrame* pCntFr = const_cast<SwContentFrame*>(static_cast<const SwContentFrame*>(pFly->Lower())); + const SwOLENode* pOLENd = pCntFr->GetNode()->GetOLENode(); + const SwGrfNode* pGrfNd = pCntFr->GetNode()->GetGrfNode(); + + if ( pOLENd ) { - uno::Reference < embed::XEmbeddedObject > xObj = pNd->GetOLEObj().GetOleRef(); + const uno::Reference < embed::XEmbeddedObject > xObj = const_cast< SwOLEObj& >(pOLENd->GetOLEObj()).GetOleRef(); + if ( xObj.is() ) { // --> improvement for the future, when more @@ -847,6 +858,13 @@ void SwDrawView::CheckPossibilities() bMoveProtect = true; } } + else if(pGrfNd) + { + // RotGrfFlyFrame: GraphicNode allows rotation(s). The loop ew are in stops + // as soon as bMoveProtect is set, but since rotation is valid only with + // a single object selected this makes no difference + bRotate = true; + } } } } @@ -874,6 +892,10 @@ void SwDrawView::CheckPossibilities() } bMoveProtect |= bProtect; bResizeProtect |= bProtect || bSzProtect; + + // RotGrfFlyFrame: allow rotation when SwGrfNode is selected and not size protected + bRotateFreeAllowed |= bRotate && !bProtect; + bRotate90Allowed |= bRotateFreeAllowed; } /// replace marked <SwDrawVirtObj>-objects by its reference object for delete marked objects. diff --git a/sw/source/core/frmedt/feshview.cxx b/sw/source/core/frmedt/feshview.cxx index a7838151adff..5fd1addda398 100644 --- a/sw/source/core/frmedt/feshview.cxx +++ b/sw/source/core/frmedt/feshview.cxx @@ -1161,6 +1161,29 @@ bool SwFEShell::IsObjSelected( const SdrObject& rObj ) const return Imp()->GetDrawView()->IsObjMarked( &rObj ); } +bool SwFEShell::IsRotationOfSwGrfNodePossible() const +{ + // RotGrfFlyFrame: check if RotationMode is possibe + const SdrView *pSdrView = Imp()->GetDrawView(); + + if(pSdrView) + { + const SdrMarkList& rList(pSdrView->GetMarkedObjectList()); + + if(1 == rList.GetMarkCount()) + { + const SwVirtFlyDrawObj* pVirtFlyDraw(dynamic_cast< const SwVirtFlyDrawObj* >(rList.GetMark(0)->GetMarkedSdrObj())); + + if(nullptr != pVirtFlyDraw) + { + return pVirtFlyDraw->ContainsSwGrfNode(); + } + } + } + + return false; +} + bool SwFEShell::IsObjSameLevelWithMarked(const SdrObject* pObj) const { if (pObj) diff --git a/sw/source/core/inc/dflyobj.hxx b/sw/source/core/inc/dflyobj.hxx index 528edc8aff39..8766b7980ebc 100644 --- a/sw/source/core/inc/dflyobj.hxx +++ b/sw/source/core/inc/dflyobj.hxx @@ -60,7 +60,7 @@ private: // RotGrfFlyFrame: Helper to acces sthe rotation angle (in 10th degrees, left-handed) // of a GraphicFrame - sal_uInt16 getPossibleRotationFromFraphicFrame() const; + sal_uInt16 getPossibleRotationFromFraphicFrame(Size& rSize) const; protected: // AW: Need own sdr::contact::ViewContact since AnchorPos from parent is @@ -77,6 +77,8 @@ public: basegfx::B2DRange getOuterBound() const; basegfx::B2DRange getInnerBound() const; + // RotGrfFlyFrame: Check if this is a SwGrfNode + bool ContainsSwGrfNode() const; SwVirtFlyDrawObj(SdrObject& rNew, SwFlyFrame* pFly); virtual ~SwVirtFlyDrawObj() override; @@ -105,6 +107,7 @@ public: const Fraction& yFact, bool bUnsetRelative = true) override; virtual void Crop(const Point& rRef, const Fraction& xFact, const Fraction& yFact) override; virtual void addCropHandles(SdrHdlList& rTarget) const override; + virtual void Rotate(const Point& rRef, long nAngle, double sn, double cs) override; // FullDrag support virtual SdrObject* getFullDragClone() const override; @@ -122,6 +125,9 @@ public: virtual bool HasMacro() const override; virtual SdrObject* CheckMacroHit (const SdrObjMacroHitRec& rRec) const override; virtual Pointer GetMacroPointer (const SdrObjMacroHitRec& rRec) const override; + + // RotGrfFlyFrame: If true, this SdrObject supports only limited rotation. + virtual bool HasLimitedRotation() const override; }; #endif diff --git a/sw/source/uibase/frmdlg/frmmgr.cxx b/sw/source/uibase/frmdlg/frmmgr.cxx index b4956cfae92b..a08f441f948b 100644 --- a/sw/source/uibase/frmdlg/frmmgr.cxx +++ b/sw/source/uibase/frmdlg/frmmgr.cxx @@ -577,9 +577,9 @@ void SwFlyFrameAttrMgr::SetHeightSizeType( SwFrameSize eType ) void SwFlyFrameAttrMgr::SetRotation(sal_uInt16 nOld, sal_uInt16 nNew, const Size& rUnrotatedSize) { // RotGrfFlyFrame: Central handling of real change of rotation here, all adaptions use this. - // Adaption of pos/size may be wanted in the future. Already tried to keep last SIze in + // Adaption of pos/size may be wanted in the future. Already tried to keep last Size in // UnrotatedSize in the SwRotationGrf Item, but this will lead to various problems. Also tried - // to use m_aSet.Put(...) as in other methods (also read methods for Rotation/UnrotatedSize) but + // to use m_aSet.Put(...) as in other methods (also tried read methods for Rotation/UnrotatedSize) but // somehow the needed ID (RES_GRFATR_ROTATION) is *not* in the SfxItemSet of the Frame, so for // now set directly. Undo/Redo is preserved by AttributeChange if(nOld != nNew) diff --git a/sw/source/uibase/shells/grfsh.cxx b/sw/source/uibase/shells/grfsh.cxx index 29251d3dc5b2..01fc01acb9c3 100644 --- a/sw/source/uibase/shells/grfsh.cxx +++ b/sw/source/uibase/shells/grfsh.cxx @@ -123,6 +123,27 @@ void SwGrfShell::Execute(SfxRequest &rReq) sal_uInt16 nSlot = rReq.GetSlot(); switch(nSlot) { + case SID_OBJECT_ROTATE: + { + // RotGrfFlyFrame: start rotation when possible + SdrView* pSdrView = rSh.GetDrawViewWithValidMarkList(); + + if(rSh.IsRotationOfSwGrfNodePossible() && pSdrView->IsRotateAllowed()) + { + if(GetView().IsDrawRotate()) + { + rSh.SetDragMode(SdrDragMode::Move); + } + else + { + rSh.SetDragMode(SdrDragMode::Rotate); + } + + GetView().FlipDrawRotate(); + } + } + break; + case SID_TWAIN_TRANSFER: { GetView().ExecuteScan( rReq ); @@ -721,6 +742,23 @@ void SwGrfShell::GetAttrState(SfxItemSet &rSet) bool bDisable = bParentCntProt; switch( nWhich ) { + case SID_OBJECT_ROTATE: + { + // RotGrfFlyFrame: steer rotation state + const bool bIsRotate(GetView().IsDrawRotate()); + SdrView* pSdrView = rSh.GetDrawViewWithValidMarkList(); + + if(!bIsRotate && !pSdrView->IsRotateAllowed()) + { + rSet.DisableItem(nWhich); + } + else + { + rSet.Put(SfxBoolItem(nWhich, bIsRotate)); + } + + break; + } case SID_INSERT_GRAPHIC: case FN_FORMAT_GRAFIC_DLG: case SID_TWAIN_TRANSFER: diff --git a/sw/uiconfig/swriter/toolbar/graphicobjectbar.xml b/sw/uiconfig/swriter/toolbar/graphicobjectbar.xml index c1ba2219af1b..6494546daebd 100644 --- a/sw/uiconfig/swriter/toolbar/graphicobjectbar.xml +++ b/sw/uiconfig/swriter/toolbar/graphicobjectbar.xml @@ -28,6 +28,7 @@ <toolbar:toolbaritem xlink:href=".uno:FlipHorizontal"/> <toolbar:toolbaritem xlink:href=".uno:RotateLeft"/> <toolbar:toolbaritem xlink:href=".uno:RotateRight"/> + <toolbar:toolbaritem xlink:href=".uno:ToggleObjectRotateMode"/> <toolbar:toolbaritem xlink:href=".uno:Rotate180" toolbar:visible="false"/> <toolbar:toolbaritem xlink:href=".uno:RotateReset" toolbar:visible="false"/> <toolbar:toolbarseparator/> commit e50b01ef4cc58bba64a41a29b83f429fdd079f25 Author: Armin Le Grand <armin.le.gr...@cib.de> Date: Fri Sep 29 12:35:44 2017 +0200 RotGrfFlyFrame: Corrected position for CropHandles Position was taken from OuterBound FlyFrame, even in current master which is wrong. There can be a distance defined between InnerBound and OuterBound that has to be taken into account Change-Id: Id88f99c0b218bd26fa1daa5e8215eced00c0baa6 diff --git a/sw/source/core/draw/dflyobj.cxx b/sw/source/core/draw/dflyobj.cxx index 770788d5d4dc..32b59b036178 100644 --- a/sw/source/core/draw/dflyobj.cxx +++ b/sw/source/core/draw/dflyobj.cxx @@ -48,6 +48,7 @@ #include "pagefrm.hxx" #include "rootfrm.hxx" #include "wrtsh.hxx" +#include <ndgrf.hxx> #include <svx/sdr/properties/defaultproperties.hxx> #include <basegfx/range/b2drange.hxx> @@ -958,10 +959,10 @@ SdrObject* SwVirtFlyDrawObj::getFullDragClone() const if(0 != nRotation) { const double fRotate(static_cast< double >(-nRotation) * (M_PI/1800.0)); - const tools::Rectangle aOutRect(GetFlyFrame()->Frame().SVRect()); + const tools::Rectangle aTmpOutRect(GetFlyFrame()->Frame().SVRect()); const basegfx::B2DRange aTargetRange( - aOutRect.Left(), aOutRect.Top(), - aOutRect.Right(), aOutRect.Bottom()); + aTmpOutRect.Left(), aTmpOutRect.Top(), + aTmpOutRect.Right(), aTmpOutRect.Bottom()); const basegfx::B2DHomMatrix aTargetTransform( basegfx::tools::createRotateAroundCenterKeepAspectRatioStayInsideRange( aTargetRange, @@ -979,10 +980,10 @@ void SwVirtFlyDrawObj::addCropHandles(SdrHdlList& rTarget) const // RotGrfFlyFrame: Adapt to possible rotated Graphic contained in FlyFrame if(GetFlyFrame()->Frame().HasArea()) { - const tools::Rectangle aOutRect(GetFlyFrame()->Frame().SVRect()); - const basegfx::B2DRange aTargetRange( - aOutRect.Left(), aOutRect.Top(), - aOutRect.Right(), aOutRect.Bottom()); + // Use InnerBound, OuterBound (same as GetFlyFrame()->Frame().SVRect()) + // may have a distance to InnerBound which needs to be taken into acocunt. + // The Graphic is mapped to InnerBound, as is the rotated Graphic. + const basegfx::B2DRange aTargetRange(getInnerBound()); if(!aTargetRange.isEmpty()) { diff --git a/sw/source/core/layout/paintfrm.cxx.orig b/sw/source/core/layout/paintfrm.cxx.orig new file mode 100644 index 000000000000..351ac1a8ba35 --- /dev/null +++ b/sw/source/core/layout/paintfrm.cxx.orig @@ -0,0 +1,7565 @@ +/* -*- 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/lazydelete.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/progress.hxx> +#include <editeng/brushitem.hxx> +#include <editeng/prntitem.hxx> +#include <editeng/boxitem.hxx> +#include <editeng/shaditem.hxx> +#include <svx/framelink.hxx> +#include <drawdoc.hxx> +#include <tgrditem.hxx> +#include <calbck.hxx> +#include <fmtsrnd.hxx> +#include <fmtclds.hxx> +#include <strings.hrc> +#include <swmodule.hxx> +#include <rootfrm.hxx> +#include <pagefrm.hxx> +#include <section.hxx> +#include <sectfrm.hxx> +#include <viewimp.hxx> +#include <dflyobj.hxx> +#include <flyfrm.hxx> +#include <viewopt.hxx> +#include <dview.hxx> +#include <dcontact.hxx> +#include <txtfrm.hxx> +#include <ftnfrm.hxx> +#include <tabfrm.hxx> +#include <rowfrm.hxx> +#include <cellfrm.hxx> +#include <notxtfrm.hxx> +#include <layact.hxx> +#include <pagedesc.hxx> +#include <ptqueue.hxx> +#include <noteurl.hxx> +#include <virtoutp.hxx> +#include <lineinfo.hxx> +#include <dbg_lay.hxx> +#include <docsh.hxx> +#include <svx/svdogrp.hxx> +#include <sortedobjs.hxx> +#include <EnhancedPDFExportHelper.hxx> +#include <bodyfrm.hxx> +#include <hffrm.hxx> +#include <colfrm.hxx> +#include <svx/sdr/contact/viewobjectcontactredirector.hxx> +#include <svx/sdr/contact/viewobjectcontact.hxx> +#include <svx/sdr/contact/viewcontact.hxx> +#include <DocumentSettingManager.hxx> +#include <IDocumentDeviceAccess.hxx> + +#include <ndole.hxx> +#include <PostItMgr.hxx> +#include <FrameControlsManager.hxx> +#include <vcl/settings.hxx> + +#include <svx/sdr/attribute/sdrallfillattributeshelper.hxx> +#include <drawinglayer/processor2d/processor2dtools.hxx> + +#include <svtools/borderhelper.hxx> + +#include "bitmaps.hlst" +#include <drawinglayer/primitive2d/polygonprimitive2d.hxx> +#include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx> +#include <drawinglayer/primitive2d/borderlineprimitive2d.hxx> +#include <drawinglayer/primitive2d/discreteshadowprimitive2d.hxx> +#include <drawinglayer/primitive2d/maskprimitive2d.hxx> +#include <drawinglayer/primitive2d/textprimitive2d.hxx> +#include <drawinglayer/primitive2d/textlayoutdevice.hxx> +#include <drawinglayer/processor2d/processorfromoutputdevice.hxx> +#include <svx/unoapi.hxx> +#include <svx/framelinkarray.hxx> +#include <comphelper/sequence.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> +#include <basegfx/color/bcolortools.hxx> + +#include <memory> +#include <vector> +#include <algorithm> +#include <wrtsh.hxx> +#include <edtwin.hxx> +#include <view.hxx> +#include <paintfrm.hxx> +#include <o3tl/typed_flags_set.hxx> + +#include <vcl/BitmapTools.hxx> + +#define COL_NOTES_SIDEPANE RGB_COLORDATA(230,230,230) +#define COL_NOTES_SIDEPANE_BORDER RGB_COLORDATA(200,200,200) +#define COL_NOTES_SIDEPANE_SCROLLAREA RGB_COLORDATA(230,230,220) + +using namespace ::editeng; +using namespace ::com::sun::star; +using ::drawinglayer::primitive2d::BorderLinePrimitive2D; +using ::drawinglayer::primitive2d::BorderLine; +using std::pair; +using std::make_pair; + +struct SwPaintProperties; + +//other subsidiary lines enabled? +#define IS_SUBS (!gProp.pSGlobalShell->GetViewOptions()->IsPagePreview() && \ + !gProp.pSGlobalShell->GetViewOptions()->IsReadonly() && \ + !gProp.pSGlobalShell->GetViewOptions()->IsFormView() &&\ + !gProp.pSGlobalShell->GetViewOptions()->IsWhitespaceHidden() &&\ + SwViewOption::IsDocBoundaries()) +//subsidiary lines for sections +#define IS_SUBS_SECTION (!gProp.pSGlobalShell->GetViewOptions()->IsPagePreview() && \ + !gProp.pSGlobalShell->GetViewOptions()->IsReadonly()&&\ + !gProp.pSGlobalShell->GetViewOptions()->IsFormView() &&\ + SwViewOption::IsSectionBoundaries()) +#define IS_SUBS_FLYS (!gProp.pSGlobalShell->GetViewOptions()->IsPagePreview() && \ + !gProp.pSGlobalShell->GetViewOptions()->IsReadonly()&&\ + !gProp.pSGlobalShell->GetViewOptions()->IsFormView() &&\ + SwViewOption::IsObjectBoundaries()) + +//Class declaration; here because they are only used in this file +enum class SubColFlags { + Page = 0x01, //Helplines of the page + Tab = 0x08, //Helplines inside tables + Fly = 0x10, //Helplines inside fly frames + Sect = 0x20, //Helplines inside sections +}; +namespace o3tl { + template<> struct typed_flags<SubColFlags> : is_typed_flags<SubColFlags, 0x39> {}; +} + +// Classes collecting the border lines and help lines +class SwLineRect : public SwRect +{ + Color aColor; + SvxBorderLineStyle nStyle; + const SwTabFrame *pTab; + SubColFlags nSubColor; //colorize subsidiary lines + bool bPainted; //already painted? + sal_uInt8 nLock; //To distinguish the line and the hell layer. +public: + SwLineRect( const SwRect &rRect, const Color *pCol, const SvxBorderLineStyle nStyle, + const SwTabFrame *pT , const SubColFlags nSCol ); + + const Color& GetColor() const { return aColor;} + SvxBorderLineStyle GetStyle() const { return nStyle; } + const SwTabFrame *GetTab() const { return pTab; } + void SetPainted() { bPainted = true; } + void Lock( bool bLock ) { if ( bLock ) + ++nLock; + else if ( nLock ) + --nLock; + } + bool IsPainted() const { return bPainted; } + bool IsLocked() const { return nLock != 0; } + SubColFlags GetSubColor() const { return nSubColor;} + + bool MakeUnion( const SwRect &rRect, SwPaintProperties const &properties ); +}; + +#ifdef IOS +static void dummy_function() +{ + pid_t pid = getpid(); + (void) pid; +} +#endif + +class SwLineRects +{ +public: + std::vector< SwLineRect > aLineRects; + typedef std::vector< SwLineRect >::const_iterator const_iterator; + typedef std::vector< SwLineRect >::iterator iterator; + typedef std::vector< SwLineRect >::reverse_iterator reverse_iterator; + typedef std::vector< SwLineRect >::size_type size_type; + size_t nLastCount; //avoid unnecessary cycles in PaintLines + SwLineRects() : nLastCount( 0 ) + { +#ifdef IOS + // Work around what is either a compiler bug in Xcode 5.1.1, + // or some unknown problem in this file. If I ifdef out this + // call, I get a crash in SwSubsRects::PaintSubsidiary: the + // address of the rLi reference variable is claimed to be + // 0x4000000! + dummy_function(); +#endif + } + void AddLineRect( const SwRect& rRect, const Color *pColor, const SvxBorderLineStyle nStyle, + const SwTabFrame *pTab, const SubColFlags nSCol, SwPaintProperties const &properties ); + void ConnectEdges( OutputDevice const *pOut, SwPaintProperties const &properties ); + void PaintLines ( OutputDevice *pOut, SwPaintProperties const &properties ); + void LockLines( bool bLock ); + + //Limit lines to 100 + bool isFull() const { return aLineRects.size()>100; } +}; + +class SwSubsRects : public SwLineRects +{ + void RemoveSuperfluousSubsidiaryLines( const SwLineRects &rRects, SwPaintProperties const &properties ); +public: + void PaintSubsidiary( OutputDevice *pOut, const SwLineRects *pRects, SwPaintProperties const &properties ); +}; + +class BorderLines +{ + drawinglayer::primitive2d::Primitive2DContainer m_Lines; +public: + void AddBorderLine(const drawinglayer::primitive2d::Primitive2DReference& rLine); + drawinglayer::primitive2d::Primitive2DContainer GetBorderLines_Clear() + { + drawinglayer::primitive2d::Primitive2DContainer lines; + lines.swap(m_Lines); + return lines; + } +}; + +// Default zoom factor +const static double aMinDistScale = 0.73; +const static double aEdgeScale = 0.5; + +//To optimize the expensive RetouchColor determination +Color aGlobalRetoucheColor; + +namespace sw +{ +Color* GetActiveRetoucheColor() +{ + return &aGlobalRetoucheColor; +} +} + +/** + * Container for static properties + */ +struct SwPaintProperties { + // Only repaint the Fly content as well as the background of the Fly content if + // a metafile is taken of the Fly. + bool bSFlyMetafile; + VclPtr<OutputDevice> pSFlyMetafileOut; + SwViewShell *pSGlobalShell; + + // Retouch for transparent Flys is done by the background of the Flys. + // The Fly itself should certainly not be spared out. See PaintBackground and + // lcl_SubtractFlys() + SwFlyFrame *pSRetoucheFly; + SwFlyFrame *pSRetoucheFly2; + SwFlyFrame *pSFlyOnlyDraw; + + // The borders will be collected in pSLines during the Paint and later + // possibly merge them. + // The help lines will be collected and merged in gProp.pSSubsLines. These will + // be compared with pSLines before the work in order to avoid help lines + // to hide borders. + BorderLines *pBLines; + SwLineRects *pSLines; + SwSubsRects *pSSubsLines; + + // global variable for sub-lines of body, header, footer, section and footnote frames. + SwSubsRects *pSSpecSubsLines; + SfxProgress *pSProgress; + + // Sizes of a pixel and the corresponding halves. Will be reset when + // entering SwRootFrame::Paint + long nSPixelSzW; + long nSPixelSzH; + long nSHalfPixelSzW; + long nSHalfPixelSzH; + long nSMinDistPixelW; + long nSMinDistPixelH; + + Color aSGlobalRetoucheColor; + + // Current zoom factor + double aSScaleX; + double aSScaleY; + + SwPaintProperties() + : bSFlyMetafile(false) + , pSFlyMetafileOut(nullptr) + , pSGlobalShell(nullptr) + , pSRetoucheFly(nullptr) + , pSRetoucheFly2(nullptr) + , pSFlyOnlyDraw(nullptr) + , pBLines(nullptr) + , pSLines(nullptr) + , pSSubsLines(nullptr) + , pSSpecSubsLines(nullptr) + , pSProgress(nullptr) + , nSPixelSzW(0) + , nSPixelSzH(0) + , nSHalfPixelSzW(0) + , nSHalfPixelSzH(0) + , nSMinDistPixelW(0) + , nSMinDistPixelH(0) + , aSScaleX(1) + , aSScaleY(1) + { + } + +}; + +static SwPaintProperties gProp; + +namespace { + +bool isTableBoundariesEnabled() +{ + if (!gProp.pSGlobalShell->GetViewOptions()->IsTable()) + return false; + + if (gProp.pSGlobalShell->GetViewOptions()->IsPagePreview()) + return false; + + if (gProp.pSGlobalShell->GetViewOptions()->IsReadonly()) + return false; + + if (gProp.pSGlobalShell->GetViewOptions()->IsFormView()) + return false; + + return SwViewOption::IsTableBoundaries(); +} + +} + +/** + * Set borders alignment statics + * Adjustment for 'small' twip-to-pixel relations: + * For 'small' twip-to-pixel relations (less then 2:1) + * values of <gProp.nSHalfPixelSzW> and <gProp.nSHalfPixelSzH> are set to ZERO + */ +void SwCalcPixStatics( vcl::RenderContext const *pOut ) +{ + // determine 'small' twip-to-pixel relation + bool bSmallTwipToPxRelW = false; + bool bSmallTwipToPxRelH = false; + { + Size aCheckTwipToPxRelSz( pOut->PixelToLogic( Size( 100, 100 )) ); + if ( (aCheckTwipToPxRelSz.Width()/100.0) < 2.0 ) + { + bSmallTwipToPxRelW = true; + } + if ( (aCheckTwipToPxRelSz.Height()/100.0) < 2.0 ) + { + bSmallTwipToPxRelH = true; + } + } + + Size aSz( pOut->PixelToLogic( Size( 1,1 )) ); + + gProp.nSPixelSzW = aSz.Width(); + if( !gProp.nSPixelSzW ) + gProp.nSPixelSzW = 1; + gProp.nSPixelSzH = aSz.Height(); + if( !gProp.nSPixelSzH ) + gProp.nSPixelSzH = 1; + + // consider 'small' twip-to-pixel relations + if ( !bSmallTwipToPxRelW ) + { + gProp.nSHalfPixelSzW = gProp.nSPixelSzW / 2 + 1; + } + else + { + gProp.nSHalfPixelSzW = 0; + } + // consider 'small' twip-to-pixel relations + if ( !bSmallTwipToPxRelH ) + { + gProp.nSHalfPixelSzH = gProp.nSPixelSzH / 2 + 1; + } + else + { + gProp.nSHalfPixelSzH = 0; + } + + gProp.nSMinDistPixelW = gProp.nSPixelSzW * 2 + 1; + gProp.nSMinDistPixelH = gProp.nSPixelSzH * 2 + 1; + + const MapMode &rMap = pOut->GetMapMode(); + gProp.aSScaleX = double(rMap.GetScaleX()); + gProp.aSScaleY = double(rMap.GetScaleY()); +} + +/** + * To be able to save the statics so the paint is more or less reentrant + */ +class SwSavePaintStatics : public SwPaintProperties +{ +public: + SwSavePaintStatics(); + ~SwSavePaintStatics(); +}; + +SwSavePaintStatics::SwSavePaintStatics() +{ + // Saving globales + bSFlyMetafile = gProp.bSFlyMetafile; + pSGlobalShell = gProp.pSGlobalShell; + pSFlyMetafileOut = gProp.pSFlyMetafileOut; + pSRetoucheFly = gProp.pSRetoucheFly; + pSRetoucheFly2 = gProp.pSRetoucheFly2; + pSFlyOnlyDraw = gProp.pSFlyOnlyDraw; + pBLines = gProp.pBLines; + pSLines = gProp.pSLines; + pSSubsLines = gProp.pSSubsLines; + pSSpecSubsLines = gProp.pSSpecSubsLines; + pSProgress = gProp.pSProgress; + nSPixelSzW = gProp.nSPixelSzW; + nSPixelSzH = gProp.nSPixelSzH; + nSHalfPixelSzW = gProp.nSHalfPixelSzW; + nSHalfPixelSzH = gProp.nSHalfPixelSzH; + nSMinDistPixelW = gProp.nSMinDistPixelW; + nSMinDistPixelH = gProp.nSMinDistPixelH ; + aSGlobalRetoucheColor = aGlobalRetoucheColor; + aSScaleX = gProp.aSScaleX; + aSScaleY = gProp.aSScaleY; + + // Restoring globales to default + gProp.bSFlyMetafile = false; + gProp.pSFlyMetafileOut = nullptr; + gProp.pSRetoucheFly = nullptr; + gProp.pSRetoucheFly2 = nullptr; + gProp.nSPixelSzW = gProp.nSPixelSzH = + gProp.nSHalfPixelSzW = gProp.nSHalfPixelSzH = + gProp.nSMinDistPixelW = gProp.nSMinDistPixelH = 0; + gProp.aSScaleX = gProp.aSScaleY = 1.0; + gProp.pBLines = nullptr; + gProp.pSLines = nullptr; + gProp.pSSubsLines = nullptr; + gProp.pSSpecSubsLines = nullptr; + gProp.pSProgress = nullptr; +} + +SwSavePaintStatics::~SwSavePaintStatics() +{ + // Restoring globales to saved one + gProp.pSGlobalShell = pSGlobalShell; + gProp.bSFlyMetafile = bSFlyMetafile; + gProp.pSFlyMetafileOut = pSFlyMetafileOut; + gProp.pSRetoucheFly = pSRetoucheFly; + gProp.pSRetoucheFly2 = pSRetoucheFly2; + gProp.pSFlyOnlyDraw = pSFlyOnlyDraw; + gProp.pBLines = pBLines; + gProp.pSLines = pSLines; + gProp.pSSubsLines = pSSubsLines; + gProp.pSSpecSubsLines = pSSpecSubsLines; + gProp.pSProgress = pSProgress; + gProp.nSPixelSzW = nSPixelSzW; + gProp.nSPixelSzH = nSPixelSzH; + gProp.nSHalfPixelSzW = nSHalfPixelSzW; + gProp.nSHalfPixelSzH = nSHalfPixelSzH; + gProp.nSMinDistPixelW = nSMinDistPixelW; + gProp.nSMinDistPixelH = nSMinDistPixelH; + aGlobalRetoucheColor = aSGlobalRetoucheColor; + gProp.aSScaleX = aSScaleX; + gProp.aSScaleY = aSScaleY; +} + +void BorderLines::AddBorderLine(const drawinglayer::primitive2d::Primitive2DReference& rLine) +{ + for (drawinglayer::primitive2d::Primitive2DContainer::reverse_iterator it = m_Lines.rbegin(); it != m_Lines.rend(); ++it) + { + const drawinglayer::primitive2d::Primitive2DReference aMerged(drawinglayer::primitive2d::tryMergeBorderLinePrimitive2D(*it, rLine)); + + if (aMerged.is()) + { + *it = aMerged; // replace existing line with merged // lcl_TryMergeBorderLine + return; + } + } + + m_Lines.append(rLine); +} + +SwLineRect::SwLineRect( const SwRect &rRect, const Color *pCol, const SvxBorderLineStyle nStyl, + const SwTabFrame *pT, const SubColFlags nSCol ) : + SwRect( rRect ), + nStyle( nStyl ), + pTab( pT ), + nSubColor( nSCol ), + bPainted( false ), + nLock( 0 ) +{ + if ( pCol != nullptr ) + aColor = *pCol; +} + +bool SwLineRect::MakeUnion( const SwRect &rRect, SwPaintProperties const & properties) +{ + // It has already been tested outside, whether the rectangles have + // the same orientation (horizontal or vertical), color, etc. + if ( Height() > Width() ) //Vertical line + { + if ( Left() == rRect.Left() && Width() == rRect.Width() ) + { + // Merge when there is no gap between the lines + const long nAdd = properties.nSPixelSzW + properties.nSHalfPixelSzW; + if ( Bottom() + nAdd >= rRect.Top() && + Top() - nAdd <= rRect.Bottom() ) + { + Bottom( std::max( Bottom(), rRect.Bottom() ) ); + Top ( std::min( Top(), rRect.Top() ) ); + return true; + } + } + } + else + { + if ( Top() == rRect.Top() && Height() == rRect.Height() ) + { + // Merge when there is no gap between the lines + const long nAdd = properties.nSPixelSzW + properties.nSHalfPixelSzW; + if ( Right() + nAdd >= rRect.Left() && + Left() - nAdd <= rRect.Right() ) + { + Right( std::max( Right(), rRect.Right() ) ); + Left ( std::min( Left(), rRect.Left() ) ); + return true; + } + } + } + return false; +} + +void SwLineRects::AddLineRect( const SwRect &rRect, const Color *pCol, const SvxBorderLineStyle nStyle, + const SwTabFrame *pTab, const SubColFlags nSCol, SwPaintProperties const & properties ) +{ + // Loop backwards because lines which can be combined, can usually be painted + // in the same context + for (reverse_iterator it = aLineRects.rbegin(); it != aLineRects.rend(); + ++it) + { + SwLineRect &rLRect = (*it); + // Test for the orientation, color, table + if ( rLRect.GetTab() == pTab && + !rLRect.IsPainted() && rLRect.GetSubColor() == nSCol && + (rLRect.Height() > rLRect.Width()) == (rRect.Height() > rRect.Width()) && + (pCol && rLRect.GetColor() == *pCol) ) + { + if ( rLRect.MakeUnion( rRect, properties ) ) + return; + } + } + aLineRects.emplace_back( rRect, pCol, nStyle, pTab, nSCol ); +} + +void SwLineRects::ConnectEdges( OutputDevice const *pOut, SwPaintProperties const & properties ) +{ + if ( pOut->GetOutDevType() != OUTDEV_PRINTER ) + { + // I'm not doing anything for a too small zoom + if ( properties.aSScaleX < aEdgeScale || properties.aSScaleY < aEdgeScale ) + return; + } + + static const long nAdd = 20; + + std::vector<SwLineRect*> aCheck; + + for (size_t i = 0; i < aLineRects.size(); ++i) + { + SwLineRect &rL1 = aLineRects[i]; + if ( !rL1.GetTab() || rL1.IsPainted() || rL1.IsLocked() ) + continue; + + aCheck.clear(); + + const bool bVert = rL1.Height() > rL1.Width(); + long nL1a, nL1b, nL1c, nL1d; + + if ( bVert ) + { + nL1a = rL1.Top(); nL1b = rL1.Left(); + nL1c = rL1.Right(); nL1d = rL1.Bottom(); + } + else + { + nL1a = rL1.Left(); nL1b = rL1.Top(); + nL1c = rL1.Bottom(); nL1d = rL1.Right(); + } + + // Collect all lines to possibly link with i1 + for (iterator it2 = aLineRects.begin(); it2 != aLineRects.end(); ++it2) + { + SwLineRect &rL2 = (*it2); + if ( rL2.GetTab() != rL1.GetTab() || + rL2.IsPainted() || + rL2.IsLocked() || + (bVert == (rL2.Height() > rL2.Width())) ) + continue; + + long nL2a, nL2b, nL2c, nL2d; + if ( bVert ) + { + nL2a = rL2.Top(); nL2b = rL2.Left(); + nL2c = rL2.Right(); nL2d = rL2.Bottom(); + } + else + { + nL2a = rL2.Left(); nL2b = rL2.Top(); + nL2c = rL2.Bottom(); nL2d = rL2.Right(); + } + + if ( (nL1a - nAdd < nL2d && nL1d + nAdd > nL2a) && + ((nL1b > nL2b && nL1c < nL2c) || + (nL1c >= nL2c && nL1b - nAdd < nL2c) || + (nL1b <= nL2b && nL1c + nAdd > nL2b)) ) + { + aCheck.push_back( &rL2 ); + } + } + if ( aCheck.size() < 2 ) + continue; + + bool bRemove = false; + + // For each line test all following ones. + for ( size_t k = 0; !bRemove && k < aCheck.size(); ++k ) + { + SwLineRect &rR1 = *aCheck[k]; + + for ( size_t k2 = k+1; !bRemove && k2 < aCheck.size(); ++k2 ) + { + SwLineRect &rR2 = *aCheck[k2]; + if ( bVert ) + { + SwLineRect *pLA = nullptr; + SwLineRect *pLB = nullptr; + if ( rR1.Top() < rR2.Top() ) + { + pLA = &rR1; pLB = &rR2; + } + else if ( rR1.Top() > rR2.Top() ) + { + pLA = &rR2; pLB = &rR1; + } + // are k1 and k2 describing a double line? + if ( pLA && pLA->Bottom() + 60 > pLB->Top() ) + { + if ( rL1.Top() < pLA->Top() ) + { + if ( rL1.Bottom() == pLA->Bottom() ) + continue; //Small mistake (where?) + + SwRect aIns( rL1 ); + aIns.Bottom( pLA->Bottom() ); + if ( !rL1.IsInside( aIns ) ) + continue; + aLineRects.emplace_back( aIns, &rL1.GetColor(), + SvxBorderLineStyle::SOLID, + rL1.GetTab(), SubColFlags::Tab ); + if ( isFull() ) + { + --i; + k = aCheck.size(); + break; + } + } + + if ( rL1.Bottom() > pLB->Bottom() ) + rL1.Top( pLB->Top() ); // extend i1 on the top + else + bRemove = true; //stopping, remove i1 + } + } + else + { + SwLineRect *pLA = nullptr; + SwLineRect *pLB = nullptr; + if ( rR1.Left() < rR2.Left() ) + { + pLA = &rR1; pLB = &rR2; + } + else if ( rR1.Left() > rR2.Left() ) + { + pLA = &rR2; pLB = &rR1; + } + // Is it double line? + if ( pLA && pLA->Right() + 60 > pLB->Left() ) + { + if ( rL1.Left() < pLA->Left() ) + { + if ( rL1.Right() == pLA->Right() ) + continue; //small error + + SwRect aIns( rL1 ); + aIns.Right( pLA->Right() ); + if ( !rL1.IsInside( aIns ) ) + continue; + aLineRects.emplace_back( aIns, &rL1.GetColor(), + SvxBorderLineStyle::SOLID, + rL1.GetTab(), SubColFlags::Tab ); + if ( isFull() ) + { + --i; + k = aCheck.size(); + break; + } + } + if ( rL1.Right() > pLB->Right() ) + rL1.Left( pLB->Left() ); + else + bRemove = true; + } + } + } + } + if ( bRemove ) + { + aLineRects.erase(aLineRects.begin() + i); + --i; + } + } +} + +void SwSubsRects::RemoveSuperfluousSubsidiaryLines( const SwLineRects &rRects, SwPaintProperties const & properties ) +{ + // All help lines that are covered by any border will be removed or split + for (size_t i = 0; i < aLineRects.size(); ++i) + { + // get a copy instead of a reference, because an <insert> may destroy + // the object due to a necessary array resize. + const SwLineRect aSubsLineRect = SwLineRect(aLineRects[i]); + + // add condition <aSubsLineRect.IsLocked()> in order to consider only + // border lines, which are *not* locked. + if ( aSubsLineRect.IsPainted() || + aSubsLineRect.IsLocked() ) + continue; + + const bool bVerticalSubs = aSubsLineRect.Height() > aSubsLineRect.Width(); + SwRect aSubsRect( aSubsLineRect ); + if ( bVerticalSubs ) + { + aSubsRect.Left ( aSubsRect.Left() - (properties.nSPixelSzW+properties.nSHalfPixelSzW) ); + aSubsRect.Right ( aSubsRect.Right() + (properties.nSPixelSzW+properties.nSHalfPixelSzW) ); + } + else + { + aSubsRect.Top ( aSubsRect.Top() - (properties.nSPixelSzH+properties.nSHalfPixelSzH) ); + aSubsRect.Bottom( aSubsRect.Bottom() + (properties.nSPixelSzH+properties.nSHalfPixelSzH) ); + } + for (const_iterator itK = rRects.aLineRects.begin(); itK != rRects.aLineRects.end(); ++itK) + { + const SwLineRect &rLine = *itK; + + // do *not* consider painted or locked border lines. + // #i1837# - locked border lines have to be considered. + if ( rLine.IsLocked () ) + continue; + + if ( !bVerticalSubs == ( rLine.Height() > rLine.Width() ) ) //same direction? + continue; + + if ( aSubsRect.IsOver( rLine ) ) + { + if ( bVerticalSubs ) // Vertical? + { + if ( aSubsRect.Left() <= rLine.Right() && + aSubsRect.Right() >= rLine.Left() ) + { + long nTmp = rLine.Top()-(properties.nSPixelSzH+1); + if ( aSubsLineRect.Top() < nTmp ) + { + SwRect aNewSubsRect( aSubsLineRect ); + aNewSubsRect.Bottom( nTmp ); + aLineRects.emplace_back( aNewSubsRect, nullptr, aSubsLineRect.GetStyle(), nullptr, + aSubsLineRect.GetSubColor() ); + } + nTmp = rLine.Bottom()+properties.nSPixelSzH+1; + if ( aSubsLineRect.Bottom() > nTmp ) + { + SwRect aNewSubsRect( aSubsLineRect ); + aNewSubsRect.Top( nTmp ); + aLineRects.emplace_back( aNewSubsRect, nullptr, aSubsLineRect.GetStyle(), nullptr, + aSubsLineRect.GetSubColor() ); + } + aLineRects.erase(aLineRects.begin() + i); + --i; + break; + } + } + else // Horizontal + { + if ( aSubsRect.Top() <= rLine.Bottom() && + aSubsRect.Bottom() >= rLine.Top() ) + { + long nTmp = rLine.Left()-(properties.nSPixelSzW+1); + if ( aSubsLineRect.Left() < nTmp ) + { + SwRect aNewSubsRect( aSubsLineRect ); + aNewSubsRect.Right( nTmp ); + aLineRects.emplace_back( aNewSubsRect, nullptr, aSubsLineRect.GetStyle(), nullptr, + aSubsLineRect.GetSubColor() ); + } + nTmp = rLine.Right()+properties.nSPixelSzW+1; + if ( aSubsLineRect.Right() > nTmp ) + { + SwRect aNewSubsRect( aSubsLineRect ); + aNewSubsRect.Left( nTmp ); + aLineRects.emplace_back( aNewSubsRect, nullptr, aSubsLineRect.GetStyle(), nullptr, + aSubsLineRect.GetSubColor() ); + } + aLineRects.erase(aLineRects.begin() + i); + --i; + break; + } + } + } + } + } +} + +void SwLineRects::LockLines( bool bLock ) +{ + for (iterator it = aLineRects.begin(); it != aLineRects.end(); ++it) + (*it).Lock( bLock ); +} + +static void lcl_DrawDashedRect( OutputDevice * pOut, SwLineRect const & rLRect ) +{ + long startX = rLRect.Left( ), endX; + long startY = rLRect.Top( ), endY; + + // Discriminate vertically stretched rect from horizontally stretched + // and restrict minimum nHalfLWidth to 1 + long nHalfLWidth = std::max( static_cast<long>(std::min( rLRect.Width( ), rLRect.Height( ) ) / 2), 1L ); + + if ( rLRect.Height( ) > rLRect.Width( ) ) + { + startX += nHalfLWidth; + endX = startX; + endY = startY + rLRect.Height( ); + } + else + { + startY += nHalfLWidth; + endY = startY; + endX = startX + rLRect.Width( ); + } + + svtools::DrawLine( *pOut, Point( startX, startY ), Point( endX, endY ), + sal_uInt32( nHalfLWidth * 2 ), rLRect.GetStyle( ) ); +} + +void SwLineRects::PaintLines( OutputDevice *pOut, SwPaintProperties const &properties ) +{ + // Paint the borders. Sadly two passes are needed. + // Once for the inside and once for the outside edges of tables + if ( aLineRects.size() != nLastCount ) + { + // #i16816# tagged pdf support + SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, nullptr, *pOut ); + + pOut->Push( PushFlags::FILLCOLOR|PushFlags::LINECOLOR ); + pOut->SetFillColor(); + pOut->SetLineColor(); + ConnectEdges( pOut, properties ); + const Color *pLast = nullptr; + + bool bPaint2nd = false; + size_t nMinCount = aLineRects.size(); + + for ( size_t i = 0; i < aLineRects.size(); ++i ) + { + SwLineRect &rLRect = aLineRects[i]; + + if ( rLRect.IsPainted() ) + continue; + + if ( rLRect.IsLocked() ) + { + nMinCount = std::min( nMinCount, i ); + continue; + } + + // Paint it now or in the second pass? + bool bPaint = true; + if ( rLRect.GetTab() ) + { + if ( rLRect.Height() > rLRect.Width() ) + { + // Vertical edge, overlapping with the table edge? + SwTwips nLLeft = rLRect.Left() - 30, + nLRight = rLRect.Right() + 30, + nTLeft = rLRect.GetTab()->Frame().Left() + rLRect.GetTab()->Prt().Left(), + nTRight = rLRect.GetTab()->Frame().Left() + rLRect.GetTab()->Prt().Right(); + if ( (nTLeft >= nLLeft && nTLeft <= nLRight) || + (nTRight>= nLLeft && nTRight<= nLRight) ) + bPaint = false; + } + else + { + // Horizontal edge, overlapping with the table edge? + SwTwips nLTop = rLRect.Top() - 30, + nLBottom = rLRect.Bottom() + 30, + nTTop = rLRect.GetTab()->Frame().Top() + rLRect.GetTab()->Prt().Top(), + nTBottom = rLRect.GetTab()->Frame().Top() + rLRect.GetTab()->Prt().Bottom(); + if ( (nTTop >= nLTop && nTTop <= nLBottom) || + (nTBottom >= nLTop && nTBottom <= nLBottom) ) + bPaint = false; + } + } + if ( bPaint ) + { + if ( !pLast || *pLast != rLRect.GetColor() ) + { + pLast = &rLRect.GetColor(); + + DrawModeFlags nOldDrawMode = pOut->GetDrawMode(); + if( properties.pSGlobalShell->GetWin() && + Application::GetSettings().GetStyleSettings().GetHighContrastMode() ) + pOut->SetDrawMode( DrawModeFlags::Default ); + + pOut->SetLineColor( *pLast ); + pOut->SetFillColor( *pLast ); + pOut->SetDrawMode( nOldDrawMode ); + } + + if( !rLRect.IsEmpty() ) + lcl_DrawDashedRect( pOut, rLRect ); + rLRect.SetPainted(); + } + else + bPaint2nd = true; + } + if ( bPaint2nd ) + { + for ( size_t i = 0; i < aLineRects.size(); ++i ) + { + SwLineRect &rLRect = aLineRects[i]; + if ( rLRect.IsPainted() ) + continue; + + if ( rLRect.IsLocked() ) + { + nMinCount = std::min( nMinCount, i ); + continue; + } + + if ( !pLast || *pLast != rLRect.GetColor() ) + { + pLast = &rLRect.GetColor(); + + DrawModeFlags nOldDrawMode = pOut->GetDrawMode(); + if( properties.pSGlobalShell->GetWin() && + Application::GetSettings().GetStyleSettings().GetHighContrastMode() ) + { + pOut->SetDrawMode( DrawModeFlags::Default ); + } + + pOut->SetFillColor( *pLast ); + pOut->SetDrawMode( nOldDrawMode ); + } + if( !rLRect.IsEmpty() ) + lcl_DrawDashedRect( pOut, rLRect ); + rLRect.SetPainted(); + } + } + nLastCount = nMinCount; + pOut->Pop(); + } +} + +void SwSubsRects::PaintSubsidiary( OutputDevice *pOut, + const SwLineRects *pRects, + SwPaintProperties const & properties ) +{ + if ( !aLineRects.empty() ) + { + // #i16816# tagged pdf support + SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, nullptr, *pOut ); + + // Remove all help line that are almost covered (tables) + for (size_type i = 0; i != aLineRects.size(); ++i) + { + SwLineRect &rLi = aLineRects[i]; + const bool bVerticalSubs = rLi.Height() > rLi.Width(); + + for (size_type k = i + 1; k != aLineRects.size(); ++k) + { + SwLineRect &rLk = aLineRects[k]; + if ( rLi.SSize() == rLk.SSize() ) + { + if ( bVerticalSubs == ( rLk.Height() > rLk.Width() ) ) + { + if ( bVerticalSubs ) + { + long nLi = rLi.Right(); + long nLk = rLk.Right(); + if ( rLi.Top() == rLk.Top() && + ((nLi < rLk.Left() && nLi+21 > rLk.Left()) || + (nLk < rLi.Left() && nLk+21 > rLi.Left()))) + { + aLineRects.erase(aLineRects.begin() + k); + // don't continue with inner loop any more: + // the array may shrink! + --i; + break; + } + } + else + { + long nLi = rLi.Bottom(); + long nLk = rLk.Bottom(); + if ( rLi.Left() == rLk.Left() && + ((nLi < rLk.Top() && nLi+21 > rLk.Top()) || + (nLk < rLi.Top() && nLk+21 > rLi.Top()))) + { + aLineRects.erase(aLineRects.begin() + k); + // don't continue with inner loop any more: + // the array may shrink! + --i; + break; + } + } + } + } + } + } + + if ( pRects && (!pRects->aLineRects.empty()) ) + RemoveSuperfluousSubsidiaryLines( *pRects, properties ); + + if ( !aLineRects.empty() ) + { + pOut->Push( PushFlags::FILLCOLOR|PushFlags::LINECOLOR ); + pOut->SetLineColor(); + + // Reset draw mode in high contrast mode in order to get fill color + // set at output device. Recover draw mode after draw of lines. + // Necessary for the subsidiary lines painted by the fly frames. + DrawModeFlags nOldDrawMode = pOut->GetDrawMode(); + if( gProp.pSGlobalShell->GetWin() && + Application::GetSettings().GetStyleSettings().GetHighContrastMode() ) + { + pOut->SetDrawMode( DrawModeFlags::Default ); + } + + for (SwSubsRects::iterator it = aLineRects.begin(); it != aLineRects.end(); + ++it) + { + SwLineRect &rLRect = (*it); + // Add condition <!rLRect.IsLocked()> to prevent paint of locked subsidiary lines. + if ( !rLRect.IsPainted() && + !rLRect.IsLocked() ) + { + const Color *pCol = nullptr; + switch ( rLRect.GetSubColor() ) + { + case SubColFlags::Page: pCol = &SwViewOption::GetDocBoundariesColor(); break; + case SubColFlags::Fly: pCol = &SwViewOption::GetObjectBoundariesColor(); break; + case SubColFlags::Tab: pCol = &SwViewOption::GetTableBoundariesColor(); break; + case SubColFlags::Sect: pCol = &SwViewOption::GetSectionBoundColor(); break; + } + + if (pCol && pOut->GetFillColor() != *pCol) + pOut->SetFillColor( *pCol ); + pOut->DrawRect( rLRect.SVRect() ); + + rLRect.SetPainted(); + } + } + + pOut->SetDrawMode( nOldDrawMode ); + + pOut->Pop(); + } + } +} + +// Various functions that are use in this file. + +/** + * Function <SwAlignRect(..)> is also used outside this file + * + * Correction: adjust rectangle on pixel level in order to make sure, + * that the border "leaves its original pixel", if it has to + * No prior adjustments for odd relation between pixel and twip + */ +void SwAlignRect( SwRect &rRect, const SwViewShell *pSh, const vcl::RenderContext* pRenderContext ) +{ + if( !rRect.HasArea() ) + return; + + // Make sure that view shell (parameter <pSh>) exists, if the output device + // is taken from this view shell --> no output device, no alignment + // Output device taken from view shell <pSh>, if <gProp.bSFlyMetafile> not set + if ( !gProp.bSFlyMetafile && !pSh ) + { + return; + } + + const vcl::RenderContext *pOut = gProp.bSFlyMetafile ? + gProp.pSFlyMetafileOut.get() : pRenderContext; + + // Hold original rectangle in pixel + const tools::Rectangle aOrgPxRect = pOut->LogicToPixel( rRect.SVRect() ); + // Determine pixel-center rectangle in twip + const SwRect aPxCenterRect( pOut->PixelToLogic( aOrgPxRect ) ); + + // Perform adjustments on pixel level. + SwRect aAlignedPxRect( aOrgPxRect ); + if ( rRect.Top() > aPxCenterRect.Top() ) + { + // 'leave pixel overlapping on top' + aAlignedPxRect.Top( aAlignedPxRect.Top() + 1 ); + } + + if ( rRect.Bottom() < aPxCenterRect.Bottom() ) + { + // 'leave pixel overlapping on bottom' + aAlignedPxRect.Bottom( aAlignedPxRect.Bottom() - 1 ); + } + + if ( rRect.Left() > aPxCenterRect.Left() ) + { + // 'leave pixel overlapping on left' + aAlignedPxRect.Left( aAlignedPxRect.Left() + 1 ); + } + + if ( rRect.Right() < aPxCenterRect.Right() ) + { + // 'leave pixel overlapping on right' + aAlignedPxRect.Right( aAlignedPxRect.Right() - 1 ); + } + + // Consider negative width/height check, if aligned SwRect has negative width/height. + // If Yes, adjust it to width/height = 0 twip. + // NOTE: A SwRect with negative width/height can occur, if the width/height + // of the given SwRect in twip was less than a pixel in twip and that + // the alignment calculates that the aligned SwRect should not contain + // the pixels the width/height is on. + if ( aAlignedPxRect.Width() < 0 ) + { + aAlignedPxRect.Width(0); + } + if ( aAlignedPxRect.Height() < 0 ) + { + aAlignedPxRect.Height(0); + } + // Consider zero width/height for converting a rectangle from + // pixel to logic it needs a width/height. Thus, set width/height + // to one, if it's zero and correct this on the twip level after the conversion. + bool bZeroWidth = false; + if ( aAlignedPxRect.Width() == 0 ) + { + aAlignedPxRect.Width(1); + bZeroWidth = true; + } + bool bZeroHeight = false; + if ( aAlignedPxRect.Height() == 0 ) + { + aAlignedPxRect.Height(1); + bZeroHeight = true; + } + + rRect = pOut->PixelToLogic( aAlignedPxRect.SVRect() ); + + // Consider zero width/height and adjust calculated aligned twip rectangle. + // Reset width/height to zero; previous negative width/height haven't to be considered. + if ( bZeroWidth ) + { + rRect.Width(0); + } + if ( bZeroHeight ) + { + rRect.Height(0); + } +} + +/** + * Helper for twip adjustments on pixel base + * + * This method compares the x- or y-pixel position of two twip-points. + * If the x-/y-pixel positions are the same, the x-/y-pixel position of + * the second twip point is adjusted by a given amount of pixels +*/ +static void lcl_CompPxPosAndAdjustPos( const vcl::RenderContext& _rOut, + const Point& _rRefPt, + Point& _rCompPt, + const bool _bChkXPos, + const sal_Int8 _nPxAdjustment ) +{ + const Point aRefPxPt = _rOut.LogicToPixel( _rRefPt ); + Point aCompPxPt = _rOut.LogicToPixel( _rCompPt ); + + if ( _bChkXPos ) + { + if ( aCompPxPt.X() == aRefPxPt.X() ) + { + aCompPxPt.X() += _nPxAdjustment ; + const Point aAdjustedCompPt = _rOut.PixelToLogic( aCompPxPt ); + _rCompPt.X() = aAdjustedCompPt.X(); + } + } + else + { + if ( aCompPxPt.Y() == aRefPxPt.Y() ) + { + aCompPxPt.Y() += _nPxAdjustment ; + const Point aAdjustedCompPt = _rOut.PixelToLogic( aCompPxPt ); + _rCompPt.Y() = aAdjustedCompPt.Y(); + } + } +} + +/** + * Method to pixel-align rectangle for drawing graphic object + * + * Because we are drawing graphics from the left-top-corner in conjunction + * with size coordinates, these coordinates have to be calculated at a pixel + * level. + * Thus, we convert the rectangle to pixel and then convert to left-top-corner + * and then get size of pixel rectangle back to logic. + * This calculation is necessary, because there's a different between + * the conversion from logic to pixel of a normal rectangle with its left-top- + * and right-bottom-corner and the same conversion of the same rectangle + * with left-top-corner and size. + * + * NOTE: Call this method before each <GraphicObject.Draw(...)> +*/ +void SwAlignGrfRect( SwRect *pGrfRect, const vcl::RenderContext &rOut ) +{ + tools::Rectangle aPxRect = rOut.LogicToPixel( pGrfRect->SVRect() ); + pGrfRect->Pos( rOut.PixelToLogic( aPxRect.TopLeft() ) ); + pGrfRect->SSize( rOut.PixelToLogic( aPxRect.GetSize() ) ); +} + +static long lcl_AlignWidth( const long nWidth, SwPaintProperties const & properties ) +{ + if ( nWidth ) + { + const long nW = nWidth % properties.nSPixelSzW; + + if ( !nW || nW > properties.nSHalfPixelSzW ) + return std::max(1L, nWidth - properties.nSHalfPixelSzW); + } + return nWidth; +} + +static long lcl_AlignHeight( const long nHeight, SwPaintProperties const & properties ) +{ + if ( nHeight ) + { + const long nH = nHeight % properties.nSPixelSzH; + + if ( !nH || nH > properties.nSHalfPixelSzH ) + return std::max(1L, nHeight - properties.nSHalfPixelSzH); + } + return nHeight; +} + +static long lcl_MinHeightDist( const long nDist, SwPaintProperties const & properties ) +{ + if ( properties.aSScaleX < aMinDistScale || properties.aSScaleY < aMinDistScale ) + return nDist; + return ::lcl_AlignHeight( std::max( nDist, properties.nSMinDistPixelH ), properties); +} + +/** + * Calculate PrtArea plus surrounding plus shadow + */ +static void lcl_CalcBorderRect( SwRect &rRect, const SwFrame *pFrame, + const SwBorderAttrs &rAttrs, + const bool bShadow, + SwPaintProperties const & properties) +{ + // Special handling for cell frames. + // The printing area of a cell frame is completely enclosed in the frame area + // and a cell frame has no shadow. Thus, for cell frames the calculated + // area equals the frame area. + // Notes: Borders of cell frames in R2L text direction will switch its side + // - left border is painted on the right; right border on the left. + // See <lcl_PaintLeftLine> and <lcl_PaintRightLine>. + if( pFrame->IsSctFrame() ) + { + rRect = pFrame->Prt(); + rRect.Pos() += pFrame->Frame().Pos(); + } + else if ( pFrame->IsCellFrame() ) + rRect = pFrame->Frame(); + else + { + rRect = pFrame->Prt(); + rRect.Pos() += pFrame->Frame().Pos(); + + if ( rAttrs.IsLine() || rAttrs.IsBorderDist() || + (bShadow && rAttrs.GetShadow().GetLocation() != SvxShadowLocation::NONE) ) + { + SwRectFn fnRect = pFrame->IsVertical() ? ( pFrame->IsVertLR() ? fnRectVertL2R : fnRectVert ) : fnRectHori; + + const SvxBoxItem &rBox = rAttrs.GetBox(); + const bool bTop = 0 != (pFrame->*fnRect->fnGetTopMargin)(); + if ( bTop ) + { + SwTwips nDiff = rBox.GetTop() ? + rBox.CalcLineSpace( SvxBoxItemLine::TOP ) : + ( rAttrs.IsBorderDist() ? + // Increase of distance by one twip is incorrect. + rBox.GetDistance( SvxBoxItemLine::TOP ) : 0 ); + if( nDiff ) + (rRect.*fnRect->fnSubTop)( nDiff ); + } + + const bool bBottom = 0 != (pFrame->*fnRect->fnGetBottomMargin)(); + if ( bBottom ) + { + SwTwips nDiff = 0; + // #i29550# + if ( pFrame->IsTabFrame() && + static_cast<const SwTabFrame*>(pFrame)->IsCollapsingBorders() ) + { + // For collapsing borders, we have to add the height of + // the height of the last line + nDiff = static_cast<const SwTabFrame*>(pFrame)->GetBottomLineSize(); + } + else + { + nDiff = rBox.GetBottom() ? + rBox.CalcLineSpace( SvxBoxItemLine::BOTTOM ) : + ( rAttrs.IsBorderDist() ? + // Increase of distance by one twip is incorrect. + rBox.GetDistance( SvxBoxItemLine::BOTTOM ) : 0 ); + } + if( nDiff ) + (rRect.*fnRect->fnAddBottom)( nDiff ); + } + + if ( rBox.GetLeft() ) + (rRect.*fnRect->fnSubLeft)( rBox.CalcLineSpace( SvxBoxItemLine::LEFT ) ); + else if ( rAttrs.IsBorderDist() ) + // Increase of distance by one twip is incorrect. + (rRect.*fnRect->fnSubLeft)( rBox.GetDistance( SvxBoxItemLine::LEFT ) ); + + if ( rBox.GetRight() ) + (rRect.*fnRect->fnAddRight)( rBox.CalcLineSpace( SvxBoxItemLine::RIGHT ) ); + else if ( rAttrs.IsBorderDist() ) + // Increase of distance by one twip is incorrect. + (rRect.*fnRect->fnAddRight)( rBox.GetDistance( SvxBoxItemLine::RIGHT ) ); + + if ( bShadow && rAttrs.GetShadow().GetLocation() != SvxShadowLocation::NONE ) + { + const SvxShadowItem &rShadow = rAttrs.GetShadow(); + if ( bTop ) + (rRect.*fnRect->fnSubTop)(rShadow.CalcShadowSpace(SvxShadowItemSide::TOP)); + (rRect.*fnRect->fnSubLeft)(rShadow.CalcShadowSpace(SvxShadowItemSide::LEFT)); + if ( bBottom ) + (rRect.*fnRect->fnAddBottom) + (rShadow.CalcShadowSpace( SvxShadowItemSide::BOTTOM )); + (rRect.*fnRect->fnAddRight)(rShadow.CalcShadowSpace(SvxShadowItemSide::RIGHT)); + } + } + } + + ::SwAlignRect( rRect, properties.pSGlobalShell, properties.pSGlobalShell ? properties.pSGlobalShell->GetOut() : nullptr ); +} + +/** + * Extend left/right border/shadow rectangle to bottom of previous frame/to + * top of next frame, if border/shadow is joined with previous/next frame + */ +static void lcl_ExtendLeftAndRight( SwRect& _rRect, + const SwFrame& _rFrame, + const SwBorderAttrs& _rAttrs, + const SwRectFn& _rRectFn ) +{ + if ( _rAttrs.JoinedWithPrev( _rFrame ) ) + { + const SwFrame* pPrevFrame = _rFrame.GetPrev(); + (_rRect.*_rRectFn->fnSetTop)( (pPrevFrame->*_rRectFn->fnGetPrtBottom)() ); + } + if ( _rAttrs.JoinedWithNext( _rFrame ) ) + { + const SwFrame* pNextFrame = _rFrame.GetNext(); + (_rRect.*_rRectFn->fnSetBottom)( (pNextFrame->*_rRectFn->fnGetPrtTop)() ); + } +} + +/// Returns a range suitable for subtraction when lcl_SubtractFlys() is used. +/// Otherwise DrawFillAttributes() expands the clip path itself. +static basegfx::B2DRange lcl_ShrinkFly(const SwRect& rRect) +{ + static MapMode aMapMode(MapUnit::MapTwip); + static const Size aSingleUnit = Application::GetDefaultDevice()->PixelToLogic(Size(1, 1), aMapMode); + + double x1 = rRect.Left() + aSingleUnit.getWidth(); + double y1 = rRect.Top() + aSingleUnit.getHeight(); + double x2 = rRect.Right() - aSingleUnit.getWidth(); + double y2 = rRect.Bottom() - aSingleUnit.getHeight(); + + return basegfx::B2DRange(x1, y1, x2, y2); +} + +static void lcl_SubtractFlys( const SwFrame *pFrame, const SwPageFrame *pPage, + const SwRect &rRect, SwRegionRects &rRegion, basegfx::tools::B2DClipState& rClipState, SwPaintProperties const & rProperties) +{ + const SwSortedObjs& rObjs = *pPage->GetSortedObjs(); + const SwFlyFrame* pSelfFly = pFrame->IsInFly() ? pFrame->FindFlyFrame() : gProp.pSRetoucheFly2; + if (!gProp.pSRetoucheFly) + gProp.pSRetoucheFly = gProp.pSRetoucheFly2; + + for (size_t j = 0; (j < rObjs.size()) && !rRegion.empty(); ++j) + { + const SwAnchoredObject* pAnchoredObj = rObjs[j]; + const SdrObject* pSdrObj = pAnchoredObj->GetDrawObj(); + + // Do not consider invisible objects + if (!pPage->GetFormat()->GetDoc()->getIDocumentDrawModelAccess().IsVisibleLayerId(pSdrObj->GetLayer())) + continue; + + if (dynamic_cast< const SwFlyFrame *>( pAnchoredObj ) == nullptr) + continue; + + const SwFlyFrame *pFly = static_cast<const SwFlyFrame*>(pAnchoredObj); + + if (pSelfFly == pFly || gProp.pSRetoucheFly == pFly || !rRect.IsOver(pFly->Frame())) + continue; + + if (!pFly->GetFormat()->GetPrint().GetValue() && + (OUTDEV_PRINTER == gProp.pSGlobalShell->GetOut()->GetOutDevType() || + gProp.pSGlobalShell->IsPreview())) + continue; + + const bool bLowerOfSelf = pSelfFly && pFly->IsLowerOf( pSelfFly ); + + //For character bound Flys only examine those Flys in which it is not + //anchored itself. + //Why only for character bound ones you may ask? It never makes sense to + //subtract frames in which it is anchored itself right? + if (pSelfFly && pSelfFly->IsLowerOf(pFly)) + continue; + + //Any why does it not apply for the RetoucheFly too? + if (gProp.pSRetoucheFly && gProp.pSRetoucheFly->IsLowerOf(pFly)) + continue; + +#if OSL_DEBUG_LEVEL > 0 + //Flys who are anchored inside their own one, must have a bigger OrdNum + //or be character bound. + if (pSelfFly && bLowerOfSelf) + { + OSL_ENSURE( pFly->IsFlyInContentFrame() || + pSdrObj->GetOrdNumDirect() > pSelfFly->GetVirtDrawObj()->GetOrdNumDirect(), + "Fly with wrong z-Order" ); + } +#endif + + bool bStopOnHell = true; + if (pSelfFly) + { + const SdrObject *pTmp = pSelfFly->GetVirtDrawObj(); + if (pSdrObj->GetLayer() == pTmp->GetLayer()) + { + if (pSdrObj->GetOrdNumDirect() < pTmp->GetOrdNumDirect()) + //In the same layer we only observe those that are above. + continue; + } + else + { + if (!bLowerOfSelf && !pFly->GetFormat()->GetOpaque().GetValue()) + //From other layers we are only interested in non + //transparent ones or those that are internal + continue; + bStopOnHell = false; + } + } + if (gProp.pSRetoucheFly) + { + const SdrObject *pTmp = gProp.pSRetoucheFly->GetVirtDrawObj(); + if ( pSdrObj->GetLayer() == pTmp->GetLayer() ) + { + if ( pSdrObj->GetOrdNumDirect() < pTmp->GetOrdNumDirect() ) + //In the same layer we only observe those that are above. + continue; + } + else + { + if (!pFly->IsLowerOf( gProp.pSRetoucheFly ) && !pFly->GetFormat()->GetOpaque().GetValue()) + //From other layers we are only interested in non + //transparent ones or those that are internal + continue; + bStopOnHell = false; + } + } + + //If the content of the Fly is transparent, we subtract it only if it's + //contained in the hell layer. + const IDocumentDrawModelAccess& rIDDMA = pFly->GetFormat()->getIDocumentDrawModelAccess(); + bool bHell = pSdrObj->GetLayer() == rIDDMA.GetHellId(); + if ( (bStopOnHell && bHell) || + /// Change internal order of condition + /// first check "!bHell", then "..->Lower()" and "..->IsNoTextFrame()" + /// have not to be performed, if frame is in "Hell" + ( !bHell && pFly->Lower() && pFly->Lower()->IsNoTextFrame() && + (static_cast<SwNoTextFrame const*>(pFly->Lower())->IsTransparent() || + static_cast<SwNoTextFrame const*>(pFly->Lower())->HasAnimation() || + pFly->GetFormat()->GetSurround().IsContour() + ) + ) + ) + continue; + + // Own if-statements for transparent background/shadow of fly frames + // in order to handle special conditions. + if (pFly->IsBackgroundTransparent()) + { + // Background <pFly> is transparent drawn. Thus normally, its region + // have not to be subtracted from given region. + // But, if method is called for a fly frame and + // <pFly> is a direct lower of this fly frame and + // <pFly> inherites its transparent background brush from its parent, + // then <pFly> frame area have to be subtracted from given region. + // NOTE: Because in Status Quo transparent backgrounds can only be + // assigned to fly frames, the handle of this special case + // avoids drawing of transparent areas more than once, if + // a fly frame inherites a transparent background from its + // parent fly frame. + if (pFrame->IsFlyFrame() && + (pFly->GetAnchorFrame()->FindFlyFrame() == pFrame) && + pFly->GetFormat()->IsBackgroundBrushInherited() + ) + { + SwRect aRect; + SwBorderAttrAccess aAccess( SwFrame::GetCache(), static_cast<SwFrame const *>(pFly) ); + const SwBorderAttrs &rAttrs = *aAccess.Get(); + ::lcl_CalcBorderRect( aRect, pFly, rAttrs, true, rProperties ); + rRegion -= aRect; + rClipState.subtractRange(lcl_ShrinkFly(aRect)); + continue; + } + else + { + continue; + } + } + + if (bHell && pFly->GetAnchorFrame()->IsInFly()) + { + //So the border won't get dismantled by the background of the other + //Fly. + SwRect aRect; + SwBorderAttrAccess aAccess( SwFrame::GetCache(), static_cast<SwFrame const *>(pFly) ); + const SwBorderAttrs &rAttrs = *aAccess.Get(); + ::lcl_CalcBorderRect( aRect, pFly, rAttrs, true, rProperties ); + rRegion -= aRect; + rClipState.subtractRange(lcl_ShrinkFly(aRect)); + } + else + { + SwRect aRect( pFly->Prt() ); + aRect += pFly->Frame().Pos(); + rRegion -= aRect; + rClipState.subtractRange(lcl_ShrinkFly(aRect)); + } + } + if (gProp.pSRetoucheFly == gProp.pSRetoucheFly2) + gProp.pSRetoucheFly = nullptr; +} + +static void lcl_implDrawGraphicBackgrd( const SvxBrushItem& _rBackgrdBrush, + vcl::RenderContext* _pOut, + const SwRect& _rAlignedPaintRect, + const GraphicObject& _rGraphicObj, + SwPaintProperties const & properties) +{ + /// determine color of background + /// If color of background brush is not "no fill"/"auto fill" or + /// <SwPaintProperties.bSFlyMetafile> is set, use color of background brush, otherwise + /// use global retouche color. + const Color aColor( ( (_rBackgrdBrush.GetColor() != COL_TRANSPARENT) || properties.bSFlyMetafile ) + ? _rBackgrdBrush.GetColor() + : aGlobalRetoucheColor ); + + /// determine, if background color have to be drawn transparent + /// and calculate transparency percent value + sal_Int8 nTransparencyPercent = 0; + bool bDrawTransparent = false; + if ( aColor.GetTransparency() != 0 ) + /// background color is transparent --> draw transparent. + { + bDrawTransparent = true; + nTransparencyPercent = (aColor.GetTransparency()*100 + 0x7F)/0xFF; + } + else if ( (_rGraphicObj.GetAttr().GetTransparency() != 0) && + (_rBackgrdBrush.GetColor() == COL_TRANSPARENT) ) + /// graphic is drawn transparent and background color is + /// "no fill"/"auto fill" --> draw transparent + { + bDrawTransparent = true; + nTransparencyPercent = (_rGraphicObj.GetAttr().GetTransparency()*100 + 0x7F)/0xFF; + } + + if ( bDrawTransparent ) + { + /// draw background transparent + if( _pOut->GetFillColor() != aColor.GetRGBColor() ) + _pOut->SetFillColor( aColor.GetRGBColor() ); + tools::PolyPolygon aPoly( _rAlignedPaintRect.SVRect() ); + _pOut->DrawTransparent( aPoly, nTransparencyPercent ); + } + else + { + /// draw background opaque + if ( _pOut->GetFillColor() != aColor ) + _pOut->SetFillColor( aColor ); + _pOut->DrawRect( _rAlignedPaintRect.SVRect() ); + } +} + +/** + * This is a local help method to draw a background for a graphic + * + * Under certain circumstances we have to draw a background for a graphic. + * This method takes care of the conditions and draws the background with the + * corresponding color. + * Method introduced for bug fix #103876# in order to optimize drawing tiled + * background graphics. Previously, this code was integrated in method + * <lcl_DrawGraphic>. + * Method implemented as a inline, checking the conditions and calling method + * method <lcl_implDrawGraphicBackgrd(..)> for the intrinsic drawing. + * + * @param _rBackgrdBrush + * background brush contain the color the background has to be drawn. + * + * @param _pOut + * output device the background has to be drawn in. + * + * @param _rAlignedPaintRect + * paint rectangle in the output device, which has to be drawn with the background. + * rectangle have to be aligned by method ::SwAlignRect + * + * @param _rGraphicObj + * graphic object, for which the background has to be drawn. Used for checking + * the transparency of its bitmap, its type and if the graphic is drawn transparent + * + * @param _bNumberingGraphic + * boolean indicating that graphic is used as a numbering. + * + * @param _bBackgrdAlreadyDrawn + * boolean (optional; default: false) indicating, if the background is already drawn. +*/ +static inline void lcl_DrawGraphicBackgrd( const SvxBrushItem& _rBackgrdBrush, + OutputDevice* _pOut, + const SwRect& _rAlignedPaintRect, + const GraphicObject& _rGraphicObj, + bool _bNumberingGraphic, + SwPaintProperties const & properties, + bool _bBackgrdAlreadyDrawn = false) +{ + // draw background with background color, if + // (1) graphic is not used as a numbering AND + // (2) background is not already drawn AND + // (3) intrinsic graphic is transparent OR intrinsic graphic doesn't exists + if ( !_bNumberingGraphic && + !_bBackgrdAlreadyDrawn && + ( _rGraphicObj.IsTransparent() || _rGraphicObj.GetType() == GraphicType::NONE ) + ) + { + lcl_implDrawGraphicBackgrd( _rBackgrdBrush, _pOut, _rAlignedPaintRect, _rGraphicObj, properties ); + } +} + +/** + * NNOTE: the transparency of the background graphic is saved in + * SvxBrushItem.GetGraphicObject(<shell>).GetAttr().Set/GetTransparency() + * and is considered in the drawing of the graphic + * + * Thus, to provide transparent background graphic for text frames nothing + * has to be coded + * + * Use align rectangle for drawing graphic Pixel-align coordinates for + * drawing graphic + * Outsource code for drawing background of the graphic + * with a background color in method <lcl_DrawGraphicBackgrd> + * + * Also, change type of <bGrfNum> and <bClip> from <bool> to <bool> + */ +static void lcl_DrawGraphic( const SvxBrushItem& rBrush, vcl::RenderContext *pOut, + SwViewShell &rSh, const SwRect &rGrf, const SwRect &rOut, + bool bClip, bool bGrfNum, + SwPaintProperties const & properties, + bool bBackgrdAlreadyDrawn ) + // add parameter <bBackgrdAlreadyDrawn> to indicate + // that the background is already drawn. +{ + // Calculate align rectangle from parameter <rGrf> and use aligned + // rectangle <aAlignedGrfRect> in the following code + SwRect aAlignedGrfRect = rGrf; + ::SwAlignRect( aAlignedGrfRect, &rSh, pOut ); + + // Change type from <bool> to <bool>. + const bool bNotInside = bClip && !rOut.IsInside( aAlignedGrfRect ); + if ( bNotInside ) + { + pOut->Push( PushFlags::CLIPREGION ); + pOut->IntersectClipRegion( rOut.SVRect() ); + } + + GraphicObject *pGrf = const_cast<GraphicObject*>(rBrush.GetGraphicObject()); + + // Outsource drawing of background with a background color + ::lcl_DrawGraphicBackgrd( rBrush, pOut, aAlignedGrfRect, *pGrf, bGrfNum, properties, bBackgrdAlreadyDrawn ); + + // Because for drawing a graphic left-top-corner and size coordinates are + // used, these coordinates have to be determined on pixel level. + ::SwAlignGrfRect( &aAlignedGrfRect, *pOut ); + + paintGraphicUsingPrimitivesHelper(*pOut, + *pGrf, pGrf->GetAttr(), aAlignedGrfRect); + + if ( bNotInside ) + pOut->Pop(); +} + +bool DrawFillAttributes( + const drawinglayer::attribute::SdrAllFillAttributesHelperPtr& rFillAttributes, + const SwRect& rOriginalLayoutRect, + const SwRegionRects& rPaintRegion, + const basegfx::tools::B2DClipState& rClipState, + vcl::RenderContext& rOut) +{ + if(rFillAttributes.get() && rFillAttributes->isUsed()) + { + basegfx::B2DRange aPaintRange( + rPaintRegion.GetOrigin().Left(), + rPaintRegion.GetOrigin().Top(), + rPaintRegion.GetOrigin().Right(), + rPaintRegion.GetOrigin().Bottom()); + + if (!aPaintRange.isEmpty() && + !rPaintRegion.empty() && + !basegfx::fTools::equalZero(aPaintRange.getWidth()) && + !basegfx::fTools::equalZero(aPaintRange.getHeight())) + { + const SvtOptionsDrawinglayer aSvtOptionsDrawinglayer; + + // need to expand for correct AAed and non-AAed visualization as primitive. + // This must probably be removed again when we will be able to get all Writer visualization + // as primitives and Writer prepares all it's stuff in high precision coordinates (also + // needs to avoid moving boundaries around to better show overlapping stuff...) + if(aSvtOptionsDrawinglayer.IsAntiAliasing()) + { + // if AAed in principle expand by 0.5 in all directions. Since painting edges of ... etc. - the rest is truncated _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits