diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /svx/source/sdr/primitive2d | |
parent | Initial commit. (diff) | |
download | libreoffice-upstream.tar.xz libreoffice-upstream.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
16 files changed, 5660 insertions, 0 deletions
diff --git a/svx/source/sdr/primitive2d/primitivefactory2d.cxx b/svx/source/sdr/primitive2d/primitivefactory2d.cxx new file mode 100644 index 000000000..c6530fd13 --- /dev/null +++ b/svx/source/sdr/primitive2d/primitivefactory2d.cxx @@ -0,0 +1,98 @@ +/* -*- 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 <com/sun/star/uno/XComponentContext.hpp> +#include <sdr/primitive2d/primitivefactory2d.hxx> +#include <svx/sdr/contact/viewcontact.hxx> +#include <svx/svdobj.hxx> +#include <svx/svdpage.hxx> +#include <svx/unoapi.hxx> + +using namespace com::sun::star; + +css::uno::Sequence< css::uno::Reference< css::graphic::XPrimitive2D > > SAL_CALL PrimitiveFactory2D::createPrimitivesFromXShape( + const uno::Reference< drawing::XShape >& xShape, + const uno::Sequence< beans::PropertyValue >& /*aParms*/ ) +{ + css::uno::Sequence< css::uno::Reference< css::graphic::XPrimitive2D > > aRetval; + + if(xShape.is()) + { + SdrObject* pSource = SdrObject::getSdrObjectFromXShape(xShape); + + if(pSource) + { + const sdr::contact::ViewContact& rSource(pSource->GetViewContact()); + drawinglayer::primitive2d::Primitive2DContainer aSourceVal; + rSource.getViewIndependentPrimitive2DContainer(aSourceVal); + aRetval = aSourceVal.toSequence(); + } + } + + return aRetval; +} + +void PrimitiveFactory2D::createPrimitivesFromXShape( + const uno::Reference< drawing::XShape >& xShape, + const uno::Sequence< beans::PropertyValue >& /*aParms*/, + drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) +{ + if(xShape.is()) + { + SdrObject* pSource = SdrObject::getSdrObjectFromXShape(xShape); + + if(pSource) + { + const sdr::contact::ViewContact& rSource(pSource->GetViewContact()); + rSource.getViewIndependentPrimitive2DContainer(rVisitor); + } + } +} + +css::uno::Sequence< css::uno::Reference< css::graphic::XPrimitive2D > > SAL_CALL PrimitiveFactory2D::createPrimitivesFromXDrawPage( + const uno::Reference< drawing::XDrawPage >& xDrawPage, + const uno::Sequence< beans::PropertyValue >& /*aParms*/ ) +{ + css::uno::Sequence< css::uno::Reference< css::graphic::XPrimitive2D > > aRetval; + + if(xDrawPage.is()) + { + SdrPage* pSource = GetSdrPageFromXDrawPage(xDrawPage); + + if(pSource) + { + const sdr::contact::ViewContact& rSource(pSource->GetViewContact()); + drawinglayer::primitive2d::Primitive2DContainer aSourceRetval; + rSource.getViewIndependentPrimitive2DContainer(aSourceRetval); + aRetval = aSourceRetval.toSequence(); + } + } + + return aRetval; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_graphic_PrimitiveFactory2D_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new PrimitiveFactory2D); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/primitive2d/sdrattributecreator.cxx b/svx/source/sdr/primitive2d/sdrattributecreator.cxx new file mode 100644 index 000000000..bafa96f95 --- /dev/null +++ b/svx/source/sdr/primitive2d/sdrattributecreator.cxx @@ -0,0 +1,1151 @@ +/* -*- 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 <sdr/primitive2d/sdrattributecreator.hxx> +#include <svl/itemset.hxx> +#include <svx/sdmetitm.hxx> +#include <svx/sdooitm.hxx> +#include <svx/sdprcitm.hxx> +#include <svx/xdef.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <svx/xlineit0.hxx> +#include <svx/xfillit0.hxx> +#include <svx/xflbmpit.hxx> +#include <svx/xlntrit.hxx> +#include <svx/xlnwtit.hxx> +#include <svx/xlinjoit.hxx> +#include <svx/xlncapit.hxx> +#include <svx/xlnclit.hxx> +#include <svx/xlnstwit.hxx> +#include <svx/xlnedwit.hxx> +#include <svx/xlnstit.hxx> +#include <svx/xlnstcit.hxx> +#include <svx/xlnedit.hxx> +#include <svx/xlnedcit.hxx> +#include <svx/xdash.hxx> +#include <svx/xlndsit.hxx> +#include <svx/xfilluseslidebackgrounditem.hxx> +#include <svx/xfltrit.hxx> +#include <svx/xflftrit.hxx> +#include <svx/xflclit.hxx> +#include <svx/xgrscit.hxx> +#include <svx/xflhtit.hxx> +#include <svx/xflbckit.hxx> +#include <svx/xflbmsxy.hxx> +#include <svx/xflbtoxy.hxx> +#include <svx/xflboxy.hxx> +#include <svx/xflbmtit.hxx> +#include <svx/xflbstit.hxx> +#include <svx/xtextit0.hxx> +#include <drawinglayer/attribute/sdrfillgraphicattribute.hxx> +#include <svx/svdotext.hxx> +#include <sdr/attribute/sdrtextattribute.hxx> +#include <svx/xbtmpit.hxx> +#include <svl/itempool.hxx> +#include <vcl/svapp.hxx> +#include <vcl/GraphicLoader.hxx> +#include <basegfx/range/b2drange.hxx> +#include <svx/svx3ditems.hxx> +#include <com/sun/star/drawing/ProjectionMode.hpp> +#include <com/sun/star/drawing/ShadeMode.hpp> +#include <drawinglayer/attribute/sdrallattribute3d.hxx> +#include <svx/rectenum.hxx> +#include <svx/sdtfchim.hxx> +#include <svx/svdoutl.hxx> +#include <svx/svdmodel.hxx> +#include <svx/xflbmsli.hxx> +#include <editeng/editstat.hxx> +#include <osl/diagnose.h> +#include <drawinglayer/attribute/fillhatchattribute.hxx> +#include <drawinglayer/attribute/fillgradientattribute.hxx> +#include <sdr/attribute/sdreffectstextattribute.hxx> +#include <sdr/attribute/sdrlineeffectstextattribute.hxx> +#include <sdr/attribute/sdrformtextattribute.hxx> +#include <sdr/attribute/sdrlinefilleffectstextattribute.hxx> +#include <drawinglayer/attribute/sdrglowattribute.hxx> +#include <drawinglayer/attribute/sdrsceneattribute3d.hxx> +#include <drawinglayer/attribute/sdrlightingattribute3d.hxx> +#include <drawinglayer/attribute/sdrlightattribute3d.hxx> +#include <sdr/attribute/sdrfilltextattribute.hxx> +#include <com/sun/star/drawing/LineCap.hpp> + +using namespace com::sun::star; + +namespace drawinglayer +{ + namespace + { + attribute::GradientStyle XGradientStyleToGradientStyle(css::awt::GradientStyle eStyle) + { + switch(eStyle) + { + case css::awt::GradientStyle_LINEAR : + { + return attribute::GradientStyle::Linear; + } + case css::awt::GradientStyle_AXIAL : + { + return attribute::GradientStyle::Axial; + } + case css::awt::GradientStyle_RADIAL : + { + return attribute::GradientStyle::Radial; + } + case css::awt::GradientStyle_ELLIPTICAL : + { + return attribute::GradientStyle::Elliptical; + } + case css::awt::GradientStyle_SQUARE : + { + return attribute::GradientStyle::Square; + } + default : + { + return attribute::GradientStyle::Rect; // css::awt::GradientStyle_RECT + } + } + } + + attribute::HatchStyle XHatchStyleToHatchStyle(css::drawing::HatchStyle eStyle) + { + switch(eStyle) + { + case css::drawing::HatchStyle_SINGLE : + { + return attribute::HatchStyle::Single; + } + case css::drawing::HatchStyle_DOUBLE : + { + return attribute::HatchStyle::Double; + } + default : + { + return attribute::HatchStyle::Triple; // css::drawing::HatchStyle_TRIPLE + } + } + } + + basegfx::B2DLineJoin LineJointToB2DLineJoin(css::drawing::LineJoint eLineJoint) + { + switch(eLineJoint) + { + case css::drawing::LineJoint_BEVEL : + { + return basegfx::B2DLineJoin::Bevel; + } + case css::drawing::LineJoint_MIDDLE : + case css::drawing::LineJoint_MITER : + { + return basegfx::B2DLineJoin::Miter; + } + case css::drawing::LineJoint_ROUND : + { + return basegfx::B2DLineJoin::Round; + } + default : // css::drawing::LineJoint_NONE + { + return basegfx::B2DLineJoin::NONE; + } + } + } + + basegfx::B2DVector RectPointToB2DVector(RectPoint eRectPoint) + { + basegfx::B2DVector aRetval(0.0, 0.0); + + // position changes X + switch(eRectPoint) + { + case RectPoint::LT: case RectPoint::LM: case RectPoint::LB: + { + aRetval.setX(-1.0); + break; + } + + case RectPoint::RT: case RectPoint::RM: case RectPoint::RB: + { + aRetval.setX(1.0); + break; + } + + default : + { + break; + } + } + + // position changes Y + switch(eRectPoint) + { + case RectPoint::LT: case RectPoint::MT: case RectPoint::RT: + { + aRetval.setY(-1.0); + break; + } + + case RectPoint::LB: case RectPoint::MB: case RectPoint::RB: + { + aRetval.setY(1.0); + break; + } + + default : + { + break; + } + } + + return aRetval; + } + + attribute::SdrGlowAttribute createNewSdrGlowAttribute(const SfxItemSet& rSet) + { + sal_Int32 nRadius = rSet.Get(SDRATTR_GLOW_RADIUS).GetValue(); + if (!nRadius) + return attribute::SdrGlowAttribute(); + Color aColor(rSet.Get(SDRATTR_GLOW_COLOR).GetColorValue()); + sal_uInt16 nTransparency(rSet.Get(SDRATTR_GLOW_TRANSPARENCY).GetValue()); + if (nTransparency) + aColor.SetAlpha(255 - std::round(nTransparency / 100.0 * 255.0)); + + attribute::SdrGlowAttribute glowAttr{ nRadius, aColor }; + return glowAttr; + } + + sal_Int32 getSoftEdgeRadius(const SfxItemSet& rSet) + { + return rSet.Get(SDRATTR_SOFTEDGE_RADIUS).GetValue(); + } + } // end of anonymous namespace +} // end of namespace drawinglayer + + +namespace drawinglayer::primitive2d +{ + attribute::SdrLineAttribute createNewSdrLineAttribute(const SfxItemSet& rSet) + { + const css::drawing::LineStyle eStyle(rSet.Get(XATTR_LINESTYLE).GetValue()); + + if(drawing::LineStyle_NONE != eStyle) + { + sal_uInt16 nTransparence(rSet.Get(XATTR_LINETRANSPARENCE).GetValue()); + + if(nTransparence > 100) + { + nTransparence = 100; + } + + if(100 != nTransparence) + { + const sal_uInt32 nWidth(rSet.Get(XATTR_LINEWIDTH).GetValue()); + const Color aColor(rSet.Get(XATTR_LINECOLOR).GetColorValue()); + const css::drawing::LineJoint eJoint(rSet.Get(XATTR_LINEJOINT).GetValue()); + const css::drawing::LineCap eCap(rSet.Get(XATTR_LINECAP).GetValue()); + ::std::vector< double > aDotDashArray; + double fFullDotDashLen(0.0); + + if(drawing::LineStyle_DASH == eStyle) + { + const XDash& rDash = rSet.Get(XATTR_LINEDASH).GetDashValue(); + + if(rDash.GetDots() || rDash.GetDashes()) + { + fFullDotDashLen = rDash.CreateDotDashArray(aDotDashArray, static_cast<double>(nWidth)); + } + } + + return attribute::SdrLineAttribute( + LineJointToB2DLineJoin(eJoint), + static_cast<double>(nWidth), + static_cast<double>(nTransparence) * 0.01, + aColor.getBColor(), + eCap, + std::move(aDotDashArray), + fFullDotDashLen); + } + } + + return attribute::SdrLineAttribute(); + } + + attribute::SdrLineStartEndAttribute createNewSdrLineStartEndAttribute( + const SfxItemSet& rSet, + double fWidth) + { + const sal_Int32 nTempStartWidth(rSet.Get(XATTR_LINESTARTWIDTH).GetValue()); + const sal_Int32 nTempEndWidth(rSet.Get(XATTR_LINEENDWIDTH).GetValue()); + basegfx::B2DPolyPolygon aStartPolyPolygon; + basegfx::B2DPolyPolygon aEndPolyPolygon; + double fStartWidth(0.0); + double fEndWidth(0.0); + bool bStartActive(false); + bool bEndActive(false); + bool bStartCentered(true); + bool bEndCentered(true); + + if(nTempStartWidth) + { + if(nTempStartWidth < 0) + { + fStartWidth = (static_cast<double>(-nTempStartWidth) * fWidth) * 0.01; + } + else + { + fStartWidth = static_cast<double>(nTempStartWidth); + } + + if(0.0 != fStartWidth) + { + aStartPolyPolygon = rSet.Get(XATTR_LINESTART).GetLineStartValue(); + + if(aStartPolyPolygon.count() && aStartPolyPolygon.getB2DPolygon(0).count()) + { + bStartActive = true; + bStartCentered = rSet.Get(XATTR_LINESTARTCENTER).GetValue(); + } + } + } + + if(nTempEndWidth) + { + if(nTempEndWidth < 0) + { + fEndWidth = (static_cast<double>(-nTempEndWidth) * fWidth) * 0.01; + } + else + { + fEndWidth = static_cast<double>(nTempEndWidth); + } + + if(0.0 != fEndWidth) + { + aEndPolyPolygon = rSet.Get(XATTR_LINEEND).GetLineEndValue(); + + if(aEndPolyPolygon.count() && aEndPolyPolygon.getB2DPolygon(0).count()) + { + bEndActive = true; + bEndCentered = rSet.Get(XATTR_LINEENDCENTER).GetValue(); + } + } + } + + if(bStartActive || bEndActive) + { + return attribute::SdrLineStartEndAttribute( + aStartPolyPolygon, aEndPolyPolygon, fStartWidth, fEndWidth, + bStartActive, bEndActive, bStartCentered, bEndCentered); + } + + return attribute::SdrLineStartEndAttribute(); + } + + attribute::SdrShadowAttribute createNewSdrShadowAttribute(const SfxItemSet& rSet) + { + const bool bShadow(rSet.Get(SDRATTR_SHADOW).GetValue()); + + if(bShadow) + { + sal_uInt16 nTransparence(rSet.Get(SDRATTR_SHADOWTRANSPARENCE).GetValue()); + + if(nTransparence > 100) + { + nTransparence = 100; + } + + if(nTransparence) + { + sal_uInt16 nFillTransparence(rSet.Get(XATTR_FILLTRANSPARENCE).GetValue()); + + if(nFillTransparence > 100) + { + nFillTransparence = 100; + } + + if(nTransparence == nFillTransparence) + { + // shadow does not really have an own transparence, but the application + // sets the shadow transparence equal to the object transparence for + // convenience. This is not useful for primitive creation, so take + // this as no shadow transparence + nTransparence = 0; + } + } + + if(100 != nTransparence) + { + const basegfx::B2DVector aOffset( + static_cast<double>(rSet.Get(SDRATTR_SHADOWXDIST).GetValue()), + static_cast<double>(rSet.Get(SDRATTR_SHADOWYDIST).GetValue())); + + const basegfx::B2DVector aSize( + static_cast<double>(rSet.Get(SDRATTR_SHADOWSIZEX).GetValue()), + static_cast<double>(rSet.Get(SDRATTR_SHADOWSIZEY).GetValue())); + + const Color aColor(rSet.Get(SDRATTR_SHADOWCOLOR).GetColorValue()); + + sal_Int32 nBlur(rSet.Get(SDRATTR_SHADOWBLUR).GetValue()); + + return attribute::SdrShadowAttribute(aOffset, aSize, static_cast<double>(nTransparence) * 0.01,nBlur, aColor.getBColor()); + } + } + + return attribute::SdrShadowAttribute(); + } + + attribute::SdrFillAttribute createNewSdrFillAttribute(const SfxItemSet& rSet) + { + const drawing::FillStyle eStyle(rSet.Get(XATTR_FILLSTYLE).GetValue()); + + sal_uInt16 nTransparence(rSet.Get(XATTR_FILLTRANSPARENCE).GetValue()); + + if(nTransparence > 100) + { + nTransparence = 100; + } + + if(drawing::FillStyle_NONE == eStyle) + { + XFillUseSlideBackgroundItem aBckItem(rSet.Get(XATTR_FILLUSESLIDEBACKGROUND)); + const bool bSlideBackgroundFill(aBckItem.GetValue()); + + if(bSlideBackgroundFill) + { + // we have SlideBackgroundFill mode, create a + // SdrFillAttribute accordingly + return attribute::SdrFillAttribute(true); + } + } + + if(drawing::FillStyle_NONE != eStyle) + { + if(100 != nTransparence) + { + // need to check XFillFloatTransparence, object fill may still be completely transparent + const XFillFloatTransparenceItem* pGradientItem; + + if((pGradientItem = rSet.GetItemIfSet(XATTR_FILLFLOATTRANSPARENCE, true)) + && pGradientItem->IsEnabled()) + { + const XGradient& rGradient = pGradientItem->GetGradientValue(); + const sal_uInt8 nStartLuminance(rGradient.GetStartColor().GetLuminance()); + const sal_uInt8 nEndLuminance(rGradient.GetEndColor().GetLuminance()); + const bool bCompletelyTransparent(0xff == nStartLuminance && 0xff == nEndLuminance); + + if(bCompletelyTransparent) + { + nTransparence = 100; + } + } + } + + if(100 != nTransparence) + { + const Color aColor(rSet.Get(XATTR_FILLCOLOR).GetColorValue()); + attribute::FillGradientAttribute aGradient; + attribute::FillHatchAttribute aHatch; + attribute::SdrFillGraphicAttribute aFillGraphic; + + switch(eStyle) + { + default: + { + // nothing to do, color is defined + break; + } + case drawing::FillStyle_GRADIENT : + { + XGradient aXGradient(rSet.Get(XATTR_FILLGRADIENT).GetGradientValue()); + + const Color aStartColor(aXGradient.GetStartColor()); + const sal_uInt16 nStartIntens(aXGradient.GetStartIntens()); + basegfx::BColor aStart(aStartColor.getBColor()); + + if(nStartIntens != 100) + { + const basegfx::BColor aBlack; + aStart = interpolate(aBlack, aStart, static_cast<double>(nStartIntens) * 0.01); + } + + const Color aEndColor(aXGradient.GetEndColor()); + const sal_uInt16 nEndIntens(aXGradient.GetEndIntens()); + basegfx::BColor aEnd(aEndColor.getBColor()); + + if(nEndIntens != 100) + { + const basegfx::BColor aBlack; + aEnd = interpolate(aBlack, aEnd, static_cast<double>(nEndIntens) * 0.01); + } + + aGradient = attribute::FillGradientAttribute( + XGradientStyleToGradientStyle(aXGradient.GetGradientStyle()), + static_cast<double>(aXGradient.GetBorder()) * 0.01, + static_cast<double>(aXGradient.GetXOffset()) * 0.01, + static_cast<double>(aXGradient.GetYOffset()) * 0.01, + toRadians(aXGradient.GetAngle()), + aStart, + aEnd, + rSet.Get(XATTR_GRADIENTSTEPCOUNT).GetValue()); + + break; + } + case drawing::FillStyle_HATCH : + { + const XHatch& rHatch(rSet.Get(XATTR_FILLHATCH).GetHatchValue()); + const Color aColorB(rHatch.GetColor()); + + aHatch = attribute::FillHatchAttribute( + XHatchStyleToHatchStyle(rHatch.GetHatchStyle()), + static_cast<double>(rHatch.GetDistance()), + toRadians(rHatch.GetAngle()), + aColorB.getBColor(), + 3, // same default as VCL, a minimum of three discrete units (pixels) offset + rSet.Get(XATTR_FILLBACKGROUND).GetValue()); + + break; + } + case drawing::FillStyle_BITMAP : + { + aFillGraphic = createNewSdrFillGraphicAttribute(rSet); + break; + } + } + + return attribute::SdrFillAttribute( + static_cast<double>(nTransparence) * 0.01, + aColor.getBColor(), + aGradient, + aHatch, + aFillGraphic); + } + } + + if(nTransparence == 100) + { + attribute::FillGradientAttribute aGradient; + attribute::FillHatchAttribute aHatch; + attribute::SdrFillGraphicAttribute aFillGraphic; + return attribute::SdrFillAttribute( + 1, + basegfx::BColor( 0, 0, 0 ), + aGradient, + aHatch, + aFillGraphic); + } + + return attribute::SdrFillAttribute(); + } + + // #i101508# Support handing over given text-to-border distances + attribute::SdrTextAttribute createNewSdrTextAttribute( + const SfxItemSet& rSet, + const SdrText& rText, + const sal_Int32* pLeft, + const sal_Int32* pUpper, + const sal_Int32* pRight, + const sal_Int32* pLower) + { + const SdrTextObj& rTextObj = rText.GetObject(); + + // Save chaining attributes + bool bChainable = rTextObj.IsChainable(); + + + if(rText.GetOutlinerParaObject()) + { + // added TextEdit text suppression + bool bInEditMode(false); + + if(rText.GetObject().getTextCount() > 1) + { + bInEditMode = rTextObj.IsInEditMode() && rText.GetObject().getActiveText() == &rText; + } + else + { + bInEditMode = rTextObj.IsInEditMode(); + } + + OutlinerParaObject aOutlinerParaObject(*rText.GetOutlinerParaObject()); + + if(bInEditMode) + { + std::optional<OutlinerParaObject> pTempObj = rTextObj.CreateEditOutlinerParaObject(); + + if(pTempObj) + { + aOutlinerParaObject = *pTempObj; + } + else + { + // #i100537# + // CreateEditOutlinerParaObject() returning no object does not mean that + // text edit mode is not active. Do not reset the flag here + // bInEditMode = false; + } + } + + const SdrTextAniKind eAniKind(rTextObj.GetTextAniKind()); + + // #i107346# + const SdrOutliner& rDrawTextOutliner(rText.GetObject().getSdrModelFromSdrObject().GetDrawOutliner(&rTextObj)); + const bool bWrongSpell(rDrawTextOutliner.GetControlWord() & EEControlBits::ONLINESPELLING); + + return attribute::SdrTextAttribute( + rText, + aOutlinerParaObject, + rSet.Get(XATTR_FORMTXTSTYLE).GetValue(), + pLeft ? *pLeft : rTextObj.GetTextLeftDistance(), + pUpper ? *pUpper : rTextObj.GetTextUpperDistance(), + pRight ? *pRight : rTextObj.GetTextRightDistance(), + pLower ? *pLower : rTextObj.GetTextLowerDistance(), + rTextObj.GetTextHorizontalAdjust(rSet), + rTextObj.GetTextVerticalAdjust(rSet), + rSet.Get(SDRATTR_TEXT_CONTOURFRAME).GetValue(), + rTextObj.IsFitToSize(), + rTextObj.IsAutoFit(), + rSet.Get(XATTR_FORMTXTHIDEFORM).GetValue(), + SdrTextAniKind::Blink == eAniKind, + SdrTextAniKind::Scroll == eAniKind || SdrTextAniKind::Alternate == eAniKind || SdrTextAniKind::Slide == eAniKind, + bInEditMode, + rSet.Get(SDRATTR_TEXT_USEFIXEDCELLHEIGHT).GetValue(), + bWrongSpell, + bChainable); + } + + return attribute::SdrTextAttribute(); + } + + attribute::FillGradientAttribute createNewTransparenceGradientAttribute(const SfxItemSet& rSet) + { + const XFillFloatTransparenceItem* pGradientItem; + + if((pGradientItem = rSet.GetItemIfSet(XATTR_FILLFLOATTRANSPARENCE)) + && pGradientItem->IsEnabled()) + { + // test if float transparence is completely transparent + const XGradient& rGradient = pGradientItem->GetGradientValue(); + const sal_uInt8 nStartLuminance(rGradient.GetStartColor().GetLuminance()); + const sal_uInt8 nEndLuminance(rGradient.GetEndColor().GetLuminance()); + const bool bCompletelyTransparent(0xff == nStartLuminance && 0xff == nEndLuminance); + const bool bNotTransparent(0x00 == nStartLuminance && 0x00 == nEndLuminance); + + // create nothing when completely transparent: This case is already checked for the + // normal fill attributes, XFILL_NONE will be used. + // create nothing when not transparent: use normal fill, no need t create a FillGradientAttribute. + // Both cases are optimizations, always creating FillGradientAttribute will work, too + if(!bNotTransparent && !bCompletelyTransparent) + { + const double fStartLum(nStartLuminance / 255.0); + const double fEndLum(nEndLuminance / 255.0); + + return attribute::FillGradientAttribute( + XGradientStyleToGradientStyle(rGradient.GetGradientStyle()), + static_cast<double>(rGradient.GetBorder()) * 0.01, + static_cast<double>(rGradient.GetXOffset()) * 0.01, + static_cast<double>(rGradient.GetYOffset()) * 0.01, + toRadians(rGradient.GetAngle()), + basegfx::BColor(fStartLum, fStartLum, fStartLum), + basegfx::BColor(fEndLum, fEndLum, fEndLum)); + } + } + + return attribute::FillGradientAttribute(); + } + + attribute::SdrFillGraphicAttribute createNewSdrFillGraphicAttribute(const SfxItemSet& rSet) + { + Graphic aGraphic(rSet.Get(XATTR_FILLBITMAP).GetGraphicObject().GetGraphic()); + + OUString aOriginURL = aGraphic.getOriginURL(); + if (aGraphic.GetType() == GraphicType::Default && !aOriginURL.isEmpty()) + { + aGraphic = vcl::graphic::loadFromURL(aGraphic.getOriginURL()); + aGraphic.setOriginURL(aOriginURL); + } + + if(GraphicType::Bitmap != aGraphic.GetType() && GraphicType::GdiMetafile != aGraphic.GetType()) + { + // no content if not bitmap or metafile + OSL_ENSURE(false, "No fill graphic in SfxItemSet (!)"); + return attribute::SdrFillGraphicAttribute(); + } + + Size aPrefSize(aGraphic.GetPrefSize()); + + if(!aPrefSize.Width() || !aPrefSize.Height()) + { + // if there is no logical size, create a size from pixel size and set MapMode accordingly + if(GraphicType::Bitmap == aGraphic.GetType()) + { + aGraphic.SetPrefSize(aGraphic.GetBitmapEx().GetSizePixel()); + aGraphic.SetPrefMapMode(MapMode(MapUnit::MapPixel)); + aPrefSize = aGraphic.GetPrefSize(); + } + } + + if(!aPrefSize.Width() || !aPrefSize.Height()) + { + // no content if no size + OSL_ENSURE(false, "Graphic has no size in SfxItemSet (!)"); + return attribute::SdrFillGraphicAttribute(); + } + + // convert size and MapMode to destination logical size and MapMode + const MapUnit aDestinationMapUnit(rSet.GetPool()->GetMetric(0)); + basegfx::B2DVector aGraphicLogicSize(aGraphic.GetPrefSize().Width(), aGraphic.GetPrefSize().Height()); + + if (aGraphic.GetPrefMapMode().GetMapUnit() != aDestinationMapUnit) + { + // #i100360# for MapUnit::MapPixel, LogicToLogic will not work properly, + // so fallback to Application::GetDefaultDevice() + Size aNewSize(0, 0); + + if(MapUnit::MapPixel == aGraphic.GetPrefMapMode().GetMapUnit()) + { + aNewSize = Application::GetDefaultDevice()->PixelToLogic( + aGraphic.GetPrefSize(), + MapMode(aDestinationMapUnit)); + } + else + { + aNewSize = OutputDevice::LogicToLogic( + aGraphic.GetPrefSize(), + aGraphic.GetPrefMapMode(), + MapMode(aDestinationMapUnit)); + } + + // #i124002# do not set new size using SetPrefSize at the graphic, this will lead to problems. + // Instead, adapt the GraphicLogicSize which will be used for further decompositions + aGraphicLogicSize = basegfx::B2DVector(aNewSize.Width(), aNewSize.Height()); + } + + // get size + const basegfx::B2DVector aSize( + static_cast<double>(rSet.Get(XATTR_FILLBMP_SIZEX).GetValue()), + static_cast<double>(rSet.Get(XATTR_FILLBMP_SIZEY).GetValue())); + const basegfx::B2DVector aOffset( + static_cast<double>(rSet.Get(XATTR_FILLBMP_TILEOFFSETX).GetValue()), + static_cast<double>(rSet.Get(XATTR_FILLBMP_TILEOFFSETY).GetValue())); + const basegfx::B2DVector aOffsetPosition( + static_cast<double>(rSet.Get(XATTR_FILLBMP_POSOFFSETX).GetValue()), + static_cast<double>(rSet.Get(XATTR_FILLBMP_POSOFFSETY).GetValue())); + + return attribute::SdrFillGraphicAttribute( + aGraphic, + aGraphicLogicSize, + aSize, + aOffset, + aOffsetPosition, + RectPointToB2DVector(rSet.GetItem<XFillBmpPosItem>(XATTR_FILLBMP_POS)->GetValue()), + rSet.Get(XATTR_FILLBMP_TILE).GetValue(), + rSet.Get(XATTR_FILLBMP_STRETCH).GetValue(), + rSet.Get(XATTR_FILLBMP_SIZELOG).GetValue()); + } + + attribute::SdrEffectsTextAttribute createNewSdrEffectsTextAttribute( + const SfxItemSet& rSet, + const SdrText* pText, + bool bSuppressText) + { + attribute::SdrTextAttribute aText; + + // #i98072# added option to suppress text + // look for text first + if(!bSuppressText && pText) + { + aText = createNewSdrTextAttribute(rSet, *pText); + } + + // try shadow + const attribute::SdrShadowAttribute aShadow(createNewSdrShadowAttribute(rSet)); + const attribute::SdrGlowAttribute aGlow(createNewSdrGlowAttribute(rSet)); + const sal_Int32 nSoftEdgeRadius(getSoftEdgeRadius(rSet)); + + return attribute::SdrEffectsTextAttribute(aShadow, aText, aGlow, nSoftEdgeRadius); + } + + attribute::SdrLineEffectsTextAttribute createNewSdrLineEffectsTextAttribute( + const SfxItemSet& rSet, + const SdrText* pText) + { + attribute::SdrLineAttribute aLine; + attribute::SdrLineStartEndAttribute aLineStartEnd; + attribute::SdrTextAttribute aText; + bool bFontworkHideContour(false); + + // look for text first + if(pText) + { + aText = createNewSdrTextAttribute(rSet, *pText); + + // when object has text and text is fontwork and hide contour is set for fontwork, force + // line and fill style to empty + if(!aText.isDefault() + && !aText.getSdrFormTextAttribute().isDefault() + && aText.isHideContour()) + { + bFontworkHideContour = true; + } + } + + // try line style + if(!bFontworkHideContour) + { + aLine = createNewSdrLineAttribute(rSet); + + if(!aLine.isDefault()) + { + // try LineStartEnd + aLineStartEnd = createNewSdrLineStartEndAttribute(rSet, aLine.getWidth()); + } + } + + if(!aLine.isDefault() || !aText.isDefault()) + { + // try shadow + const attribute::SdrShadowAttribute aShadow(createNewSdrShadowAttribute(rSet)); + const attribute::SdrGlowAttribute aGlow = createNewSdrGlowAttribute(rSet); + const sal_Int32 nSoftEdgeRadius(getSoftEdgeRadius(rSet)); + + return attribute::SdrLineEffectsTextAttribute(aLine, aLineStartEnd, aShadow, aText, + aGlow, nSoftEdgeRadius); + } + + return attribute::SdrLineEffectsTextAttribute(); + } + + attribute::SdrLineFillEffectsTextAttribute createNewSdrLineFillEffectsTextAttribute( + const SfxItemSet& rSet, + const SdrText* pText, + bool bHasContent) + { + attribute::SdrLineAttribute aLine; + attribute::SdrFillAttribute aFill; + attribute::SdrLineStartEndAttribute aLineStartEnd; + attribute::FillGradientAttribute aFillFloatTransGradient; + attribute::SdrTextAttribute aText; + bool bFontworkHideContour(false); + + // look for text first + if(pText) + { + aText = createNewSdrTextAttribute(rSet, *pText); + + // when object has text and text is fontwork and hide contour is set for fontwork, force + // line and fill style to empty + if(!aText.getSdrFormTextAttribute().isDefault() && aText.isHideContour()) + { + bFontworkHideContour = true; + } + } + + if(!bFontworkHideContour) + { + // try line style + aLine = createNewSdrLineAttribute(rSet); + + if(!aLine.isDefault()) + { + // try LineStartEnd + aLineStartEnd = createNewSdrLineStartEndAttribute(rSet, aLine.getWidth()); + } + + // try fill style + aFill = createNewSdrFillAttribute(rSet); + + if(!aFill.isDefault()) + { + // try fillfloattransparence + aFillFloatTransGradient = createNewTransparenceGradientAttribute(rSet); + } + } + + // bHasContent is used from OLE and graphic objects. Normally a possible shadow + // depends on line, fill or text to be set, but for these objects it is possible + // to have none of these, but still content which needs to have a shadow (if set), + // so shadow needs to be tried + if(bHasContent || !aLine.isDefault() || !aFill.isDefault() || !aText.isDefault()) + { + // try shadow + const attribute::SdrShadowAttribute aShadow = createNewSdrShadowAttribute(rSet); + + // glow + const attribute::SdrGlowAttribute aGlow = createNewSdrGlowAttribute(rSet); + + const sal_Int32 nSoftEdgeRadius(getSoftEdgeRadius(rSet)); + + return attribute::SdrLineFillEffectsTextAttribute(aLine, aFill, aLineStartEnd, + aShadow, aFillFloatTransGradient, + aText, aGlow, nSoftEdgeRadius); + } + + return attribute::SdrLineFillEffectsTextAttribute(); + } + + attribute::SdrLineFillShadowAttribute3D createNewSdrLineFillShadowAttribute(const SfxItemSet& rSet, bool bSuppressFill) + { + attribute::SdrFillAttribute aFill; + attribute::SdrLineStartEndAttribute aLineStartEnd; + attribute::SdrShadowAttribute aShadow; + attribute::FillGradientAttribute aFillFloatTransGradient; + + // try line style + const attribute::SdrLineAttribute aLine(createNewSdrLineAttribute(rSet)); + + if(!aLine.isDefault()) + { + // try LineStartEnd + aLineStartEnd = createNewSdrLineStartEndAttribute(rSet, aLine.getWidth()); + } + + // try fill style + if(!bSuppressFill) + { + aFill = createNewSdrFillAttribute(rSet); + + if(!aFill.isDefault()) + { + // try fillfloattransparence + aFillFloatTransGradient = createNewTransparenceGradientAttribute(rSet); + } + } + + if(!aLine.isDefault() || !aFill.isDefault()) + { + // try shadow + aShadow = createNewSdrShadowAttribute(rSet); + + return attribute::SdrLineFillShadowAttribute3D( + aLine, aFill, aLineStartEnd, aShadow, aFillFloatTransGradient); + } + + return attribute::SdrLineFillShadowAttribute3D(); + } + + attribute::SdrSceneAttribute createNewSdrSceneAttribute(const SfxItemSet& rSet) + { + // get perspective + css::drawing::ProjectionMode aProjectionMode(css::drawing::ProjectionMode_PARALLEL); + const sal_uInt16 nProjectionValue(rSet.Get(SDRATTR_3DSCENE_PERSPECTIVE).GetValue()); + + if(1 == nProjectionValue) + { + aProjectionMode = css::drawing::ProjectionMode_PERSPECTIVE; + } + + // get distance + const double fDistance(rSet.Get(SDRATTR_3DSCENE_DISTANCE).GetValue()); + + // get shadow slant + const double fShadowSlant( + basegfx::deg2rad(rSet.Get(SDRATTR_3DSCENE_SHADOW_SLANT).GetValue())); + + // get shade mode + css::drawing::ShadeMode aShadeMode(css::drawing::ShadeMode_FLAT); + const sal_uInt16 nShadeValue(rSet.Get(SDRATTR_3DSCENE_SHADE_MODE).GetValue()); + + if(1 == nShadeValue) + { + aShadeMode = css::drawing::ShadeMode_PHONG; + } + else if(2 == nShadeValue) + { + aShadeMode = css::drawing::ShadeMode_SMOOTH; + } + else if(3 == nShadeValue) + { + aShadeMode = css::drawing::ShadeMode_DRAFT; + } + + // get two sided lighting + const bool bTwoSidedLighting(rSet.Get(SDRATTR_3DSCENE_TWO_SIDED_LIGHTING).GetValue()); + + return attribute::SdrSceneAttribute(fDistance, fShadowSlant, aProjectionMode, aShadeMode, bTwoSidedLighting); + } + + attribute::SdrLightingAttribute createNewSdrLightingAttribute(const SfxItemSet& rSet) + { + // extract lights from given SfxItemSet (from scene) + ::std::vector< attribute::Sdr3DLightAttribute > aLightVector; + + if(rSet.Get(SDRATTR_3DSCENE_LIGHTON_1).GetValue()) + { + const basegfx::BColor aColor(rSet.Get(SDRATTR_3DSCENE_LIGHTCOLOR_1).GetValue().getBColor()); + const basegfx::B3DVector aDirection(rSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_1).GetValue()); + aLightVector.emplace_back(aColor, aDirection, true); + } + + if(rSet.Get(SDRATTR_3DSCENE_LIGHTON_2).GetValue()) + { + const basegfx::BColor aColor(rSet.Get(SDRATTR_3DSCENE_LIGHTCOLOR_2).GetValue().getBColor()); + const basegfx::B3DVector aDirection(rSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_2).GetValue()); + aLightVector.emplace_back(aColor, aDirection, false); + } + + if(rSet.Get(SDRATTR_3DSCENE_LIGHTON_3).GetValue()) + { + const basegfx::BColor aColor(rSet.Get(SDRATTR_3DSCENE_LIGHTCOLOR_3).GetValue().getBColor()); + const basegfx::B3DVector aDirection(rSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_3).GetValue()); + aLightVector.emplace_back(aColor, aDirection, false); + } + + if(rSet.Get(SDRATTR_3DSCENE_LIGHTON_4).GetValue()) + { + const basegfx::BColor aColor(rSet.Get(SDRATTR_3DSCENE_LIGHTCOLOR_4).GetValue().getBColor()); + const basegfx::B3DVector aDirection(rSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_4).GetValue()); + aLightVector.emplace_back(aColor, aDirection, false); + } + + if(rSet.Get(SDRATTR_3DSCENE_LIGHTON_5).GetValue()) + { + const basegfx::BColor aColor(rSet.Get(SDRATTR_3DSCENE_LIGHTCOLOR_5).GetValue().getBColor()); + const basegfx::B3DVector aDirection(rSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_5).GetValue()); + aLightVector.emplace_back(aColor, aDirection, false); + } + + if(rSet.Get(SDRATTR_3DSCENE_LIGHTON_6).GetValue()) + { + const basegfx::BColor aColor(rSet.Get(SDRATTR_3DSCENE_LIGHTCOLOR_6).GetValue().getBColor()); + const basegfx::B3DVector aDirection(rSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_6).GetValue()); + aLightVector.emplace_back(aColor, aDirection, false); + } + + if(rSet.Get(SDRATTR_3DSCENE_LIGHTON_7).GetValue()) + { + const basegfx::BColor aColor(rSet.Get(SDRATTR_3DSCENE_LIGHTCOLOR_7).GetValue().getBColor()); + const basegfx::B3DVector aDirection(rSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_7).GetValue()); + aLightVector.emplace_back(aColor, aDirection, false); + } + + if(rSet.Get(SDRATTR_3DSCENE_LIGHTON_8).GetValue()) + { + const basegfx::BColor aColor(rSet.Get(SDRATTR_3DSCENE_LIGHTCOLOR_8).GetValue().getBColor()); + const basegfx::B3DVector aDirection(rSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_8).GetValue()); + aLightVector.emplace_back(aColor, aDirection, false); + } + + // get ambient color + const Color aAmbientValue(rSet.Get(SDRATTR_3DSCENE_AMBIENTCOLOR).GetValue()); + const basegfx::BColor aAmbientLight(aAmbientValue.getBColor()); + + return attribute::SdrLightingAttribute(aAmbientLight, std::move(aLightVector)); + } + + void calculateRelativeCornerRadius(sal_Int32 nRadius, const basegfx::B2DRange& rObjectRange, double& rfCornerRadiusX, double& rfCornerRadiusY) + { + rfCornerRadiusX = rfCornerRadiusY = static_cast<double>(nRadius); + + if(0.0 != rfCornerRadiusX) + { + const double fHalfObjectWidth(rObjectRange.getWidth() * 0.5); + + if(0.0 != fHalfObjectWidth) + { + if(rfCornerRadiusX < 0.0) + { + rfCornerRadiusX = 0.0; + } + + if(rfCornerRadiusX > fHalfObjectWidth) + { + rfCornerRadiusX = fHalfObjectWidth; + } + + rfCornerRadiusX /= fHalfObjectWidth; + } + else + { + rfCornerRadiusX = 0.0; + } + } + + if(0.0 == rfCornerRadiusY) + return; + + const double fHalfObjectHeight(rObjectRange.getHeight() * 0.5); + + if(0.0 != fHalfObjectHeight) + { + if(rfCornerRadiusY < 0.0) + { + rfCornerRadiusY = 0.0; + } + + if(rfCornerRadiusY > fHalfObjectHeight) + { + rfCornerRadiusY = fHalfObjectHeight; + } + + rfCornerRadiusY /= fHalfObjectHeight; + } + else + { + rfCornerRadiusY = 0.0; + } + } + + // #i101508# Support handing over given text-to-border distances + attribute::SdrFillTextAttribute createNewSdrFillTextAttribute( + const SfxItemSet& rSet, + const SdrText* pText, + const sal_Int32* pLeft, + const sal_Int32* pUpper, + const sal_Int32* pRight, + const sal_Int32* pLower) + { + attribute::SdrFillAttribute aFill; + attribute::FillGradientAttribute aFillFloatTransGradient; + attribute::SdrTextAttribute aText; + bool bFontworkHideContour(false); + + // look for text first + if(pText) + { + aText = createNewSdrTextAttribute(rSet, *pText, pLeft, pUpper, pRight, pLower); + + // when object has text and text is fontwork and hide contour is set for fontwork, force + // fill style to empty + if(!aText.getSdrFormTextAttribute().isDefault() && aText.isHideContour()) + { + bFontworkHideContour = true; + } + } + + if(!bFontworkHideContour) + { + // try fill style + aFill = createNewSdrFillAttribute(rSet); + + if(!aFill.isDefault()) + { + // try fillfloattransparence + aFillFloatTransGradient = createNewTransparenceGradientAttribute(rSet); + } + } + + if(!aFill.isDefault() || !aText.isDefault()) + { + return attribute::SdrFillTextAttribute(aFill, aFillFloatTransGradient, aText); + } + + return attribute::SdrFillTextAttribute(); + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/primitive2d/sdrcaptionprimitive2d.cxx b/svx/source/sdr/primitive2d/sdrcaptionprimitive2d.cxx new file mode 100644 index 000000000..50787ed39 --- /dev/null +++ b/svx/source/sdr/primitive2d/sdrcaptionprimitive2d.cxx @@ -0,0 +1,159 @@ +/* -*- 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 <sdr/primitive2d/sdrcaptionprimitive2d.hxx> +#include <basegfx/polygon/b2dpolypolygon.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <sdr/primitive2d/sdrdecompositiontools.hxx> +#include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx> +#include <drawinglayer/primitive2d/sdrdecompositiontools2d.hxx> + + +using namespace com::sun::star; + + +namespace drawinglayer::primitive2d +{ + void SdrCaptionPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*aViewInformation*/) const + { + Primitive2DContainer aRetval; + + // create unit outline polygon + const basegfx::B2DPolygon aUnitOutline(basegfx::utils::createPolygonFromRect( + basegfx::B2DRange(0.0, 0.0, 1.0, 1.0), + getCornerRadiusX(), + getCornerRadiusY())); + + // add fill + if(getSdrLFSTAttribute().getFill().isDefault()) + { + // create invisible fill for HitTest + aRetval.push_back( + createHiddenGeometryPrimitives2D( + true, + basegfx::B2DPolyPolygon(aUnitOutline), + getTransform())); + } + else + { + basegfx::B2DPolyPolygon aTransformed(aUnitOutline); + + aTransformed.transform(getTransform()); + aRetval.push_back( + createPolyPolygonFillPrimitive( + aTransformed, + getSdrLFSTAttribute().getFill(), + getSdrLFSTAttribute().getFillFloatTransGradient())); + } + + // add line + if(getSdrLFSTAttribute().getLine().isDefault()) + { + // create invisible line for HitTest/BoundRect + aRetval.push_back( + createHiddenGeometryPrimitives2D( + false, + basegfx::B2DPolyPolygon(aUnitOutline), + getTransform())); + + aRetval.push_back( + createHiddenGeometryPrimitives2D( + false, + basegfx::B2DPolyPolygon(getTail()), + {})); + } + else + { + basegfx::B2DPolygon aTransformed(aUnitOutline); + + aTransformed.transform(getTransform()); + aRetval.push_back( + createPolygonLinePrimitive( + aTransformed, + getSdrLFSTAttribute().getLine(), + attribute::SdrLineStartEndAttribute())); + + aRetval.push_back( + createPolygonLinePrimitive( + getTail(), + getSdrLFSTAttribute().getLine(), + getSdrLFSTAttribute().getLineStartEnd())); + } + + // add text + if(!getSdrLFSTAttribute().getText().isDefault()) + { + aRetval.push_back( + createTextPrimitive( + basegfx::B2DPolyPolygon(aUnitOutline), + getTransform(), + getSdrLFSTAttribute().getText(), + getSdrLFSTAttribute().getLine(), + false, + false)); + } + + // add shadow + if(!getSdrLFSTAttribute().getShadow().isDefault()) + { + aRetval = createEmbeddedShadowPrimitive(std::move(aRetval), getSdrLFSTAttribute().getShadow()); + } + + rContainer.append(std::move(aRetval)); + } + + SdrCaptionPrimitive2D::SdrCaptionPrimitive2D( + const basegfx::B2DHomMatrix& rTransform, + const attribute::SdrLineFillEffectsTextAttribute& rSdrLFSTAttribute, + const basegfx::B2DPolygon& rTail, + double fCornerRadiusX, + double fCornerRadiusY) + : maTransform(rTransform), + maSdrLFSTAttribute(rSdrLFSTAttribute), + maTail(rTail), + mfCornerRadiusX(fCornerRadiusX), + mfCornerRadiusY(fCornerRadiusY) + { + } + + bool SdrCaptionPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const + { + if(BufferedDecompositionPrimitive2D::operator==(rPrimitive)) + { + const SdrCaptionPrimitive2D& rCompare = static_cast<const SdrCaptionPrimitive2D&>(rPrimitive); + + return (getCornerRadiusX() == rCompare.getCornerRadiusX() + && getCornerRadiusY() == rCompare.getCornerRadiusY() + && getTail() == rCompare.getTail() + && getTransform() == rCompare.getTransform() + && getSdrLFSTAttribute() == rCompare.getSdrLFSTAttribute()); + } + + return false; + } + + // provide unique ID + sal_uInt32 SdrCaptionPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_SDRCAPTIONPRIMITIVE2D; + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/primitive2d/sdrconnectorprimitive2d.cxx b/svx/source/sdr/primitive2d/sdrconnectorprimitive2d.cxx new file mode 100644 index 000000000..aa663fb7a --- /dev/null +++ b/svx/source/sdr/primitive2d/sdrconnectorprimitive2d.cxx @@ -0,0 +1,107 @@ +/* -*- 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 <sdr/primitive2d/sdrconnectorprimitive2d.hxx> +#include <sdr/primitive2d/sdrdecompositiontools.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx> +#include <drawinglayer/primitive2d/sdrdecompositiontools2d.hxx> +#include <basegfx/polygon/b2dpolypolygon.hxx> + + +using namespace com::sun::star; + + +namespace drawinglayer::primitive2d +{ + void SdrConnectorPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*aViewInformation*/) const + { + Primitive2DContainer aRetval; + + // add line + if(getSdrLSTAttribute().getLine().isDefault()) + { + // create invisible line for HitTest/BoundRect + aRetval.push_back( + createHiddenGeometryPrimitives2D( + basegfx::B2DPolyPolygon(getUnitPolygon()))); + } + else + { + aRetval.push_back( + createPolygonLinePrimitive( + getUnitPolygon(), + getSdrLSTAttribute().getLine(), + getSdrLSTAttribute().getLineStartEnd())); + } + + // add text + if(!getSdrLSTAttribute().getText().isDefault()) + { + aRetval.push_back( + createTextPrimitive( + basegfx::B2DPolyPolygon(getUnitPolygon()), + basegfx::B2DHomMatrix(), + getSdrLSTAttribute().getText(), + getSdrLSTAttribute().getLine(), + false, + false)); + } + + // add shadow + if(!getSdrLSTAttribute().getShadow().isDefault()) + { + aRetval = createEmbeddedShadowPrimitive( + std::move(aRetval), + getSdrLSTAttribute().getShadow()); + } + + rContainer.append(std::move(aRetval)); + } + + SdrConnectorPrimitive2D::SdrConnectorPrimitive2D( + const attribute::SdrLineEffectsTextAttribute& rSdrLSTAttribute, + const ::basegfx::B2DPolygon& rUnitPolygon) + : maSdrLSTAttribute(rSdrLSTAttribute), + maUnitPolygon(rUnitPolygon) + { + } + + bool SdrConnectorPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const + { + if(BufferedDecompositionPrimitive2D::operator==(rPrimitive)) + { + const SdrConnectorPrimitive2D& rCompare = static_cast<const SdrConnectorPrimitive2D&>(rPrimitive); + + return (getUnitPolygon() == rCompare.getUnitPolygon() + && getSdrLSTAttribute() == rCompare.getSdrLSTAttribute()); + } + + return false; + } + + // provide unique ID + sal_uInt32 SdrConnectorPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_SDRCONNECTORPRIMITIVE2D; + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/primitive2d/sdrcustomshapeprimitive2d.cxx b/svx/source/sdr/primitive2d/sdrcustomshapeprimitive2d.cxx new file mode 100644 index 000000000..dddb8aa30 --- /dev/null +++ b/svx/source/sdr/primitive2d/sdrcustomshapeprimitive2d.cxx @@ -0,0 +1,130 @@ +/* -*- 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 <sdr/primitive2d/sdrcustomshapeprimitive2d.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/polygon/b2dpolypolygon.hxx> +#include <sdr/primitive2d/sdrdecompositiontools.hxx> +#include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx> +#include <drawinglayer/attribute/sdrlineattribute.hxx> + + +using namespace com::sun::star; + + +namespace drawinglayer::primitive2d +{ + void SdrCustomShapePrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*aViewInformation*/) const + { + Primitive2DContainer aRetval(getSubPrimitives()); + + // Soft edges should be before text, since text is not affected by soft edges + if (!aRetval.empty() && getSdrSTAttribute().getSoftEdgeRadius()) + { + aRetval = createEmbeddedSoftEdgePrimitive(std::move(aRetval), + getSdrSTAttribute().getSoftEdgeRadius()); + } + + // add text + if(!getSdrSTAttribute().getText().isDefault()) + { + const basegfx::B2DPolygon& aUnitOutline(basegfx::utils::createUnitPolygon()); + + aRetval.push_back( + createTextPrimitive( + basegfx::B2DPolyPolygon(aUnitOutline), + getTextBox(), + getSdrSTAttribute().getText(), + attribute::SdrLineAttribute(), + false, + getWordWrap())); + } + + // tdf#132199: put glow before shadow, to have shadow of the glow, not the opposite + if (!aRetval.empty() && !getSdrSTAttribute().getGlow().isDefault()) + { + // glow + aRetval = createEmbeddedGlowPrimitive(std::move(aRetval), getSdrSTAttribute().getGlow()); + } + + // add shadow + if(!aRetval.empty() && !getSdrSTAttribute().getShadow().isDefault()) + { + // #i105323# add generic shadow only for 2D shapes. For + // 3D shapes shadow will be set at the individual created + // visualisation objects and be visualized by the 3d renderer + // as a single shadow. + + // The shadow for AutoShapes could be handled uniformly by not setting any + // shadow items at the helper model objects and only adding shadow here for + // 2D and 3D (and it works, too), but this would lead to two 3D scenes for + // the 3D object; one for the shadow and one for the content. The one for the + // shadow will be correct (using ColorModifierStack), but expensive. + if(!get3DShape()) + { + aRetval = createEmbeddedShadowPrimitive(std::move(aRetval), getSdrSTAttribute().getShadow(), + maTransform); + } + } + + rContainer.append(std::move(aRetval)); + } + + SdrCustomShapePrimitive2D::SdrCustomShapePrimitive2D( + const attribute::SdrEffectsTextAttribute& rSdrSTAttribute, + Primitive2DContainer&& rSubPrimitives, + const basegfx::B2DHomMatrix& rTextBox, + bool bWordWrap, + bool b3DShape, + const basegfx::B2DHomMatrix& rTransform) + : maSdrSTAttribute(rSdrSTAttribute), + maSubPrimitives(std::move(rSubPrimitives)), + maTextBox(rTextBox), + mbWordWrap(bWordWrap), + mb3DShape(b3DShape), + maTransform(rTransform) + { + } + + bool SdrCustomShapePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const + { + if(BufferedDecompositionPrimitive2D::operator==(rPrimitive)) + { + const SdrCustomShapePrimitive2D& rCompare = static_cast<const SdrCustomShapePrimitive2D&>(rPrimitive); + + return (getSdrSTAttribute() == rCompare.getSdrSTAttribute() + && getSubPrimitives() == rCompare.getSubPrimitives() + && getTextBox() == rCompare.getTextBox() + && getWordWrap() == rCompare.getWordWrap() + && get3DShape() == rCompare.get3DShape()); + } + + return false; + } + + // provide unique ID + sal_uInt32 SdrCustomShapePrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_SDRCUSTOMSHAPEPRIMITIVE2D; + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/primitive2d/sdrdecompositiontools.cxx b/svx/source/sdr/primitive2d/sdrdecompositiontools.cxx new file mode 100644 index 000000000..a1838b03f --- /dev/null +++ b/svx/source/sdr/primitive2d/sdrdecompositiontools.cxx @@ -0,0 +1,884 @@ +/* -*- 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 <sdr/primitive2d/sdrdecompositiontools.hxx> +#include <drawinglayer/primitive2d/baseprimitive2d.hxx> +#include <drawinglayer/primitive2d/PolygonStrokeArrowPrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolyPolygonGradientPrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolyPolygonHatchPrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolyPolygonGraphicPrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx> +#include <drawinglayer/primitive2d/softedgeprimitive2d.hxx> +#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx> +#include <drawinglayer/primitive2d/transparenceprimitive2d.hxx> +#include <basegfx/polygon/b2dpolypolygontools.hxx> +#include <drawinglayer/primitive2d/fillgradientprimitive2d.hxx> +#include <drawinglayer/attribute/strokeattribute.hxx> +#include <drawinglayer/attribute/linestartendattribute.hxx> +#include <drawinglayer/attribute/sdrfillgraphicattribute.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <drawinglayer/primitive2d/shadowprimitive2d.hxx> +#include <sdr/attribute/sdrtextattribute.hxx> +#include <drawinglayer/primitive2d/glowprimitive2d.hxx> +#include <sdr/primitive2d/sdrtextprimitive2d.hxx> +#include <svx/svdotext.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <drawinglayer/primitive2d/animatedprimitive2d.hxx> +#include <drawinglayer/animation/animationtiming.hxx> +#include <drawinglayer/primitive2d/maskprimitive2d.hxx> +#include <drawinglayer/geometry/viewinformation2d.hxx> +#include <drawinglayer/primitive2d/texthierarchyprimitive2d.hxx> +#include <drawinglayer/attribute/sdrfillattribute.hxx> +#include <drawinglayer/attribute/sdrlineattribute.hxx> +#include <drawinglayer/attribute/sdrlinestartendattribute.hxx> +#include <drawinglayer/attribute/sdrshadowattribute.hxx> +#include <drawinglayer/attribute/sdrglowattribute.hxx> +#include <osl/diagnose.h> + +// for SlideBackgroundFillPrimitive2D +#include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx> +#include <svx/unoapi.hxx> +#include <svx/svdpage.hxx> +#include <sdr/primitive2d/sdrattributecreator.hxx> +#include <sdr/contact/viewcontactofmasterpagedescriptor.hxx> + +using namespace com::sun::star; + + +namespace drawinglayer::primitive2d +{ +namespace +{ +// See also: SdrTextObj::AdjustRectToTextDistance +basegfx::B2DRange getTextAnchorRange(const attribute::SdrTextAttribute& rText, + const basegfx::B2DRange& rSnapRange) +{ + // Take vertical text orientation into account when deciding + // which dimension is its width, and which is its height + const OutlinerParaObject& rOutlinerParaObj = rText.getOutlinerParaObject(); + const bool bVerticalWriting(rOutlinerParaObj.IsEffectivelyVertical()); + const double fWidthForText = bVerticalWriting ? rSnapRange.getHeight() : rSnapRange.getWidth(); + // create a range describing the wanted text position and size (aTextAnchorRange). This + // means to use the text distance values here + // If the margin is larger than the entire width of the text area, then limit the + // margin. + const double fTextLeftDistance + = std::min(static_cast<double>(rText.getTextLeftDistance()), fWidthForText); + const double nTextRightDistance + = std::min(static_cast<double>(rText.getTextRightDistance()), fWidthForText); + double fDistanceForTextL, fDistanceForTextT, fDistanceForTextR, fDistanceForTextB; + if (!bVerticalWriting) + { + fDistanceForTextL = fTextLeftDistance; + fDistanceForTextT = rText.getTextUpperDistance(); + fDistanceForTextR = nTextRightDistance; + fDistanceForTextB = rText.getTextLowerDistance(); + } + else if (rOutlinerParaObj.IsTopToBottom()) + { + fDistanceForTextL = rText.getTextLowerDistance(); + fDistanceForTextT = fTextLeftDistance; + fDistanceForTextR = rText.getTextUpperDistance(); + fDistanceForTextB = nTextRightDistance; + } + else + { + fDistanceForTextL = rText.getTextUpperDistance(); + fDistanceForTextT = nTextRightDistance; + fDistanceForTextR = rText.getTextLowerDistance(); + fDistanceForTextB = fTextLeftDistance; + } + const basegfx::B2DPoint aTopLeft(rSnapRange.getMinX() + fDistanceForTextL, + rSnapRange.getMinY() + fDistanceForTextT); + const basegfx::B2DPoint aBottomRight(rSnapRange.getMaxX() - fDistanceForTextR, + rSnapRange.getMaxY() - fDistanceForTextB); + basegfx::B2DRange aAnchorRange; + aAnchorRange.expand(aTopLeft); + aAnchorRange.expand(aBottomRight); + + // If the shape has no width, then don't attempt to break the text into multiple + // lines, not a single character would satisfy a zero width requirement. + // SdrTextObj::impDecomposeBlockTextPrimitive() uses the same constant to + // effectively set no limits. + if (!bVerticalWriting && aAnchorRange.getWidth() == 0) + { + aAnchorRange.expand(basegfx::B2DPoint(aTopLeft.getX() - 1000000, aTopLeft.getY())); + aAnchorRange.expand(basegfx::B2DPoint(aBottomRight.getX() + 1000000, aBottomRight.getY())); + } + else if (bVerticalWriting && aAnchorRange.getHeight() == 0) + { + aAnchorRange.expand(basegfx::B2DPoint(aTopLeft.getX(), aTopLeft.getY() - 1000000)); + aAnchorRange.expand(basegfx::B2DPoint(aBottomRight.getX(), aBottomRight.getY() + 1000000)); + } + return aAnchorRange; +} + +drawinglayer::attribute::SdrFillAttribute getMasterPageFillAttribute( + const geometry::ViewInformation2D& rViewInformation, + basegfx::B2DVector& rPageSize) +{ + drawinglayer::attribute::SdrFillAttribute aRetval; + + // get SdrPage + const SdrPage* pVisualizedPage(GetSdrPageFromXDrawPage(rViewInformation.getVisualizedPage())); + + if(nullptr != pVisualizedPage) + { + // copy needed values for further processing + rPageSize.setX(pVisualizedPage->GetWidth()); + rPageSize.setY(pVisualizedPage->GetHeight()); + + if(pVisualizedPage->IsMasterPage()) + { + // the page is a MasterPage, so we are in MasterPage view + // still need #i110846#, see ViewContactOfMasterPage + if(pVisualizedPage->getSdrPageProperties().GetStyleSheet()) + { + // create page fill attributes with correct properties + aRetval = drawinglayer::primitive2d::createNewSdrFillAttribute( + pVisualizedPage->getSdrPageProperties().GetItemSet()); + } + } + else + { + // the page is *no* MasterPage, we are in normal Page view, get the MasterPage + if(pVisualizedPage->TRG_HasMasterPage()) + { + sdr::contact::ViewContact& rVC(pVisualizedPage->TRG_GetMasterPageDescriptorViewContact()); + sdr::contact::ViewContactOfMasterPageDescriptor* pVCOMPD( + dynamic_cast<sdr::contact::ViewContactOfMasterPageDescriptor*>(&rVC)); + + if(nullptr != pVCOMPD) + { + // in this case the still needed #i110846# is part of + // getCorrectSdrPageProperties, that's the main reason to re-use + // that call/functionality here + const SdrPageProperties* pCorrectProperties( + pVCOMPD->GetMasterPageDescriptor().getCorrectSdrPageProperties()); + + if(pCorrectProperties) + { + // create page fill attributes when correct properties were identified + aRetval = drawinglayer::primitive2d::createNewSdrFillAttribute( + pCorrectProperties->GetItemSet()); + } + } + } + } + } + + return aRetval; +} + +// provide a Primitive2D for the SlideBackgroundFill-mode. It is capable +// of expressing that state of fill and it's decomposition implements all +// needed preparation of the geometry in an isolated and controllable +// space and way. +// It is currently simple buffered (due to being derived from +// BufferedDecompositionPrimitive2D) and detects if FillStyle changes +class SlideBackgroundFillPrimitive2D final : public BufferedDecompositionPrimitive2D +{ +private: + /// the basegfx::B2DPolyPolygon geometry + basegfx::B2DPolyPolygon maPolyPolygon; + + /// the last SdrFillAttribute the geometry was created for + drawinglayer::attribute::SdrFillAttribute maLastFill; + +protected: + // create decomposition data + virtual void create2DDecomposition( + Primitive2DContainer& rContainer, + const geometry::ViewInformation2D& rViewInformation) const override; + +public: + /// constructor + SlideBackgroundFillPrimitive2D( + const basegfx::B2DPolyPolygon& rPolyPolygon); + + /// check existing decomposition data, call parent + virtual void get2DDecomposition( + Primitive2DDecompositionVisitor& rVisitor, + const geometry::ViewInformation2D& rViewInformation) const override; + + /// data read access + const basegfx::B2DPolyPolygon& getB2DPolyPolygon() const { return maPolyPolygon; } + + /// compare operator + virtual bool operator==(const BasePrimitive2D& rPrimitive) const override; + + /// get range + virtual basegfx::B2DRange getB2DRange(const geometry::ViewInformation2D& rViewInformation) const override; + + /// provide unique ID + virtual sal_uInt32 getPrimitive2DID() const override; +}; + +SlideBackgroundFillPrimitive2D::SlideBackgroundFillPrimitive2D( + const basegfx::B2DPolyPolygon& rPolyPolygon) +: BufferedDecompositionPrimitive2D() +, maPolyPolygon(rPolyPolygon) +, maLastFill() +{ +} + +void SlideBackgroundFillPrimitive2D::create2DDecomposition( + Primitive2DContainer& rContainer, + const geometry::ViewInformation2D& rViewInformation) const +{ + basegfx::B2DVector aPageSize; + + // get fill from target Page, this will check for all needed things + // like MasterPage/relationships, etc. (see getMasterPageFillAttribute impl above) + drawinglayer::attribute::SdrFillAttribute aFill( + getMasterPageFillAttribute(rViewInformation, aPageSize)); + + // if fill is on default (empty), nothing will be shown, we are done + if(aFill.isDefault()) + return; + + // Get PolygonRange of own local geometry + const basegfx::B2DRange aPolygonRange(getB2DPolyPolygon().getB2DRange()); + + // if local geometry is empty, nothing will be shown, we are done + if(aPolygonRange.isEmpty()) + return; + + // Get PageRange + const basegfx::B2DRange aPageRange(0.0, 0.0, aPageSize.getX(), aPageSize.getY()); + + // if local geometry does not overlap with PageRange, nothing will be shown, we are done + if(!aPageRange.overlaps(aPolygonRange)) + return; + + // create FillPrimitive2D with the geometry (the PolyPolygon) and + // the page's definitonRange to: + // - on one hand limit to geometry + // - on the other hand allow continutation of fill outside of + // MasterPage's range + const attribute::FillGradientAttribute aEmptyFillTransparenceGradient; + const Primitive2DReference aCreatedFill( + createPolyPolygonFillPrimitive( + getB2DPolyPolygon(), // geometry + aPageRange, // definition range + aFill, + aEmptyFillTransparenceGradient)); + + rContainer = Primitive2DContainer { aCreatedFill }; +} + +void SlideBackgroundFillPrimitive2D::get2DDecomposition( + Primitive2DDecompositionVisitor& rVisitor, + const geometry::ViewInformation2D& rViewInformation) const +{ + basegfx::B2DVector aPageSize; + drawinglayer::attribute::SdrFillAttribute aFill; + + if(!getBuffered2DDecomposition().empty()) + { + aFill = getMasterPageFillAttribute(rViewInformation, aPageSize); + + if(!(aFill == maLastFill)) + { + // conditions of last local decomposition have changed, delete + const_cast< SlideBackgroundFillPrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer()); + } + } + + if(getBuffered2DDecomposition().empty()) + { + // remember last Fill + const_cast< SlideBackgroundFillPrimitive2D* >(this)->maLastFill = aFill; + } + + // use parent implementation + BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation); +} + +bool SlideBackgroundFillPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const +{ + if (BufferedDecompositionPrimitive2D::operator==(rPrimitive)) + { + const SlideBackgroundFillPrimitive2D& rCompare + = static_cast<const SlideBackgroundFillPrimitive2D&>(rPrimitive); + + return getB2DPolyPolygon() == rCompare.getB2DPolyPolygon(); + } + + return false; +} + +basegfx::B2DRange SlideBackgroundFillPrimitive2D::getB2DRange( + const geometry::ViewInformation2D& /*rViewInformation*/) const +{ + // return range + return basegfx::utils::getRange(getB2DPolyPolygon()); +} + +// provide unique ID +sal_uInt32 SlideBackgroundFillPrimitive2D::getPrimitive2DID() const +{ + return PRIMITIVE2D_ID_SLIDEBACKGROUNDFILLPRIMITIVE2D; +} + +}; // end of anonymous namespace + + class TransparencePrimitive2D; + + Primitive2DReference createPolyPolygonFillPrimitive( + const basegfx::B2DPolyPolygon& rPolyPolygon, + const attribute::SdrFillAttribute& rFill, + const attribute::FillGradientAttribute& rFillGradient) + { + // when we have no given definition range, use the range of the given geometry + // also for definition (simplest case) + const basegfx::B2DRange aRange(basegfx::utils::getRange(rPolyPolygon)); + + return createPolyPolygonFillPrimitive( + rPolyPolygon, + aRange, + rFill, + rFillGradient); + } + + Primitive2DReference createPolyPolygonFillPrimitive( + const basegfx::B2DPolyPolygon& rPolyPolygon, + const basegfx::B2DRange& rDefinitionRange, + const attribute::SdrFillAttribute& rFill, + const attribute::FillGradientAttribute& rFillGradient) + { + if(basegfx::fTools::moreOrEqual(rFill.getTransparence(), 1.0)) + { + return Primitive2DReference(); + } + + // prepare fully scaled polygon + rtl::Reference<BasePrimitive2D> pNewFillPrimitive; + + if(!rFill.getGradient().isDefault()) + { + pNewFillPrimitive = new PolyPolygonGradientPrimitive2D( + rPolyPolygon, + rDefinitionRange, + rFill.getGradient()); + } + else if(!rFill.getHatch().isDefault()) + { + pNewFillPrimitive = new PolyPolygonHatchPrimitive2D( + rPolyPolygon, + rDefinitionRange, + rFill.getColor(), + rFill.getHatch()); + } + else if(!rFill.getFillGraphic().isDefault()) + { + pNewFillPrimitive = new PolyPolygonGraphicPrimitive2D( + rPolyPolygon, + rDefinitionRange, + rFill.getFillGraphic().createFillGraphicAttribute(rDefinitionRange)); + } + else if(rFill.isSlideBackgroundFill()) + { + // create needed Primitive2D representation for + // SlideBackgroundFill-mode + pNewFillPrimitive = new SlideBackgroundFillPrimitive2D( + rPolyPolygon); + } + else + { + pNewFillPrimitive = new PolyPolygonColorPrimitive2D( + rPolyPolygon, + rFill.getColor()); + } + + if(0.0 != rFill.getTransparence()) + { + // create simpleTransparencePrimitive, add created fill primitive + Primitive2DContainer aContent { pNewFillPrimitive }; + return Primitive2DReference(new UnifiedTransparencePrimitive2D(std::move(aContent), rFill.getTransparence())); + } + else if(!rFillGradient.isDefault()) + { + // create sequence with created fill primitive + Primitive2DContainer aContent { pNewFillPrimitive }; + + // create FillGradientPrimitive2D for transparence and add to new sequence + // fillGradientPrimitive is enough here (compared to PolyPolygonGradientPrimitive2D) since float transparence will be masked anyways + const basegfx::B2DRange aRange(basegfx::utils::getRange(rPolyPolygon)); + Primitive2DReference xRefB( + new FillGradientPrimitive2D( + aRange, + rDefinitionRange, + rFillGradient)); + Primitive2DContainer aAlpha { xRefB }; + + // create TransparencePrimitive2D using alpha and content + return Primitive2DReference(new TransparencePrimitive2D(std::move(aContent), std::move(aAlpha))); + } + else + { + // add to decomposition + return pNewFillPrimitive; + } + } + + Primitive2DReference createPolygonLinePrimitive( + const basegfx::B2DPolygon& rPolygon, + const attribute::SdrLineAttribute& rLine, + const attribute::SdrLineStartEndAttribute& rStroke) + { + // create line and stroke attribute + const attribute::LineAttribute aLineAttribute(rLine.getColor(), rLine.getWidth(), rLine.getJoin(), rLine.getCap()); + const attribute::StrokeAttribute aStrokeAttribute(std::vector(rLine.getDotDashArray()), rLine.getFullDotDashLen()); + rtl::Reference<BasePrimitive2D> pNewLinePrimitive; + + if(!rPolygon.isClosed() && !rStroke.isDefault()) + { + attribute::LineStartEndAttribute aStart(rStroke.getStartWidth(), rStroke.getStartPolyPolygon(), rStroke.isStartCentered()); + attribute::LineStartEndAttribute aEnd(rStroke.getEndWidth(), rStroke.getEndPolyPolygon(), rStroke.isEndCentered()); + + // create data + pNewLinePrimitive = new PolygonStrokeArrowPrimitive2D(rPolygon, aLineAttribute, aStrokeAttribute, aStart, aEnd); + } + else + { + // create data + pNewLinePrimitive = new PolygonStrokePrimitive2D(rPolygon, aLineAttribute, aStrokeAttribute); + } + + if(0.0 != rLine.getTransparence()) + { + // create simpleTransparencePrimitive, add created fill primitive + Primitive2DContainer aContent { pNewLinePrimitive }; + return Primitive2DReference(new UnifiedTransparencePrimitive2D(std::move(aContent), rLine.getTransparence())); + } + else + { + // add to decomposition + return pNewLinePrimitive; + } + } + + Primitive2DReference createTextPrimitive( + const basegfx::B2DPolyPolygon& rUnitPolyPolygon, + const basegfx::B2DHomMatrix& rObjectTransform, + const attribute::SdrTextAttribute& rText, + const attribute::SdrLineAttribute& rStroke, + bool bCellText, + bool bWordWrap) + { + basegfx::B2DHomMatrix aAnchorTransform(rObjectTransform); + rtl::Reference<SdrTextPrimitive2D> pNew; + + if(rText.isContour()) + { + // contour text + if(!rStroke.isDefault() && 0.0 != rStroke.getWidth()) + { + // take line width into account and shrink contour polygon accordingly + // decompose to get scale + basegfx::B2DVector aScale, aTranslate; + double fRotate, fShearX; + rObjectTransform.decompose(aScale, aTranslate, fRotate, fShearX); + + // scale outline to object's size to allow growing with value relative to that size + // and also to keep aspect ratio + basegfx::B2DPolyPolygon aScaledUnitPolyPolygon(rUnitPolyPolygon); + aScaledUnitPolyPolygon.transform(basegfx::utils::createScaleB2DHomMatrix( + fabs(aScale.getX()), fabs(aScale.getY()))); + + // grow the polygon. To shrink, use negative value (half width) + aScaledUnitPolyPolygon = basegfx::utils::growInNormalDirection(aScaledUnitPolyPolygon, -(rStroke.getWidth() * 0.5)); + + // scale back to unit polygon + aScaledUnitPolyPolygon.transform(basegfx::utils::createScaleB2DHomMatrix( + 0.0 != aScale.getX() ? 1.0 / aScale.getX() : 1.0, + 0.0 != aScale.getY() ? 1.0 / aScale.getY() : 1.0)); + + // create with unit polygon + pNew = new SdrContourTextPrimitive2D( + &rText.getSdrText(), + rText.getOutlinerParaObject(), + aScaledUnitPolyPolygon, + rObjectTransform); + } + else + { + // create with unit polygon + pNew = new SdrContourTextPrimitive2D( + &rText.getSdrText(), + rText.getOutlinerParaObject(), + rUnitPolyPolygon, + rObjectTransform); + } + } + else if(!rText.getSdrFormTextAttribute().isDefault()) + { + // text on path, use scaled polygon + basegfx::B2DPolyPolygon aScaledPolyPolygon(rUnitPolyPolygon); + aScaledPolyPolygon.transform(rObjectTransform); + pNew = new SdrPathTextPrimitive2D( + &rText.getSdrText(), + rText.getOutlinerParaObject(), + aScaledPolyPolygon, + rText.getSdrFormTextAttribute()); + } + else + { + // rObjectTransform is the whole SdrObject transformation from unit rectangle + // to its size and position. Decompose to allow working with single values. + basegfx::B2DVector aScale, aTranslate; + double fRotate, fShearX; + rObjectTransform.decompose(aScale, aTranslate, fRotate, fShearX); + + // extract mirroring + const bool bMirrorX(basegfx::fTools::less(aScale.getX(), 0.0)); + const bool bMirrorY(basegfx::fTools::less(aScale.getY(), 0.0)); + aScale = basegfx::absolute(aScale); + + // Get the real size, since polygon outline and scale + // from the object transformation may vary (e.g. ellipse segments) + basegfx::B2DHomMatrix aJustScaleTransform; + aJustScaleTransform.set(0, 0, aScale.getX()); + aJustScaleTransform.set(1, 1, aScale.getY()); + basegfx::B2DPolyPolygon aScaledUnitPolyPolygon(rUnitPolyPolygon); + aScaledUnitPolyPolygon.transform(aJustScaleTransform); + const basegfx::B2DRange aTextAnchorRange + = getTextAnchorRange(rText, basegfx::utils::getRange(aScaledUnitPolyPolygon)); + + // now create a transformation from this basic range (aTextAnchorRange) + // #i121494# if we have no scale use at least 1.0 to have a carrier e.g. for + // mirror values, else these will get lost + aAnchorTransform = basegfx::utils::createScaleTranslateB2DHomMatrix( + basegfx::fTools::equalZero(aTextAnchorRange.getWidth()) ? 1.0 : aTextAnchorRange.getWidth(), + basegfx::fTools::equalZero(aTextAnchorRange.getHeight()) ? 1.0 : aTextAnchorRange.getHeight(), + aTextAnchorRange.getMinX(), aTextAnchorRange.getMinY()); + + // apply mirroring + aAnchorTransform.scale(bMirrorX ? -1.0 : 1.0, bMirrorY ? -1.0 : 1.0); + + // apply object's other transforms + aAnchorTransform = basegfx::utils::createShearXRotateTranslateB2DHomMatrix(fShearX, fRotate, aTranslate) + * aAnchorTransform; + + if(rText.isFitToSize()) + { + // stretched text in range + pNew = new SdrStretchTextPrimitive2D( + &rText.getSdrText(), + rText.getOutlinerParaObject(), + aAnchorTransform, + rText.isFixedCellHeight()); + } + else if(rText.isAutoFit()) + { + // isotropically scaled text in range + pNew = new SdrAutoFitTextPrimitive2D( + &rText.getSdrText(), + rText.getOutlinerParaObject(), + aAnchorTransform, + bWordWrap); + } + else if( rText.isChainable() && !rText.isInEditMode() ) + { + pNew = new SdrChainedTextPrimitive2D( + &rText.getSdrText(), + rText.getOutlinerParaObject(), + aAnchorTransform ); + } + else // text in range + { + // build new primitive + pNew = new SdrBlockTextPrimitive2D( + &rText.getSdrText(), + rText.getOutlinerParaObject(), + aAnchorTransform, + rText.getSdrTextHorzAdjust(), + rText.getSdrTextVertAdjust(), + rText.isFixedCellHeight(), + rText.isScroll(), + bCellText, + bWordWrap); + } + } + + OSL_ENSURE(pNew != nullptr, "createTextPrimitive: no text primitive created (!)"); + + if(rText.isBlink()) + { + // prepare animation and primitive list + drawinglayer::animation::AnimationEntryList aAnimationList; + rText.getBlinkTextTiming(aAnimationList); + + if(0.0 != aAnimationList.getDuration()) + { + // create content sequence + Primitive2DReference xRefA(pNew); + Primitive2DContainer aContent { xRefA }; + + // create and add animated switch primitive + return Primitive2DReference(new AnimatedBlinkPrimitive2D(aAnimationList, std::move(aContent))); + } + else + { + // add to decomposition + return Primitive2DReference(pNew); + } + } + + if(rText.isScroll()) + { + // suppress scroll when FontWork + if(rText.getSdrFormTextAttribute().isDefault()) + { + // get scroll direction + const SdrTextAniDirection eDirection(rText.getSdrText().GetObject().GetTextAniDirection()); + const bool bHorizontal(SdrTextAniDirection::Left == eDirection || SdrTextAniDirection::Right == eDirection); + + // decompose to get separated values for the scroll box + basegfx::B2DVector aScale, aTranslate; + double fRotate, fShearX; + aAnchorTransform.decompose(aScale, aTranslate, fRotate, fShearX); + + // build transform from scaled only to full AnchorTransform and inverse + const basegfx::B2DHomMatrix aSRT(basegfx::utils::createShearXRotateTranslateB2DHomMatrix( + fShearX, fRotate, aTranslate)); + basegfx::B2DHomMatrix aISRT(aSRT); + aISRT.invert(); + + // bring the primitive back to scaled only and get scaled range, create new clone for this + rtl::Reference<SdrTextPrimitive2D> pNew2 = pNew->createTransformedClone(aISRT); + OSL_ENSURE(pNew2, "createTextPrimitive: Could not create transformed clone of text primitive (!)"); + pNew = pNew2.get(); + + // create neutral geometry::ViewInformation2D for local range and decompose calls. This is okay + // since the decompose is view-independent + geometry::ViewInformation2D aViewInformation2D; + + // get range + const basegfx::B2DRange aScaledRange(pNew->getB2DRange(aViewInformation2D)); + + // create left outside and right outside transformations. Also take care + // of the clip rectangle + basegfx::B2DHomMatrix aLeft, aRight; + basegfx::B2DPoint aClipTopLeft(0.0, 0.0); + basegfx::B2DPoint aClipBottomRight(aScale.getX(), aScale.getY()); + + if(bHorizontal) + { + aClipTopLeft.setY(aScaledRange.getMinY()); + aClipBottomRight.setY(aScaledRange.getMaxY()); + aLeft.translate(-aScaledRange.getMaxX(), 0.0); + aRight.translate(aScale.getX() - aScaledRange.getMinX(), 0.0); + } + else + { + aClipTopLeft.setX(aScaledRange.getMinX()); + aClipBottomRight.setX(aScaledRange.getMaxX()); + aLeft.translate(0.0, -aScaledRange.getMaxY()); + aRight.translate(0.0, aScale.getY() - aScaledRange.getMinY()); + } + + aLeft *= aSRT; + aRight *= aSRT; + + // prepare animation list + drawinglayer::animation::AnimationEntryList aAnimationList; + + if(bHorizontal) + { + rText.getScrollTextTiming(aAnimationList, aScale.getX(), aScaledRange.getWidth()); + } + else + { + rText.getScrollTextTiming(aAnimationList, aScale.getY(), aScaledRange.getHeight()); + } + + if(0.0 != aAnimationList.getDuration()) + { + // create a new Primitive2DContainer containing the animated text in its scaled only state. + // use the decomposition to force to simple text primitives, those will no longer + // need the outliner for formatting (alternatively it is also possible to just add + // pNew to aNewPrimitiveSequence) + Primitive2DContainer aAnimSequence; + pNew->get2DDecomposition(aAnimSequence, aViewInformation2D); + pNew.clear(); + + // create a new animatedInterpolatePrimitive and add it + Primitive2DReference xRefA(new AnimatedInterpolatePrimitive2D({ aLeft, aRight }, aAnimationList, std::move(aAnimSequence))); + Primitive2DContainer aContent { xRefA }; + + // scrolling needs an encapsulating clipping primitive + const basegfx::B2DRange aClipRange(aClipTopLeft, aClipBottomRight); + basegfx::B2DPolygon aClipPolygon(basegfx::utils::createPolygonFromRect(aClipRange)); + aClipPolygon.transform(aSRT); + return Primitive2DReference(new MaskPrimitive2D(basegfx::B2DPolyPolygon(aClipPolygon), std::move(aContent))); + } + else + { + // add to decomposition + return Primitive2DReference(pNew); + } + } + } + + if(rText.isInEditMode()) + { + // #i97628# + // encapsulate with TextHierarchyEditPrimitive2D to allow renderers + // to suppress actively edited content if needed + Primitive2DReference xRefA(pNew); + Primitive2DContainer aContent { xRefA }; + + // create and add TextHierarchyEditPrimitive2D primitive + return Primitive2DReference(new TextHierarchyEditPrimitive2D(std::move(aContent))); + } + else + { + // add to decomposition + return pNew; + } + } + + Primitive2DContainer createEmbeddedShadowPrimitive( + Primitive2DContainer&& rContent, + const attribute::SdrShadowAttribute& rShadow, + const basegfx::B2DHomMatrix& rObjectMatrix, + const Primitive2DContainer* pContentForShadow) + { + if(rContent.empty()) + return std::move(rContent); + + basegfx::B2DHomMatrix aShadowOffset; + + if(rShadow.getSize().getX() != 100000) + { + basegfx::B2DTuple aScale; + basegfx::B2DTuple aTranslate; + double fRotate = 0; + double fShearX = 0; + rObjectMatrix.decompose(aScale, aTranslate, fRotate, fShearX); + // Scale the shadow + double nTranslateX = aTranslate.getX(); + double nTranslateY = aTranslate.getY(); + + // The origin for scaling is the top left corner by default. A negative + // shadow offset changes the origin. + if (rShadow.getOffset().getX() < 0) + nTranslateX += aScale.getX(); + if (rShadow.getOffset().getY() < 0) + nTranslateY += aScale.getY(); + + aShadowOffset.translate(-nTranslateX, -nTranslateY); + aShadowOffset.scale(rShadow.getSize().getX() * 0.00001, rShadow.getSize().getY() * 0.00001); + aShadowOffset.translate(nTranslateX, nTranslateY); + } + + aShadowOffset.translate(rShadow.getOffset().getX(), rShadow.getOffset().getY()); + + // create shadow primitive and add content + const Primitive2DContainer& rContentForShadow + = pContentForShadow ? *pContentForShadow : rContent; + int nContentWithTransparence = std::count_if( + rContentForShadow.begin(), rContentForShadow.end(), + [](const Primitive2DReference& xChild) { + auto pChild = dynamic_cast<BufferedDecompositionPrimitive2D*>(xChild.get()); + return pChild && pChild->getTransparenceForShadow() != 0; + }); + if (nContentWithTransparence == 0) + { + Primitive2DContainer aRetval(2); + aRetval[0] = Primitive2DReference( + new ShadowPrimitive2D( + aShadowOffset, + rShadow.getColor(), + rShadow.getBlur(), + Primitive2DContainer(pContentForShadow ? *pContentForShadow : rContent))); + + if (0.0 != rShadow.getTransparence()) + { + // create SimpleTransparencePrimitive2D + Primitive2DContainer aTempContent{ aRetval[0] }; + + aRetval[0] = Primitive2DReference( + new UnifiedTransparencePrimitive2D( + std::move(aTempContent), + rShadow.getTransparence())); + } + + aRetval[1] = Primitive2DReference(new GroupPrimitive2D(std::move(rContent))); + return aRetval; + } + + Primitive2DContainer aRetval; + for (const auto& xChild : rContentForShadow) + { + double fChildTransparence = 0.0; + auto pChild = dynamic_cast<BufferedDecompositionPrimitive2D*>(xChild.get()); + if (pChild) + { + fChildTransparence = pChild->getTransparenceForShadow(); + fChildTransparence /= 100; + } + aRetval.push_back(Primitive2DReference( + new ShadowPrimitive2D(aShadowOffset, rShadow.getColor(), rShadow.getBlur(), + Primitive2DContainer({ xChild })))); + if (rShadow.getTransparence() != 0.0 || fChildTransparence != 0.0) + { + Primitive2DContainer aTempContent{ aRetval.back() }; + + double fChildAlpha = 1.0 - fChildTransparence; + double fShadowAlpha = 1.0 - rShadow.getTransparence(); + double fTransparence = 1.0 - fChildAlpha * fShadowAlpha; + aRetval.back() = Primitive2DReference(new UnifiedTransparencePrimitive2D( + std::move(aTempContent), fTransparence)); + } + } + + aRetval.push_back( + Primitive2DReference(new GroupPrimitive2D(std::move(rContent)))); + return aRetval; + } + + Primitive2DContainer createEmbeddedGlowPrimitive( + Primitive2DContainer&& rContent, + const attribute::SdrGlowAttribute& rGlow) + { + if(rContent.empty()) + return std::move(rContent); + Primitive2DContainer aRetval(2); + aRetval[0] = Primitive2DReference( + new GlowPrimitive2D(rGlow.getColor(), rGlow.getRadius(), Primitive2DContainer(rContent))); + aRetval[1] = Primitive2DReference(new GroupPrimitive2D(Primitive2DContainer(rContent))); + return aRetval; + } + + Primitive2DContainer createEmbeddedSoftEdgePrimitive(Primitive2DContainer&& aContent, + sal_Int32 nRadius) + { + if (aContent.empty() || !nRadius) + return std::move(aContent); + Primitive2DContainer aRetval(1); + aRetval[0] = Primitive2DReference(new SoftEdgePrimitive2D(nRadius, std::move(aContent))); + return aRetval; + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/primitive2d/sdrellipseprimitive2d.cxx b/svx/source/sdr/primitive2d/sdrellipseprimitive2d.cxx new file mode 100644 index 000000000..89c348beb --- /dev/null +++ b/svx/source/sdr/primitive2d/sdrellipseprimitive2d.cxx @@ -0,0 +1,266 @@ +/* -*- 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 <sdr/primitive2d/sdrellipseprimitive2d.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/polygon/b2dpolypolygon.hxx> +#include <sdr/primitive2d/sdrdecompositiontools.hxx> +#include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> +#include <drawinglayer/primitive2d/sdrdecompositiontools2d.hxx> + + +using namespace com::sun::star; + + +namespace drawinglayer::primitive2d +{ + void SdrEllipsePrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*aViewInformation*/) const + { + Primitive2DContainer aRetval; + + // create unit outline polygon + // Do use createPolygonFromUnitCircle, but let create from first quadrant to mimic old geometry creation. + // This is needed to have the same look when stroke is used since the polygon start point defines the + // stroke start, too. + basegfx::B2DPolygon aUnitOutline(basegfx::utils::createPolygonFromUnitCircle(1)); + + // scale and move UnitEllipse to UnitObject (-1,-1 1,1) -> (0,0 1,1) + const basegfx::B2DHomMatrix aUnitCorrectionMatrix( + basegfx::utils::createScaleTranslateB2DHomMatrix(0.5, 0.5, 0.5, 0.5)); + + // apply to the geometry + aUnitOutline.transform(aUnitCorrectionMatrix); + + // add fill + if(!getSdrLFSTAttribute().getFill().isDefault()) + { + basegfx::B2DPolyPolygon aTransformed(aUnitOutline); + + aTransformed.transform(getTransform()); + aRetval.push_back( + createPolyPolygonFillPrimitive( + aTransformed, + getSdrLFSTAttribute().getFill(), + getSdrLFSTAttribute().getFillFloatTransGradient())); + } + + // add line + if(getSdrLFSTAttribute().getLine().isDefault()) + { + // create invisible line for HitTest/BoundRect + aRetval.push_back( + createHiddenGeometryPrimitives2D( + false, + basegfx::B2DPolyPolygon(aUnitOutline), + getTransform())); + } + else + { + basegfx::B2DPolygon aTransformed(aUnitOutline); + + aTransformed.transform(getTransform()); + aRetval.push_back( + createPolygonLinePrimitive( + aTransformed, + getSdrLFSTAttribute().getLine(), + attribute::SdrLineStartEndAttribute())); + } + + // add text + if(!getSdrLFSTAttribute().getText().isDefault()) + { + aRetval.push_back( + createTextPrimitive( + basegfx::B2DPolyPolygon(aUnitOutline), + getTransform(), + getSdrLFSTAttribute().getText(), + getSdrLFSTAttribute().getLine(), + false, + false)); + } + + // add shadow + if(!getSdrLFSTAttribute().getShadow().isDefault()) + { + aRetval = createEmbeddedShadowPrimitive( + std::move(aRetval), + getSdrLFSTAttribute().getShadow()); + } + + rContainer.append(std::move(aRetval)); + } + + SdrEllipsePrimitive2D::SdrEllipsePrimitive2D( + const basegfx::B2DHomMatrix& rTransform, + const attribute::SdrLineFillEffectsTextAttribute& rSdrLFSTAttribute) + : maTransform(rTransform), + maSdrLFSTAttribute(rSdrLFSTAttribute) + { + } + + bool SdrEllipsePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const + { + if(BufferedDecompositionPrimitive2D::operator==(rPrimitive)) + { + const SdrEllipsePrimitive2D& rCompare = static_cast<const SdrEllipsePrimitive2D&>(rPrimitive); + + return (getTransform() == rCompare.getTransform() + && getSdrLFSTAttribute() == rCompare.getSdrLFSTAttribute()); + } + + return false; + } + + // provide unique ID + sal_uInt32 SdrEllipsePrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_SDRELLIPSEPRIMITIVE2D; + } + + + + void SdrEllipseSegmentPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*aViewInformation*/) const + { + Primitive2DContainer aRetval; + + // create unit outline polygon + basegfx::B2DPolygon aUnitOutline(basegfx::utils::createPolygonFromUnitEllipseSegment(mfStartAngle, mfEndAngle)); + + if(mbCloseSegment) + { + if(mbCloseUsingCenter) + { + // for compatibility, insert the center point at polygon start to get the same + // line stroking pattern as the old painting mechanisms. + aUnitOutline.insert(0, basegfx::B2DPoint(0.0, 0.0)); + } + + aUnitOutline.setClosed(true); + } + + // move and scale UnitEllipse to UnitObject (-1,-1 1,1) -> (0,0 1,1) + const basegfx::B2DHomMatrix aUnitCorrectionMatrix( + basegfx::utils::createScaleTranslateB2DHomMatrix(0.5, 0.5, 0.5, 0.5)); + + // apply to the geometry + aUnitOutline.transform(aUnitCorrectionMatrix); + + // add fill + if(!getSdrLFSTAttribute().getFill().isDefault() && aUnitOutline.isClosed()) + { + basegfx::B2DPolyPolygon aTransformed(aUnitOutline); + + aTransformed.transform(getTransform()); + aRetval.push_back( + createPolyPolygonFillPrimitive( + aTransformed, + getSdrLFSTAttribute().getFill(), + getSdrLFSTAttribute().getFillFloatTransGradient())); + } + + // add line + if(getSdrLFSTAttribute().getLine().isDefault()) + { + // create invisible line for HitTest/BoundRect + aRetval.push_back( + createHiddenGeometryPrimitives2D( + false, + basegfx::B2DPolyPolygon(aUnitOutline), + getTransform())); + } + else + { + basegfx::B2DPolygon aTransformed(aUnitOutline); + + aTransformed.transform(getTransform()); + aRetval.push_back( + createPolygonLinePrimitive( + aTransformed, + getSdrLFSTAttribute().getLine(), + getSdrLFSTAttribute().getLineStartEnd())); + } + + // add text + if(!getSdrLFSTAttribute().getText().isDefault()) + { + aRetval.push_back( + createTextPrimitive( + basegfx::B2DPolyPolygon(aUnitOutline), + getTransform(), + getSdrLFSTAttribute().getText(), + getSdrLFSTAttribute().getLine(), + false, + false)); + } + + // add shadow + if(!getSdrLFSTAttribute().getShadow().isDefault()) + { + aRetval = createEmbeddedShadowPrimitive( + std::move(aRetval), + getSdrLFSTAttribute().getShadow()); + } + + rContainer.append(std::move(aRetval)); + } + + SdrEllipseSegmentPrimitive2D::SdrEllipseSegmentPrimitive2D( + const basegfx::B2DHomMatrix& rTransform, + const attribute::SdrLineFillEffectsTextAttribute& rSdrLFSTAttribute, + double fStartAngle, + double fEndAngle, + bool bCloseSegment, + bool bCloseUsingCenter) + : SdrEllipsePrimitive2D(rTransform, rSdrLFSTAttribute), + mfStartAngle(fStartAngle), + mfEndAngle(fEndAngle), + mbCloseSegment(bCloseSegment), + mbCloseUsingCenter(bCloseUsingCenter) + { + } + + bool SdrEllipseSegmentPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const + { + if(SdrEllipsePrimitive2D::operator==(rPrimitive)) + { + const SdrEllipseSegmentPrimitive2D& rCompare = static_cast<const SdrEllipseSegmentPrimitive2D&>(rPrimitive); + + if( mfStartAngle == rCompare.mfStartAngle + && mfEndAngle == rCompare.mfEndAngle + && mbCloseSegment == rCompare.mbCloseSegment + && mbCloseUsingCenter == rCompare.mbCloseUsingCenter) + { + return true; + } + } + + return false; + } + + // provide unique ID + sal_uInt32 SdrEllipseSegmentPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_SDRELLIPSESEGMENTPRIMITIVE2D; + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/primitive2d/sdrframeborderprimitive2d.cxx b/svx/source/sdr/primitive2d/sdrframeborderprimitive2d.cxx new file mode 100644 index 000000000..0610f43b0 --- /dev/null +++ b/svx/source/sdr/primitive2d/sdrframeborderprimitive2d.cxx @@ -0,0 +1,928 @@ +/* -*- 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 <svx/sdr/primitive2d/sdrframeborderprimitive2d.hxx> +#include <drawinglayer/primitive2d/borderlineprimitive2d.hxx> +#include <drawinglayer/geometry/viewinformation2d.hxx> +#include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <svtools/borderhelper.hxx> + +namespace +{ + double snapToDiscreteUnit( + double fValue, + double fMinimalDiscreteUnit) + { + if(0.0 != fValue) + { + fValue = std::max(fValue, fMinimalDiscreteUnit); + } + + return fValue; + } + + class StyleVectorCombination + { + private: + struct OffsetAndHalfWidthAndColor + { + double mfOffset; + double mfHalfWidth; + Color maColor; + + OffsetAndHalfWidthAndColor(double offset, double halfWidth, Color color) : + mfOffset(offset), + mfHalfWidth(halfWidth), + maColor(color) + {} + }; + + double mfRefModeOffset; + basegfx::B2DVector maB2DVector; + double mfAngle; + std::vector< OffsetAndHalfWidthAndColor > maOffsets; + + public: + StyleVectorCombination( + const svx::frame::Style& rStyle, + const basegfx::B2DVector& rB2DVector, + double fAngle, + bool bMirrored, + const Color* pForceColor, + double fMinimalDiscreteUnit) + : mfRefModeOffset(0.0), + maB2DVector(rB2DVector), + mfAngle(fAngle) + { + if (!rStyle.IsUsed()) + return; + + svx::frame::RefMode aRefMode(rStyle.GetRefMode()); + Color aPrim(rStyle.GetColorPrim()); + Color aSecn(rStyle.GetColorSecn()); + const bool bSecnUsed(0.0 != rStyle.Secn()); + + // Get the single segment line widths. This is the point where the + // minimal discrete unit will be used if given (fMinimalDiscreteUnit). If + // not given it's 0.0 and thus will have no influence. + double fPrim(snapToDiscreteUnit(rStyle.Prim(), fMinimalDiscreteUnit)); + const double fDist(snapToDiscreteUnit(rStyle.Dist(), fMinimalDiscreteUnit)); + double fSecn(snapToDiscreteUnit(rStyle.Secn(), fMinimalDiscreteUnit)); + + // Of course also do not use svx::frame::Style::GetWidth() for obvious + // reasons. + const double fStyleWidth(fPrim + fDist + fSecn); + + if(bMirrored) + { + switch(aRefMode) + { + case svx::frame::RefMode::Begin: aRefMode = svx::frame::RefMode::End; break; + case svx::frame::RefMode::End: aRefMode = svx::frame::RefMode::Begin; break; + default: break; + } + + if(bSecnUsed) + { + std::swap(aPrim, aSecn); + std::swap(fPrim, fSecn); + } + } + + if (svx::frame::RefMode::Centered != aRefMode) + { + const double fHalfWidth(fStyleWidth * 0.5); + + if (svx::frame::RefMode::Begin == aRefMode) + { + // move aligned below vector + mfRefModeOffset = fHalfWidth; + } + else if (svx::frame::RefMode::End == aRefMode) + { + // move aligned above vector + mfRefModeOffset = -fHalfWidth; + } + } + + if (bSecnUsed) + { + // both or all three lines used + const bool bPrimTransparent(rStyle.GetColorPrim().IsFullyTransparent()); + const bool bDistTransparent(!rStyle.UseGapColor() || rStyle.GetColorGap().IsFullyTransparent()); + const bool bSecnTransparent(aSecn.IsFullyTransparent()); + + if(!bPrimTransparent || !bDistTransparent || !bSecnTransparent) + { + const double a(mfRefModeOffset - (fStyleWidth * 0.5)); + const double b(a + fPrim); + const double c(b + fDist); + const double d(c + fSecn); + + maOffsets.push_back( + OffsetAndHalfWidthAndColor( + (a + b) * 0.5, + fPrim * 0.5, + nullptr != pForceColor ? *pForceColor : aPrim)); + + maOffsets.push_back( + OffsetAndHalfWidthAndColor( + (b + c) * 0.5, + fDist * 0.5, + rStyle.UseGapColor() + ? (nullptr != pForceColor ? *pForceColor : rStyle.GetColorGap()) + : COL_TRANSPARENT)); + + maOffsets.push_back( + OffsetAndHalfWidthAndColor( + (c + d) * 0.5, + fSecn * 0.5, + nullptr != pForceColor ? *pForceColor : aSecn)); + } + } + else + { + // one line used, push two values, from outer to inner + if(!rStyle.GetColorPrim().IsFullyTransparent()) + { + maOffsets.push_back( + OffsetAndHalfWidthAndColor( + mfRefModeOffset, + fPrim * 0.5, + nullptr != pForceColor ? *pForceColor : aPrim)); + } + } + } + + double getRefModeOffset() const { return mfRefModeOffset; } + const basegfx::B2DVector& getB2DVector() const { return maB2DVector; } + double getAngle() const { return mfAngle; } + bool empty() const { return maOffsets.empty(); } + size_t size() const { return maOffsets.size(); } + + void getColorAndOffsetAndHalfWidth(size_t nIndex, Color& rColor, double& rfOffset, double& rfHalfWidth) const + { + if(nIndex >= maOffsets.size()) + return; + const OffsetAndHalfWidthAndColor& rCandidate(maOffsets[nIndex]); + rfOffset = rCandidate.mfOffset; + rfHalfWidth = rCandidate.mfHalfWidth; + rColor = rCandidate.maColor; + } + }; + + class StyleVectorTable + { + private: + std::vector< StyleVectorCombination > maEntries; + + public: + StyleVectorTable() + { + } + + void add( + const svx::frame::Style& rStyle, + const basegfx::B2DVector& rMyVector, + const basegfx::B2DVector& rOtherVector, + bool bMirrored, + double fMinimalDiscreteUnit) + { + if(!rStyle.IsUsed() || basegfx::areParallel(rMyVector, rOtherVector)) + return; + + // create angle between both. angle() needs vectors pointing away from the same point, + // so take the mirrored one. Add M_PI to get from -pi..+pi to [0..M_PI_2] for sorting + const double fAngle(basegfx::B2DVector(-rMyVector.getX(), -rMyVector.getY()).angle(rOtherVector) + M_PI); + maEntries.emplace_back( + rStyle, + rOtherVector, + fAngle, + bMirrored, + nullptr, + fMinimalDiscreteUnit); + } + + void sort() + { + // sort inverse from highest to lowest + std::sort( + maEntries.begin(), + maEntries.end(), + [](const StyleVectorCombination& a, const StyleVectorCombination& b) + { return a.getAngle() > b.getAngle(); }); + } + + bool empty() const { return maEntries.empty(); } + const std::vector< StyleVectorCombination >& getEntries() const{ return maEntries; } + }; + + struct CutSet + { + double mfOLML; + double mfORML; + double mfOLMR; + double mfORMR; + + CutSet() : mfOLML(0.0), mfORML(0.0), mfOLMR(0.0), mfORMR(0.0) + { + } + + bool operator<( const CutSet& rOther) const + { + const double fA(mfOLML + mfORML + mfOLMR + mfORMR); + const double fB(rOther.mfOLML + rOther.mfORML + rOther.mfOLMR + rOther.mfORMR); + + return fA < fB; + } + + double getSum() const { return mfOLML + mfORML + mfOLMR + mfORMR; } + }; + + void getCutSet( + CutSet& rCutSet, + const basegfx::B2DPoint& rLeft, + const basegfx::B2DPoint& rRight, + const basegfx::B2DVector& rX, + const basegfx::B2DPoint& rOtherLeft, + const basegfx::B2DPoint& rOtherRight, + const basegfx::B2DVector& rOtherX) + { + basegfx::utils::findCut( + rLeft, + rX, + rOtherLeft, + rOtherX, + CutFlagValue::LINE, + &rCutSet.mfOLML); + + basegfx::utils::findCut( + rRight, + rX, + rOtherLeft, + rOtherX, + CutFlagValue::LINE, + &rCutSet.mfOLMR); + + basegfx::utils::findCut( + rLeft, + rX, + rOtherRight, + rOtherX, + CutFlagValue::LINE, + &rCutSet.mfORML); + + basegfx::utils::findCut( + rRight, + rX, + rOtherRight, + rOtherX, + CutFlagValue::LINE, + &rCutSet.mfORMR); + } + + struct ExtendSet + { + double mfExtLeft; + double mfExtRight; + + ExtendSet() : mfExtLeft(0.0), mfExtRight(0.0) {} + }; + + void getExtends( + std::vector<ExtendSet>& rExtendSet, // target Left/Right values to fill + const basegfx::B2DPoint& rOrigin, // own vector start + const StyleVectorCombination& rCombination, // own vector and offsets for lines + const basegfx::B2DVector& rPerpendX, // normalized perpendicular to own vector + const std::vector< StyleVectorCombination >& rStyleVector) // other vectors emerging in this point + { + if(!(!rCombination.empty() && !rStyleVector.empty() && rCombination.size() == rExtendSet.size())) + return; + + const size_t nOffsetA(rCombination.size()); + + if(1 == nOffsetA) + { + Color aMyColor; double fMyOffset(0.0); double fMyHalfWidth(0.0); + rCombination.getColorAndOffsetAndHalfWidth(0, aMyColor, fMyOffset, fMyHalfWidth); + + if(!aMyColor.IsFullyTransparent()) + { + const basegfx::B2DPoint aLeft(rOrigin + (rPerpendX * (fMyOffset - fMyHalfWidth))); + const basegfx::B2DPoint aRight(rOrigin + (rPerpendX * (fMyOffset + fMyHalfWidth))); + std::vector< CutSet > aCutSets; + + for(const auto& rStyleCandidate : rStyleVector) + { + const basegfx::B2DVector aOtherPerpend(basegfx::getNormalizedPerpendicular(rStyleCandidate.getB2DVector())); + const size_t nOffsetB(rStyleCandidate.size()); + + for(size_t other(0); other < nOffsetB; other++) + { + Color aOtherColor; double fOtherOffset(0.0); double fOtherHalfWidth(0.0); + rStyleCandidate.getColorAndOffsetAndHalfWidth(other, aOtherColor, fOtherOffset, fOtherHalfWidth); + + if(!aOtherColor.IsFullyTransparent()) + { + const basegfx::B2DPoint aOtherLeft(rOrigin + (aOtherPerpend * (fOtherOffset - fOtherHalfWidth))); + const basegfx::B2DPoint aOtherRight(rOrigin + (aOtherPerpend * (fOtherOffset + fOtherHalfWidth))); + + CutSet aNewCutSet; + getCutSet(aNewCutSet, aLeft, aRight, rCombination.getB2DVector(), aOtherLeft, aOtherRight, rStyleCandidate.getB2DVector()); + aCutSets.push_back(aNewCutSet); + } + } + } + + if(!aCutSets.empty()) + { + CutSet aCutSet(aCutSets[0]); + const size_t nNumCutSets(aCutSets.size()); + + if(1 != nNumCutSets) + { + double fCutSet(aCutSet.getSum()); + + for(size_t a(1); a < nNumCutSets; a++) + { + const CutSet& rCandidate(aCutSets[a]); + const double fCandidate(rCandidate.getSum()); + + if(basegfx::fTools::equalZero(fCandidate - fCutSet)) + { + // both have equal center point, use medium cut + const double fNewOLML(std::max(std::min(rCandidate.mfOLML, rCandidate.mfORML), std::min(aCutSet.mfOLML, aCutSet.mfORML))); + const double fNewORML(std::min(std::max(rCandidate.mfOLML, rCandidate.mfORML), std::max(aCutSet.mfOLML, aCutSet.mfORML))); + const double fNewOLMR(std::max(std::min(rCandidate.mfOLMR, rCandidate.mfORMR), std::min(aCutSet.mfOLMR, aCutSet.mfORMR))); + const double fNewORMR(std::min(std::max(rCandidate.mfOLMR, rCandidate.mfORMR), std::max(aCutSet.mfOLMR, aCutSet.mfORMR))); + aCutSet.mfOLML = fNewOLML; + aCutSet.mfORML = fNewORML; + aCutSet.mfOLMR = fNewOLMR; + aCutSet.mfORMR = fNewORMR; + fCutSet = aCutSet.getSum(); + } + else if(fCandidate < fCutSet) + { + // get minimum + fCutSet = fCandidate; + aCutSet = rCandidate; + } + } + } + + ExtendSet& rExt(rExtendSet[0]); + + rExt.mfExtLeft = std::min(aCutSet.mfOLML, aCutSet.mfORML); + rExt.mfExtRight = std::min(aCutSet.mfOLMR, aCutSet.mfORMR); + } + } + } + else + { + size_t nVisEdgeUp(0); + size_t nVisEdgeDn(0); + + for(size_t my(0); my < nOffsetA; my++) + { + Color aMyColor; double fMyOffset(0.0); double fMyHalfWidth(0.0); + rCombination.getColorAndOffsetAndHalfWidth(my, aMyColor, fMyOffset, fMyHalfWidth); + + if(!aMyColor.IsFullyTransparent()) + { + const basegfx::B2DPoint aLeft(rOrigin + (rPerpendX * (fMyOffset - fMyHalfWidth))); + const basegfx::B2DPoint aRight(rOrigin + (rPerpendX * (fMyOffset + fMyHalfWidth))); + const bool bUpper(my <= (nOffsetA >> 1)); + const StyleVectorCombination& rStyleCandidate(bUpper ? rStyleVector.front() : rStyleVector.back()); + const basegfx::B2DVector aOtherPerpend(basegfx::getNormalizedPerpendicular(rStyleCandidate.getB2DVector())); + const size_t nOffsetB(rStyleCandidate.size()); + std::vector< CutSet > aCutSets; + + for(size_t other(0); other < nOffsetB; other++) + { + Color aOtherColor; double fOtherOffset(0.0); double fOtherHalfWidth(0.0); + rStyleCandidate.getColorAndOffsetAndHalfWidth(other, aOtherColor, fOtherOffset, fOtherHalfWidth); + + if(!aOtherColor.IsFullyTransparent()) + { + const basegfx::B2DPoint aOtherLeft(rOrigin + (aOtherPerpend * (fOtherOffset - fOtherHalfWidth))); + const basegfx::B2DPoint aOtherRight(rOrigin + (aOtherPerpend * (fOtherOffset + fOtherHalfWidth))); + CutSet aCutSet; + getCutSet(aCutSet, aLeft, aRight, rCombination.getB2DVector(), aOtherLeft, aOtherRight, rStyleCandidate.getB2DVector()); + aCutSets.push_back(aCutSet); + } + } + + if(!aCutSets.empty()) + { + // sort: min to start, max to end + std::sort(aCutSets.begin(), aCutSets.end()); + const bool bOtherUpper(rStyleCandidate.getAngle() > M_PI); + + // check if we need min or max + // bUpper bOtherUpper MinMax + // t t max + // t f min + // f f max + // f t min + const bool bMax(bUpper == bOtherUpper); + size_t nBaseIndex(0); + const size_t nNumCutSets(aCutSets.size()); + + if(bMax) + { + // access at end + nBaseIndex = nNumCutSets - 1 - (bUpper ? nVisEdgeUp : nVisEdgeDn); + } + else + { + // access at start + nBaseIndex = bUpper ? nVisEdgeUp : nVisEdgeDn; + } + + const size_t nSecuredIndex(std::clamp(nBaseIndex, size_t(0), size_t(nNumCutSets - 1))); + const CutSet& rCutSet(aCutSets[nSecuredIndex]); + ExtendSet& rExt(rExtendSet[my]); + + rExt.mfExtLeft = std::min(rCutSet.mfOLML, rCutSet.mfORML); + rExt.mfExtRight = std::min(rCutSet.mfOLMR, rCutSet.mfORMR); + } + + if(bUpper) + { + nVisEdgeUp++; + } + else + { + nVisEdgeDn++; + } + } + } + } + } + + /** + * Helper method to create the correct drawinglayer::primitive2d::BorderLinePrimitive2D + * for the given data, especially the correct drawinglayer::primitive2d::BorderLine entries + * including the correctly solved/created LineStartEnd extends + * + * rTarget : Here the evtl. created BorderLinePrimitive2D will be appended + * rOrigin : StartPoint of the Borderline + * rX : Vector of the Borderline + * rBorder : svx::frame::Style of the of the Borderline + * rStartStyleVectorTable : All other Borderlines which have to be taken into account because + * they have the same StartPoint as the current Borderline. These will be used to calculate + * the correct LineStartEnd extends tor the BorderLinePrimitive2D. The definition should be + * built up using svx::frame::StyleVectorTable and StyleVectorTable::add and includes: + * rStyle : the svx::frame::Style of one other BorderLine + * rMyVector : the Vector of the *new* to-be-defined BorderLine, identical to rX + * rOtherVector: the Vector of one other BorderLine (may be, but does not need to be normalized), + * always *pointing away* from the common StartPoint rOrigin + * bMirrored : define if rStyle of one other BorderLine shall be mirrored (e.g. bottom-right edges) + * With multiple BorderLines the definitions have to be CounterClockWise. This will be + * ensured by StyleVectorTable sorting the entries, but knowing this may allow more efficient + * data creation. + * rEndStyleVectorTable: All other BorderLines that have the same EndPoint. There are differences to + * the Start definitions: + * - do not forget to consequently use -rX for rMyVector + * - definitions have to be ClockWise for the EndBorderLines, will be ensured by sorting + * + * If you take all this into account, you will get correctly extended BorderLinePrimitive2D + * representations for the new to be defined BorderLine. That extensions will overlap nicely + * with the corresponding BorderLines and take all multiple line definitions in the ::Style into + * account. + * The internal solver is *not limited* to ::Style(s) with three parts (Left/Gap/Right), this is + * just due to svx::frame::Style's definitions. A new solver based on this one can be created + * anytime using more mulötiple borders based on the more flexible + * std::vector< drawinglayer::primitive2d::BorderLine > if needed. + */ + void CreateBorderPrimitives( + drawinglayer::primitive2d::Primitive2DContainer& rTarget, /// target for created primitives + const basegfx::B2DPoint& rOrigin, /// start point of borderline + const basegfx::B2DVector& rX, /// X-Axis of borderline with length + const svx::frame::Style& rBorder, /// Style of borderline + const StyleVectorTable& rStartStyleVectorTable, /// Styles and vectors (pointing away) at borderline start, ccw + const StyleVectorTable& rEndStyleVectorTable, /// Styles and vectors (pointing away) at borderline end, cw + const Color* pForceColor, /// If specified, overrides frame border color. + double fMinimalDiscreteUnit) /// minimal discrete unit to use for svx::frame::Style width values + { + // get offset color pairs for style, one per visible line + const StyleVectorCombination aCombination( + rBorder, + rX, + 0.0, + false, + pForceColor, + fMinimalDiscreteUnit); + + if(aCombination.empty()) + return; + + const basegfx::B2DVector aPerpendX(basegfx::getNormalizedPerpendicular(rX)); + const bool bHasStartStyles(!rStartStyleVectorTable.empty()); + const bool bHasEndStyles(!rEndStyleVectorTable.empty()); + const size_t nOffsets(aCombination.size()); + std::vector<ExtendSet> aExtendSetStart(nOffsets); + std::vector<ExtendSet> aExtendSetEnd(nOffsets); + + if(bHasStartStyles) + { + // create extends for line starts, use given point/vector and offsets + getExtends(aExtendSetStart, rOrigin, aCombination, aPerpendX, rStartStyleVectorTable.getEntries()); + } + + if(bHasEndStyles) + { + // Create extends for line ends, create inverse point/vector and inverse offsets. + const StyleVectorCombination aMirroredCombination( + rBorder, + -rX, + 0.0, + true, + pForceColor, + fMinimalDiscreteUnit); + + getExtends(aExtendSetEnd, rOrigin + rX, aMirroredCombination, -aPerpendX, rEndStyleVectorTable.getEntries()); + + // also need to inverse the result to apply to the correct lines + std::reverse(aExtendSetEnd.begin(), aExtendSetEnd.end()); + } + + std::vector< drawinglayer::primitive2d::BorderLine > aBorderlines; + const double fNegLength(-rX.getLength()); + + for(size_t a(0); a < nOffsets; a++) + { + Color aMyColor; + double fMyOffset(0.0); + double fMyHalfWidth(0.0); + aCombination.getColorAndOffsetAndHalfWidth(a, aMyColor, fMyOffset, fMyHalfWidth); + const ExtendSet& rExtStart(aExtendSetStart[a]); + const ExtendSet& rExtEnd(aExtendSetEnd[a]); + + if(aMyColor.IsFullyTransparent()) + { + aBorderlines.push_back( + drawinglayer::primitive2d::BorderLine( + fMyHalfWidth * 2.0)); + } + else + { + aBorderlines.push_back( + drawinglayer::primitive2d::BorderLine( + drawinglayer::attribute::LineAttribute( + aMyColor.getBColor(), + fMyHalfWidth * 2.0), + fNegLength * rExtStart.mfExtLeft, + fNegLength * rExtStart.mfExtRight, + fNegLength * rExtEnd.mfExtRight, + fNegLength * rExtEnd.mfExtLeft)); + } + } + + static const double fPatScFact(10.0); // 10.0 multiply, see old code + std::vector<double> aDashing(svtools::GetLineDashing(rBorder.Type(), rBorder.PatternScale() * fPatScFact)); + const drawinglayer::attribute::StrokeAttribute aStrokeAttribute(std::move(aDashing)); + const basegfx::B2DPoint aStart(rOrigin + (aPerpendX * aCombination.getRefModeOffset())); + + rTarget.append( + drawinglayer::primitive2d::Primitive2DReference( + new drawinglayer::primitive2d::BorderLinePrimitive2D( + aStart, + aStart + rX, + std::move(aBorderlines), + aStrokeAttribute))); + } + + double getMinimalNonZeroValue(double fCurrent, double fNew) + { + if(0.0 != fNew) + { + if(0.0 != fCurrent) + { + fCurrent = std::min(fNew, fCurrent); + } + else + { + fCurrent = fNew; + } + } + + return fCurrent; + } + + double getMinimalNonZeroBorderWidthFromStyle(double fCurrent, const svx::frame::Style& rStyle) + { + if(rStyle.IsUsed()) + { + fCurrent = getMinimalNonZeroValue(fCurrent, rStyle.Prim()); + fCurrent = getMinimalNonZeroValue(fCurrent, rStyle.Dist()); + fCurrent = getMinimalNonZeroValue(fCurrent, rStyle.Secn()); + } + + return fCurrent; + } +} + +namespace drawinglayer::primitive2d +{ + SdrFrameBorderData::SdrConnectStyleData::SdrConnectStyleData( + const svx::frame::Style& rStyle, + const basegfx::B2DVector& rNormalizedPerpendicular, + bool bStyleMirrored) + : maStyle(rStyle), + maNormalizedPerpendicular(rNormalizedPerpendicular), + mbStyleMirrored(bStyleMirrored) + { + } + + bool SdrFrameBorderData::SdrConnectStyleData::operator==(const SdrFrameBorderData::SdrConnectStyleData& rCompare) const + { + return mbStyleMirrored == rCompare.mbStyleMirrored + && maStyle == rCompare.maStyle + && maNormalizedPerpendicular == rCompare.maNormalizedPerpendicular; + } + + SdrFrameBorderData::SdrFrameBorderData( + const basegfx::B2DPoint& rOrigin, + const basegfx::B2DVector& rX, + const svx::frame::Style& rStyle, + const Color* pForceColor) + : maOrigin(rOrigin), + maX(rX), + maStyle(rStyle), + maColor(nullptr != pForceColor ? *pForceColor : Color()), + mbForceColor(nullptr != pForceColor) + { + } + + void SdrFrameBorderData::addSdrConnectStyleData( + bool bStart, + const svx::frame::Style& rStyle, + const basegfx::B2DVector& rNormalizedPerpendicular, + bool bStyleMirrored) + { + if(rStyle.IsUsed()) + { + if(bStart) + { + maStart.emplace_back(rStyle, rNormalizedPerpendicular, bStyleMirrored); + } + else + { + maEnd.emplace_back(rStyle, rNormalizedPerpendicular, bStyleMirrored); + } + } + } + + void SdrFrameBorderData::create2DDecomposition( + Primitive2DContainer& rContainer, + double fMinimalDiscreteUnit) const + { + StyleVectorTable aStartVector; + StyleVectorTable aEndVector; + const basegfx::B2DVector aAxis(-maX); + + for(const auto& rStart : maStart) + { + aStartVector.add( + rStart.getStyle(), + maX, + rStart.getNormalizedPerpendicular(), + rStart.getStyleMirrored(), + fMinimalDiscreteUnit); + } + + for(const auto& rEnd : maEnd) + { + aEndVector.add( + rEnd.getStyle(), + aAxis, + rEnd.getNormalizedPerpendicular(), + rEnd.getStyleMirrored(), + fMinimalDiscreteUnit); + } + + aStartVector.sort(); + aEndVector.sort(); + + CreateBorderPrimitives( + rContainer, + maOrigin, + maX, + maStyle, + aStartVector, + aEndVector, + mbForceColor ? &maColor : nullptr, + fMinimalDiscreteUnit); + } + + double SdrFrameBorderData::getMinimalNonZeroBorderWidth() const + { + double fRetval(getMinimalNonZeroBorderWidthFromStyle(0.0, maStyle)); + + for(const auto& rStart : maStart) + { + fRetval = getMinimalNonZeroBorderWidthFromStyle(fRetval, rStart.getStyle()); + } + + for(const auto& rEnd : maEnd) + { + fRetval = getMinimalNonZeroBorderWidthFromStyle(fRetval, rEnd.getStyle()); + } + + return fRetval; + } + + + bool SdrFrameBorderData::operator==(const SdrFrameBorderData& rCompare) const + { + return maOrigin == rCompare.maOrigin + && maX == rCompare.maX + && maStyle == rCompare.maStyle + && maColor == rCompare.maColor + && mbForceColor == rCompare.mbForceColor + && maStart == rCompare.maStart + && maEnd == rCompare.maEnd; + } + + + void SdrFrameBorderPrimitive2D::create2DDecomposition( + Primitive2DContainer& rContainer, + const geometry::ViewInformation2D& /*aViewInformation*/) const + { + if(!getFrameBorders()) + { + return; + } + + Primitive2DContainer aRetval; + + // Check and use the minimal non-zero BorderWidth for decompose + // if that is set and wanted + const double fMinimalDiscreteUnit(doForceToSingleDiscreteUnit() + ? mfMinimalNonZeroBorderWidthUsedForDecompose + : 0.0); + + { + // decompose all buffered SdrFrameBorderData entries and try to merge them + // to reduce existing number of BorderLinePrimitive2D(s) + for(const auto& rCandidate : *getFrameBorders()) + { + // get decomposition on one SdrFrameBorderData entry + Primitive2DContainer aPartial; + rCandidate.create2DDecomposition( + aPartial, + fMinimalDiscreteUnit); + + for(const auto& aCandidatePartial : aPartial) + { + if(aRetval.empty()) + { + // no local data yet, just add as 1st entry, done + aRetval.append(aCandidatePartial); + } + else + { + bool bDidMerge(false); + + for(auto& aCandidateRetval : aRetval) + { + // try to merge by appending new data to existing data + const drawinglayer::primitive2d::Primitive2DReference aMergeRetvalPartial( + drawinglayer::primitive2d::tryMergeBorderLinePrimitive2D( + static_cast<BorderLinePrimitive2D*>(aCandidateRetval.get()), + static_cast<BorderLinePrimitive2D*>(aCandidatePartial.get()))); + + if(aMergeRetvalPartial.is()) + { + // could append, replace existing data with merged data, done + aCandidateRetval = aMergeRetvalPartial; + bDidMerge = true; + break; + } + + // try to merge by appending existing data to new data + const drawinglayer::primitive2d::Primitive2DReference aMergePartialRetval( + drawinglayer::primitive2d::tryMergeBorderLinePrimitive2D( + static_cast<BorderLinePrimitive2D*>(aCandidatePartial.get()), + static_cast<BorderLinePrimitive2D*>(aCandidateRetval.get()))); + + if(aMergePartialRetval.is()) + { + // could append, replace existing data with merged data, done + aCandidateRetval = aMergePartialRetval; + bDidMerge = true; + break; + } + } + + if(!bDidMerge) + { + // no merge after checking all existing data, append as new segment + aRetval.append(aCandidatePartial); + } + } + } + } + } + + rContainer.append(std::move(aRetval)); + } + + SdrFrameBorderPrimitive2D::SdrFrameBorderPrimitive2D( + std::shared_ptr<SdrFrameBorderDataVector>& rFrameBorders, + bool bForceToSingleDiscreteUnit) + : maFrameBorders(std::move(rFrameBorders)), + mfMinimalNonZeroBorderWidth(0.0), + mfMinimalNonZeroBorderWidthUsedForDecompose(0.0), + mbForceToSingleDiscreteUnit(bForceToSingleDiscreteUnit) + { + if(getFrameBorders() && doForceToSingleDiscreteUnit()) + { + // detect used minimal non-zero partial border width + for(const auto& rCandidate : *getFrameBorders()) + { + mfMinimalNonZeroBorderWidth = getMinimalNonZeroValue( + mfMinimalNonZeroBorderWidth, + rCandidate.getMinimalNonZeroBorderWidth()); + } + } + } + + bool SdrFrameBorderPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const + { + if(BufferedDecompositionPrimitive2D::operator==(rPrimitive)) + { + const SdrFrameBorderPrimitive2D& rCompare = static_cast<const SdrFrameBorderPrimitive2D&>(rPrimitive); + + return (getFrameBorders() == rCompare.getFrameBorders() + || (getFrameBorders() && rCompare.getFrameBorders() + && *getFrameBorders() == *rCompare.getFrameBorders())) + && doForceToSingleDiscreteUnit() == rCompare.doForceToSingleDiscreteUnit(); + } + + return false; + } + + void SdrFrameBorderPrimitive2D::get2DDecomposition( + Primitive2DDecompositionVisitor& rVisitor, + const geometry::ViewInformation2D& rViewInformation) const + { + if(doForceToSingleDiscreteUnit()) + { + // Get the current DiscreteUnit, look at X and Y and use the maximum + const basegfx::B2DVector aDiscreteVector(rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 1.0)); + double fDiscreteUnit(std::min(fabs(aDiscreteVector.getX()), fabs(aDiscreteVector.getY()))); + + if(fDiscreteUnit <= mfMinimalNonZeroBorderWidth) + { + // no need to use it, reset + fDiscreteUnit = 0.0; + } + + if(fDiscreteUnit != mfMinimalNonZeroBorderWidthUsedForDecompose) + { + // conditions of last local decomposition have changed, delete + // possible content + if(!getBuffered2DDecomposition().empty()) + { + const_cast< SdrFrameBorderPrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer()); + } + + // remember new conditions + const_cast< SdrFrameBorderPrimitive2D* >(this)->mfMinimalNonZeroBorderWidthUsedForDecompose = fDiscreteUnit; + } + } + + // call parent. This will call back ::create2DDecomposition above + // where mfMinimalNonZeroBorderWidthUsedForDecompose will be used + // when doForceToSingleDiscreteUnit() is true + BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation); + } + + // provide unique ID + sal_uInt32 SdrFrameBorderPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_SDRFRAMEBORDERTPRIMITIVE2D; + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/primitive2d/sdrgrafprimitive2d.cxx b/svx/source/sdr/primitive2d/sdrgrafprimitive2d.cxx new file mode 100644 index 000000000..f5ac21cfc --- /dev/null +++ b/svx/source/sdr/primitive2d/sdrgrafprimitive2d.cxx @@ -0,0 +1,167 @@ +/* -*- 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 <sdr/primitive2d/sdrgrafprimitive2d.hxx> +#include <drawinglayer/primitive2d/graphicprimitive2d.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <sdr/primitive2d/sdrdecompositiontools.hxx> +#include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> + +namespace drawinglayer::primitive2d +{ +void SdrGrafPrimitive2D::create2DDecomposition( + Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*aViewInformation*/) const +{ + Primitive2DContainer aRetval; + + // create unit outline polygon + const basegfx::B2DPolygon& aUnitOutline(basegfx::utils::createUnitPolygon()); + + // add fill, but only when graphic is transparent + if (!getSdrLFSTAttribute().getFill().isDefault() && isTransparent()) + { + basegfx::B2DPolyPolygon aTransformed(aUnitOutline); + + aTransformed.transform(getTransform()); + aRetval.push_back( + createPolyPolygonFillPrimitive(aTransformed, getSdrLFSTAttribute().getFill(), + getSdrLFSTAttribute().getFillFloatTransGradient())); + } + + // add graphic content + if (0 != getGraphicAttr().GetAlpha()) + { + // standard graphic fill + const Primitive2DReference xGraphicContentPrimitive( + new GraphicPrimitive2D(getTransform(), getGraphicObject(), getGraphicAttr())); + aRetval.push_back(xGraphicContentPrimitive); + } + + // add line + if (!getSdrLFSTAttribute().getLine().isDefault()) + { + // if line width is given, polygon needs to be grown by half of it to make the + // outline to be outside of the bitmap + if (0.0 != getSdrLFSTAttribute().getLine().getWidth()) + { + // decompose to get scale + basegfx::B2DVector aScale, aTranslate; + double fRotate, fShearX; + getTransform().decompose(aScale, aTranslate, fRotate, fShearX); + + // create expanded range (add relative half line width to unit rectangle) + double fHalfLineWidth(getSdrLFSTAttribute().getLine().getWidth() * 0.5); + double fScaleX(0.0 != aScale.getX() ? fHalfLineWidth / fabs(aScale.getX()) : 1.0); + double fScaleY(0.0 != aScale.getY() ? fHalfLineWidth / fabs(aScale.getY()) : 1.0); + const basegfx::B2DRange aExpandedRange(-fScaleX, -fScaleY, 1.0 + fScaleX, + 1.0 + fScaleY); + basegfx::B2DPolygon aExpandedUnitOutline( + basegfx::utils::createPolygonFromRect(aExpandedRange)); + + aExpandedUnitOutline.transform(getTransform()); + aRetval.push_back(createPolygonLinePrimitive(aExpandedUnitOutline, + getSdrLFSTAttribute().getLine(), + attribute::SdrLineStartEndAttribute())); + } + else + { + basegfx::B2DPolygon aTransformed(aUnitOutline); + + aTransformed.transform(getTransform()); + aRetval.push_back(createPolygonLinePrimitive(aTransformed, + getSdrLFSTAttribute().getLine(), + attribute::SdrLineStartEndAttribute())); + } + } + + // Soft edges should be before text, since text is not affected by soft edges + if (!aRetval.empty() && getSdrLFSTAttribute().getSoftEdgeRadius()) + { + aRetval = createEmbeddedSoftEdgePrimitive(std::move(aRetval), + getSdrLFSTAttribute().getSoftEdgeRadius()); + } + + // add text + if (!getSdrLFSTAttribute().getText().isDefault()) + { + aRetval.push_back(createTextPrimitive(basegfx::B2DPolyPolygon(aUnitOutline), getTransform(), + getSdrLFSTAttribute().getText(), + getSdrLFSTAttribute().getLine(), false, false)); + } + + // tdf#132199: put glow before shadow, to have shadow of the glow, not the opposite + if (!aRetval.empty() && !getSdrLFSTAttribute().getGlow().isDefault()) + { + // glow + aRetval = createEmbeddedGlowPrimitive(std::move(aRetval), getSdrLFSTAttribute().getGlow()); + } + + // add shadow + if (!getSdrLFSTAttribute().getShadow().isDefault()) + { + aRetval = createEmbeddedShadowPrimitive(std::move(aRetval), + getSdrLFSTAttribute().getShadow(), getTransform()); + } + + rContainer.append(std::move(aRetval)); +} + +SdrGrafPrimitive2D::SdrGrafPrimitive2D( + const basegfx::B2DHomMatrix& rTransform, + const attribute::SdrLineFillEffectsTextAttribute& rSdrLFSTAttribute, + const GraphicObject& rGraphicObject, const GraphicAttr& rGraphicAttr) + : maTransform(rTransform) + , maSdrLFSTAttribute(rSdrLFSTAttribute) + , maGraphicObject(rGraphicObject) + , maGraphicAttr(rGraphicAttr) +{ + // reset some values from GraphicAttr which are part of transformation already + maGraphicAttr.SetRotation(0_deg10); +} + +bool SdrGrafPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const +{ + if (BufferedDecompositionPrimitive2D::operator==(rPrimitive)) + { + const SdrGrafPrimitive2D& rCompare = static_cast<const SdrGrafPrimitive2D&>(rPrimitive); + + return (getTransform() == rCompare.getTransform() + && getSdrLFSTAttribute() == rCompare.getSdrLFSTAttribute() + && getGraphicObject() == rCompare.getGraphicObject() + && getGraphicAttr() == rCompare.getGraphicAttr()); + } + + return false; +} + +bool SdrGrafPrimitive2D::isTransparent() const +{ + return ((255 != getGraphicAttr().GetAlpha()) || (getGraphicObject().IsTransparent())); +} + +// provide unique ID +sal_uInt32 SdrGrafPrimitive2D::getPrimitive2DID() const +{ + return PRIMITIVE2D_ID_SDRGRAFPRIMITIVE2D; +} + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/primitive2d/sdrmeasureprimitive2d.cxx b/svx/source/sdr/primitive2d/sdrmeasureprimitive2d.cxx new file mode 100644 index 000000000..aada2aee0 --- /dev/null +++ b/svx/source/sdr/primitive2d/sdrmeasureprimitive2d.cxx @@ -0,0 +1,498 @@ +/* -*- 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 <sdr/primitive2d/sdrmeasureprimitive2d.hxx> +#include <sdr/primitive2d/sdrdecompositiontools.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <sdr/primitive2d/sdrtextprimitive2d.hxx> +#include <sdr/attribute/sdrtextattribute.hxx> +#include <basegfx/polygon/b2dpolypolygontools.hxx> +#include <rtl/ref.hxx> +#include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> +#include <drawinglayer/primitive2d/hiddengeometryprimitive2d.hxx> +#include <osl/diagnose.h> + + +using namespace com::sun::star; + + +namespace drawinglayer::primitive2d +{ + Primitive2DReference SdrMeasurePrimitive2D::impCreatePart( + const attribute::SdrLineAttribute& rLineAttribute, + const basegfx::B2DHomMatrix& rObjectMatrix, + const basegfx::B2DPoint& rStart, + const basegfx::B2DPoint& rEnd, + bool bLeftActive, + bool bRightActive) const + { + const attribute::SdrLineStartEndAttribute& rLineStartEnd = getSdrLSTAttribute().getLineStartEnd(); + basegfx::B2DPolygon aPolygon; + + aPolygon.append(rStart); + aPolygon.append(rEnd); + aPolygon.transform(rObjectMatrix); + + if(rLineStartEnd.isDefault() || (!bLeftActive && !bRightActive)) + { + return createPolygonLinePrimitive( + aPolygon, + rLineAttribute, + attribute::SdrLineStartEndAttribute()); + } + + if(bLeftActive && bRightActive) + { + return createPolygonLinePrimitive( + aPolygon, + rLineAttribute, + rLineStartEnd); + } + + const basegfx::B2DPolyPolygon aEmpty; + const attribute::SdrLineStartEndAttribute aLineStartEnd( + bLeftActive ? rLineStartEnd.getStartPolyPolygon() : aEmpty, bRightActive ? rLineStartEnd.getEndPolyPolygon() : aEmpty, + bLeftActive ? rLineStartEnd.getStartWidth() : 0.0, bRightActive ? rLineStartEnd.getEndWidth() : 0.0, + bLeftActive && rLineStartEnd.isStartActive(), bRightActive && rLineStartEnd.isEndActive(), + bLeftActive && rLineStartEnd.isStartCentered(), bRightActive && rLineStartEnd.isEndCentered()); + + return createPolygonLinePrimitive( + aPolygon, + rLineAttribute, + aLineStartEnd); + } + + void SdrMeasurePrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& aViewInformation) const + { + Primitive2DContainer aRetval; + rtl::Reference<SdrBlockTextPrimitive2D> xBlockText; + basegfx::B2DRange aTextRange; + const basegfx::B2DVector aLine(getEnd() - getStart()); + const double fDistance(aLine.getLength()); + const double fAngle(atan2(aLine.getY(), aLine.getX())); + bool bAutoUpsideDown(false); + const attribute::SdrTextAttribute rTextAttribute = getSdrLSTAttribute().getText(); + const basegfx::B2DHomMatrix aObjectMatrix( + basegfx::utils::createShearXRotateTranslateB2DHomMatrix(0.0, fAngle, getStart())); + + // prepare text, but do not add yet; it needs to be aligned to + // the line geometry + if(!rTextAttribute.isDefault()) + { + basegfx::B2DHomMatrix aTextMatrix; + double fTestAngle(fAngle); + + if(getTextRotation()) + { + aTextMatrix.rotate(-M_PI_2); + fTestAngle -= (M_PI_2); + + if(getTextAutoAngle() && fTestAngle < -M_PI) + { + fTestAngle += 2 * M_PI; + } + } + + if(getTextAutoAngle()) + { + if(fTestAngle > (M_PI / 4.0) || fTestAngle < (-M_PI * (3.0 / 4.0))) + { + bAutoUpsideDown = true; + } + } + + // create primitive and get text range + xBlockText = new SdrBlockTextPrimitive2D( + &rTextAttribute.getSdrText(), + rTextAttribute.getOutlinerParaObject(), + aTextMatrix, + SDRTEXTHORZADJUST_CENTER, + SDRTEXTVERTADJUST_CENTER, + rTextAttribute.isScroll(), + false, + false, + false); + + aTextRange = xBlockText->getB2DRange(aViewInformation); + } + + // prepare line attribute and result + double fTextX; + double fTextY; + { + const attribute::SdrLineAttribute rLineAttribute(getSdrLSTAttribute().getLine()); + bool bArrowsOutside(false); + bool bMainLineSplitted(false); + const attribute::SdrLineStartEndAttribute& rLineStartEnd = getSdrLSTAttribute().getLineStartEnd(); + double fStartArrowW(0.0); + double fStartArrowH(0.0); + double fEndArrowW(0.0); + double fEndArrowH(0.0); + + if(!rLineStartEnd.isDefault()) + { + if(rLineStartEnd.isStartActive()) + { + const basegfx::B2DRange aArrowRange(basegfx::utils::getRange(rLineStartEnd.getStartPolyPolygon())); + fStartArrowW = rLineStartEnd.getStartWidth(); + fStartArrowH = aArrowRange.getHeight() * fStartArrowW / aArrowRange.getWidth(); + + if(rLineStartEnd.isStartCentered()) + { + fStartArrowH *= 0.5; + } + } + + if(rLineStartEnd.isEndActive()) + { + const basegfx::B2DRange aArrowRange(basegfx::utils::getRange(rLineStartEnd.getEndPolyPolygon())); + fEndArrowW = rLineStartEnd.getEndWidth(); + fEndArrowH = aArrowRange.getHeight() * fEndArrowW / aArrowRange.getWidth(); + + if(rLineStartEnd.isEndCentered()) + { + fEndArrowH *= 0.5; + } + } + } + + const double fSpaceNeededByArrows(fStartArrowH + fEndArrowH + ((fStartArrowW + fEndArrowW) * 0.5)); + const double fArrowsOutsideLen((fStartArrowH + fEndArrowH + fStartArrowW + fEndArrowW) * 0.5); + const double fHalfLineWidth(rLineAttribute.getWidth() * 0.5); + + if(fSpaceNeededByArrows > fDistance) + { + bArrowsOutside = true; + } + + MeasureTextPosition eHorizontal(getHorizontal()); + MeasureTextPosition eVertical(getVertical()); + + if(MEASURETEXTPOSITION_AUTOMATIC == eVertical) + { + eVertical = MEASURETEXTPOSITION_NEGATIVE; + } + + if(MEASURETEXTPOSITION_CENTERED == eVertical) + { + bMainLineSplitted = true; + } + + if(MEASURETEXTPOSITION_AUTOMATIC == eHorizontal) + { + if(aTextRange.getWidth() > fDistance) + { + eHorizontal = MEASURETEXTPOSITION_NEGATIVE; + } + else + { + eHorizontal = MEASURETEXTPOSITION_CENTERED; + } + + if(bMainLineSplitted) + { + if(aTextRange.getWidth() + fSpaceNeededByArrows > fDistance) + { + bArrowsOutside = true; + } + } + else + { + const double fSmallArrowNeed(fStartArrowH + fEndArrowH + ((fStartArrowW + fEndArrowW) * 0.125)); + + if(aTextRange.getWidth() + fSmallArrowNeed > fDistance) + { + bArrowsOutside = true; + } + } + } + + if(MEASURETEXTPOSITION_CENTERED != eHorizontal) + { + bArrowsOutside = true; + } + + // switch text above/below? + if(getBelow() || (bAutoUpsideDown && !getTextRotation())) + { + if(MEASURETEXTPOSITION_NEGATIVE == eVertical) + { + eVertical = MEASURETEXTPOSITION_POSITIVE; + } + else if(MEASURETEXTPOSITION_POSITIVE == eVertical) + { + eVertical = MEASURETEXTPOSITION_NEGATIVE; + } + } + + const double fMainLineOffset(getBelow() ? getDistance() : -getDistance()); + const basegfx::B2DPoint aMainLeft(0.0, fMainLineOffset); + const basegfx::B2DPoint aMainRight(fDistance, fMainLineOffset); + + // main line + if(bArrowsOutside) + { + double fLenLeft(fArrowsOutsideLen); + double fLenRight(fArrowsOutsideLen); + + if(!bMainLineSplitted) + { + if(MEASURETEXTPOSITION_NEGATIVE == eHorizontal) + { + fLenLeft = fStartArrowH + aTextRange.getWidth(); + } + else if(MEASURETEXTPOSITION_POSITIVE == eHorizontal) + { + fLenRight = fEndArrowH + aTextRange.getWidth(); + } + } + + const basegfx::B2DPoint aMainLeftLeft(aMainLeft.getX() - fLenLeft, aMainLeft.getY()); + const basegfx::B2DPoint aMainRightRight(aMainRight.getX() + fLenRight, aMainRight.getY()); + + aRetval.push_back(impCreatePart(rLineAttribute, aObjectMatrix, aMainLeftLeft, aMainLeft, false, true)); + aRetval.push_back(impCreatePart(rLineAttribute, aObjectMatrix, aMainRight, aMainRightRight, true, false)); + + if(!bMainLineSplitted || MEASURETEXTPOSITION_CENTERED != eHorizontal) + { + aRetval.push_back(impCreatePart(rLineAttribute, aObjectMatrix, aMainLeft, aMainRight, false, false)); + } + } + else + { + if(bMainLineSplitted) + { + const double fHalfLength((fDistance - (aTextRange.getWidth() + (fStartArrowH + fEndArrowH) * 0.25)) * 0.5); + const basegfx::B2DPoint aMainInnerLeft(aMainLeft.getX() + fHalfLength, aMainLeft.getY()); + const basegfx::B2DPoint aMainInnerRight(aMainRight.getX() - fHalfLength, aMainRight.getY()); + + aRetval.push_back(impCreatePart(rLineAttribute, aObjectMatrix, aMainLeft, aMainInnerLeft, true, false)); + aRetval.push_back(impCreatePart(rLineAttribute, aObjectMatrix, aMainInnerRight, aMainRight, false, true)); + } + else + { + aRetval.push_back(impCreatePart(rLineAttribute, aObjectMatrix, aMainLeft, aMainRight, true, true)); + } + } + + // left/right help line value preparation + const double fTopEdge(getBelow() ? getUpper() + getDistance() : -getUpper() - getDistance()); + const double fBottomLeft(getBelow() ? getLower() - getLeftDelta() : getLeftDelta() - getLower()); + const double fBottomRight(getBelow() ? getLower() - getRightDelta() : getRightDelta() - getLower()); + + // left help line + const basegfx::B2DPoint aLeftUp(0.0, fTopEdge); + const basegfx::B2DPoint aLeftDown(0.0, fBottomLeft); + + aRetval.push_back(impCreatePart(rLineAttribute, aObjectMatrix, aLeftDown, aLeftUp, false, false)); + + // right help line + const basegfx::B2DPoint aRightUp(fDistance, fTopEdge); + const basegfx::B2DPoint aRightDown(fDistance, fBottomRight); + + aRetval.push_back(impCreatePart(rLineAttribute, aObjectMatrix, aRightDown, aRightUp, false, false)); + + // text horizontal position + if(MEASURETEXTPOSITION_NEGATIVE == eHorizontal) + { + // left + const double fSmall(fArrowsOutsideLen * 0.18); + fTextX = aMainLeft.getX() - (fStartArrowH + aTextRange.getWidth() + fSmall + fHalfLineWidth); + + if(bMainLineSplitted) + { + fTextX -= (fArrowsOutsideLen - fStartArrowH); + } + + if(!rTextAttribute.isDefault()) + { + fTextX -= rTextAttribute.getTextRightDistance(); + } + } + else if(MEASURETEXTPOSITION_POSITIVE == eHorizontal) + { + // right + const double fSmall(fArrowsOutsideLen * 0.18); + fTextX = aMainRight.getX() + (fEndArrowH + fSmall + fHalfLineWidth); + + if(bMainLineSplitted) + { + fTextX += (fArrowsOutsideLen - fEndArrowH); + } + + if(!rTextAttribute.isDefault()) + { + fTextX += rTextAttribute.getTextLeftDistance(); + } + } + else // MEASURETEXTPOSITION_CENTERED + { + // centered + fTextX = aMainLeft.getX() + ((fDistance - aTextRange.getWidth()) * 0.5); + + if(!rTextAttribute.isDefault()) + { + fTextX += (rTextAttribute.getTextLeftDistance() - rTextAttribute.getTextRightDistance()) / 2L; + } + } + + // text vertical position + if(MEASURETEXTPOSITION_NEGATIVE == eVertical) + { + // top + const double fSmall(fArrowsOutsideLen * 0.10); + fTextY = aMainLeft.getY() - (aTextRange.getHeight() + fSmall + fHalfLineWidth); + + if(!rTextAttribute.isDefault()) + { + fTextY -= rTextAttribute.getTextLowerDistance(); + } + } + else if(MEASURETEXTPOSITION_POSITIVE == eVertical) + { + // bottom + const double fSmall(fArrowsOutsideLen * 0.10); + fTextY = aMainLeft.getY() + (fSmall + fHalfLineWidth); + + if(!rTextAttribute.isDefault()) + { + fTextY += rTextAttribute.getTextUpperDistance(); + } + } + else // MEASURETEXTPOSITION_CENTERED + { + // centered + fTextY = aMainLeft.getY() - (aTextRange.getHeight() * 0.5); + + if(!rTextAttribute.isDefault()) + { + fTextY += (rTextAttribute.getTextUpperDistance() - rTextAttribute.getTextLowerDistance()) / 2L; + } + } + } + + if(getSdrLSTAttribute().getLine().isDefault()) + { + // embed line geometry to invisible (100% transparent) line group for HitTest + Primitive2DReference xHiddenLines(new HiddenGeometryPrimitive2D(std::move(aRetval))); + + aRetval = Primitive2DContainer { xHiddenLines }; + } + + if(xBlockText.is()) + { + // create transformation to text primitive end position + basegfx::B2DHomMatrix aChange; + + // handle auto text rotation + if(bAutoUpsideDown) + { + aChange.rotate(M_PI); + } + + // move from aTextRange.TopLeft to fTextX, fTextY + aChange.translate(fTextX - aTextRange.getMinX(), fTextY - aTextRange.getMinY()); + + // apply object matrix + aChange *= aObjectMatrix; + + // apply to existing text primitive + rtl::Reference<SdrTextPrimitive2D> pNewBlockText = xBlockText->createTransformedClone(aChange); + OSL_ENSURE(pNewBlockText, "SdrMeasurePrimitive2D::create2DDecomposition: Could not create transformed clone of text primitive (!)"); + xBlockText.clear(); + + // add to local primitives + aRetval.push_back(pNewBlockText); + } + + // add shadow + if(!getSdrLSTAttribute().getShadow().isDefault()) + { + aRetval = createEmbeddedShadowPrimitive( + std::move(aRetval), + getSdrLSTAttribute().getShadow()); + } + + rContainer.append(std::move(aRetval)); + } + + SdrMeasurePrimitive2D::SdrMeasurePrimitive2D( + const attribute::SdrLineEffectsTextAttribute& rSdrLSTAttribute, + const basegfx::B2DPoint& rStart, + const basegfx::B2DPoint& rEnd, + MeasureTextPosition eHorizontal, + MeasureTextPosition eVertical, + double fDistance, + double fUpper, + double fLower, + double fLeftDelta, + double fRightDelta, + bool bBelow, + bool bTextRotation, + bool bTextAutoAngle) + : maSdrLSTAttribute(rSdrLSTAttribute), + maStart(rStart), + maEnd(rEnd), + meHorizontal(eHorizontal), + meVertical(eVertical), + mfDistance(fDistance), + mfUpper(fUpper), + mfLower(fLower), + mfLeftDelta(fLeftDelta), + mfRightDelta(fRightDelta), + mbBelow(bBelow), + mbTextRotation(bTextRotation), + mbTextAutoAngle(bTextAutoAngle) + { + } + + bool SdrMeasurePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const + { + if(BufferedDecompositionPrimitive2D::operator==(rPrimitive)) + { + const SdrMeasurePrimitive2D& rCompare = static_cast<const SdrMeasurePrimitive2D&>(rPrimitive); + + return (getStart() == rCompare.getStart() + && getEnd() == rCompare.getEnd() + && getHorizontal() == rCompare.getHorizontal() + && getVertical() == rCompare.getVertical() + && getDistance() == rCompare.getDistance() + && getUpper() == rCompare.getUpper() + && getLower() == rCompare.getLower() + && getLeftDelta() == rCompare.getLeftDelta() + && getRightDelta() == rCompare.getRightDelta() + && getBelow() == rCompare.getBelow() + && getTextRotation() == rCompare.getTextRotation() + && getTextAutoAngle() == rCompare.getTextAutoAngle() + && getSdrLSTAttribute() == rCompare.getSdrLSTAttribute()); + } + + return false; + } + + // provide unique ID + sal_uInt32 SdrMeasurePrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_SDRMEASUREPRIMITIVE2D; + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/primitive2d/sdrole2primitive2d.cxx b/svx/source/sdr/primitive2d/sdrole2primitive2d.cxx new file mode 100644 index 000000000..e7576b498 --- /dev/null +++ b/svx/source/sdr/primitive2d/sdrole2primitive2d.cxx @@ -0,0 +1,176 @@ +/* -*- 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 <sdr/primitive2d/sdrole2primitive2d.hxx> +#include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <sdr/primitive2d/sdrdecompositiontools.hxx> +#include <drawinglayer/primitive2d/sdrdecompositiontools2d.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <basegfx/polygon/b2dpolypolygon.hxx> + + +using namespace com::sun::star; + + +namespace drawinglayer::primitive2d +{ + SdrOle2Primitive2D::SdrOle2Primitive2D( + Primitive2DContainer&& rOLEContent, + const basegfx::B2DHomMatrix& rTransform, + const attribute::SdrLineFillEffectsTextAttribute& rSdrLFSTAttribute) + : maOLEContent(std::move(rOLEContent)), + maTransform(rTransform), + maSdrLFSTAttribute(rSdrLFSTAttribute) + { + } + + bool SdrOle2Primitive2D::operator==(const BasePrimitive2D& rPrimitive) const + { + if(BasePrimitive2D::operator==(rPrimitive)) + { + const SdrOle2Primitive2D& rCompare = static_cast<const SdrOle2Primitive2D&>(rPrimitive); + + // #i108636# The standard operator== on two UNO sequences did not work as i + // would have expected; it just checks the .is() states and the data type + // of the sequence. What i need here is detection of equality of the whole + // sequence content, thus i need to use the arePrimitive2DSequencesEqual helper + // here instead of the operator== which lead to always returning false and thus + // always re-decompositions of the subcontent. + if(getOLEContent() == rCompare.getOLEContent() + && getTransform() == rCompare.getTransform() + && getSdrLFSTAttribute() == rCompare.getSdrLFSTAttribute()) + { + return true; + } + } + + return false; + } + + void SdrOle2Primitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& /*aViewInformation*/) const + { + // to take care of getSdrLFSTAttribute() later, the same as in SdrGrafPrimitive2D::create2DDecomposition + // should happen. For the moment we only need the OLE itself + // Added complete primitive preparation using getSdrLFSTAttribute() now. + Primitive2DContainer aRetval; + + // create unit outline polygon + const basegfx::B2DPolygon& aUnitOutline(basegfx::utils::createUnitPolygon()); + + // add fill + if(!getSdrLFSTAttribute().getFill().isDefault()) + { + basegfx::B2DPolyPolygon aTransformed(aUnitOutline); + + aTransformed.transform(getTransform()); + aRetval.push_back( + createPolyPolygonFillPrimitive( + aTransformed, + getSdrLFSTAttribute().getFill(), + getSdrLFSTAttribute().getFillFloatTransGradient())); + } + + // add line + // #i97981# condition was inverse to purpose. When being compatible to paint version, + // border needs to be suppressed + if(!getSdrLFSTAttribute().getLine().isDefault()) + { + // if line width is given, polygon needs to be grown by half of it to make the + // outline to be outside of the bitmap + if(0.0 != getSdrLFSTAttribute().getLine().getWidth()) + { + // decompose to get scale + basegfx::B2DVector aScale, aTranslate; + double fRotate, fShearX; + getTransform().decompose(aScale, aTranslate, fRotate, fShearX); + + // create expanded range (add relative half line width to unit rectangle) + double fHalfLineWidth(getSdrLFSTAttribute().getLine().getWidth() * 0.5); + double fScaleX(0.0 != aScale.getX() ? fHalfLineWidth / fabs(aScale.getX()) : 1.0); + double fScaleY(0.0 != aScale.getY() ? fHalfLineWidth / fabs(aScale.getY()) : 1.0); + const basegfx::B2DRange aExpandedRange(-fScaleX, -fScaleY, 1.0 + fScaleX, 1.0 + fScaleY); + basegfx::B2DPolygon aExpandedUnitOutline(basegfx::utils::createPolygonFromRect(aExpandedRange)); + + aExpandedUnitOutline.transform(getTransform()); + aRetval.push_back( + createPolygonLinePrimitive( + aExpandedUnitOutline, + getSdrLFSTAttribute().getLine(), + attribute::SdrLineStartEndAttribute())); + } + else + { + basegfx::B2DPolygon aTransformed(aUnitOutline); + + aTransformed.transform(getTransform()); + aRetval.push_back( + createPolygonLinePrimitive( + aTransformed, + getSdrLFSTAttribute().getLine(), + attribute::SdrLineStartEndAttribute())); + } + } + else + { + // if initially no line is defined, create one for HitTest and BoundRect + aRetval.push_back( + createHiddenGeometryPrimitives2D( + false, + basegfx::B2DPolyPolygon(aUnitOutline), + getTransform())); + } + + // add graphic content + aRetval.append(getOLEContent()); + + // add text, no need to suppress to stay compatible since text was + // always supported by the old paints, too + if(!getSdrLFSTAttribute().getText().isDefault()) + { + aRetval.push_back( + createTextPrimitive( + basegfx::B2DPolyPolygon(aUnitOutline), + getTransform(), + getSdrLFSTAttribute().getText(), + getSdrLFSTAttribute().getLine(), + false, + false)); + } + + // add shadow + if(!getSdrLFSTAttribute().getShadow().isDefault()) + { + aRetval = createEmbeddedShadowPrimitive( + std::move(aRetval), + getSdrLFSTAttribute().getShadow()); + } + + rVisitor.visit(std::move(aRetval)); + } + + // provide unique ID + sal_uInt32 SdrOle2Primitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_SDROLE2PRIMITIVE2D; + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/primitive2d/sdrolecontentprimitive2d.cxx b/svx/source/sdr/primitive2d/sdrolecontentprimitive2d.cxx new file mode 100644 index 000000000..7edd0e601 --- /dev/null +++ b/svx/source/sdr/primitive2d/sdrolecontentprimitive2d.cxx @@ -0,0 +1,179 @@ +/* -*- 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 <sdr/primitive2d/sdrolecontentprimitive2d.hxx> +#include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx> +#include <svx/svdoole2.hxx> +#include <vcl/svapp.hxx> +#include <drawinglayer/primitive2d/graphicprimitive2d.hxx> +#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx> +#include <svtools/colorcfg.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> + + +namespace drawinglayer::primitive2d +{ + void SdrOleContentPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*aViewInformation*/) const + { + const SdrOle2Obj* pSource = mpSdrOle2Obj.get(); + bool bScaleContent(false); + Graphic aGraphic; + + if(pSource) + { + const Graphic* pOLEGraphic = pSource->GetGraphic(); + + if(pOLEGraphic) + { + aGraphic = *pOLEGraphic; + bScaleContent = pSource->IsEmptyPresObj(); + } + } +#ifdef _WIN32 // Little point in displaying the "broken OLE" graphic on OSes that don't have real OLE, maybe? + if(GraphicType::NONE == aGraphic.GetType()) + { + // no source, use fallback resource empty OLE graphic + aGraphic = SdrOle2Obj::GetEmptyOLEReplacementGraphic(); + bScaleContent = true; + } +#endif + if(GraphicType::NONE == aGraphic.GetType()) + return; + + const GraphicObject aGraphicObject(aGraphic); + const GraphicAttr aGraphicAttr; + + if(bScaleContent) + { + // get transformation atoms + basegfx::B2DVector aScale, aTranslate; + double fRotate, fShearX; + getObjectTransform().decompose(aScale, aTranslate, fRotate, fShearX); + + // get PrefSize from the graphic in 100th mm + Size aPrefSize(aGraphic.GetPrefSize()); + + if(MapUnit::MapPixel == aGraphic.GetPrefMapMode().GetMapUnit()) + { + aPrefSize = Application::GetDefaultDevice()->PixelToLogic(aPrefSize, MapMode(MapUnit::Map100thMM)); + } + else + { + aPrefSize = OutputDevice::LogicToLogic(aPrefSize, aGraphic.GetPrefMapMode(), MapMode(MapUnit::Map100thMM)); + } + + const double fOffsetX((aScale.getX() - aPrefSize.getWidth()) / 2.0); + const double fOffsetY((aScale.getY() - aPrefSize.getHeight()) / 2.0); + + if(basegfx::fTools::moreOrEqual(fOffsetX, 0.0) && basegfx::fTools::moreOrEqual(fOffsetY, 0.0)) + { + // if content fits into frame, create it + basegfx::B2DHomMatrix aInnerObjectMatrix(basegfx::utils::createScaleTranslateB2DHomMatrix( + aPrefSize.getWidth(), aPrefSize.getHeight(), fOffsetX, fOffsetY)); + aInnerObjectMatrix = basegfx::utils::createShearXRotateTranslateB2DHomMatrix(fShearX, fRotate, aTranslate) + * aInnerObjectMatrix; + + const drawinglayer::primitive2d::Primitive2DReference aGraphicPrimitive( + new drawinglayer::primitive2d::GraphicPrimitive2D( + aInnerObjectMatrix, + aGraphicObject, + aGraphicAttr)); + rContainer.push_back(aGraphicPrimitive); + } + } + else + { + // create graphic primitive for content + const drawinglayer::primitive2d::Primitive2DReference aGraphicPrimitive( + new drawinglayer::primitive2d::GraphicPrimitive2D( + getObjectTransform(), + aGraphicObject, + aGraphicAttr)); + rContainer.push_back(aGraphicPrimitive); + } + + // a standard gray outline is created for scaled content + if(!bScaleContent) + return; + + const svtools::ColorConfig aColorConfig; + const svtools::ColorConfigValue aColor(aColorConfig.GetColorValue(svtools::OBJECTBOUNDARIES)); + + if(aColor.bIsVisible) + { + basegfx::B2DPolygon aOutline(basegfx::utils::createUnitPolygon()); + const Color aVclColor(aColor.nColor); + aOutline.transform(getObjectTransform()); + const drawinglayer::primitive2d::Primitive2DReference xOutline( + new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(aOutline, aVclColor.getBColor())); + rContainer.push_back(xOutline); + } + } + + SdrOleContentPrimitive2D::SdrOleContentPrimitive2D( + const SdrOle2Obj& rSdrOle2Obj, + const basegfx::B2DHomMatrix& rObjectTransform, + sal_uInt32 nGraphicVersion + ) + : mpSdrOle2Obj(const_cast< SdrOle2Obj* >(&rSdrOle2Obj)), + maObjectTransform(rObjectTransform), + mnGraphicVersion(nGraphicVersion) + { + } + + bool SdrOleContentPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const + { + if( BufferedDecompositionPrimitive2D::operator==(rPrimitive) ) + { + const SdrOleContentPrimitive2D& rCompare = static_cast<const SdrOleContentPrimitive2D&>(rPrimitive); + const bool bBothNot(!mpSdrOle2Obj.is() && !rCompare.mpSdrOle2Obj.is()); + const bool bBothAndEqual(mpSdrOle2Obj.is() && rCompare.mpSdrOle2Obj.is() + && mpSdrOle2Obj.get() == rCompare.mpSdrOle2Obj.get()); + + return ((bBothNot || bBothAndEqual) + && getObjectTransform() == rCompare.getObjectTransform() + + // #i104867# to find out if the Graphic content of the + // OLE has changed, use GraphicVersion number + && mnGraphicVersion == rCompare.mnGraphicVersion + ); + } + + return false; + } + + basegfx::B2DRange SdrOleContentPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const + { + basegfx::B2DRange aRange(0.0, 0.0, 1.0, 1.0); + aRange.transform(getObjectTransform()); + + return aRange; + } + + // provide unique ID + sal_uInt32 SdrOleContentPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_SDROLECONTENTPRIMITIVE2D; + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/primitive2d/sdrpathprimitive2d.cxx b/svx/source/sdr/primitive2d/sdrpathprimitive2d.cxx new file mode 100644 index 000000000..829525e2b --- /dev/null +++ b/svx/source/sdr/primitive2d/sdrpathprimitive2d.cxx @@ -0,0 +1,156 @@ +/* -*- 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 <sdr/primitive2d/sdrpathprimitive2d.hxx> +#include <sdr/primitive2d/sdrdecompositiontools.hxx> +#include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx> +#include <drawinglayer/primitive2d/sdrdecompositiontools2d.hxx> + + +using namespace com::sun::star; + + +namespace drawinglayer::primitive2d +{ + void SdrPathPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*aViewInformation*/) const + { + Primitive2DContainer aRetval; + + // add fill + if(!getSdrLFSTAttribute().getFill().isDefault() + && getUnitPolyPolygon().isClosed()) + { + // #i108255# no need to use correctOrientations here; target is + // straight visualisation + basegfx::B2DPolyPolygon aTransformed(getUnitPolyPolygon()); + aTransformed.transform(getTransform()); + + // OperationSmiley: Check if a UnitDefinitionPolyPolygon is set + if(getUnitDefinitionPolyPolygon().count() + && getUnitDefinitionPolyPolygon() != getUnitPolyPolygon()) + { + // if yes, use the B2DRange of it's transformed form + basegfx::B2DPolyPolygon aTransformedDefinition(getUnitDefinitionPolyPolygon()); + aTransformedDefinition.transform(getTransform()); + + aRetval.push_back( + createPolyPolygonFillPrimitive( + aTransformed, + aTransformedDefinition.getB2DRange(), + getSdrLFSTAttribute().getFill(), + getSdrLFSTAttribute().getFillFloatTransGradient())); + } + else + { + aRetval.push_back( + createPolyPolygonFillPrimitive( + aTransformed, + getSdrLFSTAttribute().getFill(), + getSdrLFSTAttribute().getFillFloatTransGradient())); + } + } + + // add line + if(getSdrLFSTAttribute().getLine().isDefault()) + { + // if initially no line is defined, create one for HitTest and BoundRect + aRetval.push_back( + createHiddenGeometryPrimitives2D( + false, + getUnitPolyPolygon(), + getTransform())); + } + else + { + Primitive2DContainer aTemp(getUnitPolyPolygon().count()); + + for(sal_uInt32 a(0); a < getUnitPolyPolygon().count(); a++) + { + basegfx::B2DPolygon aTransformed(getUnitPolyPolygon().getB2DPolygon(a)); + + aTransformed.transform(getTransform()); + aTemp[a] = createPolygonLinePrimitive( + aTransformed, + getSdrLFSTAttribute().getLine(), + getSdrLFSTAttribute().getLineStartEnd()); + } + + aRetval.append(aTemp); + } + + // add text + if(!getSdrLFSTAttribute().getText().isDefault()) + { + aRetval.push_back( + createTextPrimitive( + getUnitPolyPolygon(), + getTransform(), + getSdrLFSTAttribute().getText(), + getSdrLFSTAttribute().getLine(), + false, + false)); + } + + // add shadow + if(!getSdrLFSTAttribute().getShadow().isDefault()) + { + aRetval = createEmbeddedShadowPrimitive( + std::move(aRetval), + getSdrLFSTAttribute().getShadow()); + } + + rContainer.append(std::move(aRetval)); + } + + SdrPathPrimitive2D::SdrPathPrimitive2D( + const basegfx::B2DHomMatrix& rTransform, + const attribute::SdrLineFillEffectsTextAttribute& rSdrLFSTAttribute, + const basegfx::B2DPolyPolygon& rUnitPolyPolygon, + const basegfx::B2DPolyPolygon& rUnitDefinitionPolyPolygon) + : maTransform(rTransform), + maSdrLFSTAttribute(rSdrLFSTAttribute), + maUnitPolyPolygon(rUnitPolyPolygon), + maUnitDefinitionPolyPolygon(rUnitDefinitionPolyPolygon) + { + } + + bool SdrPathPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const + { + if(BufferedDecompositionPrimitive2D::operator==(rPrimitive)) + { + const SdrPathPrimitive2D& rCompare = static_cast<const SdrPathPrimitive2D&>(rPrimitive); + + return (getUnitPolyPolygon() == rCompare.getUnitPolyPolygon() + && getUnitDefinitionPolyPolygon() == rCompare.getUnitDefinitionPolyPolygon() + && getTransform() == rCompare.getTransform() + && getSdrLFSTAttribute() == rCompare.getSdrLFSTAttribute()); + } + + return false; + } + + // provide unique ID + sal_uInt32 SdrPathPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_SDRPATHPRIMITIVE2D; + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/primitive2d/sdrprimitivetools.cxx b/svx/source/sdr/primitive2d/sdrprimitivetools.cxx new file mode 100644 index 000000000..6376fa796 --- /dev/null +++ b/svx/source/sdr/primitive2d/sdrprimitivetools.cxx @@ -0,0 +1,64 @@ +/* -*- 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 <sdr/primitive2d/sdrprimitivetools.hxx> +#include <vcl/lazydelete.hxx> +#include <vcl/BitmapTools.hxx> +#include <mutex> + + +// helper methods + +namespace drawinglayer::primitive2d +{ + BitmapEx createDefaultCross_3x3(const basegfx::BColor& rBColor) + { + static vcl::DeleteOnDeinit< BitmapEx > aRetVal(vcl::DeleteOnDeinitFlag::Empty); + static basegfx::BColor aBColor; + static std::mutex aMutex; + + std::scoped_lock aGuard(aMutex); + + if(!aRetVal.get() || rBColor != aBColor) + { + // copy values + aBColor = rBColor; + + // create bitmap + Color c(aBColor); + sal_uInt8 r = c.GetRed(); + sal_uInt8 g = c.GetGreen(); + sal_uInt8 b = c.GetBlue(); + sal_uInt8 a = 255; + const sal_uInt8 cross[] = { + 0, 0, 0, a, r, g, b, 0, 0, 0, 0, a, + r, g, b, 0, r, g, b, 0, r, g, b, 0, + 0, 0, 0, a, r, g, b, 0, 0, 0, 0, a + }; + BitmapEx aBitmap = vcl::bitmap::CreateFromData(cross, 3, 3, 12, vcl::PixelFormat::N32_BPP); + + // create and exchange at aRetVal + aRetVal.set(aBitmap); + } + + return aRetVal.get() ? *aRetVal.get() : BitmapEx(); + } +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/primitive2d/sdrrectangleprimitive2d.cxx b/svx/source/sdr/primitive2d/sdrrectangleprimitive2d.cxx new file mode 100644 index 000000000..f289ea6bc --- /dev/null +++ b/svx/source/sdr/primitive2d/sdrrectangleprimitive2d.cxx @@ -0,0 +1,152 @@ +/* -*- 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 <sdr/primitive2d/sdrrectangleprimitive2d.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <sdr/primitive2d/sdrdecompositiontools.hxx> +#include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx> +#include <drawinglayer/primitive2d/sdrdecompositiontools2d.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <basegfx/polygon/b2dpolypolygon.hxx> + + +using namespace com::sun::star; + + +namespace drawinglayer::primitive2d +{ + void SdrRectanglePrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*aViewInformation*/) const + { + Primitive2DContainer aRetval; + + // create unit outline polygon + const basegfx::B2DPolygon aUnitOutline(basegfx::utils::createPolygonFromRect( + basegfx::B2DRange(0.0, 0.0, 1.0, 1.0), + getCornerRadiusX(), + getCornerRadiusY())); + + // add fill + if(!getSdrLFSTAttribute().getFill().isDefault()) + { + basegfx::B2DPolyPolygon aTransformed(aUnitOutline); + + aTransformed.transform(getTransform()); + aRetval.push_back( + createPolyPolygonFillPrimitive( + aTransformed, + getSdrLFSTAttribute().getFill(), + getSdrLFSTAttribute().getFillFloatTransGradient())); + } + else if(getForceFillForHitTest()) + { + // if no fill and it's a text frame, create a fill for HitTest and + // BoundRect fallback + aRetval.push_back( + createHiddenGeometryPrimitives2D( + true, + basegfx::B2DPolyPolygon(aUnitOutline), + getTransform())); + } + + // add line + if(!getSdrLFSTAttribute().getLine().isDefault()) + { + basegfx::B2DPolygon aTransformed(aUnitOutline); + + aTransformed.transform(getTransform()); + aRetval.push_back( + createPolygonLinePrimitive( + aTransformed, + getSdrLFSTAttribute().getLine(), + attribute::SdrLineStartEndAttribute())); + } + else if(!getForceFillForHitTest()) + { + // if initially no line is defined and it's not a text frame, create + // a line for HitTest and BoundRect + aRetval.push_back( + createHiddenGeometryPrimitives2D( + false, + basegfx::B2DPolyPolygon(aUnitOutline), + getTransform())); + } + + // add text + if(!getSdrLFSTAttribute().getText().isDefault()) + { + aRetval.push_back( + createTextPrimitive( + basegfx::B2DPolyPolygon(aUnitOutline), + getTransform(), + getSdrLFSTAttribute().getText(), + getSdrLFSTAttribute().getLine(), + false, + false)); + } + + // add shadow + if(!getSdrLFSTAttribute().getShadow().isDefault()) + { + aRetval = createEmbeddedShadowPrimitive( + std::move(aRetval), + getSdrLFSTAttribute().getShadow()); + } + + rContainer.append(std::move(aRetval)); + } + + SdrRectanglePrimitive2D::SdrRectanglePrimitive2D( + const basegfx::B2DHomMatrix& rTransform, + const attribute::SdrLineFillEffectsTextAttribute& rSdrLFSTAttribute, + double fCornerRadiusX, + double fCornerRadiusY, + bool bForceFillForHitTest) + : maTransform(rTransform), + maSdrLFSTAttribute(rSdrLFSTAttribute), + mfCornerRadiusX(fCornerRadiusX), + mfCornerRadiusY(fCornerRadiusY), + mbForceFillForHitTest(bForceFillForHitTest) + { + } + + bool SdrRectanglePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const + { + if(BufferedDecompositionPrimitive2D::operator==(rPrimitive)) + { + const SdrRectanglePrimitive2D& rCompare = static_cast<const SdrRectanglePrimitive2D&>(rPrimitive); + + return (getCornerRadiusX() == rCompare.getCornerRadiusX() + && getCornerRadiusY() == rCompare.getCornerRadiusY() + && getTransform() == rCompare.getTransform() + && getSdrLFSTAttribute() == rCompare.getSdrLFSTAttribute() + && getForceFillForHitTest() == rCompare.getForceFillForHitTest()); + } + + return false; + } + + // provide unique ID + sal_uInt32 SdrRectanglePrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_SDRRECTANGLEPRIMITIVE2D; + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/primitive2d/sdrtextprimitive2d.cxx b/svx/source/sdr/primitive2d/sdrtextprimitive2d.cxx new file mode 100644 index 000000000..6311c123d --- /dev/null +++ b/svx/source/sdr/primitive2d/sdrtextprimitive2d.cxx @@ -0,0 +1,545 @@ +/* -*- 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 <sdr/primitive2d/sdrtextprimitive2d.hxx> +#include <svx/svdotext.hxx> +#include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx> +#include <drawinglayer/primitive2d/texthierarchyprimitive2d.hxx> +#include <editeng/outlobj.hxx> +#include <editeng/editobj.hxx> +#include <editeng/flditem.hxx> +#include <drawinglayer/geometry/viewinformation2d.hxx> +#include <svx/unoapi.hxx> +#include <svx/svdpage.hxx> +#include <svx/svdmodel.hxx> +#include <svx/svdoutl.hxx> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <osl/diagnose.h> + + +using namespace com::sun::star; + + +namespace +{ + sal_Int16 getPageNumber(const uno::Reference< drawing::XDrawPage >& rxDrawPage) + { + sal_Int16 nRetval(0); + uno::Reference< beans::XPropertySet > xSet(rxDrawPage, uno::UNO_QUERY); + + if (xSet.is()) + { + try + { + const uno::Any aNumber(xSet->getPropertyValue("Number")); + aNumber >>= nRetval; + } + catch(const uno::Exception&) + { + OSL_ASSERT(false); + } + } + + return nRetval; + } + + sal_Int16 getPageCount(const uno::Reference< drawing::XDrawPage >& rxDrawPage) + { + sal_Int16 nRetval(0); + SdrPage* pPage = GetSdrPageFromXDrawPage(rxDrawPage); + + if(pPage) + { + if( (pPage->GetPageNum() == 0) && !pPage->IsMasterPage() ) + { + // handout page! + return pPage->getSdrModelFromSdrPage().getHandoutPageCount(); + } + else + { + const sal_uInt16 nPageCount(pPage->getSdrModelFromSdrPage().GetPageCount()); + nRetval = (static_cast<sal_Int16>(nPageCount) - 1) / 2; + } + } + + return nRetval; + } +} // end of anonymous namespace + + +namespace drawinglayer::primitive2d +{ + // support for XTEXT_PAINTSHAPE_BEGIN/XTEXT_PAINTSHAPE_END Metafile comments + // for slideshow. This uses TextHierarchyBlockPrimitive2D to mark a text block. + // ATM there is only one text block per SdrObject, this may get more in the future + void SdrTextPrimitive2D::encapsulateWithTextHierarchyBlockPrimitive2D(Primitive2DContainer& rContainer, Primitive2DContainer&& aCandidate) + { + rContainer.push_back(new TextHierarchyBlockPrimitive2D(drawinglayer::primitive2d::Primitive2DContainer(aCandidate))); + } + + SdrTextPrimitive2D::SdrTextPrimitive2D( + const SdrText* pSdrText, + const OutlinerParaObject& rOutlinerParaObject) + : mrSdrText(const_cast< SdrText* >(pSdrText)), + maOutlinerParaObject(rOutlinerParaObject), + mnLastPageNumber(0), + mnLastPageCount(0), + mbContainsPageField(false), + mbContainsPageCountField(false), + mbContainsOtherFields(false) + { + const EditTextObject& rETO = maOutlinerParaObject.GetTextObject(); + + mbContainsPageField = rETO.HasField(SvxPageField::CLASS_ID); + mbContainsPageCountField = rETO.HasField(SvxPagesField::CLASS_ID); + mbContainsOtherFields = rETO.HasField(SvxHeaderField::CLASS_ID) + || rETO.HasField(SvxFooterField::CLASS_ID) + || rETO.HasField(SvxDateTimeField::CLASS_ID) + || rETO.HasField(SvxAuthorField::CLASS_ID); + } + + const SdrText* SdrTextPrimitive2D::getSdrText() const { return mrSdrText.get(); } + + bool SdrTextPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const + { + if(BufferedDecompositionPrimitive2D::operator==(rPrimitive)) + { + const SdrTextPrimitive2D& rCompare = static_cast<const SdrTextPrimitive2D&>(rPrimitive); + + return ( + + // compare OPO and content, but not WrongList + getOutlinerParaObject() == rCompare.getOutlinerParaObject() + + // also compare WrongList (not-persistent data, but visualized) + && getOutlinerParaObject().isWrongListEqual(rCompare.getOutlinerParaObject())); + } + + return false; + } + + void SdrTextPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const + { + uno::Reference< drawing::XDrawPage > xCurrentlyVisualizingPage; + bool bCurrentlyVisualizingPageIsSet(false); + Color aNewTextBackgroundColor; + bool bNewTextBackgroundColorIsSet(false); + sal_Int16 nCurrentlyValidPageNumber(0); + sal_Int16 nCurrentlyValidPageCount(0); + + if(!getBuffered2DDecomposition().empty()) + { + bool bDoDelete(false); + + // check visualized page + if(mbContainsPageField || mbContainsPageCountField || mbContainsOtherFields) + { + // get visualized page and remember + xCurrentlyVisualizingPage = rViewInformation.getVisualizedPage(); + bCurrentlyVisualizingPageIsSet = true; + + if(xCurrentlyVisualizingPage != mxLastVisualizingPage) + { + bDoDelete = true; + } + + // #i98870# check visualized PageNumber + if(!bDoDelete && mbContainsPageField) + { + nCurrentlyValidPageNumber = getPageNumber(xCurrentlyVisualizingPage); + + if(nCurrentlyValidPageNumber != mnLastPageNumber) + { + bDoDelete = true; + } + } + + // #i98870# check visualized PageCount, too + if(!bDoDelete && mbContainsPageCountField) + { + nCurrentlyValidPageCount = getPageCount(xCurrentlyVisualizingPage); + + if(nCurrentlyValidPageCount != mnLastPageCount) + { + bDoDelete = true; + } + } + } + + // #i101443# check change of TextBackgroundolor + if(!bDoDelete && getSdrText()) + { + SdrOutliner& rDrawOutliner = getSdrText()->GetObject().getSdrModelFromSdrObject().GetDrawOutliner(); + aNewTextBackgroundColor = rDrawOutliner.GetBackgroundColor(); + bNewTextBackgroundColorIsSet = true; + + if(aNewTextBackgroundColor != maLastTextBackgroundColor) + { + bDoDelete = true; + } + } + + if(bDoDelete) + { + const_cast< SdrTextPrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer()); + } + } + + if(getBuffered2DDecomposition().empty()) + { + if(!bCurrentlyVisualizingPageIsSet && mbContainsPageField) + { + xCurrentlyVisualizingPage = rViewInformation.getVisualizedPage(); + } + + if(!nCurrentlyValidPageNumber && mbContainsPageField) + { + nCurrentlyValidPageNumber = getPageNumber(xCurrentlyVisualizingPage); + } + + if(!nCurrentlyValidPageCount && mbContainsPageCountField) + { + nCurrentlyValidPageCount = getPageCount(xCurrentlyVisualizingPage); + } + + if(!bNewTextBackgroundColorIsSet && getSdrText()) + { + SdrOutliner& rDrawOutliner = getSdrText()->GetObject().getSdrModelFromSdrObject().GetDrawOutliner(); + aNewTextBackgroundColor = rDrawOutliner.GetBackgroundColor(); + } + + const_cast< SdrTextPrimitive2D* >(this)->mxLastVisualizingPage = xCurrentlyVisualizingPage; + const_cast< SdrTextPrimitive2D* >(this)->mnLastPageNumber = nCurrentlyValidPageNumber; + const_cast< SdrTextPrimitive2D* >(this)->mnLastPageCount = nCurrentlyValidPageCount; + const_cast< SdrTextPrimitive2D* >(this)->maLastTextBackgroundColor = aNewTextBackgroundColor; + } + + // call parent + BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation); + } + + + + + void SdrContourTextPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& aViewInformation) const + { + Primitive2DContainer aRetval; + getSdrText()->GetObject().impDecomposeContourTextPrimitive(aRetval, *this, aViewInformation); + + encapsulateWithTextHierarchyBlockPrimitive2D(rContainer, std::move(aRetval)); + } + + SdrContourTextPrimitive2D::SdrContourTextPrimitive2D( + const SdrText* pSdrText, + const OutlinerParaObject& rOutlinerParaObject, + const basegfx::B2DPolyPolygon& rUnitPolyPolygon, + const basegfx::B2DHomMatrix& rObjectTransform) + : SdrTextPrimitive2D(pSdrText, rOutlinerParaObject), + maUnitPolyPolygon(rUnitPolyPolygon), + maObjectTransform(rObjectTransform) + { + } + + bool SdrContourTextPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const + { + if(SdrTextPrimitive2D::operator==(rPrimitive)) + { + const SdrContourTextPrimitive2D& rCompare = static_cast<const SdrContourTextPrimitive2D&>(rPrimitive); + + return (getUnitPolyPolygon() == rCompare.getUnitPolyPolygon() + && getObjectTransform() == rCompare.getObjectTransform()); + } + + return false; + } + + rtl::Reference<SdrTextPrimitive2D> SdrContourTextPrimitive2D::createTransformedClone(const basegfx::B2DHomMatrix& rTransform) const + { + return new SdrContourTextPrimitive2D( + getSdrText(), + getOutlinerParaObject(), + getUnitPolyPolygon(), + rTransform * getObjectTransform()); + } + + // provide unique ID + sal_uInt32 SdrContourTextPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_SDRCONTOURTEXTPRIMITIVE2D; + } + + + + void SdrPathTextPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& aViewInformation) const + { + Primitive2DContainer aRetval; + getSdrText()->GetObject().impDecomposePathTextPrimitive(aRetval, *this, aViewInformation); + + encapsulateWithTextHierarchyBlockPrimitive2D(rContainer, std::move(aRetval)); + } + + SdrPathTextPrimitive2D::SdrPathTextPrimitive2D( + const SdrText* pSdrText, + const OutlinerParaObject& rOutlinerParaObject, + const basegfx::B2DPolyPolygon& rPathPolyPolygon, + const attribute::SdrFormTextAttribute& rSdrFormTextAttribute) + : SdrTextPrimitive2D(pSdrText, rOutlinerParaObject), + maPathPolyPolygon(rPathPolyPolygon), + maSdrFormTextAttribute(rSdrFormTextAttribute) + { + } + + bool SdrPathTextPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const + { + if(SdrTextPrimitive2D::operator==(rPrimitive)) + { + const SdrPathTextPrimitive2D& rCompare = static_cast<const SdrPathTextPrimitive2D&>(rPrimitive); + + return (getPathPolyPolygon() == rCompare.getPathPolyPolygon() + && getSdrFormTextAttribute() == rCompare.getSdrFormTextAttribute()); + } + + return false; + } + + rtl::Reference<SdrTextPrimitive2D> SdrPathTextPrimitive2D::createTransformedClone(const basegfx::B2DHomMatrix& rTransform) const + { + basegfx::B2DPolyPolygon aNewPolyPolygon(getPathPolyPolygon()); + aNewPolyPolygon.transform(rTransform); + + return new SdrPathTextPrimitive2D( + getSdrText(), + getOutlinerParaObject(), + aNewPolyPolygon, + getSdrFormTextAttribute()); + } + + // provide unique ID + sal_uInt32 SdrPathTextPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_SDRPATHTEXTPRIMITIVE2D; + } + + + + void SdrBlockTextPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& aViewInformation) const + { + Primitive2DContainer aRetval; + getSdrText()->GetObject().impDecomposeBlockTextPrimitive(aRetval, *this, aViewInformation); + + encapsulateWithTextHierarchyBlockPrimitive2D(rContainer, std::move(aRetval)); + } + + SdrBlockTextPrimitive2D::SdrBlockTextPrimitive2D( + const SdrText* pSdrText, + const OutlinerParaObject& rOutlinerParaObject, + const basegfx::B2DHomMatrix& rTextRangeTransform, + SdrTextHorzAdjust aSdrTextHorzAdjust, + SdrTextVertAdjust aSdrTextVertAdjust, + bool bFixedCellHeight, + bool bUnlimitedPage, + bool bCellText, + bool bWordWrap) + : SdrTextPrimitive2D(pSdrText, rOutlinerParaObject), + maTextRangeTransform(rTextRangeTransform), + maSdrTextHorzAdjust(aSdrTextHorzAdjust), + maSdrTextVertAdjust(aSdrTextVertAdjust), + mbFixedCellHeight(bFixedCellHeight), + mbUnlimitedPage(bUnlimitedPage), + mbCellText(bCellText), + mbWordWrap(bWordWrap) + { + } + + bool SdrBlockTextPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const + { + if(SdrTextPrimitive2D::operator==(rPrimitive)) + { + const SdrBlockTextPrimitive2D& rCompare = static_cast<const SdrBlockTextPrimitive2D&>(rPrimitive); + + return (getTextRangeTransform() == rCompare.getTextRangeTransform() + && getSdrTextHorzAdjust() == rCompare.getSdrTextHorzAdjust() + && getSdrTextVertAdjust() == rCompare.getSdrTextVertAdjust() + && isFixedCellHeight() == rCompare.isFixedCellHeight() + && getUnlimitedPage() == rCompare.getUnlimitedPage() + && getCellText() == rCompare.getCellText() + && getWordWrap() == rCompare.getWordWrap()); + } + + return false; + } + + rtl::Reference<SdrTextPrimitive2D> SdrBlockTextPrimitive2D::createTransformedClone(const basegfx::B2DHomMatrix& rTransform) const + { + return new SdrBlockTextPrimitive2D( + getSdrText(), + getOutlinerParaObject(), + rTransform * getTextRangeTransform(), + getSdrTextHorzAdjust(), + getSdrTextVertAdjust(), + isFixedCellHeight(), + getUnlimitedPage(), + getCellText(), + getWordWrap()); + } + + // provide unique ID + sal_uInt32 SdrBlockTextPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_SDRBLOCKTEXTPRIMITIVE2D; + } + + + + void SdrAutoFitTextPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& aViewInformation) const + { + Primitive2DContainer aRetval; + getSdrText()->GetObject().impDecomposeAutoFitTextPrimitive(aRetval, *this, aViewInformation); + + encapsulateWithTextHierarchyBlockPrimitive2D(rContainer, std::move(aRetval)); + } + + SdrAutoFitTextPrimitive2D::SdrAutoFitTextPrimitive2D( + const SdrText* pSdrText, + const OutlinerParaObject& rParaObj, + const ::basegfx::B2DHomMatrix& rTextRangeTransform, + bool bWordWrap) + : SdrTextPrimitive2D(pSdrText, rParaObj), + maTextRangeTransform(rTextRangeTransform), + mbWordWrap(bWordWrap) + { + } + + bool SdrAutoFitTextPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const + { + if(SdrTextPrimitive2D::operator==(rPrimitive)) + { + const SdrBlockTextPrimitive2D& rCompare = static_cast<const SdrBlockTextPrimitive2D&>(rPrimitive); + + return (getTextRangeTransform() == rCompare.getTextRangeTransform() + && getWordWrap() == rCompare.getWordWrap()); + } + + return false; + } + + rtl::Reference<SdrTextPrimitive2D> SdrAutoFitTextPrimitive2D::createTransformedClone(const ::basegfx::B2DHomMatrix& rTransform) const + { + return new SdrAutoFitTextPrimitive2D(getSdrText(), getOutlinerParaObject(), rTransform * getTextRangeTransform(), getWordWrap()); + } + + // provide unique ID + sal_uInt32 SdrAutoFitTextPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_SDRAUTOFITTEXTPRIMITIVE2D; + } + + + + + SdrChainedTextPrimitive2D::SdrChainedTextPrimitive2D( + const SdrText* pSdrText, + const OutlinerParaObject& rOutlinerParaObject, + const basegfx::B2DHomMatrix& rTextRangeTransform) + : SdrTextPrimitive2D(pSdrText, rOutlinerParaObject), + maTextRangeTransform(rTextRangeTransform) + { } + + void SdrChainedTextPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& aViewInformation) const + { + Primitive2DContainer aRetval; + getSdrText()->GetObject().impDecomposeChainedTextPrimitive(aRetval, *this, aViewInformation); + + encapsulateWithTextHierarchyBlockPrimitive2D(rContainer, std::move(aRetval)); + } + + bool SdrChainedTextPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const + { + if(SdrTextPrimitive2D::operator==(rPrimitive)) + { + const SdrBlockTextPrimitive2D& rCompare = static_cast<const SdrBlockTextPrimitive2D&>(rPrimitive); + + return (getTextRangeTransform() == rCompare.getTextRangeTransform()); + } + + return false; + } + + rtl::Reference<SdrTextPrimitive2D> SdrChainedTextPrimitive2D::createTransformedClone(const basegfx::B2DHomMatrix& rTransform) const + { + return new SdrChainedTextPrimitive2D(getSdrText(), getOutlinerParaObject(), rTransform * getTextRangeTransform()); + } + + // provide unique ID + sal_uInt32 SdrChainedTextPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_SDRCHAINEDTEXTPRIMITIVE2D; + } + + + void SdrStretchTextPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& aViewInformation) const + { + Primitive2DContainer aRetval; + getSdrText()->GetObject().impDecomposeStretchTextPrimitive(aRetval, *this, aViewInformation); + + encapsulateWithTextHierarchyBlockPrimitive2D(rContainer, std::move(aRetval)); + } + + SdrStretchTextPrimitive2D::SdrStretchTextPrimitive2D( + const SdrText* pSdrText, + const OutlinerParaObject& rOutlinerParaObject, + const basegfx::B2DHomMatrix& rTextRangeTransform, + bool bFixedCellHeight) + : SdrTextPrimitive2D(pSdrText, rOutlinerParaObject), + maTextRangeTransform(rTextRangeTransform), + mbFixedCellHeight(bFixedCellHeight) + { + } + + bool SdrStretchTextPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const + { + if(SdrTextPrimitive2D::operator==(rPrimitive)) + { + const SdrStretchTextPrimitive2D& rCompare = static_cast<const SdrStretchTextPrimitive2D&>(rPrimitive); + + return (getTextRangeTransform() == rCompare.getTextRangeTransform() + && isFixedCellHeight() == rCompare.isFixedCellHeight()); + } + + return false; + } + + rtl::Reference<SdrTextPrimitive2D> SdrStretchTextPrimitive2D::createTransformedClone(const basegfx::B2DHomMatrix& rTransform) const + { + return new SdrStretchTextPrimitive2D( + getSdrText(), + getOutlinerParaObject(), + rTransform * getTextRangeTransform(), + isFixedCellHeight()); + } + + // provide unique ID + sal_uInt32 SdrStretchTextPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_SDRSTRETCHTEXTPRIMITIVE2D; + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |