diff options
Diffstat (limited to 'sw/source/core/doc/notxtfrm.cxx')
-rw-r--r-- | sw/source/core/doc/notxtfrm.cxx | 1549 |
1 files changed, 1549 insertions, 0 deletions
diff --git a/sw/source/core/doc/notxtfrm.cxx b/sw/source/core/doc/notxtfrm.cxx new file mode 100644 index 000000000..550f0df50 --- /dev/null +++ b/sw/source/core/doc/notxtfrm.cxx @@ -0,0 +1,1549 @@ +/* -*- 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 <hintids.hxx> +#include <tools/urlobj.hxx> +#include <vcl/imapobj.hxx> +#include <vcl/imap.hxx> +#include <svl/urihelper.hxx> +#include <sfx2/progress.hxx> +#include <sfx2/printer.hxx> +#include <editeng/udlnitem.hxx> +#include <editeng/colritem.hxx> +#include <editeng/boxitem.hxx> +#include <fmturl.hxx> +#include <fmtsrnd.hxx> +#include <frmfmt.hxx> +#include <swrect.hxx> +#include <fesh.hxx> +#include <doc.hxx> +#include <IDocumentSettingAccess.hxx> +#include <IDocumentStylePoolAccess.hxx> +#include <IDocumentDeviceAccess.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <flyfrm.hxx> +#include <flyfrms.hxx> +#include <frmtool.hxx> +#include <viewopt.hxx> +#include <viewimp.hxx> +#include <pam.hxx> +#include <hints.hxx> +#include <rootfrm.hxx> +#include <dflyobj.hxx> +#include <pagefrm.hxx> +#include <notxtfrm.hxx> +#include <grfatr.hxx> +#include <charatr.hxx> +#include <ndnotxt.hxx> +#include <ndgrf.hxx> +#include <ndole.hxx> +#include <swregion.hxx> +#include <poolfmt.hxx> +#include <mdiexp.hxx> +#include <strings.hrc> +#include <accessibilityoptions.hxx> +#include <com/sun/star/embed/EmbedMisc.hpp> +#include <com/sun/star/embed/EmbedStates.hpp> +#include <com/sun/star/embed/XEmbeddedObject.hpp> +#include <svtools/embedhlp.hxx> +#include <dview.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <drawinglayer/processor2d/baseprocessor2d.hxx> +#include <drawinglayer/primitive2d/graphicprimitive2d.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> +#include <basegfx/utils/b2dclipstate.hxx> +#include <drawinglayer/processor2d/processor2dtools.hxx> +#include <txtfly.hxx> +#include <drawinglayer/primitive2d/maskprimitive2d.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <drawinglayer/primitive2d/objectinfoprimitive2d.hxx> + +// MM02 needed for VOC mechanism and getting the OC - may be moved to an own file +#include <svx/sdrpagewindow.hxx> +#include <svx/svdpagv.hxx> +#include <svx/sdr/contact/viewcontact.hxx> +#include <svx/sdr/contact/viewobjectcontact.hxx> +#include <svx/sdr/contact/objectcontact.hxx> +#include <svx/sdr/contact/displayinfo.hxx> + +using namespace com::sun::star; + +static bool GetRealURL( const SwGrfNode& rNd, OUString& rText ) +{ + bool bRet = rNd.GetFileFilterNms( &rText, nullptr ); + if( bRet ) + rText = URIHelper::removePassword( rText, INetURLObject::EncodeMechanism::WasEncoded, + INetURLObject::DecodeMechanism::Unambiguous); + if (rText.startsWith("data:image")) rText = "inline image"; + + return bRet; +} + +static void lcl_PaintReplacement( const SwRect &rRect, const OUString &rText, + const SwViewShell &rSh, const SwNoTextFrame *pFrame, + bool bDefect ) +{ + static vcl::Font aFont = [&]() + { + vcl::Font tmp; + tmp.SetWeight( WEIGHT_BOLD ); + tmp.SetStyleName( OUString() ); + tmp.SetFamilyName("Arial Unicode"); + tmp.SetFamily( FAMILY_SWISS ); + tmp.SetTransparent( true ); + return tmp; + }(); + + Color aCol( COL_RED ); + FontLineStyle eUnderline = LINESTYLE_NONE; + const SwFormatURL &rURL = pFrame->FindFlyFrame()->GetFormat()->GetURL(); + if( !rURL.GetURL().isEmpty() || rURL.GetMap() ) + { + bool bVisited = false; + if ( rURL.GetMap() ) + { + ImageMap *pMap = const_cast<ImageMap*>(rURL.GetMap()); + for( size_t i = 0; i < pMap->GetIMapObjectCount(); ++i ) + { + IMapObject *pObj = pMap->GetIMapObject( i ); + if( rSh.GetDoc()->IsVisitedURL( pObj->GetURL() ) ) + { + bVisited = true; + break; + } + } + } + else if ( !rURL.GetURL().isEmpty() ) + bVisited = rSh.GetDoc()->IsVisitedURL( rURL.GetURL() ); + + SwFormat *pFormat = rSh.GetDoc()->getIDocumentStylePoolAccess().GetFormatFromPool( static_cast<sal_uInt16> + (bVisited ? RES_POOLCHR_INET_VISIT : RES_POOLCHR_INET_NORMAL ) ); + aCol = pFormat->GetColor().GetValue(); + eUnderline = pFormat->GetUnderline().GetLineStyle(); + } + + aFont.SetUnderline( eUnderline ); + aFont.SetColor( aCol ); + + const BitmapEx& rBmp = const_cast<SwViewShell&>(rSh).GetReplacementBitmap(bDefect); + Graphic::DrawEx( rSh.GetOut(), rText, aFont, rBmp, rRect.Pos(), rRect.SSize() ); +} + +SwNoTextFrame::SwNoTextFrame(SwNoTextNode * const pNode, SwFrame* pSib ) +: SwContentFrame( pNode, pSib ), + // RotateFlyFrame3 + mpTransformableSwFrame(), + // MM02 + mpViewContact() +{ + mnFrameType = SwFrameType::NoTxt; +} + +SwContentFrame *SwNoTextNode::MakeFrame( SwFrame* pSib ) +{ + return new SwNoTextFrame(this, pSib); +} + +void SwNoTextFrame::DestroyImpl() +{ + StopAnimation(); + + SwContentFrame::DestroyImpl(); +} + +SwNoTextFrame::~SwNoTextFrame() +{ +} + +void SetOutDev( SwViewShell *pSh, OutputDevice *pOut ) +{ + pSh->mpOut = pOut; +} + +static void lcl_ClearArea( const SwFrame &rFrame, + vcl::RenderContext &rOut, const SwRect& rPtArea, + const SwRect &rGrfArea ) +{ + SwRegionRects aRegion( rPtArea, 4 ); + aRegion -= rGrfArea; + + if ( !aRegion.empty() ) + { + const SvxBrushItem *pItem; + const Color *pCol; + SwRect aOrigRect; + drawinglayer::attribute::SdrAllFillAttributesHelperPtr aFillAttributes; + + if ( rFrame.GetBackgroundBrush( aFillAttributes, pItem, pCol, aOrigRect, false, /*bConsiderTextBox=*/false ) ) + { + SwRegionRects const region(rPtArea); + basegfx::utils::B2DClipState aClipState; + const bool bDone(::DrawFillAttributes(aFillAttributes, aOrigRect, region, aClipState, rOut)); + + if(!bDone) + { + for( const auto &rRegion : aRegion ) + { + ::DrawGraphic( pItem, &rOut, aOrigRect, rRegion ); + } + } + } + else + { + rOut.Push( PushFlags::FILLCOLOR|PushFlags::LINECOLOR ); + rOut.SetFillColor( rFrame.getRootFrame()->GetCurrShell()->Imp()->GetRetoucheColor()); + rOut.SetLineColor(); + for( const auto &rRegion : aRegion ) + rOut.DrawRect( rRegion.SVRect() ); + rOut.Pop(); + } + } +} + +void SwNoTextFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const& rRect, SwPrintData const*const) const +{ + if ( getFrameArea().IsEmpty() ) + return; + + const SwViewShell* pSh = getRootFrame()->GetCurrShell(); + if( !pSh->GetViewOptions()->IsGraphic() ) + { + StopAnimation(); + // #i6467# - no paint of placeholder for page preview + if ( pSh->GetWin() && !pSh->IsPreview() ) + { + const SwNoTextNode* pNd = GetNode()->GetNoTextNode(); + OUString aText( pNd->GetTitle() ); + if ( aText.isEmpty() && pNd->IsGrfNode() ) + GetRealURL( *static_cast<const SwGrfNode*>(pNd), aText ); + if( aText.isEmpty() ) + aText = FindFlyFrame()->GetFormat()->GetName(); + lcl_PaintReplacement( getFrameArea(), aText, *pSh, this, false ); + } + return; + } + + if( pSh->GetAccessibilityOptions()->IsStopAnimatedGraphics() || + // #i9684# Stop animation during printing/pdf export + !pSh->GetWin() ) + StopAnimation(); + + SfxProgress::EnterLock(); // No progress reschedules in paint (SwapIn) + + rRenderContext.Push(); + bool bClip = true; + tools::PolyPolygon aPoly; + + SwNoTextNode& rNoTNd = const_cast<SwNoTextNode&>(*static_cast<const SwNoTextNode*>(GetNode())); + SwGrfNode* pGrfNd = rNoTNd.GetGrfNode(); + if( pGrfNd ) + pGrfNd->SetFrameInPaint( true ); + + // #i13147# - add 2nd parameter with value <true> to + // method call <FindFlyFrame().GetContour(..)> to indicate that it is called + // for paint in order to avoid load of the intrinsic graphic. + if ( ( !rRenderContext.GetConnectMetaFile() || + !pSh->GetWin() ) && + FindFlyFrame()->GetContour( aPoly, true ) + ) + { + rRenderContext.SetClipRegion(vcl::Region(aPoly)); + bClip = false; + } + + SwRect aOrigPaint( rRect ); + if ( HasAnimation() && pSh->GetWin() ) + { + aOrigPaint = getFrameArea(); aOrigPaint += getFramePrintArea().Pos(); + } + + SwRect aGrfArea( getFrameArea() ); + SwRect aPaintArea( aGrfArea ); + + // In case the picture fly frm was clipped, render it with the origin + // size instead of scaling it + if ( pGrfNd && rNoTNd.getIDocumentSettingAccess()->get( DocumentSettingId::CLIPPED_PICTURES ) ) + { + const SwFlyFreeFrame *pFly = dynamic_cast< const SwFlyFreeFrame* >( FindFlyFrame() ); + if( pFly ) + { + bool bGetUnclippedFrame=true; + const SfxPoolItem* pItem; + if( pFly->GetFormat() && SfxItemState::SET == pFly->GetFormat()->GetItemState(RES_BOX, false, &pItem) ) + { + const SvxBoxItem& rBox = *static_cast<const SvxBoxItem*>(pItem); + if( rBox.HasBorder( /*bTreatPaddingAsBorder*/true) ) + bGetUnclippedFrame = false; + } + + if( bGetUnclippedFrame ) + aGrfArea = SwRect( getFrameArea().Pos( ), pFly->GetUnclippedFrame( ).SSize( ) ); + } + } + + aPaintArea.Intersection_( aOrigPaint ); + + SwRect aNormal( getFrameArea().Pos() + getFramePrintArea().Pos(), getFramePrintArea().SSize() ); + aNormal.Justify(); // Normalized rectangle for the comparisons + + if( aPaintArea.IsOver( aNormal ) ) + { + // Calculate the four to-be-deleted rectangles + if( pSh->GetWin() ) + ::lcl_ClearArea( *this, rRenderContext, aPaintArea, aNormal ); + + // The intersection of the PaintArea and the Bitmap contains the absolutely visible area of the Frame + aPaintArea.Intersection_( aNormal ); + + if ( bClip ) + rRenderContext.IntersectClipRegion( aPaintArea.SVRect() ); + /// delete unused 3rd parameter + PaintPicture( &rRenderContext, aGrfArea ); + } + else + // If it's not visible, simply delete the given Area + lcl_ClearArea( *this, rRenderContext, aPaintArea, SwRect() ); + if( pGrfNd ) + pGrfNd->SetFrameInPaint( false ); + + rRenderContext.Pop(); + SfxProgress::LeaveLock(); +} + +/** Calculate the position and the size of the graphic in the Frame, + corresponding to the current graphic attributes + + @param Point the position in the Frame (also returned) + @param Size the graphic's size (also returned) + @param nMirror the current mirror attribute +*/ +static void lcl_CalcRect( Point& rPt, Size& rDim, MirrorGraph nMirror ) +{ + if( nMirror == MirrorGraph::Vertical || nMirror == MirrorGraph::Both ) + { + rPt.setX(rPt.getX() + rDim.Width() -1); + rDim.setWidth( -rDim.Width() ); + } + + if( nMirror == MirrorGraph::Horizontal || nMirror == MirrorGraph::Both ) + { + rPt.setY(rPt.getY() + rDim.Height() -1); + rDim.setHeight( -rDim.Height() ); + } +} + +/** Calculate the Bitmap's position and the size within the passed rectangle */ +void SwNoTextFrame::GetGrfArea( SwRect &rRect, SwRect* pOrigRect ) const +{ + // Currently only used for scaling, cropping and mirroring the contour of graphics! + // Everything else is handled by GraphicObject + // We put the graphic's visible rectangle into rRect. + // pOrigRect contains position and size of the whole graphic. + + // RotateFlyFrame3: SwFrame may be transformed. Get untransformed + // SwRect(s) as base of calculation + const TransformableSwFrame* pTransformableSwFrame(getTransformableSwFrame()); + const SwRect aFrameArea(pTransformableSwFrame ? pTransformableSwFrame->getUntransformedFrameArea() : getFrameArea()); + const SwRect aFramePrintArea(pTransformableSwFrame ? pTransformableSwFrame->getUntransformedFramePrintArea() : getFramePrintArea()); + + const SwAttrSet& rAttrSet = GetNode()->GetSwAttrSet(); + const SwCropGrf& rCrop = rAttrSet.GetCropGrf(); + MirrorGraph nMirror = rAttrSet.GetMirrorGrf().GetValue(); + + if( rAttrSet.GetMirrorGrf().IsGrfToggle() ) + { + if( !(FindPageFrame()->GetVirtPageNum() % 2) ) + { + switch ( nMirror ) + { + case MirrorGraph::Dont: nMirror = MirrorGraph::Vertical; break; + case MirrorGraph::Vertical: nMirror = MirrorGraph::Dont; break; + case MirrorGraph::Horizontal: nMirror = MirrorGraph::Both; break; + default: nMirror = MirrorGraph::Horizontal; break; + } + } + } + + // We read graphic from the Node, if needed. + // It may fail, however. + long nLeftCrop, nRightCrop, nTopCrop, nBottomCrop; + Size aOrigSz( static_cast<const SwNoTextNode*>(GetNode())->GetTwipSize() ); + if ( !aOrigSz.Width() ) + { + aOrigSz.setWidth( aFramePrintArea.Width() ); + nLeftCrop = -rCrop.GetLeft(); + nRightCrop = -rCrop.GetRight(); + } + else + { + nLeftCrop = std::max( aOrigSz.Width() - + (rCrop.GetRight() + rCrop.GetLeft()), long(1) ); + const double nScale = double(aFramePrintArea.Width()) / double(nLeftCrop); + nLeftCrop = long(nScale * -rCrop.GetLeft() ); + nRightCrop = long(nScale * -rCrop.GetRight() ); + } + + // crop values have to be mirrored too + if( nMirror == MirrorGraph::Vertical || nMirror == MirrorGraph::Both ) + { + long nTmpCrop = nLeftCrop; + nLeftCrop = nRightCrop; + nRightCrop= nTmpCrop; + } + + if( !aOrigSz.Height() ) + { + aOrigSz.setHeight( aFramePrintArea.Height() ); + nTopCrop = -rCrop.GetTop(); + nBottomCrop= -rCrop.GetBottom(); + } + else + { + nTopCrop = std::max( aOrigSz.Height() - (rCrop.GetTop() + rCrop.GetBottom()), long(1) ); + const double nScale = double(aFramePrintArea.Height()) / double(nTopCrop); + nTopCrop = long(nScale * -rCrop.GetTop() ); + nBottomCrop= long(nScale * -rCrop.GetBottom() ); + } + + // crop values have to be mirrored too + if( nMirror == MirrorGraph::Horizontal || nMirror == MirrorGraph::Both ) + { + long nTmpCrop = nTopCrop; + nTopCrop = nBottomCrop; + nBottomCrop= nTmpCrop; + } + + Size aVisSz( aFramePrintArea.SSize() ); + Size aGrfSz( aVisSz ); + Point aVisPt( aFrameArea.Pos() + aFramePrintArea.Pos() ); + Point aGrfPt( aVisPt ); + + // Set the "visible" rectangle first + if ( nLeftCrop > 0 ) + { + aVisPt.setX(aVisPt.getX() + nLeftCrop); + aVisSz.AdjustWidth( -nLeftCrop ); + } + if ( nTopCrop > 0 ) + { + aVisPt.setY(aVisPt.getY() + nTopCrop); + aVisSz.AdjustHeight( -nTopCrop ); + } + if ( nRightCrop > 0 ) + aVisSz.AdjustWidth( -nRightCrop ); + if ( nBottomCrop > 0 ) + aVisSz.AdjustHeight( -nBottomCrop ); + + rRect.Pos ( aVisPt ); + rRect.SSize( aVisSz ); + + // Calculate the whole graphic if needed + if ( pOrigRect ) + { + Size aTmpSz( aGrfSz ); + aGrfPt.setX(aGrfPt.getX() + nLeftCrop); + aTmpSz.AdjustWidth( -(nLeftCrop + nRightCrop) ); + aGrfPt.setY(aGrfPt.getY() + nTopCrop); + aTmpSz.AdjustHeight( -(nTopCrop + nBottomCrop) ); + + if( MirrorGraph::Dont != nMirror ) + lcl_CalcRect( aGrfPt, aTmpSz, nMirror ); + + pOrigRect->Pos ( aGrfPt ); + pOrigRect->SSize( aTmpSz ); + } +} + +/** By returning the surrounding Fly's size which equals the graphic's size */ +const Size& SwNoTextFrame::GetSize() const +{ + // Return the Frame's size + const SwFrame *pFly = FindFlyFrame(); + if( !pFly ) + pFly = this; + return pFly->getFramePrintArea().SSize(); +} + +void SwNoTextFrame::MakeAll(vcl::RenderContext* pRenderContext) +{ + // RotateFlyFrame3 - inner frame. Get rotation and check if used + const double fRotation(getLocalFrameRotation()); + const bool bRotated(!basegfx::fTools::equalZero(fRotation)); + + if(bRotated) + { + SwFlyFreeFrame* pUpperFly(dynamic_cast< SwFlyFreeFrame* >(GetUpper())); + + if(pUpperFly) + { + if(!pUpperFly->isFrameAreaDefinitionValid()) + { + // RotateFlyFrame3: outer frame *needs* to be layouted first, force this by calling + // it's ::Calc directly + pUpperFly->Calc(pRenderContext); + } + + // Reset outer frame to unrotated state. This is necessary to make the + // layouting below work as currently implemented in Writer. As expected + // using Transformations allows to do this on the fly due to all information + // being included there. + // The full solution would be to adapt the whole layouting + // process of Writer to take care of Transformations, but that + // is currently beyond scope + if(pUpperFly->isTransformableSwFrame()) + { + pUpperFly->getTransformableSwFrame()->restoreFrameAreas(); + } + } + + // Re-layout may be partially (see all isFrameAreaDefinitionValid() flags), + // so resetting the local SwFrame(s) in the local SwFrameAreaDefinition is also + // needed (e.g. for PrintPreview). + // Reset to BoundAreas will be done below automatically + if(isTransformableSwFrame()) + { + getTransformableSwFrame()->restoreFrameAreas(); + } + } + + SwContentNotify aNotify( this ); + SwBorderAttrAccess aAccess( SwFrame::GetCache(), this ); + const SwBorderAttrs &rAttrs = *aAccess.Get(); + + while ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() ) + { + MakePos(); + + if ( !isFrameAreaSizeValid() ) + { + SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this); + aFrm.Width( GetUpper()->getFramePrintArea().Width() ); + } + + MakePrtArea( rAttrs ); + + if ( !isFrameAreaSizeValid() ) + { + setFrameAreaSizeValid(true); + Format(getRootFrame()->GetCurrShell()->GetOut()); + } + } + + // RotateFlyFrame3 - inner frame + if(bRotated) + { + SwFlyFreeFrame* pUpperFly(dynamic_cast< SwFlyFreeFrame* >(GetUpper())); + + if(pUpperFly) + { + // restore outer frame back to Transformed state, that means + // set the SwFrameAreaDefinition(s) back to BoundAreas of + // the transformed SwFrame. All needed information is part + // of the already correctly created Transformations of the + // upper frame, so it can be re-created on the fly + if(pUpperFly->isTransformableSwFrame()) + { + pUpperFly->getTransformableSwFrame()->adaptFrameAreasToTransformations(); + } + } + + // After the unrotated layout is finished, apply possible set rotation to it + // get center from outer frame (layout frame) to be on the safe side + const Point aCenter(GetUpper() ? GetUpper()->getFrameArea().Center() : getFrameArea().Center()); + const basegfx::B2DPoint aB2DCenter(aCenter.X(), aCenter.Y()); + + if(!mpTransformableSwFrame) + { + mpTransformableSwFrame.reset(new TransformableSwFrame(*this)); + } + + getTransformableSwFrame()->createFrameAreaTransformations( + fRotation, + aB2DCenter); + getTransformableSwFrame()->adaptFrameAreasToTransformations(); + } + else + { + // reset transformations to show that they are not used + mpTransformableSwFrame.reset(); + } +} + +// RotateFlyFrame3 - Support for Transformations - outer frame +basegfx::B2DHomMatrix SwNoTextFrame::getFrameAreaTransformation() const +{ + if(isTransformableSwFrame()) + { + // use pre-created transformation + return getTransformableSwFrame()->getLocalFrameAreaTransformation(); + } + + // call parent + return SwContentFrame::getFrameAreaTransformation(); +} + +basegfx::B2DHomMatrix SwNoTextFrame::getFramePrintAreaTransformation() const +{ + if(isTransformableSwFrame()) + { + // use pre-created transformation + return getTransformableSwFrame()->getLocalFramePrintAreaTransformation(); + } + + // call parent + return SwContentFrame::getFramePrintAreaTransformation(); +} + +// RotateFlyFrame3 - Support for Transformations +void SwNoTextFrame::transform_translate(const Point& rOffset) +{ + // call parent - this will do the basic transform for SwRect(s) + // in the SwFrameAreaDefinition + SwContentFrame::transform_translate(rOffset); + + // check if the Transformations need to be adapted + if(isTransformableSwFrame()) + { + const basegfx::B2DHomMatrix aTransform( + basegfx::utils::createTranslateB2DHomMatrix( + rOffset.X(), rOffset.Y())); + + // transform using TransformableSwFrame + getTransformableSwFrame()->transform(aTransform); + } +} + +// RotateFlyFrame3 - inner frame +// Check if we contain a SwGrfNode and get possible rotation from it +double SwNoTextFrame::getLocalFrameRotation() const +{ + const SwNoTextNode* pSwNoTextNode(nullptr != GetNode() ? GetNode()->GetNoTextNode() : nullptr); + + if(nullptr != pSwNoTextNode) + { + const SwGrfNode* pSwGrfNode(pSwNoTextNode->GetGrfNode()); + + if(nullptr != pSwGrfNode) + { + const SwAttrSet& rSwAttrSet(pSwGrfNode->GetSwAttrSet()); + const SwRotationGrf& rSwRotationGrf(rSwAttrSet.GetRotationGrf()); + const double fRotate(static_cast< double >(-rSwRotationGrf.GetValue()) * (M_PI/1800.0)); + + return basegfx::normalizeToRange(fRotate, F_2PI); + } + } + + // no rotation + return 0.0; +} + +/** Calculate the Bitmap's site, if needed */ +void SwNoTextFrame::Format( vcl::RenderContext* /*pRenderContext*/, const SwBorderAttrs * ) +{ + const Size aNewSize( GetSize() ); + + // Did the height change? + SwTwips nChgHght = IsVertical() ? + static_cast<SwTwips>(aNewSize.Width() - getFramePrintArea().Width()) : + static_cast<SwTwips>(aNewSize.Height() - getFramePrintArea().Height()); + if( nChgHght > 0) + Grow( nChgHght ); + else if( nChgHght < 0) + Shrink( std::min(getFramePrintArea().Height(), -nChgHght) ); +} + +bool SwNoTextFrame::GetCharRect( SwRect &rRect, const SwPosition& rPos, + SwCursorMoveState *pCMS, bool /*bAllowFarAway*/ ) const +{ + if ( &rPos.nNode.GetNode() != static_cast<SwNode const *>(GetNode()) ) + return false; + + Calc(getRootFrame()->GetCurrShell()->GetOut()); + SwRect aFrameRect( getFrameArea() ); + rRect = aFrameRect; + rRect.Pos( getFrameArea().Pos() + getFramePrintArea().Pos() ); + rRect.SSize( getFramePrintArea().SSize() ); + + rRect.Justify(); + + // Is the Bitmap in the visible area at all? + if( !aFrameRect.IsOver( rRect ) ) + { + // If not, then the Cursor is on the Frame + rRect = aFrameRect; + rRect.Width( 1 ); + } + else + rRect.Intersection_( aFrameRect ); + + if ( pCMS && pCMS->m_bRealHeight ) + { + pCMS->m_aRealHeight.setY(rRect.Height()); + pCMS->m_aRealHeight.setX(0); + } + + return true; +} + +bool SwNoTextFrame::GetModelPositionForViewPoint(SwPosition* pPos, Point& , + SwCursorMoveState*, bool ) const +{ + SwContentNode* pCNd = const_cast<SwContentNode*>(GetNode()); + pPos->nNode = *pCNd; + pPos->nContent.Assign( pCNd, 0 ); + return true; +} + +void SwNoTextFrame::ClearCache() +{ + SwFlyFrame* pFly = FindFlyFrame(); + if( pFly && pFly->GetFormat()->GetSurround().IsContour() ) + { + ClrContourCache( pFly->GetVirtDrawObj() ); + pFly->NotifyBackground( FindPageFrame(), getFramePrintArea(), PrepareHint::FlyFrameAttributesChanged ); + } +} + +void SwNoTextFrame::Modify( const SfxPoolItem* pOld, const SfxPoolItem* pNew ) +{ + sal_uInt16 nWhich = pNew ? pNew->Which() : pOld ? pOld->Which() : 0; + + // #i73788# + // no <SwContentFrame::Modify(..)> for RES_LINKED_GRAPHIC_STREAM_ARRIVED + if ( RES_GRAPHIC_PIECE_ARRIVED != nWhich && + RES_GRAPHIC_ARRIVED != nWhich && + RES_GRF_REREAD_AND_INCACHE != nWhich && + RES_LINKED_GRAPHIC_STREAM_ARRIVED != nWhich ) + { + SwContentFrame::Modify( pOld, pNew ); + } + + bool bComplete = true; + + switch( nWhich ) + { + case RES_OBJECTDYING: + break; + + case RES_GRF_REREAD_AND_INCACHE: + if( SwNodeType::Grf == GetNode()->GetNodeType() ) + { + // TODO: Remove - due to GraphicObject refactoring + bComplete = false; + } + break; + + case RES_UPDATE_ATTR: + if (GetNode()->GetNodeType() != SwNodeType::Grf) { + break; + } + [[fallthrough]]; + case RES_FMT_CHG: + ClearCache(); + break; + + case RES_ATTRSET_CHG: + { + sal_uInt16 n; + for( n = RES_GRFATR_BEGIN; n < RES_GRFATR_END; ++n ) + if( SfxItemState::SET == static_cast<const SwAttrSetChg*>(pOld)->GetChgSet()-> + GetItemState( n, false )) + { + ClearCache(); + + if(RES_GRFATR_ROTATION == n) + { + // RotGrfFlyFrame: Update Handles in view, these may be rotation-dependent + // (e.g. crop handles) and need a visualisation update + if ( GetNode()->GetNodeType() == SwNodeType::Grf ) + { + SwGrfNode* pNd = static_cast<SwGrfNode*>( GetNode()); + SwViewShell *pVSh = pNd->GetDoc()->getIDocumentLayoutAccess().GetCurrentViewShell(); + + if(pVSh) + { + SdrView* pDrawView = pVSh->GetDrawView(); + + if(pDrawView) + { + pDrawView->AdjustMarkHdl(nullptr); + } + } + + // RotateFlyFrame3 - invalidate needed for ContentFrame (inner, this) + // and LayoutFrame (outer, GetUpper). It is possible to only invalidate + // the outer frame, but that leads to an in-between state that gets + // potentially painted + if(GetUpper()) + { + GetUpper()->InvalidateAll_(); + } + + InvalidateAll_(); + } + } + break; + } + if( RES_GRFATR_END == n ) // not found + return ; + } + break; + + case RES_GRAPHIC_PIECE_ARRIVED: + case RES_GRAPHIC_ARRIVED: + // i73788# - handle RES_LINKED_GRAPHIC_STREAM_ARRIVED as RES_GRAPHIC_ARRIVED + case RES_LINKED_GRAPHIC_STREAM_ARRIVED: + if ( GetNode()->GetNodeType() == SwNodeType::Grf ) + { + bComplete = false; + SwGrfNode* pNd = static_cast<SwGrfNode*>( GetNode()); + + ClearCache(); + + SwRect aRect( getFrameArea() ); + + SwViewShell *pVSh = pNd->GetDoc()->getIDocumentLayoutAccess().GetCurrentViewShell(); + if( !pVSh ) + break; + + for(SwViewShell& rShell : pVSh->GetRingContainer()) + { + SET_CURR_SHELL( &rShell ); + if( rShell.IsPreview() ) + { + if( rShell.GetWin() ) + ::RepaintPagePreview( &rShell, aRect ); + } + else if ( rShell.VisArea().IsOver( aRect ) && + OUTDEV_WINDOW == rShell.GetOut()->GetOutDevType() ) + { + // invalidate instead of painting + rShell.GetWin()->Invalidate( aRect.SVRect() ); + } + } + } + break; + + default: + if ( !pNew || !isGRFATR(nWhich) ) + return; + } + + if( bComplete ) + { + InvalidatePrt(); + SetCompletePaint(); + } +} + +static void lcl_correctlyAlignRect( SwRect& rAlignedGrfArea, const SwRect& rInArea, vcl::RenderContext const * pOut ) +{ + + if(!pOut) + return; + tools::Rectangle aPxRect = pOut->LogicToPixel( rInArea.SVRect() ); + tools::Rectangle aNewPxRect( aPxRect ); + while( aNewPxRect.Left() < aPxRect.Left() ) + { + rAlignedGrfArea.AddLeft( 1 ); + aNewPxRect = pOut->LogicToPixel( rAlignedGrfArea.SVRect() ); + } + while( aNewPxRect.Top() < aPxRect.Top() ) + { + rAlignedGrfArea.AddTop(+1); + aNewPxRect = pOut->LogicToPixel( rAlignedGrfArea.SVRect() ); + } + while( aNewPxRect.Bottom() > aPxRect.Bottom() ) + { + rAlignedGrfArea.AddBottom( -1 ); + aNewPxRect = pOut->LogicToPixel( rAlignedGrfArea.SVRect() ); + } + while( aNewPxRect.Right() > aPxRect.Right() ) + { + rAlignedGrfArea.AddRight(-1); + aNewPxRect = pOut->LogicToPixel( rAlignedGrfArea.SVRect() ); + } +} + +static bool paintUsingPrimitivesHelper( + vcl::RenderContext& rOutputDevice, + const drawinglayer::primitive2d::Primitive2DContainer& rSequence, + const basegfx::B2DRange& rSourceRange, + const basegfx::B2DRange& rTargetRange) +{ + if(!rSequence.empty() && !basegfx::fTools::equalZero(rSourceRange.getWidth()) && !basegfx::fTools::equalZero(rSourceRange.getHeight())) + { + if(!basegfx::fTools::equalZero(rTargetRange.getWidth()) && !basegfx::fTools::equalZero(rTargetRange.getHeight())) + { + // map graphic range to target range. This will e.g. automatically include + // the mapping from 1/100th mm content to twips if needed when the target + // range is defined in twips + const basegfx::B2DHomMatrix aMappingTransform( + basegfx::utils::createSourceRangeTargetRangeTransform( + rSourceRange, + rTargetRange)); + + // Fill ViewInformation. Use MappingTransform here, so there is no need to + // embed the primitives to it. Use original TargetRange here so there is also + // no need to embed the primitives to a MaskPrimitive for cropping. This works + // only in this case where the graphic object cannot be rotated, though. + const drawinglayer::geometry::ViewInformation2D aViewInformation2D( + aMappingTransform, + rOutputDevice.GetViewTransformation(), + rTargetRange, + nullptr, + 0.0, + uno::Sequence< beans::PropertyValue >()); + + // get a primitive processor for rendering + std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor2D( + drawinglayer::processor2d::createProcessor2DFromOutputDevice( + rOutputDevice, aViewInformation2D) ); + if(pProcessor2D) + { + // render and cleanup + pProcessor2D->process(rSequence); + return true; + } + } + } + + return false; +} + +// MM02 original using falölback to VOC and primitive-based version +void paintGraphicUsingPrimitivesHelper( + vcl::RenderContext & rOutputDevice, + GraphicObject const& rGrfObj, + GraphicAttr const& rGraphicAttr, + const basegfx::B2DHomMatrix& rGraphicTransform, + const OUString& rName, + const OUString& rTitle, + const OUString& rDescription) +{ + // RotGrfFlyFrame: unify using GraphicPrimitive2D + // -> the primitive handles all crop and mirror stuff + // -> the primitive renderer will create the needed pdf export data + // -> if bitmap content, it will be cached system-dependent + drawinglayer::primitive2d::Primitive2DContainer aContent(1); + aContent[0] = new drawinglayer::primitive2d::GraphicPrimitive2D( + rGraphicTransform, + rGrfObj, + rGraphicAttr); + + // MM02 use primitive-based version for visualization + paintGraphicUsingPrimitivesHelper( + rOutputDevice, + aContent, + rGraphicTransform, + rName, + rTitle, + rDescription); +} + +// MM02 new VOC and primitive-based version +void paintGraphicUsingPrimitivesHelper( + vcl::RenderContext & rOutputDevice, + drawinglayer::primitive2d::Primitive2DContainer& rContent, + const basegfx::B2DHomMatrix& rGraphicTransform, + const OUString& rName, + const OUString& rTitle, + const OUString& rDescription) +{ + // RotateFlyFrame3: If ClipRegion is set at OutputDevice, we + // need to use that. Usually the renderer would be a VCL-based + // PrimitiveRenderer, but there are system-specific shortcuts that + // will *not* use the VCL-Paint of Bitmap and thus ignore this. + // Anyways, indirectly using a CLipRegion set at the target OutDev + // when using a PrimitiveRenderer is a non-valid implication. + // First tried only to use when HasPolyPolygonOrB2DPolyPolygon(), + // but there is an optimization at ClipRegion creation that detects + // a single Rectangle in a tools::PolyPolygon and forces to a simple + // RegionBand-based implementation, so cannot use it here. + if(rOutputDevice.IsClipRegion()) + { + const basegfx::B2DPolyPolygon aClip(rOutputDevice.GetClipRegion().GetAsB2DPolyPolygon()); + + if(0 != aClip.count()) + { + // tdf#114076: Expand ClipRange to next PixelBound + // Do this by going to basegfx::B2DRange, adding a + // single pixel size and using floor/ceil to go to + // full integer (as needed for pixels). Also need + // to go back to basegfx::B2DPolyPolygon for the + // creation of the needed MaskPrimitive2D. + // The general problem is that Writer is scrolling + // using blitting the unchanged parts, this forces + // this part of the scroll to pixel coordinate steps, + // while the ViewTransformation for paint nowadays has + // a sub-pixel precision. This results in an offset + // up to one pixel in radius. To solve this for now, + // we need to expand to the next outer pixel bound. + // Hopefully in the future we will someday be able to + // stay on the full available precision, but this + // will need a change in the repaint/scroll paradigm. + const basegfx::B2DRange aClipRange(aClip.getB2DRange()); + const basegfx::B2DVector aSinglePixelXY(rOutputDevice.GetInverseViewTransformation() * basegfx::B2DVector(1.0, 1.0)); + const basegfx::B2DRange aExpandedClipRange( + floor(aClipRange.getMinX() - aSinglePixelXY.getX()), + floor(aClipRange.getMinY() - aSinglePixelXY.getY()), + ceil(aClipRange.getMaxX() + aSinglePixelXY.getX()), + ceil(aClipRange.getMaxY() + aSinglePixelXY.getY())); + + // create the enclosing rectangle as polygon + basegfx::B2DPolyPolygon aTarget(basegfx::utils::createPolygonFromRect(aExpandedClipRange)); + + // tdf#124272 the fix above (tdf#114076) was too rough - the + // clip region used may be a PolyPolygon. In that case that + // PolyPolygon would have to be scaled to mentioned PixelBounds. + // Since that is not really possible geometrically (would need + // more some 'grow in outside direction' but with unequal grow + // values in all directions - just maaany problems + // involved), use a graphical trick: The topology of the + // PolyPolygon uses the standard FillRule, so adding the now + // guaranteed to be bigger or equal bounding (enclosing) + // rectangle twice as polygon will expand the BoundRange, but + // not change the geometry visualization at all + if(!rOutputDevice.GetClipRegion().IsRectangle()) + { + // double the outer rectangle range polygon to have it + // included twice + aTarget.append(aTarget.getB2DPolygon(0)); + + // add the original clip 'inside' (due to being smaller + // or equal). That PolyPolygon may have an unknown number + // of polygons (>=1) + aTarget.append(aClip); + } + + drawinglayer::primitive2d::MaskPrimitive2D* pNew( + new drawinglayer::primitive2d::MaskPrimitive2D( + aTarget, + rContent)); + rContent.resize(1); + rContent[0] = pNew; + } + } + + if(!rName.isEmpty() || !rTitle.isEmpty() || !rDescription.isEmpty()) + { + // Embed to ObjectInfoPrimitive2D when we have Name/Title/Description + // information available + drawinglayer::primitive2d::ObjectInfoPrimitive2D* pNew( + new drawinglayer::primitive2d::ObjectInfoPrimitive2D( + rContent, + rName, + rTitle, + rDescription)); + rContent.resize(1); + rContent[0] = pNew; + } + + basegfx::B2DRange aTargetRange(0.0, 0.0, 1.0, 1.0); + aTargetRange.transform(rGraphicTransform); + + paintUsingPrimitivesHelper( + rOutputDevice, + rContent, + aTargetRange, + aTargetRange); +} + +// DrawContact section +namespace { // anonymous namespace +class ViewObjectContactOfSwNoTextFrame : public sdr::contact::ViewObjectContact +{ +protected: + virtual drawinglayer::primitive2d::Primitive2DContainer createPrimitive2DSequence( + const sdr::contact::DisplayInfo& rDisplayInfo) const override; + +public: + ViewObjectContactOfSwNoTextFrame( + sdr::contact::ObjectContact& rObjectContact, + sdr::contact::ViewContact& rViewContact); +}; + +class ViewContactOfSwNoTextFrame : public sdr::contact::ViewContact +{ +private: + // owner + const SwNoTextFrame& mrSwNoTextFrame; + +protected: + // Create an Object-Specific ViewObjectContact, set ViewContact and + // ObjectContact. Always needs to return something. + virtual sdr::contact::ViewObjectContact& CreateObjectSpecificViewObjectContact( + sdr::contact::ObjectContact& rObjectContact) override; + +public: + // read-access to owner + const SwNoTextFrame& getSwNoTextFrame() const { return mrSwNoTextFrame; } + + // basic constructor, used from SwNoTextFrame. + explicit ViewContactOfSwNoTextFrame(const SwNoTextFrame& rSwNoTextFrame); +}; + +drawinglayer::primitive2d::Primitive2DContainer ViewObjectContactOfSwNoTextFrame::createPrimitive2DSequence( + const sdr::contact::DisplayInfo& /*rDisplayInfo*/) const +{ + // MM02 get all the parameters formally used in paintGraphicUsingPrimitivesHelper + ViewContactOfSwNoTextFrame& rVCOfNTF(static_cast<ViewContactOfSwNoTextFrame&>(GetViewContact())); + const SwNoTextFrame& rSwNoTextFrame(rVCOfNTF.getSwNoTextFrame()); + SwNoTextNode& rNoTNd(const_cast<SwNoTextNode&>(*static_cast<const SwNoTextNode*>(rSwNoTextFrame.GetNode()))); + SwGrfNode* pGrfNd(rNoTNd.GetGrfNode()); + + if(nullptr != pGrfNd) + { + const bool bPrn(GetObjectContact().isOutputToPrinter() || GetObjectContact().isOutputToRecordingMetaFile()); + const GraphicObject& rGrfObj(pGrfNd->GetGrfObj(bPrn)); + GraphicAttr aGraphicAttr; + pGrfNd->GetGraphicAttr(aGraphicAttr, &rSwNoTextFrame); + const basegfx::B2DHomMatrix aGraphicTransform(rSwNoTextFrame.getFrameAreaTransformation()); + + // MM02 this is the right place in the VOC-Mechanism to create + // the primitives for visualization - these will be automatically + // buffered and reused + drawinglayer::primitive2d::Primitive2DContainer aContent(1); + aContent[0] = new drawinglayer::primitive2d::GraphicPrimitive2D( + aGraphicTransform, + rGrfObj, + aGraphicAttr); + + return aContent; + } + + return drawinglayer::primitive2d::Primitive2DContainer(); +} + +ViewObjectContactOfSwNoTextFrame::ViewObjectContactOfSwNoTextFrame( + sdr::contact::ObjectContact& rObjectContact, + sdr::contact::ViewContact& rViewContact) +: sdr::contact::ViewObjectContact(rObjectContact, rViewContact) +{ +} + +sdr::contact::ViewObjectContact& ViewContactOfSwNoTextFrame::CreateObjectSpecificViewObjectContact( + sdr::contact::ObjectContact& rObjectContact) +{ + sdr::contact::ViewObjectContact* pRetval = new ViewObjectContactOfSwNoTextFrame(rObjectContact, *this); + return *pRetval; +} + +ViewContactOfSwNoTextFrame::ViewContactOfSwNoTextFrame( + const SwNoTextFrame& rSwNoTextFrame +) +: sdr::contact::ViewContact(), + mrSwNoTextFrame(rSwNoTextFrame) +{ +} +} // end of anonymous namespace + +sdr::contact::ViewContact& SwNoTextFrame::GetViewContact() const +{ + if(!mpViewContact) + { + const_cast< SwNoTextFrame* >(this)->mpViewContact = + std::make_unique<ViewContactOfSwNoTextFrame>(*this); + } + + return *mpViewContact; +} + +/** Paint the graphic. + + We require either a QuickDraw-Bitmap or a graphic here. If we do not have + either, we return a replacement. + + @todo use aligned rectangle for drawing graphic. + @todo pixel-align coordinations for drawing graphic. */ +void SwNoTextFrame::PaintPicture( vcl::RenderContext* pOut, const SwRect &rGrfArea ) const +{ + SwViewShell* pShell = getRootFrame()->GetCurrShell(); + + SwNoTextNode& rNoTNd = const_cast<SwNoTextNode&>(*static_cast<const SwNoTextNode*>(GetNode())); + SwGrfNode* pGrfNd = rNoTNd.GetGrfNode(); + SwOLENode* pOLENd = rNoTNd.GetOLENode(); + + const bool bPrn = pOut == rNoTNd.getIDocumentDeviceAccess().getPrinter( false ) || + pOut->GetConnectMetaFile(); + + const bool bIsChart = pOLENd && pOLENd->GetOLEObj().GetObject().IsChart(); + + // calculate aligned rectangle from parameter <rGrfArea>. + // Use aligned rectangle <aAlignedGrfArea> instead of <rGrfArea> in + // the following code. + SwRect aAlignedGrfArea = rGrfArea; + ::SwAlignRect( aAlignedGrfArea, pShell, pOut ); + + if( !bIsChart ) + { + // Because for drawing a graphic left-top-corner and size coordinations are + // used, these coordinations have to be determined on pixel level. + ::SwAlignGrfRect( &aAlignedGrfArea, *pOut ); + } + else //if( bIsChart ) + { + // #i78025# charts own borders are not completely visible + // the above pixel correction is not correct - at least not for charts + // so a different pixel correction is chosen here + // this might be a good idea for all other OLE objects also, + // but as I cannot oversee the consequences I fix it only for charts for now + lcl_correctlyAlignRect( aAlignedGrfArea, rGrfArea, pOut ); + } + + if( pGrfNd ) + { + // Fix for bug fdo#33781 + const AntialiasingFlags nFormerAntialiasingAtOutput( pOut->GetAntialiasing() ); + if (pShell->Imp()->GetDrawView()->IsAntiAliasing()) + { + pOut->SetAntialiasing( nFormerAntialiasingAtOutput | AntialiasingFlags::EnableB2dDraw ); + } + + bool bContinue = true; + const GraphicObject& rGrfObj = pGrfNd->GetGrfObj(bPrn); + + GraphicAttr aGrfAttr; + pGrfNd->GetGraphicAttr( aGrfAttr, this ); + + if( !bPrn ) + { + // #i73788# + if ( pGrfNd->IsLinkedInputStreamReady() ) + { + pGrfNd->UpdateLinkWithInputStream(); + } + // #i85717#, #i90395# - check, if asynchronous retrieval + // if input stream for the graphic is possible + else if ( ( rGrfObj.GetType() == GraphicType::Default || + rGrfObj.GetType() == GraphicType::NONE ) && + pGrfNd->IsLinkedFile() && + pGrfNd->IsAsyncRetrieveInputStreamPossible() ) + { + Size aTmpSz; + ::sfx2::SvLinkSource* pGrfObj = pGrfNd->GetLink()->GetObj(); + if( !pGrfObj || + !pGrfObj->IsDataComplete() || + !(aTmpSz = pGrfNd->GetTwipSize()).Width() || + !aTmpSz.Height()) + { + pGrfNd->TriggerAsyncRetrieveInputStream(); // #i73788# + } + OUString aText( pGrfNd->GetTitle() ); + if ( aText.isEmpty() ) + GetRealURL( *pGrfNd, aText ); + ::lcl_PaintReplacement( aAlignedGrfArea, aText, *pShell, this, false ); + bContinue = false; + } + } + + if( bContinue ) + { + if( rGrfObj.GetGraphic().IsSupportedGraphic()) + { + const bool bAnimate = rGrfObj.IsAnimated() && + !pShell->IsPreview() && + !pShell->GetAccessibilityOptions()->IsStopAnimatedGraphics() && + // #i9684# Stop animation during printing/pdf export + pShell->GetWin(); + + if( bAnimate && + FindFlyFrame() != ::GetFlyFromMarked( nullptr, pShell )) + { + OutputDevice* pVout; + if( pOut == pShell->GetOut() && SwRootFrame::FlushVout() ) + { + pVout = pOut; + pOut = pShell->GetOut(); + } + else if( pShell->GetWin() && pOut->IsVirtual() ) + { + pVout = pOut; + pOut = pShell->GetWin(); + } + else + pVout = nullptr; + + OSL_ENSURE( !pOut->IsVirtual() || + pShell->GetViewOptions()->IsPDFExport() || pShell->isOutputToWindow(), + "pOut should not be a virtual device" ); + + pGrfNd->StartGraphicAnimation(pOut, aAlignedGrfArea.Pos(), + aAlignedGrfArea.SSize(), reinterpret_cast<sal_IntPtr>(this), + pVout ); + } + else + { + // MM02 To allow system-dependent buffering of the involved + // bitmaps it is necessary to re-use the involved primitives + // and their already executed decomposition (also for + // performance reasons). This is usually done in DrawingLayer + // by using the VOC-Mechanism (see descriptions elsewhere). + // To get that here, make the involved SwNoTextFrame (this) + // a sdr::contact::ViewContact supplier by supporting + // a GetViewContact() - call. For ObjectContact we can use + // the already existing ObjectContact from the involved + // DrawingLayer. For this, the helper classes + // ViewObjectContactOfSwNoTextFrame + // ViewContactOfSwNoTextFrame + // are created which support the VOC-mechanism in its minimal + // form. This allows automatic and view-dependent (multiple edit + // windows, print, etc.) re-use of the created primitives. + // Also: Will be very useful when completely changing the Writer + // repaint to VOC and Primitives, too. + static const char* pDisableMM02Goodies(getenv("SAL_DISABLE_MM02_GOODIES")); + static bool bUseViewObjectContactMechanism(nullptr == pDisableMM02Goodies); + // tdf#130951 for safety reasons use fallback if ViewObjectContactMechanism + // fails for some reason - usually could only be not to find the correct + // SdrPageWindow + bool bSucceeded(false); + + if(bUseViewObjectContactMechanism) + { + // MM02 use VOC-mechanism and buffer primitives + SwViewShellImp* pImp(pShell->Imp()); + SdrPageView* pPageView(nullptr != pImp + ? pImp->GetPageView() + : nullptr); + // tdf#130951 caution - target may be Window, use the correct OutputDevice + OutputDevice* pTarget(pShell->isOutputToWindow() + ? pShell->GetWin() + : pShell->GetOut()); + SdrPageWindow* pPageWindow(nullptr != pPageView && nullptr != pTarget + ? pPageView->FindPageWindow(*pTarget) + : nullptr); + + if(nullptr != pPageWindow) + { + sdr::contact::ObjectContact& rOC(pPageWindow->GetObjectContact()); + sdr::contact::ViewContact& rVC(GetViewContact()); + sdr::contact::ViewObjectContact& rVOC(rVC.GetViewObjectContact(rOC)); + sdr::contact::DisplayInfo aDisplayInfo; + + drawinglayer::primitive2d::Primitive2DContainer aPrimitives(rVOC.getPrimitive2DSequence(aDisplayInfo)); + const basegfx::B2DHomMatrix aGraphicTransform(getFrameAreaTransformation()); + + paintGraphicUsingPrimitivesHelper( + *pOut, + aPrimitives, + aGraphicTransform, + nullptr == pGrfNd->GetFlyFormat() ? OUString() : pGrfNd->GetFlyFormat()->GetName(), + rNoTNd.GetTitle(), + rNoTNd.GetDescription()); + bSucceeded = true; + } + } + + if(!bSucceeded) + { + // MM02 fallback to direct paint with primitive-recreation + // which will block reusage of system-dependent bitmap data + const basegfx::B2DHomMatrix aGraphicTransform(getFrameAreaTransformation()); + + paintGraphicUsingPrimitivesHelper( + *pOut, + rGrfObj, + aGrfAttr, + aGraphicTransform, + nullptr == pGrfNd->GetFlyFormat() ? OUString() : pGrfNd->GetFlyFormat()->GetName(), + rNoTNd.GetTitle(), + rNoTNd.GetDescription()); + } + } + } + else + { + const char* pResId = nullptr; + + if( GraphicType::NONE == rGrfObj.GetType() ) + pResId = STR_COMCORE_READERROR; + else if ( !rGrfObj.GetGraphic().IsSupportedGraphic() ) + pResId = STR_COMCORE_CANT_SHOW; + + OUString aText; + if ( !pResId && + (aText = pGrfNd->GetTitle()).isEmpty() && + (!GetRealURL( *pGrfNd, aText ) || aText.isEmpty())) + { + pResId = STR_COMCORE_READERROR; + } + if (pResId) + aText = SwResId(pResId); + + ::lcl_PaintReplacement( aAlignedGrfArea, aText, *pShell, this, true ); + } + } + + if ( pShell->Imp()->GetDrawView()->IsAntiAliasing() ) + pOut->SetAntialiasing( nFormerAntialiasingAtOutput ); + } + else // bIsChart || pOLENd + { + // Fix for bug fdo#33781 + const AntialiasingFlags nFormerAntialiasingAtOutput( pOut->GetAntialiasing() ); + if (pShell->Imp()->GetDrawView()->IsAntiAliasing()) + { + AntialiasingFlags nNewAntialiasingAtOutput = nFormerAntialiasingAtOutput | AntialiasingFlags::EnableB2dDraw; + + // #i99665# + // Adjust AntiAliasing mode at output device for chart OLE + if ( pOLENd->IsChart() ) + nNewAntialiasingAtOutput |= AntialiasingFlags::PixelSnapHairline; + + pOut->SetAntialiasing( nNewAntialiasingAtOutput ); + } + + bool bDone(false); + + if(bIsChart) + { + basegfx::B2DRange aSourceRange; + const drawinglayer::primitive2d::Primitive2DContainer aSequence( + pOLENd->GetOLEObj().tryToGetChartContentAsPrimitive2DSequence( + aSourceRange, + bPrn)); + + if(!aSequence.empty() && !aSourceRange.isEmpty()) + { + const basegfx::B2DRange aTargetRange( + aAlignedGrfArea.Left(), aAlignedGrfArea.Top(), + aAlignedGrfArea.Right(), aAlignedGrfArea.Bottom()); + + bDone = paintUsingPrimitivesHelper( + *pOut, + aSequence, + aSourceRange, + aTargetRange); + } + } + + if(!bDone && pOLENd) + { + // SwOLENode does not have a known GraphicObject, need to + // work with Graphic instead + const Graphic* pGraphic = pOLENd->GetGraphic(); + const Point aPosition(aAlignedGrfArea.Pos()); + const Size aSize(aAlignedGrfArea.SSize()); + + if ( pGraphic && pGraphic->GetType() != GraphicType::NONE ) + { + pGraphic->Draw( pOut, aPosition, aSize ); + + // shade the representation if the object is activated outplace + uno::Reference < embed::XEmbeddedObject > xObj = pOLENd->GetOLEObj().GetOleRef(); + if ( xObj.is() && xObj->getCurrentState() == embed::EmbedStates::ACTIVE ) + { + + ::svt::EmbeddedObjectRef::DrawShading( + tools::Rectangle( + aPosition, + aSize), + pOut); + } + } + else + { + ::svt::EmbeddedObjectRef::DrawPaintReplacement( + tools::Rectangle(aPosition, aSize), + pOLENd->GetOLEObj().GetCurrentPersistName(), + pOut); + } + + sal_Int64 nMiscStatus = pOLENd->GetOLEObj().GetOleRef()->getStatus( pOLENd->GetAspect() ); + if ( !bPrn && dynamic_cast< const SwCursorShell *>( pShell ) != nullptr && + (nMiscStatus & embed::EmbedMisc::MS_EMBED_ACTIVATEWHENVISIBLE)) + { + const SwFlyFrame *pFly = FindFlyFrame(); + assert( pFly != nullptr ); + static_cast<SwFEShell*>(pShell)->ConnectObj( pOLENd->GetOLEObj().GetObject(), pFly->getFramePrintArea(), pFly->getFrameArea()); + } + } + + // see #i99665# + if (pShell->Imp()->GetDrawView()->IsAntiAliasing()) + { + pOut->SetAntialiasing( nFormerAntialiasingAtOutput ); + } + } +} + +bool SwNoTextFrame::IsTransparent() const +{ + const SwViewShell* pSh = getRootFrame()->GetCurrShell(); + + if ( !pSh || !pSh->GetViewOptions()->IsGraphic() ) + { + return true; + } + + const SwGrfNode *pNd; + + if( nullptr != (pNd = GetNode()->GetGrfNode()) ) + { + if(pNd->IsTransparent()) + { + return true; + } + } + + // RotateFlyFrame3: If we are transformed, there are 'free' areas between + // the Graphic and the Border/Padding stuff - at least as long as those + // (Border and Padding) are not transformed, too + if(isTransformableSwFrame()) + { + // we can be more specific - rotations of multiples of + // 90 degrees will leave no gaps. Go from [0.0 .. F_2PI] + // to [0 .. 360] and check modulo 90 + const long nRot(static_cast<long>(basegfx::rad2deg(getLocalFrameRotation()))); + const bool bMultipleOf90(0 == (nRot % 90)); + + if(!bMultipleOf90) + { + return true; + } + } + + //#29381# OLE are always transparent + if(nullptr != GetNode()->GetOLENode()) + { + return true; + } + + // return false by default to avoid background paint + return false; +} + +void SwNoTextFrame::StopAnimation( OutputDevice* pOut ) const +{ + // Stop animated graphics + const SwGrfNode* pGrfNd = GetNode()->GetGrfNode(); + + if( pGrfNd && pGrfNd->IsAnimated() ) + { + const_cast< SwGrfNode* >(pGrfNd)->StopGraphicAnimation( pOut, reinterpret_cast<sal_IntPtr>(this) ); + } +} + +bool SwNoTextFrame::HasAnimation() const +{ + const SwGrfNode* pGrfNd = GetNode()->GetGrfNode(); + return pGrfNd && pGrfNd->IsAnimated(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |