/* -*- 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 #include #include #include #include #include #include #include #include #include "EnhancedCustomShape3d.hxx" #include "EnhancedCustomShapeFontWork.hxx" #include "EnhancedCustomShapeHandle.hxx" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace css; using namespace css::uno; class SdrObject; class SdrObjCustomShape; namespace { class EnhancedCustomShapeEngine : public cppu::WeakImplHelper < css::lang::XInitialization, css::lang::XServiceInfo, css::drawing::XCustomShapeEngine > { css::uno::Reference< css::drawing::XShape > mxShape; bool mbForceGroupWithText; std::unique_ptr ImplForceGroupWithText( const SdrObjCustomShape& rSdrObjCustomShape, std::unique_ptr pRenderedShape); public: EnhancedCustomShapeEngine(); // XInterface virtual void SAL_CALL acquire() noexcept override; virtual void SAL_CALL release() noexcept override; // XInitialization virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override; // XServiceInfo virtual OUString SAL_CALL getImplementationName() override; virtual sal_Bool SAL_CALL supportsService( const OUString& rServiceName ) override; virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; // XCustomShapeEngine virtual css::uno::Reference< css::drawing::XShape > SAL_CALL render() override; virtual css::awt::Rectangle SAL_CALL getTextBounds() override; virtual css::drawing::PolyPolygonBezierCoords SAL_CALL getLineGeometry() override; virtual css::uno::Sequence< css::uno::Reference< css::drawing::XCustomShapeHandle > > SAL_CALL getInteraction() override; }; EnhancedCustomShapeEngine::EnhancedCustomShapeEngine() : mbForceGroupWithText ( false ) { } // XInterface void SAL_CALL EnhancedCustomShapeEngine::acquire() noexcept { OWeakObject::acquire(); } void SAL_CALL EnhancedCustomShapeEngine::release() noexcept { OWeakObject::release(); } // XInitialization void SAL_CALL EnhancedCustomShapeEngine::initialize( const Sequence< Any >& aArguments ) { Sequence< beans::PropertyValue > aParameter; for ( const auto& rArgument : aArguments ) { if ( rArgument >>= aParameter ) break; } for ( const beans::PropertyValue& rProp : std::as_const(aParameter) ) { if ( rProp.Name == "CustomShape" ) rProp.Value >>= mxShape; else if ( rProp.Name == "ForceGroupWithText" ) rProp.Value >>= mbForceGroupWithText; } } // XServiceInfo OUString SAL_CALL EnhancedCustomShapeEngine::getImplementationName() { return "com.sun.star.drawing.EnhancedCustomShapeEngine"; } sal_Bool SAL_CALL EnhancedCustomShapeEngine::supportsService( const OUString& rServiceName ) { return cppu::supportsService(this, rServiceName); } Sequence< OUString > SAL_CALL EnhancedCustomShapeEngine::getSupportedServiceNames() { return { "com.sun.star.drawing.CustomShapeEngine" }; } // XCustomShapeEngine std::unique_ptr EnhancedCustomShapeEngine::ImplForceGroupWithText( const SdrObjCustomShape& rSdrObjCustomShape, std::unique_ptr pRenderedShape) { const bool bHasText(rSdrObjCustomShape.HasText()); if ( pRenderedShape || bHasText ) { // applying shadow const SdrObject* pShadowGeometry(rSdrObjCustomShape.GetSdrObjectShadowFromCustomShape()); if ( pShadowGeometry ) { if ( pRenderedShape ) { if ( dynamic_cast( pRenderedShape.get() ) == nullptr ) { auto pTmp = std::move(pRenderedShape); pRenderedShape.reset(new SdrObjGroup(rSdrObjCustomShape.getSdrModelFromSdrObject())); static_cast(pRenderedShape.get())->GetSubList()->NbcInsertObject( pTmp.release() ); } static_cast(pRenderedShape.get())->GetSubList()->NbcInsertObject( pShadowGeometry->CloneSdrObject(pShadowGeometry->getSdrModelFromSdrObject()), 0); } else { pRenderedShape.reset( pShadowGeometry->CloneSdrObject(pShadowGeometry->getSdrModelFromSdrObject()) ); } } // apply text if ( bHasText ) { // #i37011# also create a text object and add at rPos + 1 std::unique_ptr pTextObj( SdrObjFactory::MakeNewObject( rSdrObjCustomShape.getSdrModelFromSdrObject(), rSdrObjCustomShape.GetObjInventor(), SdrObjKind::Text) ); // Copy text content OutlinerParaObject* pParaObj(rSdrObjCustomShape.GetOutlinerParaObject()); if( pParaObj ) pTextObj->NbcSetOutlinerParaObject( *pParaObj ); // copy all attributes SfxItemSet aTargetItemSet(rSdrObjCustomShape.GetMergedItemSet()); // clear fill and line style aTargetItemSet.Put(XLineStyleItem(drawing::LineStyle_NONE)); aTargetItemSet.Put(XFillStyleItem(drawing::FillStyle_NONE)); // get the text bounds and set at text object tools::Rectangle aTextBounds(rSdrObjCustomShape.GetSnapRect()); auto pSdrObjCustomShape = dynamic_cast< SdrObjCustomShape* >(SdrObject::getSdrObjectFromXShape(mxShape)); if(pSdrObjCustomShape) { EnhancedCustomShape2d aCustomShape2d(*pSdrObjCustomShape); aTextBounds = aCustomShape2d.GetTextRect(); } pTextObj->SetSnapRect( aTextBounds ); // if rotated, copy GeoStat, too. const GeoStat& rSourceGeo(rSdrObjCustomShape.GetGeoStat()); if ( rSourceGeo.nRotationAngle ) { pTextObj->NbcRotate( rSdrObjCustomShape.GetSnapRect().Center(), rSourceGeo.nRotationAngle, rSourceGeo.mfSinRotationAngle, rSourceGeo.mfCosRotationAngle); } // set modified ItemSet at text object pTextObj->SetMergedItemSet(aTargetItemSet); if ( pRenderedShape ) { if ( dynamic_cast( pRenderedShape.get() ) == nullptr ) { auto pTmp = std::move(pRenderedShape); pRenderedShape.reset(new SdrObjGroup(rSdrObjCustomShape.getSdrModelFromSdrObject())); static_cast(pRenderedShape.get())->GetSubList()->NbcInsertObject( pTmp.release() ); } static_cast(pRenderedShape.get())->GetSubList()->NbcInsertObject( pTextObj.release() ); } else pRenderedShape = std::move(pTextObj); } // force group if ( pRenderedShape ) { if ( dynamic_cast( pRenderedShape.get() ) == nullptr ) { auto pTmp = std::move(pRenderedShape); pRenderedShape.reset(new SdrObjGroup(rSdrObjCustomShape.getSdrModelFromSdrObject())); static_cast(pRenderedShape.get())->GetSubList()->NbcInsertObject( pTmp.release() ); } } } return pRenderedShape; } void SetTemporary( uno::Reference< drawing::XShape > const & xShape ) { if ( xShape.is() ) { SvxShape* pShape = comphelper::getFromUnoTunnel( xShape ); if ( pShape ) pShape->TakeSdrObjectOwnership(); } } Reference< drawing::XShape > SAL_CALL EnhancedCustomShapeEngine::render() { SdrObjCustomShape* pSdrObjCustomShape = dynamic_cast< SdrObjCustomShape* >(SdrObject::getSdrObjectFromXShape(mxShape)); if(!pSdrObjCustomShape) { return Reference< drawing::XShape >(); } // retrieving the TextPath property to check if feature is enabled const SdrCustomShapeGeometryItem& rGeometryItem(pSdrObjCustomShape->GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY )); bool bTextPathOn = false; const uno::Any* pAny = rGeometryItem.GetPropertyValueByName( "TextPath", "TextPath" ); if ( pAny ) *pAny >>= bTextPathOn; EnhancedCustomShape2d aCustomShape2d(*pSdrObjCustomShape); Degree100 nRotateAngle = aCustomShape2d.GetRotateAngle(); bool bFlipV = aCustomShape2d.IsFlipVert(); bool bFlipH = aCustomShape2d.IsFlipHorz(); bool bLineGeometryNeededOnly = bTextPathOn; std::unique_ptr xRenderedShape(aCustomShape2d.CreateObject(bLineGeometryNeededOnly)); if (xRenderedShape) { if ( bTextPathOn ) { std::unique_ptr xRenderedFontWork( EnhancedCustomShapeFontWork::CreateFontWork( xRenderedShape.get(), *pSdrObjCustomShape)); if (xRenderedFontWork) { xRenderedShape = std::move(xRenderedFontWork); } } std::unique_ptr xRenderedShape3d(EnhancedCustomShape3d::Create3DObject(xRenderedShape.get(), *pSdrObjCustomShape)); if (xRenderedShape3d) { bFlipV = bFlipH = false; nRotateAngle = 0_deg100; xRenderedShape = std::move(xRenderedShape3d); } tools::Rectangle aRect(pSdrObjCustomShape->GetSnapRect()); const GeoStat& rGeoStat(pSdrObjCustomShape->GetGeoStat()); if ( rGeoStat.nShearAngle ) { Degree100 nShearAngle = rGeoStat.nShearAngle; double nTan = rGeoStat.mfTanShearAngle; if (bFlipV != bFlipH) { nShearAngle = -nShearAngle; nTan = -nTan; } xRenderedShape->Shear(pSdrObjCustomShape->GetSnapRect().Center(), nShearAngle, nTan, false); } if(nRotateAngle ) xRenderedShape->NbcRotate(pSdrObjCustomShape->GetSnapRect().Center(), nRotateAngle); if ( bFlipV ) { Point aLeft( aRect.Left(), ( aRect.Top() + aRect.Bottom() ) >> 1 ); Point aRight( aLeft.X() + 1000, aLeft.Y() ); xRenderedShape->NbcMirror( aLeft, aRight ); } if ( bFlipH ) { Point aTop( ( aRect.Left() + aRect.Right() ) >> 1, aRect.Top() ); Point aBottom( aTop.X(), aTop.Y() + 1000 ); xRenderedShape->NbcMirror( aTop, aBottom ); } xRenderedShape->NbcSetStyleSheet(pSdrObjCustomShape->GetStyleSheet(), true); xRenderedShape->RecalcSnapRect(); } if ( mbForceGroupWithText ) { xRenderedShape = ImplForceGroupWithText( *pSdrObjCustomShape, std::move(xRenderedShape)); } Reference< drawing::XShape > xShape; if (xRenderedShape) { aCustomShape2d.ApplyGluePoints(xRenderedShape.get()); SdrObject* pRenderedShape = xRenderedShape.release(); xShape = SvxDrawPage::CreateShapeByTypeAndInventor( pRenderedShape->GetObjIdentifier(), pRenderedShape->GetObjInventor(), pRenderedShape ); } SetTemporary( xShape ); return xShape; } awt::Rectangle SAL_CALL EnhancedCustomShapeEngine::getTextBounds() { awt::Rectangle aTextRect; if (SdrObjCustomShape* pSdrObjCustomShape = dynamic_cast< SdrObjCustomShape* >(SdrObject::getSdrObjectFromXShape(mxShape))) { uno::Reference< document::XActionLockable > xLockable( mxShape, uno::UNO_QUERY ); if(xLockable.is() && !xLockable->isActionLocked()) { EnhancedCustomShape2d aCustomShape2d(*pSdrObjCustomShape); tools::Rectangle aRect( aCustomShape2d.GetTextRect() ); aTextRect.X = aRect.Left(); aTextRect.Y = aRect.Top(); aTextRect.Width = aRect.GetWidth(); aTextRect.Height = aRect.GetHeight(); } } return aTextRect; } drawing::PolyPolygonBezierCoords SAL_CALL EnhancedCustomShapeEngine::getLineGeometry() { drawing::PolyPolygonBezierCoords aPolyPolygonBezierCoords; SdrObjCustomShape* pSdrObjCustomShape = dynamic_cast< SdrObjCustomShape* >(SdrObject::getSdrObjectFromXShape(mxShape)); if(pSdrObjCustomShape) { EnhancedCustomShape2d aCustomShape2d(*pSdrObjCustomShape); SdrObjectUniquePtr pObj = aCustomShape2d.CreateLineGeometry(); if ( pObj ) { tools::Rectangle aRect(pSdrObjCustomShape->GetSnapRect()); bool bFlipV = aCustomShape2d.IsFlipVert(); bool bFlipH = aCustomShape2d.IsFlipHorz(); const GeoStat& rGeoStat(pSdrObjCustomShape->GetGeoStat()); if ( rGeoStat.nShearAngle ) { Degree100 nShearAngle = rGeoStat.nShearAngle; double nTan = rGeoStat.mfTanShearAngle; if (bFlipV != bFlipH) { nShearAngle = -nShearAngle; nTan = -nTan; } pObj->Shear( aRect.Center(), nShearAngle, nTan, false); } Degree100 nRotateAngle = aCustomShape2d.GetRotateAngle(); if( nRotateAngle ) pObj->NbcRotate( aRect.Center(), nRotateAngle ); if ( bFlipH ) { Point aTop( ( aRect.Left() + aRect.Right() ) >> 1, aRect.Top() ); Point aBottom( aTop.X(), aTop.Y() + 1000 ); pObj->NbcMirror( aTop, aBottom ); } if ( bFlipV ) { Point aLeft( aRect.Left(), ( aRect.Top() + aRect.Bottom() ) >> 1 ); Point aRight( aLeft.X() + 1000, aLeft.Y() ); pObj->NbcMirror( aLeft, aRight ); } basegfx::B2DPolyPolygon aPolyPolygon; SdrObjListIter aIter( *pObj, SdrIterMode::DeepWithGroups ); while ( aIter.IsMore() ) { basegfx::B2DPolyPolygon aPP; const SdrObject* pNext = aIter.Next(); if ( auto pPathObj = dynamic_cast(pNext) ) { aPP = pPathObj->GetPathPoly(); } else { SdrObjectUniquePtr pNewObj = pNext->ConvertToPolyObj( false, false ); SdrPathObj* pPath = dynamic_cast( pNewObj.get() ); if ( pPath ) aPP = pPath->GetPathPoly(); } if ( aPP.count() ) aPolyPolygon.append(aPP); } pObj.reset(); basegfx::utils::B2DPolyPolygonToUnoPolyPolygonBezierCoords( aPolyPolygon, aPolyPolygonBezierCoords ); } } return aPolyPolygonBezierCoords; } Sequence< Reference< drawing::XCustomShapeHandle > > SAL_CALL EnhancedCustomShapeEngine::getInteraction() { sal_uInt32 i, nHdlCount = 0; SdrObjCustomShape* pSdrObjCustomShape = dynamic_cast< SdrObjCustomShape* >(SdrObject::getSdrObjectFromXShape(mxShape)); if(pSdrObjCustomShape) { EnhancedCustomShape2d aCustomShape2d(*pSdrObjCustomShape); nHdlCount = aCustomShape2d.GetHdlCount(); } Sequence< Reference< drawing::XCustomShapeHandle > > aSeq( nHdlCount ); auto aSeqRange = asNonConstRange(aSeq); for ( i = 0; i < nHdlCount; i++ ) aSeqRange[ i ] = new EnhancedCustomShapeHandle( mxShape, i ); return aSeq; } } extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * com_sun_star_drawing_EnhancedCustomShapeEngine_get_implementation( css::uno::XComponentContext *, css::uno::Sequence const &) { return cppu::acquire(new EnhancedCustomShapeEngine); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */