/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#include <vcl/lazydelete.hxx>
#include <sfx2/docfile.hxx>
#include <sfx2/progress.hxx>
#include <editeng/brushitem.hxx>
#include <editeng/prntitem.hxx>
#include <editeng/boxitem.hxx>
#include <editeng/shaditem.hxx>
#include <svx/framelink.hxx>
#include <drawdoc.hxx>
#include <tgrditem.hxx>
#include <calbck.hxx>
#include <fmtsrnd.hxx>
#include <fmtclds.hxx>
#include <strings.hrc>
#include <swmodule.hxx>
#include <rootfrm.hxx>
#include <pagefrm.hxx>
#include <section.hxx>
#include <sectfrm.hxx>
#include <viewimp.hxx>
#include <dflyobj.hxx>
#include <flyfrm.hxx>
#include <viewopt.hxx>
#include <dview.hxx>
#include <dcontact.hxx>
#include <txtfrm.hxx>
#include <ftnfrm.hxx>
#include <tabfrm.hxx>
#include <rowfrm.hxx>
#include <cellfrm.hxx>
#include <notxtfrm.hxx>
#include <layact.hxx>
#include <pagedesc.hxx>
#include <ptqueue.hxx>
#include <noteurl.hxx>
#include "virtoutp.hxx"
#include <lineinfo.hxx>
#include <dbg_lay.hxx>
#include <docsh.hxx>
#include <svx/svdogrp.hxx>
#include <sortedobjs.hxx>
#include <EnhancedPDFExportHelper.hxx>
#include <bodyfrm.hxx>
#include <hffrm.hxx>
#include <colfrm.hxx>
#include <sw_primitivetypes2d.hxx>

#include <svx/sdr/contact/viewobjectcontactredirector.hxx>
#include <svx/sdr/contact/viewobjectcontact.hxx>
#include <svx/sdr/contact/viewcontact.hxx>
#include <DocumentSettingManager.hxx>
#include <IDocumentDeviceAccess.hxx>

#include <ndole.hxx>
#include <PostItMgr.hxx>
#include <FrameControlsManager.hxx>
#include <vcl/settings.hxx>

#include <svx/sdr/attribute/sdrallfillattributeshelper.hxx>
#include <drawinglayer/processor2d/processor2dtools.hxx>

#include <svtools/borderhelper.hxx>

#include <bitmaps.hlst>
#include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
#include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx>
#include <drawinglayer/primitive2d/borderlineprimitive2d.hxx>
#include <drawinglayer/primitive2d/discreteshadowprimitive2d.hxx>
#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
#include <drawinglayer/primitive2d/textprimitive2d.hxx>
#include <drawinglayer/primitive2d/textlayoutdevice.hxx>
#include <drawinglayer/processor2d/processorfromoutputdevice.hxx>
#include <svx/unoapi.hxx>
#include <svx/framelinkarray.hxx>
#include <comphelper/sequence.hxx>
#include <basegfx/matrix/b2dhommatrixtools.hxx>
#include <basegfx/color/bcolortools.hxx>

#include <memory>
#include <vector>
#include <algorithm>
#include <wrtsh.hxx>
#include <edtwin.hxx>
#include <view.hxx>
#include <paintfrm.hxx>
#include <textboxhelper.hxx>
#include <o3tl/typed_flags_set.hxx>

#include <vcl/BitmapTools.hxx>

#define COL_NOTES_SIDEPANE                  Color(230,230,230)
#define COL_NOTES_SIDEPANE_BORDER           Color(200,200,200)
#define COL_NOTES_SIDEPANE_SCROLLAREA       Color(230,230,220)

using namespace ::editeng;
using namespace ::com::sun::star;
using ::drawinglayer::primitive2d::BorderLinePrimitive2D;
using ::drawinglayer::primitive2d::BorderLine;
using std::pair;
using std::make_pair;

struct SwPaintProperties;

//other subsidiary lines enabled?
#define IS_SUBS (!gProp.pSGlobalShell->GetViewOptions()->IsPagePreview() && \
                 !gProp.pSGlobalShell->GetViewOptions()->IsReadonly() && \
                 !gProp.pSGlobalShell->GetViewOptions()->IsFormView() &&\
                 !gProp.pSGlobalShell->GetViewOptions()->IsWhitespaceHidden() &&\
                 SwViewOption::IsDocBoundaries())
//subsidiary lines for sections
#define IS_SUBS_SECTION (!gProp.pSGlobalShell->GetViewOptions()->IsPagePreview() && \
                         !gProp.pSGlobalShell->GetViewOptions()->IsReadonly()&&\
                         !gProp.pSGlobalShell->GetViewOptions()->IsFormView() &&\
                          SwViewOption::IsSectionBoundaries())
#define IS_SUBS_FLYS (!gProp.pSGlobalShell->GetViewOptions()->IsPagePreview() && \
                      !gProp.pSGlobalShell->GetViewOptions()->IsReadonly()&&\
                      !gProp.pSGlobalShell->GetViewOptions()->IsFormView() &&\
                       SwViewOption::IsObjectBoundaries())

//Class declaration; here because they are only used in this file
enum class SubColFlags {
    Page     = 0x01,    //Helplines of the page
    Tab      = 0x08,   //Helplines inside tables
    Fly      = 0x10,    //Helplines inside fly frames
    Sect     = 0x20,    //Helplines inside sections
};
namespace o3tl {
    template<> struct typed_flags<SubColFlags> : is_typed_flags<SubColFlags, 0x39> {};
}

// Classes collecting the border lines and help lines
class SwLineRect : public SwRect
{
    Color             aColor;
    SvxBorderLineStyle nStyle;
    const SwTabFrame *pTab;
    SubColFlags       nSubColor;  //colorize subsidiary lines
    bool              bPainted;   //already painted?
    sal_uInt8         nLock;      //To distinguish the line and the hell layer.
public:
    SwLineRect( const SwRect &rRect, const Color *pCol, const SvxBorderLineStyle nStyle,
                const SwTabFrame *pT , const SubColFlags nSCol );

    const Color&         GetColor() const { return aColor;}
    SvxBorderLineStyle   GetStyle() const { return nStyle; }
    const SwTabFrame      *GetTab()   const { return pTab;  }
    void  SetPainted()                    { bPainted = true; }
    void  Lock( bool bLock )              { if ( bLock )
                                                ++nLock;
                                            else if ( nLock )
                                                --nLock;
                                          }
    bool        IsPainted()               const { return bPainted; }
    bool        IsLocked()                const { return nLock != 0;  }
    SubColFlags GetSubColor()             const { return nSubColor;}

    bool MakeUnion( const SwRect &rRect, SwPaintProperties const &properties );
};

#ifdef IOS
static void dummy_function()
{
    pid_t pid = getpid();
    (void) pid;
}
#endif

class SwLineRects
{
public:
    std::vector< SwLineRect > aLineRects;
    typedef std::vector< SwLineRect >::const_iterator const_iterator;
    typedef std::vector< SwLineRect >::iterator iterator;
    typedef std::vector< SwLineRect >::reverse_iterator reverse_iterator;
    typedef std::vector< SwLineRect >::size_type size_type;
    size_t nLastCount;  //avoid unnecessary cycles in PaintLines
    SwLineRects() : nLastCount( 0 )
    {
#ifdef IOS
        // Work around what is either a compiler bug in Xcode 5.1.1,
        // or some unknown problem in this file. If I ifdef out this
        // call, I get a crash in SwSubsRects::PaintSubsidiary: the
        // address of the rLi reference variable is claimed to be
        // 0x4000000!
        dummy_function();
#endif
    }
    void AddLineRect( const SwRect& rRect,  const Color *pColor, const SvxBorderLineStyle nStyle,
                      const SwTabFrame *pTab, const SubColFlags nSCol, SwPaintProperties const &properties );
    void ConnectEdges( OutputDevice const *pOut, SwPaintProperties const &properties );
    void PaintLines  ( OutputDevice *pOut, SwPaintProperties const &properties );
    void LockLines( bool bLock );

    //Limit lines to 100
    bool isFull() const { return aLineRects.size()>100; }
};

class SwSubsRects : public SwLineRects
{
    void RemoveSuperfluousSubsidiaryLines( const SwLineRects &rRects, SwPaintProperties const &properties );
public:
    void PaintSubsidiary( OutputDevice *pOut, const SwLineRects *pRects, SwPaintProperties const &properties );
};

class BorderLines
{
    drawinglayer::primitive2d::Primitive2DContainer m_Lines;
public:
    void AddBorderLines(const drawinglayer::primitive2d::Primitive2DContainer& rContainer);
    drawinglayer::primitive2d::Primitive2DContainer GetBorderLines_Clear()
    {
        drawinglayer::primitive2d::Primitive2DContainer lines;
        lines.swap(m_Lines);
        return lines;
    }
};

// Default zoom factor
const static double aEdgeScale = 0.5;

//To optimize the expensive RetouchColor determination
Color aGlobalRetoucheColor;

namespace sw
{
Color* GetActiveRetoucheColor()
{
    return &aGlobalRetoucheColor;
}
}

/**
 * Container for static properties
 */
struct SwPaintProperties {
    // Only repaint the Fly content as well as the background of the Fly content if
    // a metafile is taken of the Fly.
    bool                bSFlyMetafile;
    VclPtr<OutputDevice> pSFlyMetafileOut;
    SwViewShell        *pSGlobalShell;

    // Retouch for transparent Flys is done by the background of the Flys.
    // The Fly itself should certainly not be spared out. See PaintSwFrameBackground and
    // lcl_SubtractFlys()
    SwFlyFrame           *pSRetoucheFly;
    SwFlyFrame           *pSRetoucheFly2;
    SwFlyFrame           *pSFlyOnlyDraw;

    // The borders will be collected in pSLines during the Paint and later
    // possibly merge them.
    // The help lines will be collected and merged in gProp.pSSubsLines. These will
    // be compared with pSLines before the work in order to avoid help lines
    // to hide borders.
    std::unique_ptr<BorderLines> pBLines;
    std::unique_ptr<SwLineRects> pSLines;
    std::unique_ptr<SwSubsRects> pSSubsLines;

    // global variable for sub-lines of body, header, footer, section and footnote frames.
    std::unique_ptr<SwSubsRects> pSSpecSubsLines;
    SfxProgress        *pSProgress;

    // Sizes of a pixel and the corresponding halves. Will be reset when
    // entering SwRootFrame::PaintSwFrame
    long                nSPixelSzW;
    long                nSPixelSzH;
    long                nSHalfPixelSzW;
    long                nSHalfPixelSzH;
    long                nSMinDistPixelW;
    long                nSMinDistPixelH;

    Color               aSGlobalRetoucheColor;

    // Current zoom factor
    double              aSScaleX;
    double              aSScaleY;

    SwPaintProperties()
      : bSFlyMetafile(false)
      , pSFlyMetafileOut(nullptr)
      , pSGlobalShell(nullptr)
      , pSRetoucheFly(nullptr)
      , pSRetoucheFly2(nullptr)
      , pSFlyOnlyDraw(nullptr)
      , pSProgress(nullptr)
      , nSPixelSzW(0)
      , nSPixelSzH(0)
      , nSHalfPixelSzW(0)
      , nSHalfPixelSzH(0)
      , nSMinDistPixelW(0)
      , nSMinDistPixelH(0)
      , aSScaleX(1)
      , aSScaleY(1)
    {
    }

};

static SwPaintProperties gProp;

namespace {

bool isTableBoundariesEnabled()
{
    if (!gProp.pSGlobalShell->GetViewOptions()->IsTable())
        return false;

    if (gProp.pSGlobalShell->GetViewOptions()->IsPagePreview())
        return false;

    if (gProp.pSGlobalShell->GetViewOptions()->IsReadonly())
        return false;

    if (gProp.pSGlobalShell->GetViewOptions()->IsFormView())
        return false;

    return SwViewOption::IsTableBoundaries();
}

}

/**
 * Set borders alignment statics
 * Adjustment for 'small' twip-to-pixel relations:
 * For 'small' twip-to-pixel relations (less then 2:1)
 * values of <gProp.nSHalfPixelSzW> and <gProp.nSHalfPixelSzH> are set to ZERO
 */
void SwCalcPixStatics( vcl::RenderContext const *pOut )
{
    // determine 'small' twip-to-pixel relation
    bool bSmallTwipToPxRelW = false;
    bool bSmallTwipToPxRelH = false;
    {
        Size aCheckTwipToPxRelSz( pOut->PixelToLogic( Size( 100, 100 )) );
        if ( (aCheckTwipToPxRelSz.Width()/100.0) < 2.0 )
        {
            bSmallTwipToPxRelW = true;
        }
        if ( (aCheckTwipToPxRelSz.Height()/100.0) < 2.0 )
        {
            bSmallTwipToPxRelH = true;
        }
    }

    Size aSz( pOut->PixelToLogic( Size( 1,1 )) );

    gProp.nSPixelSzW = aSz.Width();
    if( !gProp.nSPixelSzW )
        gProp.nSPixelSzW = 1;
    gProp.nSPixelSzH = aSz.Height();
    if( !gProp.nSPixelSzH )
        gProp.nSPixelSzH = 1;

    // consider 'small' twip-to-pixel relations
    if ( !bSmallTwipToPxRelW )
    {
        gProp.nSHalfPixelSzW = gProp.nSPixelSzW / 2 + 1;
    }
    else
    {
        gProp.nSHalfPixelSzW = 0;
    }
    // consider 'small' twip-to-pixel relations
    if ( !bSmallTwipToPxRelH )
    {
        gProp.nSHalfPixelSzH = gProp.nSPixelSzH / 2 + 1;
    }
    else
    {
        gProp.nSHalfPixelSzH = 0;
    }

    gProp.nSMinDistPixelW = gProp.nSPixelSzW * 2 + 1;
    gProp.nSMinDistPixelH = gProp.nSPixelSzH * 2 + 1;

    const MapMode &rMap = pOut->GetMapMode();
    gProp.aSScaleX = double(rMap.GetScaleX());
    gProp.aSScaleY = double(rMap.GetScaleY());
}

/**
 * To be able to save the statics so the paint is more or less reentrant
 */
class SwSavePaintStatics : public SwPaintProperties
{
public:
    SwSavePaintStatics();
    ~SwSavePaintStatics();
};

SwSavePaintStatics::SwSavePaintStatics()
{
    // Saving globales
    bSFlyMetafile = gProp.bSFlyMetafile;
    pSGlobalShell = gProp.pSGlobalShell;
    pSFlyMetafileOut = gProp.pSFlyMetafileOut;
    pSRetoucheFly = gProp.pSRetoucheFly;
    pSRetoucheFly2 = gProp.pSRetoucheFly2;
    pSFlyOnlyDraw = gProp.pSFlyOnlyDraw;
    pBLines = std::move(gProp.pBLines);
    pSLines = std::move(gProp.pSLines);
    pSSubsLines = std::move(gProp.pSSubsLines);
    pSSpecSubsLines = std::move(gProp.pSSpecSubsLines);
    pSProgress = gProp.pSProgress;
    nSPixelSzW = gProp.nSPixelSzW;
    nSPixelSzH = gProp.nSPixelSzH;
    nSHalfPixelSzW = gProp.nSHalfPixelSzW;
    nSHalfPixelSzH = gProp.nSHalfPixelSzH;
    nSMinDistPixelW = gProp.nSMinDistPixelW;
    nSMinDistPixelH = gProp.nSMinDistPixelH ;
    aSGlobalRetoucheColor = aGlobalRetoucheColor;
    aSScaleX = gProp.aSScaleX;
    aSScaleY = gProp.aSScaleY;

    // Restoring globales to default
    gProp.bSFlyMetafile = false;
    gProp.pSFlyMetafileOut = nullptr;
    gProp.pSRetoucheFly  = nullptr;
    gProp.pSRetoucheFly2 = nullptr;
    gProp.nSPixelSzW = gProp.nSPixelSzH =
    gProp.nSHalfPixelSzW = gProp.nSHalfPixelSzH =
    gProp.nSMinDistPixelW = gProp.nSMinDistPixelH = 0;
    gProp.aSScaleX = gProp.aSScaleY = 1.0;
    gProp.pSProgress = nullptr;
}

SwSavePaintStatics::~SwSavePaintStatics()
{
    // Restoring globales to saved one
    gProp.pSGlobalShell       = pSGlobalShell;
    gProp.bSFlyMetafile       = bSFlyMetafile;
    gProp.pSFlyMetafileOut    = pSFlyMetafileOut;
    gProp.pSRetoucheFly       = pSRetoucheFly;
    gProp.pSRetoucheFly2      = pSRetoucheFly2;
    gProp.pSFlyOnlyDraw       = pSFlyOnlyDraw;
    gProp.pBLines             = std::move(pBLines);
    gProp.pSLines             = std::move(pSLines);
    gProp.pSSubsLines         = std::move(pSSubsLines);
    gProp.pSSpecSubsLines     = std::move(pSSpecSubsLines);
    gProp.pSProgress          = pSProgress;
    gProp.nSPixelSzW          = nSPixelSzW;
    gProp.nSPixelSzH          = nSPixelSzH;
    gProp.nSHalfPixelSzW      = nSHalfPixelSzW;
    gProp.nSHalfPixelSzH      = nSHalfPixelSzH;
    gProp.nSMinDistPixelW     = nSMinDistPixelW;
    gProp.nSMinDistPixelH     = nSMinDistPixelH;
    aGlobalRetoucheColor      = aSGlobalRetoucheColor;
    gProp.aSScaleX            = aSScaleX;
    gProp.aSScaleY            = aSScaleY;
}

void BorderLines::AddBorderLines(const drawinglayer::primitive2d::Primitive2DContainer& rContainer)
{
    if(!rContainer.empty())
    {
        m_Lines.append(rContainer);
    }
}

SwLineRect::SwLineRect( const SwRect &rRect, const Color *pCol, const SvxBorderLineStyle nStyl,
                        const SwTabFrame *pT, const SubColFlags nSCol ) :
    SwRect( rRect ),
    nStyle( nStyl ),
    pTab( pT ),
    nSubColor( nSCol ),
    bPainted( false ),
    nLock( 0 )
{
    if ( pCol != nullptr )
        aColor = *pCol;
}

bool SwLineRect::MakeUnion( const SwRect &rRect, SwPaintProperties const & properties)
{
    // It has already been tested outside, whether the rectangles have
    // the same orientation (horizontal or vertical), color, etc.
    if ( Height() > Width() ) //Vertical line
    {
        if ( Left()  == rRect.Left() && Width() == rRect.Width() )
        {
            // Merge when there is no gap between the lines
            const long nAdd = properties.nSPixelSzW + properties.nSHalfPixelSzW;
            if ( Bottom() + nAdd >= rRect.Top() &&
                 Top()    - nAdd <= rRect.Bottom()  )
            {
                Bottom( std::max( Bottom(), rRect.Bottom() ) );
                Top   ( std::min( Top(),    rRect.Top()    ) );
                return true;
            }
        }
    }
    else
    {
        if ( Top()  == rRect.Top() && Height() == rRect.Height() )
        {
            // Merge when there is no gap between the lines
            const long nAdd = properties.nSPixelSzW + properties.nSHalfPixelSzW;
            if ( Right() + nAdd >= rRect.Left() &&
                 Left()  - nAdd <= rRect.Right() )
            {
                Right( std::max( Right(), rRect.Right() ) );
                Left ( std::min( Left(),  rRect.Left()  ) );
                return true;
            }
        }
    }
    return false;
}

void SwLineRects::AddLineRect( const SwRect &rRect, const Color *pCol, const SvxBorderLineStyle nStyle,
                               const SwTabFrame *pTab, const SubColFlags nSCol, SwPaintProperties const & properties )
{
    // Loop backwards because lines which can be combined, can usually be painted
    // in the same context
    for (reverse_iterator it = aLineRects.rbegin(); it != aLineRects.rend();
         ++it)
    {
        SwLineRect &rLRect = (*it);
        // Test for the orientation, color, table
        if ( rLRect.GetTab() == pTab &&
             !rLRect.IsPainted() && rLRect.GetSubColor() == nSCol &&
             (rLRect.Height() > rLRect.Width()) == (rRect.Height() > rRect.Width()) &&
             (pCol && rLRect.GetColor() == *pCol) )
        {
            if ( rLRect.MakeUnion( rRect, properties ) )
                return;
        }
    }
    aLineRects.emplace_back( rRect, pCol, nStyle, pTab, nSCol );
}

void SwLineRects::ConnectEdges( OutputDevice const *pOut, SwPaintProperties const & properties )
{
    if ( pOut->GetOutDevType() != OUTDEV_PRINTER )
    {
        // I'm not doing anything for a too small zoom
        if ( properties.aSScaleX < aEdgeScale || properties.aSScaleY < aEdgeScale )
            return;
    }

    static const long nAdd = 20;

    std::vector<SwLineRect*> aCheck;

    for (size_t i = 0; i < aLineRects.size(); ++i)
    {
        SwLineRect &rL1 = aLineRects[i];
        if ( !rL1.GetTab() || rL1.IsPainted() || rL1.IsLocked() )
            continue;

        aCheck.clear();

        const bool bVert = rL1.Height() > rL1.Width();
        long nL1a, nL1b, nL1c, nL1d;

        if ( bVert )
        {
            nL1a = rL1.Top();   nL1b = rL1.Left();
            nL1c = rL1.Right(); nL1d = rL1.Bottom();
        }
        else
        {
            nL1a = rL1.Left();   nL1b = rL1.Top();
            nL1c = rL1.Bottom(); nL1d = rL1.Right();
        }

        // Collect all lines to possibly link with i1
        for (iterator it2 = aLineRects.begin(); it2 != aLineRects.end(); ++it2)
        {
            SwLineRect &rL2 = (*it2);
            if ( rL2.GetTab() != rL1.GetTab() ||
                 rL2.IsPainted()              ||
                 rL2.IsLocked()               ||
                 (bVert == (rL2.Height() > rL2.Width())) )
                continue;

            long nL2a, nL2b, nL2c, nL2d;
            if ( bVert )
            {
                nL2a = rL2.Top();   nL2b = rL2.Left();
                nL2c = rL2.Right(); nL2d = rL2.Bottom();
            }
            else
            {
                nL2a = rL2.Left();   nL2b = rL2.Top();
                nL2c = rL2.Bottom(); nL2d = rL2.Right();
            }

            if ( (nL1a - nAdd < nL2d && nL1d + nAdd > nL2a) &&
                  ((nL1b >  nL2b && nL1c        < nL2c) ||
                   (nL1c >= nL2c && nL1b - nAdd < nL2c) ||
                   (nL1b <= nL2b && nL1c + nAdd > nL2b)) )
            {
                aCheck.push_back( &rL2 );
            }
        }
        if ( aCheck.size() < 2 )
            continue;

        bool bRemove = false;

        // For each line test all following ones.
        for ( size_t k = 0; !bRemove && k < aCheck.size(); ++k )
        {
            SwLineRect &rR1 = *aCheck[k];

            for ( size_t k2 = k+1; !bRemove && k2 < aCheck.size(); ++k2 )
            {
                SwLineRect &rR2 = *aCheck[k2];
                if ( bVert )
                {
                    SwLineRect *pLA = nullptr;
                    SwLineRect *pLB = nullptr;
                    if ( rR1.Top() < rR2.Top() )
                    {
                        pLA = &rR1; pLB = &rR2;
                    }
                    else if ( rR1.Top() > rR2.Top() )
                    {
                        pLA = &rR2; pLB = &rR1;
                    }
                    // are k1 and k2 describing a double line?
                    if ( pLA && pLA->Bottom() + 60 > pLB->Top() )
                    {
                        if ( rL1.Top() < pLA->Top() )
                        {
                            if ( rL1.Bottom() == pLA->Bottom() )
                                continue;    //Small mistake (where?)

                            SwRect aIns( rL1 );
                            aIns.Bottom( pLA->Bottom() );
                            if ( !rL1.IsInside( aIns ) )
                                continue;
                            aLineRects.emplace_back( aIns, &rL1.GetColor(),
                                        SvxBorderLineStyle::SOLID,
                                        rL1.GetTab(), SubColFlags::Tab );
                            if ( isFull() )
                            {
                                --i;
                                k = aCheck.size();
                                break;
                            }
                        }

                        if ( rL1.Bottom() > pLB->Bottom() )
                            rL1.Top( pLB->Top() ); // extend i1 on the top
                        else
                            bRemove = true; //stopping, remove i1
                    }
                }
                else
                {
                    SwLineRect *pLA = nullptr;
                    SwLineRect *pLB = nullptr;
                    if ( rR1.Left() < rR2.Left() )
                    {
                        pLA = &rR1; pLB = &rR2;
                    }
                    else if ( rR1.Left() > rR2.Left() )
                    {
                        pLA = &rR2; pLB = &rR1;
                    }
                    // Is it double line?
                    if ( pLA && pLA->Right() + 60 > pLB->Left() )
                    {
                        if ( rL1.Left() < pLA->Left() )
                        {
                            if ( rL1.Right() == pLA->Right() )
                                continue;    //small error

                            SwRect aIns( rL1 );
                            aIns.Right( pLA->Right() );
                            if ( !rL1.IsInside( aIns ) )
                                continue;
                            aLineRects.emplace_back( aIns, &rL1.GetColor(),
                                        SvxBorderLineStyle::SOLID,
                                        rL1.GetTab(), SubColFlags::Tab );
                            if ( isFull() )
                            {
                                --i;
                                k = aCheck.size();
                                break;
                            }
                        }
                        if ( rL1.Right() > pLB->Right() )
                            rL1.Left( pLB->Left() );
                        else
                            bRemove = true;
                    }
                }
            }
        }
        if ( bRemove )
        {
            aLineRects.erase(aLineRects.begin() + i);
            --i;
        }
    }
}

void SwSubsRects::RemoveSuperfluousSubsidiaryLines( const SwLineRects &rRects, SwPaintProperties const & properties )
{
    // All help lines that are covered by any border will be removed or split
    for (size_t i = 0; i < aLineRects.size(); ++i)
    {
        // get a copy instead of a reference, because an <insert> may destroy
        // the object due to a necessary array resize.
        const SwLineRect aSubsLineRect = SwLineRect(aLineRects[i]);

        // add condition <aSubsLineRect.IsLocked()> in order to consider only
        // border lines, which are *not* locked.
        if ( aSubsLineRect.IsPainted() ||
             aSubsLineRect.IsLocked() )
            continue;

        const bool bVerticalSubs = aSubsLineRect.Height() > aSubsLineRect.Width();
        SwRect aSubsRect( aSubsLineRect );
        if ( bVerticalSubs )
        {
            aSubsRect.Left  ( aSubsRect.Left()  - (properties.nSPixelSzW+properties.nSHalfPixelSzW) );
            aSubsRect.Right ( aSubsRect.Right() + (properties.nSPixelSzW+properties.nSHalfPixelSzW) );
        }
        else
        {
            aSubsRect.Top   ( aSubsRect.Top()    - (properties.nSPixelSzH+properties.nSHalfPixelSzH) );
            aSubsRect.Bottom( aSubsRect.Bottom() + (properties.nSPixelSzH+properties.nSHalfPixelSzH) );
        }
        for (const_iterator itK = rRects.aLineRects.begin(); itK != rRects.aLineRects.end(); ++itK)
        {
            const SwLineRect &rLine = *itK;

            // do *not* consider painted or locked border lines.
            // #i1837# - locked border lines have to be considered.
            if ( rLine.IsLocked () )
                continue;

            if ( !bVerticalSubs == ( rLine.Height() > rLine.Width() ) ) //same direction?
                continue;

            if ( aSubsRect.IsOver( rLine ) )
            {
                if ( bVerticalSubs ) // Vertical?
                {
                    if ( aSubsRect.Left()  <= rLine.Right() &&
                         aSubsRect.Right() >= rLine.Left() )
                    {
                        long nTmp = rLine.Top()-(properties.nSPixelSzH+1);
                        if ( aSubsLineRect.Top() < nTmp )
                        {
                            SwRect aNewSubsRect( aSubsLineRect );
                            aNewSubsRect.Bottom( nTmp );
                            aLineRects.emplace_back( aNewSubsRect, nullptr, aSubsLineRect.GetStyle(), nullptr,
                                                aSubsLineRect.GetSubColor() );
                        }
                        nTmp = rLine.Bottom()+properties.nSPixelSzH+1;
                        if ( aSubsLineRect.Bottom() > nTmp )
                        {
                            SwRect aNewSubsRect( aSubsLineRect );
                            aNewSubsRect.Top( nTmp );
                            aLineRects.emplace_back( aNewSubsRect, nullptr, aSubsLineRect.GetStyle(), nullptr,
                                                aSubsLineRect.GetSubColor() );
                        }
                        aLineRects.erase(aLineRects.begin() + i);
                        --i;
                        break;
                    }
                }
                else // Horizontal
                {
                    if ( aSubsRect.Top() <= rLine.Bottom() &&
                         aSubsRect.Bottom() >= rLine.Top() )
                    {
                        long nTmp = rLine.Left()-(properties.nSPixelSzW+1);
                        if ( aSubsLineRect.Left() < nTmp )
                        {
                            SwRect aNewSubsRect( aSubsLineRect );
                            aNewSubsRect.Right( nTmp );
                            aLineRects.emplace_back( aNewSubsRect, nullptr, aSubsLineRect.GetStyle(), nullptr,
                                                aSubsLineRect.GetSubColor() );
                        }
                        nTmp = rLine.Right()+properties.nSPixelSzW+1;
                        if ( aSubsLineRect.Right() > nTmp )
                        {
                            SwRect aNewSubsRect( aSubsLineRect );
                            aNewSubsRect.Left( nTmp );
                            aLineRects.emplace_back( aNewSubsRect, nullptr, aSubsLineRect.GetStyle(), nullptr,
                                                aSubsLineRect.GetSubColor() );
                        }
                        aLineRects.erase(aLineRects.begin() + i);
                        --i;
                        break;
                    }
                }
            }
        }
    }
}

void SwLineRects::LockLines( bool bLock )
{
    for (iterator it = aLineRects.begin(); it != aLineRects.end(); ++it)
       (*it).Lock( bLock );
}

static void lcl_DrawDashedRect( OutputDevice * pOut, SwLineRect const & rLRect )
{
    long startX = rLRect.Left(  ), endX;
    long startY = rLRect.Top(  ),  endY;

    // Discriminate vertically stretched rect from horizontally stretched
    // and restrict minimum nHalfLWidth to 1
    long nHalfLWidth = std::max( static_cast<long>(std::min( rLRect.Width(  ), rLRect.Height(  ) ) / 2), 1L );

    if ( rLRect.Height(  ) > rLRect.Width(  ) )
    {
        startX += nHalfLWidth;
        endX = startX;
        endY = startY + rLRect.Height(  );
    }
    else
    {
        startY += nHalfLWidth;
        endY = startY;
        endX = startX + rLRect.Width(  );
    }

    svtools::DrawLine( *pOut, Point( startX, startY ), Point( endX, endY ),
            sal_uInt32( nHalfLWidth * 2 ), rLRect.GetStyle( ) );
}

void SwLineRects::PaintLines( OutputDevice *pOut, SwPaintProperties const &properties )
{
    // Paint the borders. Sadly two passes are needed.
    // Once for the inside and once for the outside edges of tables
    if ( aLineRects.size() == nLastCount )
        return;

    // #i16816# tagged pdf support
    SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, nullptr, *pOut );

    pOut->Push( PushFlags::FILLCOLOR|PushFlags::LINECOLOR );
    pOut->SetFillColor();
    pOut->SetLineColor();
    ConnectEdges( pOut, properties );
    const Color *pLast = nullptr;

    bool bPaint2nd = false;
    size_t nMinCount = aLineRects.size();

    for ( size_t i = 0; i < aLineRects.size(); ++i )
    {
        SwLineRect &rLRect = aLineRects[i];

        if ( rLRect.IsPainted() )
            continue;

        if ( rLRect.IsLocked() )
        {
            nMinCount = std::min( nMinCount, i );
            continue;
        }

        // Paint it now or in the second pass?
        bool bPaint = true;
        if ( rLRect.GetTab() )
        {
            if ( rLRect.Height() > rLRect.Width() )
            {
                // Vertical edge, overlapping with the table edge?
                SwTwips nLLeft  = rLRect.Left()  - 30,
                        nLRight = rLRect.Right() + 30,
                        nTLeft  = rLRect.GetTab()->getFrameArea().Left() + rLRect.GetTab()->getFramePrintArea().Left(),
                        nTRight = rLRect.GetTab()->getFrameArea().Left() + rLRect.GetTab()->getFramePrintArea().Right();
                if ( (nTLeft >= nLLeft && nTLeft <= nLRight) ||
                     (nTRight>= nLLeft && nTRight<= nLRight) )
                    bPaint = false;
            }
            else
            {
                // Horizontal edge, overlapping with the table edge?
                SwTwips nLTop    = rLRect.Top()    - 30,
                        nLBottom = rLRect.Bottom() + 30,
                        nTTop    = rLRect.GetTab()->getFrameArea().Top()  + rLRect.GetTab()->getFramePrintArea().Top(),
                        nTBottom = rLRect.GetTab()->getFrameArea().Top()  + rLRect.GetTab()->getFramePrintArea().Bottom();
                if ( (nTTop    >= nLTop && nTTop      <= nLBottom) ||
                     (nTBottom >= nLTop && nTBottom <= nLBottom) )
                    bPaint = false;
            }
        }
        if ( bPaint )
        {
            if ( !pLast || *pLast != rLRect.GetColor() )
            {
                pLast = &rLRect.GetColor();

                DrawModeFlags nOldDrawMode = pOut->GetDrawMode();
                if( properties.pSGlobalShell->GetWin() &&
                    Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
                    pOut->SetDrawMode( DrawModeFlags::Default );

                pOut->SetLineColor( *pLast );
                pOut->SetFillColor( *pLast );
                pOut->SetDrawMode( nOldDrawMode );
            }

            if( !rLRect.IsEmpty() )
                lcl_DrawDashedRect( pOut, rLRect );
            rLRect.SetPainted();
        }
        else
            bPaint2nd = true;
    }
    if ( bPaint2nd )
    {
        for ( size_t i = 0; i < aLineRects.size(); ++i )
        {
            SwLineRect &rLRect = aLineRects[i];
            if ( rLRect.IsPainted() )
                continue;

            if ( rLRect.IsLocked() )
            {
                nMinCount = std::min( nMinCount, i );
                continue;
            }

            if ( !pLast || *pLast != rLRect.GetColor() )
            {
                pLast = &rLRect.GetColor();

                DrawModeFlags nOldDrawMode = pOut->GetDrawMode();
                if( properties.pSGlobalShell->GetWin() &&
                    Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
                {
                    pOut->SetDrawMode( DrawModeFlags::Default );
                }

                pOut->SetFillColor( *pLast );
                pOut->SetDrawMode( nOldDrawMode );
            }
            if( !rLRect.IsEmpty() )
                lcl_DrawDashedRect( pOut, rLRect );
            rLRect.SetPainted();
        }
    }
    nLastCount = nMinCount;
    pOut->Pop();

}

void SwSubsRects::PaintSubsidiary( OutputDevice *pOut,
                                   const SwLineRects *pRects,
                                   SwPaintProperties const & properties )
{
    if ( !aLineRects.empty() )
    {
        // #i16816# tagged pdf support
        SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, nullptr, *pOut );

        // Remove all help line that are almost covered (tables)
        for (size_type i = 0; i != aLineRects.size(); ++i)
        {
            SwLineRect &rLi = aLineRects[i];
            const bool bVerticalSubs = rLi.Height() > rLi.Width();

            for (size_type k = i + 1; k != aLineRects.size(); ++k)
            {
                SwLineRect &rLk = aLineRects[k];
                if ( rLi.SSize() == rLk.SSize() )
                {
                    if ( bVerticalSubs == ( rLk.Height() > rLk.Width() ) )
                    {
                        if ( bVerticalSubs )
                        {
                            long nLi = rLi.Right();
                            long nLk = rLk.Right();
                            if ( rLi.Top() == rLk.Top() &&
                                 ((nLi < rLk.Left() && nLi+21 > rLk.Left()) ||
                                  (nLk < rLi.Left() && nLk+21 > rLi.Left())))
                            {
                                aLineRects.erase(aLineRects.begin() + k);
                                // don't continue with inner loop any more:
                                // the array may shrink!
                                --i;
                                break;
                            }
                        }
                        else
                        {
                            long nLi = rLi.Bottom();
                            long nLk = rLk.Bottom();
                            if ( rLi.Left() == rLk.Left() &&
                                 ((nLi < rLk.Top() && nLi+21 > rLk.Top()) ||
                                  (nLk < rLi.Top() && nLk+21 > rLi.Top())))
                            {
                                aLineRects.erase(aLineRects.begin() + k);
                                // don't continue with inner loop any more:
                                // the array may shrink!
                                --i;
                                break;
                            }
                        }
                    }
                }
            }
        }

        if ( pRects && (!pRects->aLineRects.empty()) )
            RemoveSuperfluousSubsidiaryLines( *pRects, properties );

        if ( !aLineRects.empty() )
        {
            pOut->Push( PushFlags::FILLCOLOR|PushFlags::LINECOLOR );
            pOut->SetLineColor();

            // Reset draw mode in high contrast mode in order to get fill color
            // set at output device. Recover draw mode after draw of lines.
            // Necessary for the subsidiary lines painted by the fly frames.
            DrawModeFlags nOldDrawMode = pOut->GetDrawMode();
            if( gProp.pSGlobalShell->GetWin() &&
                Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
            {
                pOut->SetDrawMode( DrawModeFlags::Default );
            }

            for (SwSubsRects::iterator it = aLineRects.begin(); it != aLineRects.end();
                 ++it)
            {
                SwLineRect &rLRect = (*it);
                // Add condition <!rLRect.IsLocked()> to prevent paint of locked subsidiary lines.
                if ( !rLRect.IsPainted() &&
                     !rLRect.IsLocked() )
                {
                    const Color *pCol = nullptr;
                    switch ( rLRect.GetSubColor() )
                    {
                        case SubColFlags::Page: pCol = &SwViewOption::GetDocBoundariesColor(); break;
                        case SubColFlags::Fly: pCol = &SwViewOption::GetObjectBoundariesColor(); break;
                        case SubColFlags::Tab: pCol = &SwViewOption::GetTableBoundariesColor(); break;
                        case SubColFlags::Sect: pCol = &SwViewOption::GetSectionBoundColor(); break;
                    }

                    if (pCol && pOut->GetFillColor() != *pCol)
                        pOut->SetFillColor( *pCol );
                    pOut->DrawRect( rLRect.SVRect() );

                    rLRect.SetPainted();
                }
            }

            pOut->SetDrawMode( nOldDrawMode );

            pOut->Pop();
        }
    }
}

// Various functions that are use in this file.

/**
 * Function <SwAlignRect(..)> is also used outside this file
 *
 * Correction: adjust rectangle on pixel level in order to make sure,
 * that the border "leaves its original pixel", if it has to
 * No prior adjustments for odd relation between pixel and twip
 */
void SwAlignRect( SwRect &rRect, const SwViewShell *pSh, const vcl::RenderContext* pRenderContext )
{
    if( !rRect.HasArea() )
        return;

    // Make sure that view shell (parameter <pSh>) exists, if the output device
    // is taken from this view shell --> no output device, no alignment
    // Output device taken from view shell <pSh>, if <gProp.bSFlyMetafile> not set
    if ( !gProp.bSFlyMetafile && !pSh )
    {
        return;
    }

    const vcl::RenderContext *pOut = gProp.bSFlyMetafile ?
                        gProp.pSFlyMetafileOut.get() : pRenderContext;

    // Hold original rectangle in pixel
    const tools::Rectangle aOrgPxRect = pOut->LogicToPixel( rRect.SVRect() );
    // Determine pixel-center rectangle in twip
    const SwRect aPxCenterRect( pOut->PixelToLogic( aOrgPxRect ) );

    // Perform adjustments on pixel level.
    SwRect aAlignedPxRect( aOrgPxRect );
    if ( rRect.Top() > aPxCenterRect.Top() )
    {
        // 'leave pixel overlapping on top'
        aAlignedPxRect.Top( aAlignedPxRect.Top() + 1 );
    }

    if ( rRect.Bottom() < aPxCenterRect.Bottom() )
    {
        // 'leave pixel overlapping on bottom'
        aAlignedPxRect.Bottom( aAlignedPxRect.Bottom() - 1 );
    }

    if ( rRect.Left() > aPxCenterRect.Left() )
    {
        // 'leave pixel overlapping on left'
        aAlignedPxRect.Left( aAlignedPxRect.Left() + 1 );
    }

    if ( rRect.Right() < aPxCenterRect.Right() )
    {
        // 'leave pixel overlapping on right'
        aAlignedPxRect.Right( aAlignedPxRect.Right() - 1 );
    }

    // Consider negative width/height check, if aligned SwRect has negative width/height.
    // If Yes, adjust it to width/height = 0 twip.
    // NOTE: A SwRect with negative width/height can occur, if the width/height
    //     of the given SwRect in twip was less than a pixel in twip and that
    //     the alignment calculates that the aligned SwRect should not contain
    //     the pixels the width/height is on.
    if ( aAlignedPxRect.Width() < 0 )
    {
        aAlignedPxRect.Width(0);
    }
    if ( aAlignedPxRect.Height() < 0 )
    {
        aAlignedPxRect.Height(0);
    }
    // Consider zero width/height for converting a rectangle from
    // pixel to logic it needs a width/height. Thus, set width/height
    // to one, if it's zero and correct this on the twip level after the conversion.
    bool bZeroWidth = false;
    if ( aAlignedPxRect.Width() == 0 )
    {
        aAlignedPxRect.Width(1);
        bZeroWidth = true;
    }
    bool bZeroHeight = false;
    if ( aAlignedPxRect.Height() == 0 )
    {
        aAlignedPxRect.Height(1);
        bZeroHeight = true;
    }

    rRect = pOut->PixelToLogic( aAlignedPxRect.SVRect() );

    // Consider zero width/height and adjust calculated aligned twip rectangle.
    // Reset width/height to zero; previous negative width/height haven't to be considered.
    if ( bZeroWidth )
    {
        rRect.Width(0);
    }
    if ( bZeroHeight )
    {
        rRect.Height(0);
    }
}

/**
 * Method to pixel-align rectangle for drawing graphic object
 *
 * Because we are drawing graphics from the left-top-corner in conjunction
 * with size coordinates, these coordinates have to be calculated at a pixel
 * level.
 * Thus, we convert the rectangle to pixel and then convert to left-top-corner
 * and then get size of pixel rectangle back to logic.
 * This calculation is necessary, because there's a different between
 * the conversion from logic to pixel of a normal rectangle with its left-top-
 * and right-bottom-corner and the same conversion of the same rectangle
 * with left-top-corner and size.
 *
 * NOTE: Call this method before each <GraphicObject.Draw(...)>
*/
void SwAlignGrfRect( SwRect *pGrfRect, const vcl::RenderContext &rOut )
{
    tools::Rectangle aPxRect = rOut.LogicToPixel( pGrfRect->SVRect() );
    pGrfRect->Pos( rOut.PixelToLogic( aPxRect.TopLeft() ) );
    pGrfRect->SSize( rOut.PixelToLogic( aPxRect.GetSize() ) );
}

static long lcl_AlignWidth( const long nWidth, SwPaintProperties const & properties )
{
    if ( nWidth )
    {
        const long nW = nWidth % properties.nSPixelSzW;

        if ( !nW || nW > properties.nSHalfPixelSzW )
            return std::max(1L, nWidth - properties.nSHalfPixelSzW);
    }
    return nWidth;
}

static long lcl_AlignHeight( const long nHeight, SwPaintProperties const & properties )
{
    if ( nHeight )
    {
        const long nH = nHeight % properties.nSPixelSzH;

        if ( !nH || nH > properties.nSHalfPixelSzH )
            return std::max(1L, nHeight - properties.nSHalfPixelSzH);
    }
    return nHeight;
}

/**
 * Calculate PrtArea plus surrounding plus shadow
 */
static void lcl_CalcBorderRect( SwRect &rRect, const SwFrame *pFrame,
                                        const SwBorderAttrs &rAttrs,
                                        const bool bShadow,
                                        SwPaintProperties const & properties)
{
    // Special handling for cell frames.
    // The printing area of a cell frame is completely enclosed in the frame area
    // and a cell frame has no shadow. Thus, for cell frames the calculated
    // area equals the frame area.
    // Notes: Borders of cell frames in R2L text direction will switch its side
    //        - left border is painted on the right; right border on the left.
    //        See <lcl_PaintLeftLine> and <lcl_PaintRightLine>.
    if( pFrame->IsSctFrame() )
    {
        rRect = pFrame->getFramePrintArea();
        rRect.Pos() += pFrame->getFrameArea().Pos();
    }
    else if ( pFrame->IsCellFrame() )
        rRect = pFrame->getFrameArea();
    else
    {
        rRect = pFrame->getFramePrintArea();
        rRect.Pos() += pFrame->getFrameArea().Pos();

        if ( rAttrs.IsLine() || rAttrs.IsBorderDist() ||
             (bShadow && rAttrs.GetShadow().GetLocation() != SvxShadowLocation::NONE) )
        {
            SwRectFn fnRect = pFrame->IsVertical() ? ( pFrame->IsVertLR() ? fnRectVertL2R : fnRectVert ) : fnRectHori;

            const SvxBoxItem &rBox = rAttrs.GetBox();
            const bool bTop = 0 != (pFrame->*fnRect->fnGetTopMargin)();
            if ( bTop )
            {
                SwTwips nDiff = rBox.GetTop() ?
                    rBox.CalcLineSpace( SvxBoxItemLine::TOP ) :
                    ( rAttrs.IsBorderDist() ?
                      // Increase of distance by one twip is incorrect.
                      rBox.GetDistance( SvxBoxItemLine::TOP ) : 0 );
                if( nDiff )
                    (rRect.*fnRect->fnSubTop)( nDiff );
            }

            const bool bBottom = 0 != (pFrame->*fnRect->fnGetBottomMargin)();
            if ( bBottom )
            {
                SwTwips nDiff = 0;
                // #i29550#
                if ( pFrame->IsTabFrame() &&
                     static_cast<const SwTabFrame*>(pFrame)->IsCollapsingBorders() )
                {
                    // For collapsing borders, we have to add the height of
                    // the height of the last line
                    nDiff = static_cast<const SwTabFrame*>(pFrame)->GetBottomLineSize();
                }
                else
                {
                    nDiff = rBox.GetBottom() ?
                    rBox.CalcLineSpace( SvxBoxItemLine::BOTTOM ) :
                    ( rAttrs.IsBorderDist() ?
                      // Increase of distance by one twip is incorrect.
                      rBox.GetDistance( SvxBoxItemLine::BOTTOM ) : 0 );
                }
                if( nDiff )
                    (rRect.*fnRect->fnAddBottom)( nDiff );
            }

            if ( rBox.GetLeft() )
                (rRect.*fnRect->fnSubLeft)( rBox.CalcLineSpace( SvxBoxItemLine::LEFT ) );
            else if ( rAttrs.IsBorderDist() )
                 // Increase of distance by one twip is incorrect.
                (rRect.*fnRect->fnSubLeft)( rBox.GetDistance( SvxBoxItemLine::LEFT ) );

            if ( rBox.GetRight() )
                (rRect.*fnRect->fnAddRight)( rBox.CalcLineSpace( SvxBoxItemLine::RIGHT ) );
            else if ( rAttrs.IsBorderDist() )
                 // Increase of distance by one twip is incorrect.
                (rRect.*fnRect->fnAddRight)( rBox.GetDistance( SvxBoxItemLine::RIGHT ) );

            if ( bShadow && rAttrs.GetShadow().GetLocation() != SvxShadowLocation::NONE )
            {
                const SvxShadowItem &rShadow = rAttrs.GetShadow();
                if ( bTop )
                    (rRect.*fnRect->fnSubTop)(rShadow.CalcShadowSpace(SvxShadowItemSide::TOP));
                (rRect.*fnRect->fnSubLeft)(rShadow.CalcShadowSpace(SvxShadowItemSide::LEFT));
                if ( bBottom )
                    (rRect.*fnRect->fnAddBottom)
                                    (rShadow.CalcShadowSpace( SvxShadowItemSide::BOTTOM ));
                (rRect.*fnRect->fnAddRight)(rShadow.CalcShadowSpace(SvxShadowItemSide::RIGHT));
            }
        }
    }

    ::SwAlignRect( rRect, properties.pSGlobalShell, properties.pSGlobalShell ? properties.pSGlobalShell->GetOut() : nullptr );
}

/**
 * Extend left/right border/shadow rectangle to bottom of previous frame/to
 * top of next frame, if border/shadow is joined with previous/next frame
 */
static void lcl_ExtendLeftAndRight( SwRect&                _rRect,
                                         const SwFrame&           _rFrame,
                                         const SwBorderAttrs&   _rAttrs,
                                         const SwRectFn&        _rRectFn )
{
    if ( _rAttrs.JoinedWithPrev( _rFrame ) )
    {
        const SwFrame* pPrevFrame = _rFrame.GetPrev();
        (_rRect.*_rRectFn->fnSetTop)( (pPrevFrame->*_rRectFn->fnGetPrtBottom)() );
    }
    if ( _rAttrs.JoinedWithNext( _rFrame ) )
    {
        const SwFrame* pNextFrame = _rFrame.GetNext();
        (_rRect.*_rRectFn->fnSetBottom)( (pNextFrame->*_rRectFn->fnGetPrtTop)() );
    }
}

/// Returns a range suitable for subtraction when lcl_SubtractFlys() is used.
/// Otherwise DrawFillAttributes() expands the clip path itself.
static basegfx::B2DRange lcl_ShrinkFly(const SwRect& rRect)
{
    static MapMode aMapMode(MapUnit::MapTwip);
    static const Size aSingleUnit = Application::GetDefaultDevice()->PixelToLogic(Size(1, 1), aMapMode);

    double x1 = rRect.Left() + aSingleUnit.getWidth();
    double y1 = rRect.Top() + aSingleUnit.getHeight();
    double x2 = rRect.Right() - aSingleUnit.getWidth();
    double y2 = rRect.Bottom() - aSingleUnit.getHeight();

    return basegfx::B2DRange(x1, y1, x2, y2);
}

static void lcl_SubtractFlys( const SwFrame *pFrame, const SwPageFrame *pPage,
   const SwRect &rRect, SwRegionRects &rRegion, basegfx::utils::B2DClipState& rClipState, SwPaintProperties const & rProperties)
{
    const SwSortedObjs& rObjs = *pPage->GetSortedObjs();
    const SwFlyFrame* pSelfFly = pFrame->IsInFly() ? pFrame->FindFlyFrame() : gProp.pSRetoucheFly2;
    if (!gProp.pSRetoucheFly)
        gProp.pSRetoucheFly = gProp.pSRetoucheFly2;

    for (size_t j = 0; (j < rObjs.size()) && !rRegion.empty(); ++j)
    {
        const SwAnchoredObject* pAnchoredObj = rObjs[j];
        const SdrObject* pSdrObj = pAnchoredObj->GetDrawObj();

        // Do not consider invisible objects
        if (!pPage->GetFormat()->GetDoc()->getIDocumentDrawModelAccess().IsVisibleLayerId(pSdrObj->GetLayer()))
            continue;

        if (dynamic_cast< const SwFlyFrame *>( pAnchoredObj ) ==  nullptr)
            continue;

        const SwFlyFrame *pFly = static_cast<const SwFlyFrame*>(pAnchoredObj);

        if (pSelfFly == pFly || gProp.pSRetoucheFly == pFly || !rRect.IsOver(pFly->getFrameArea()))
            continue;

        if (!pFly->GetFormat()->GetPrint().GetValue() &&
                (OUTDEV_PRINTER == gProp.pSGlobalShell->GetOut()->GetOutDevType() ||
                gProp.pSGlobalShell->IsPreview()))
            continue;

        const bool bLowerOfSelf = pSelfFly && pFly->IsLowerOf( pSelfFly );

        //For character bound Flys only examine those Flys in which it is not
        //anchored itself.
        //Why only for character bound ones you may ask? It never makes sense to
        //subtract frames in which it is anchored itself right?
        if (pSelfFly && pSelfFly->IsLowerOf(pFly))
            continue;

        //Any why does it not apply for the RetoucheFly too?
        if (gProp.pSRetoucheFly && gProp.pSRetoucheFly->IsLowerOf(pFly))
            continue;

#if OSL_DEBUG_LEVEL > 0
        //Flys who are anchored inside their own one, must have a bigger OrdNum
        //or be character bound.
        if (pSelfFly && bLowerOfSelf)
        {
            OSL_ENSURE( pFly->IsFlyInContentFrame() ||
                    pSdrObj->GetOrdNumDirect() > pSelfFly->GetVirtDrawObj()->GetOrdNumDirect(),
                    "Fly with wrong z-Order" );
        }
#endif

        bool bStopOnHell = true;
        if (pSelfFly)
        {
            const SdrObject *pTmp = pSelfFly->GetVirtDrawObj();
            if (pSdrObj->GetLayer() == pTmp->GetLayer())
            {
                if (pSdrObj->GetOrdNumDirect() < pTmp->GetOrdNumDirect())
                    //In the same layer we only observe those that are above.
                    continue;
            }
            else
            {
                if (!bLowerOfSelf && !pFly->GetFormat()->GetOpaque().GetValue())
                    //From other layers we are only interested in non
                    //transparent ones or those that are internal
                    continue;
                bStopOnHell = false;
            }
        }
        if (gProp.pSRetoucheFly)
        {
            const SdrObject *pTmp = gProp.pSRetoucheFly->GetVirtDrawObj();
            if ( pSdrObj->GetLayer() == pTmp->GetLayer() )
            {
                if ( pSdrObj->GetOrdNumDirect() < pTmp->GetOrdNumDirect() )
                    //In the same layer we only observe those that are above.
                    continue;
            }
            else
            {
                if (!pFly->IsLowerOf( gProp.pSRetoucheFly ) && !pFly->GetFormat()->GetOpaque().GetValue())
                    //From other layers we are only interested in non
                    //transparent ones or those that are internal
                    continue;
                bStopOnHell = false;
            }
        }

        //If the content of the Fly is transparent, we subtract it only if it's
        //contained in the hell layer.
        const IDocumentDrawModelAccess& rIDDMA = pFly->GetFormat()->getIDocumentDrawModelAccess();
        bool bHell = pSdrObj->GetLayer() == rIDDMA.GetHellId();
        if ( (bStopOnHell && bHell) ||
             /// Change internal order of condition
             ///    first check "!bHell", then "..->Lower()" and "..->IsNoTextFrame()"
             ///    have not to be performed, if frame is in "Hell"
             ( !bHell && pFly->Lower() && pFly->Lower()->IsNoTextFrame() &&
               (static_cast<SwNoTextFrame const*>(pFly->Lower())->IsTransparent() ||
                static_cast<SwNoTextFrame const*>(pFly->Lower())->HasAnimation() ||
                 pFly->GetFormat()->GetSurround().IsContour()
               )
             )
           )
            continue;

        // Own if-statements for transparent background/shadow of fly frames
        // in order to handle special conditions.
        if (pFly->IsBackgroundTransparent())
        {
            // Background <pFly> is transparent drawn. Thus normally, its region
            // have not to be subtracted from given region.
            // But, if method is called for a fly frame and
            // <pFly> is a direct lower of this fly frame and
            // <pFly> inherites its transparent background brush from its parent,
            // then <pFly> frame area have to be subtracted from given region.
            // NOTE: Because in Status Quo transparent backgrounds can only be
            //     assigned to fly frames, the handle of this special case
            //     avoids drawing of transparent areas more than once, if
            //     a fly frame inherites a transparent background from its
            //     parent fly frame.
            if (pFrame->IsFlyFrame() &&
                (pFly->GetAnchorFrame()->FindFlyFrame() == pFrame) &&
                pFly->GetFormat()->IsBackgroundBrushInherited()
               )
            {
                SwRect aRect;
                SwBorderAttrAccess aAccess( SwFrame::GetCache(), static_cast<SwFrame const *>(pFly) );
                const SwBorderAttrs &rAttrs = *aAccess.Get();
                ::lcl_CalcBorderRect( aRect, pFly, rAttrs, true, rProperties );
                rRegion -= aRect;
                rClipState.subtractRange(lcl_ShrinkFly(aRect));
                continue;
            }
            else
            {
                continue;
            }
        }

        if (bHell && pFly->GetAnchorFrame()->IsInFly())
        {
            //So the border won't get dismantled by the background of the other
            //Fly.
            SwRect aRect;
            SwBorderAttrAccess aAccess( SwFrame::GetCache(), static_cast<SwFrame const *>(pFly) );
            const SwBorderAttrs &rAttrs = *aAccess.Get();
            ::lcl_CalcBorderRect( aRect, pFly, rAttrs, true, rProperties );
            rRegion -= aRect;
            rClipState.subtractRange(lcl_ShrinkFly(aRect));
        }
        else
        {
            SwRect aRect( pFly->getFramePrintArea() );
            aRect += pFly->getFrameArea().Pos();
            rRegion -= aRect;
            rClipState.subtractRange(lcl_ShrinkFly(aRect));
        }
    }
    if (gProp.pSRetoucheFly == gProp.pSRetoucheFly2)
        gProp.pSRetoucheFly = nullptr;
}

static void lcl_implDrawGraphicBackgrd( const SvxBrushItem& _rBackgrdBrush,
                                 vcl::RenderContext* _pOut,
                                 const SwRect& _rAlignedPaintRect,
                                 const GraphicObject& _rGraphicObj,
                                 SwPaintProperties const & properties)
{
    /// determine color of background
    ///     If color of background brush is not "no fill"/"auto fill" or
    ///     <SwPaintProperties.bSFlyMetafile> is set, use color of background brush, otherwise
    ///     use global retouche color.
    const Color aColor( ( (_rBackgrdBrush.GetColor() != COL_TRANSPARENT) || properties.bSFlyMetafile )
                        ? _rBackgrdBrush.GetColor()
                        : aGlobalRetoucheColor );

    /// determine, if background color have to be drawn transparent
    /// and calculate transparency percent value
    sal_Int8 nTransparencyPercent = 0;
    bool bDrawTransparent = false;
    if ( aColor.GetTransparency() != 0 )
    ///     background color is transparent --> draw transparent.
    {
        bDrawTransparent = true;
        nTransparencyPercent = (aColor.GetTransparency()*100 + 0x7F)/0xFF;
    }
    else if ( (_rGraphicObj.GetAttr().GetTransparency() != 0) &&
                (_rBackgrdBrush.GetColor() == COL_TRANSPARENT) )
    ///     graphic is drawn transparent and background color is
    ///     "no fill"/"auto fill" --> draw transparent
    {
        bDrawTransparent = true;
        nTransparencyPercent = (_rGraphicObj.GetAttr().GetTransparency()*100 + 0x7F)/0xFF;
    }

    if ( bDrawTransparent )
    {
        /// draw background transparent
        if( _pOut->GetFillColor() != aColor.GetRGBColor() )
            _pOut->SetFillColor( aColor.GetRGBColor() );
        tools::PolyPolygon aPoly( _rAlignedPaintRect.SVRect() );
        _pOut->DrawTransparent( aPoly, nTransparencyPercent );
    }
    else
    {
        /// draw background opaque
        if ( _pOut->GetFillColor() != aColor )
            _pOut->SetFillColor( aColor );
        _pOut->DrawRect( _rAlignedPaintRect.SVRect() );
    }
}

/**
 * This is a local help method to draw a background for a graphic
 *
 * Under certain circumstances we have to draw a background for a graphic.
 * This method takes care of the conditions and draws the background with the
 * corresponding color.
 * Method introduced for bug fix #103876# in order to optimize drawing tiled
 * background graphics. Previously, this code was integrated in method
 * <lcl_DrawGraphic>.
 * Method implemented as a inline, checking the conditions and calling method
 * method <lcl_implDrawGraphicBackgrd(..)> for the intrinsic drawing.
 *
 * @param _rBackgrdBrush
 * background brush contain the color the background has to be drawn.
 *
 * @param _pOut
 * output device the background has to be drawn in.
 *
 * @param _rAlignedPaintRect
 * paint rectangle in the output device, which has to be drawn with the background.
 * rectangle have to be aligned by method ::SwAlignRect
 *
 * @param _rGraphicObj
 * graphic object, for which the background has to be drawn. Used for checking
 * the transparency of its bitmap, its type and if the graphic is drawn transparent
 *
 * @param _bNumberingGraphic
 * boolean indicating that graphic is used as a numbering.
 *
 * @param _bBackgrdAlreadyDrawn
 * boolean (optional; default: false) indicating, if the background is already drawn.
*/
static inline void lcl_DrawGraphicBackgrd( const SvxBrushItem& _rBackgrdBrush,
                                    OutputDevice* _pOut,
                                    const SwRect& _rAlignedPaintRect,
                                    const GraphicObject& _rGraphicObj,
                                    bool _bNumberingGraphic,
                                    SwPaintProperties const & properties,
                                    bool _bBackgrdAlreadyDrawn = false)
{
    // draw background with background color, if
    //     (1) graphic is not used as a numbering AND
    //     (2) background is not already drawn AND
    //     (3) intrinsic graphic is transparent OR intrinsic graphic doesn't exists
    if ( !_bNumberingGraphic &&
         !_bBackgrdAlreadyDrawn &&
         ( _rGraphicObj.IsTransparent() || _rGraphicObj.GetType() == GraphicType::NONE  )
       )
    {
        lcl_implDrawGraphicBackgrd( _rBackgrdBrush, _pOut, _rAlignedPaintRect, _rGraphicObj, properties );
    }
}

/**
 * NNOTE: the transparency of the background graphic is saved in
 * SvxBrushItem.GetGraphicObject(<shell>).GetAttr().Set/GetTransparency()
 * and is considered in the drawing of the graphic
 *
 * Thus, to provide transparent background graphic for text frames nothing
 * has to be coded
 *
 * Use align rectangle for drawing graphic Pixel-align coordinates for
 * drawing graphic
 * Outsource code for drawing background of the graphic
 * with a background color in method <lcl_DrawGraphicBackgrd>
 *
 * Also, change type of <bGrfNum> and <bClip> from <bool> to <bool>
 */
static void lcl_DrawGraphic( const SvxBrushItem& rBrush, vcl::RenderContext *pOut,
                      SwViewShell &rSh, const SwRect &rGrf, const SwRect &rOut,
                      bool bGrfNum,
                      SwPaintProperties const & properties,
                      bool bBackgrdAlreadyDrawn )
                      // add parameter <bBackgrdAlreadyDrawn> to indicate
                      // that the background is already drawn.
{
    // Calculate align rectangle from parameter <rGrf> and use aligned
    // rectangle <aAlignedGrfRect> in the following code
    SwRect aAlignedGrfRect = rGrf;
    ::SwAlignRect( aAlignedGrfRect, &rSh, pOut );

    // Change type from <bool> to <bool>.
    const bool bNotInside = !rOut.IsInside( aAlignedGrfRect );
    if ( bNotInside )
    {
        pOut->Push( PushFlags::CLIPREGION );
        pOut->IntersectClipRegion( rOut.SVRect() );
    }

    GraphicObject *pGrf = const_cast<GraphicObject*>(rBrush.GetGraphicObject());

    // Outsource drawing of background with a background color
    ::lcl_DrawGraphicBackgrd( rBrush, pOut, aAlignedGrfRect, *pGrf, bGrfNum, properties, bBackgrdAlreadyDrawn );

    // Because for drawing a graphic left-top-corner and size coordinates are
    // used, these coordinates have to be determined on pixel level.
    ::SwAlignGrfRect( &aAlignedGrfRect, *pOut );

    const basegfx::B2DHomMatrix aGraphicTransform(
        basegfx::utils::createScaleTranslateB2DHomMatrix(
            aAlignedGrfRect.Width(), aAlignedGrfRect.Height(),
            aAlignedGrfRect.Left(), aAlignedGrfRect.Top()));

    paintGraphicUsingPrimitivesHelper(
        *pOut,
        *pGrf,
        pGrf->GetAttr(),
        aGraphicTransform);

    if ( bNotInside )
        pOut->Pop();
}

bool DrawFillAttributes(
    const drawinglayer::attribute::SdrAllFillAttributesHelperPtr& rFillAttributes,
    const SwRect& rOriginalLayoutRect,
    const SwRegionRects& rPaintRegion,
    const basegfx::utils::B2DClipState& rClipState,
    vcl::RenderContext& rOut)
{
    if(rFillAttributes.get() && rFillAttributes->isUsed())
    {
        basegfx::B2DRange aPaintRange(
            rPaintRegion.GetOrigin().Left(),
            rPaintRegion.GetOrigin().Top(),
            rPaintRegion.GetOrigin().Right(),
            rPaintRegion.GetOrigin().Bottom());

        if (!aPaintRange.isEmpty() &&
            !rPaintRegion.empty() &&
            !basegfx::fTools::equalZero(aPaintRange.getWidth()) &&
            !basegfx::fTools::equalZero(aPaintRange.getHeight()))
        {
            const SvtOptionsDrawinglayer aSvtOptionsDrawinglayer;

            // need to expand for correct AAed and non-AAed visualization as primitive.
            // This must probably be removed again when we will be able to get all Writer visualization
            // as primitives and Writer prepares all it's stuff in high precision coordinates (also
            // needs to avoid moving boundaries around to better show overlapping stuff...)
            if(aSvtOptionsDrawinglayer.IsAntiAliasing())
            {
                // if AAed in principle expand by 0.5 in all directions. Since painting edges of
                // AAed regions does not add to no transparence (0.5 opacity covered by 0.5 opacity
                // is not full opacity but 0.75 opacity) we need some overlap here to avoid paint
                // artifacts. Checked experimentally - a little bit more in Y is needed, probably
                // due to still existing integer alignment and crunching in writer.
                static double fExpandX = 0.55;
                static double fExpandY = 0.70;
                const basegfx::B2DVector aSingleUnit(rOut.GetInverseViewTransformation() * basegfx::B2DVector(fExpandX, fExpandY));

                aPaintRange.expand(aPaintRange.getMinimum() - aSingleUnit);
                aPaintRange.expand(aPaintRange.getMaximum() + aSingleUnit);
            }
            else
            {
                // if not AAed expand by one unit to bottom right due to the missing unit
                // from SwRect/Rectangle integer handling
                const basegfx::B2DVector aSingleUnit(rOut.GetInverseViewTransformation() * basegfx::B2DVector(1.0, 1.0));

                aPaintRange.expand(aPaintRange.getMaximum() + aSingleUnit);
            }

            const basegfx::B2DRange aDefineRange(
                rOriginalLayoutRect.Left(),
                rOriginalLayoutRect.Top(),
                rOriginalLayoutRect.Right(),
                rOriginalLayoutRect.Bottom());

            const drawinglayer::primitive2d::Primitive2DContainer& rSequence = rFillAttributes->getPrimitive2DSequence(
                aPaintRange,
                aDefineRange);

            if(rSequence.size())
            {
                drawinglayer::primitive2d::Primitive2DContainer const*
                    pPrimitives(&rSequence);
                drawinglayer::primitive2d::Primitive2DContainer primitives;
                // tdf#86578 the awful lcl_SubtractFlys hack
                if (rPaintRegion.size() > 1 || rPaintRegion[0] != rPaintRegion.GetOrigin())
                {
                    basegfx::B2DPolyPolygon const maskRegion(rClipState.getClipPoly());
                    primitives.resize(1);
                    primitives[0] = new drawinglayer::primitive2d::MaskPrimitive2D(
                            maskRegion, rSequence);
                    pPrimitives = &primitives;
                }
                assert(pPrimitives && pPrimitives->size());

                const drawinglayer::geometry::ViewInformation2D aViewInformation2D(
                    basegfx::B2DHomMatrix(),
                    rOut.GetViewTransformation(),
                    aPaintRange,
                    nullptr,
                    0.0,
                    uno::Sequence< beans::PropertyValue >());
                drawinglayer::processor2d::BaseProcessor2D* pProcessor = drawinglayer::processor2d::createProcessor2DFromOutputDevice(
                    rOut,
                    aViewInformation2D);

                if(pProcessor)
                {
                    pProcessor->process(*pPrimitives);

                    delete pProcessor;

                    return true;
                }
            }
        }
    }

    return false;
}

void DrawGraphic(
    const SvxBrushItem *pBrush,
    vcl::RenderContext *pOutDev,
    const SwRect &rOrg,
    const SwRect &rOut,
    const sal_uInt8 nGrfNum,
    const bool bConsiderBackgroundTransparency )
    // Add 6th parameter to indicate that method should
    // consider background transparency, saved in the color of the brush item
{
    SwViewShell &rSh = *gProp.pSGlobalShell;
    bool bReplaceGrfNum = GRFNUM_REPLACE == nGrfNum;
    bool bGrfNum = GRFNUM_NO != nGrfNum;
    Size aGrfSize;
    SvxGraphicPosition ePos = GPOS_NONE;
    if( pBrush && !bReplaceGrfNum )
    {
        if( rSh.GetViewOptions()->IsGraphic() )
        {
            OUString referer;
            SfxObjectShell * sh = rSh.GetDoc()->GetPersist();
            if (sh != nullptr && sh->HasName()) {
                referer = sh->GetMedium()->GetName();
            }
            const Graphic* pGrf = pBrush->GetGraphic(referer);
            if( pGrf && GraphicType::NONE != pGrf->GetType() )
            {
                ePos = pBrush->GetGraphicPos();
                if( pGrf->IsSupportedGraphic() )
                    // don't the use the specific output device! Bug 94802
                    aGrfSize = ::GetGraphicSizeTwip( *pGrf, nullptr );
            }
        }
        else
            bReplaceGrfNum = bGrfNum;
    }

    SwRect aGrf;
    aGrf.SSize( aGrfSize );
    bool bDraw = true;
    bool bRetouche = true;
    switch ( ePos )
    {
    case GPOS_LT:
        aGrf.Pos() = rOrg.Pos();
        break;

    case GPOS_MT:
        aGrf.Pos().setY( rOrg.Top() );
        aGrf.Pos().setX( rOrg.Left() + rOrg.Width()/2 - aGrfSize.Width()/2 );
        break;

    case GPOS_RT:
        aGrf.Pos().setY( rOrg.Top() );
        aGrf.Pos().setX( rOrg.Right() - aGrfSize.Width() );
        break;

    case GPOS_LM:
        aGrf.Pos().setY( rOrg.Top() + rOrg.Height()/2 - aGrfSize.Height()/2 );
        aGrf.Pos().setX( rOrg.Left() );
        break;

    case GPOS_MM:
        aGrf.Pos().setY( rOrg.Top() + rOrg.Height()/2 - aGrfSize.Height()/2 );
        aGrf.Pos().setX( rOrg.Left() + rOrg.Width()/2 - aGrfSize.Width()/2 );
        break;

    case GPOS_RM:
        aGrf.Pos().setY( rOrg.Top() + rOrg.Height()/2 - aGrfSize.Height()/2 );
        aGrf.Pos().setX( rOrg.Right() - aGrfSize.Width() );
        break;

    case GPOS_LB:
        aGrf.Pos().setY( rOrg.Bottom() - aGrfSize.Height() );
        aGrf.Pos().setX( rOrg.Left() );
        break;

    case GPOS_MB:
        aGrf.Pos().setY( rOrg.Bottom() - aGrfSize.Height() );
        aGrf.Pos().setX( rOrg.Left() + rOrg.Width()/2 - aGrfSize.Width()/2 );
        break;

    case GPOS_RB:
        aGrf.Pos().setY( rOrg.Bottom() - aGrfSize.Height() );
        aGrf.Pos().setX( rOrg.Right() - aGrfSize.Width() );
        break;

    case GPOS_AREA:
        aGrf = rOrg;
        // Despite the fact that the background graphic has to fill the complete
        // area, we already checked, whether the graphic will completely fill out
        // the region the <rOut> that is to be painted. Thus, nothing has to be
        // touched again.
        // E.g. this is the case for a Fly Frame without a background
        // brush positioned on the border of the page which inherited the background
        // brush from the page.
        bRetouche = !rOut.IsInside( aGrf );
        break;

    case GPOS_TILED:
        {
            // draw background of tiled graphic before drawing tiled graphic in loop
            // determine graphic object
            GraphicObject* pGraphicObj = const_cast< GraphicObject* >(pBrush->GetGraphicObject());
            // calculate aligned paint rectangle
            SwRect aAlignedPaintRect = rOut;
            ::SwAlignRect( aAlignedPaintRect, &rSh, pOutDev );
            // draw background color for aligned paint rectangle
            lcl_DrawGraphicBackgrd( *pBrush, pOutDev, aAlignedPaintRect, *pGraphicObj, bGrfNum, gProp );

            // set left-top-corner of background graphic to left-top-corner of the
            // area, from which the background brush is determined.
            aGrf.Pos() = rOrg.Pos();
            // setup clipping at output device
            pOutDev->Push( PushFlags::CLIPREGION );
            pOutDev->IntersectClipRegion( rOut.SVRect() );
            // use new method <GraphicObject::DrawTiled(::)>
            {
                // calculate paint offset
                Point aPaintOffset( aAlignedPaintRect.Pos() - aGrf.Pos() );
                // draw background graphic tiled for aligned paint rectangle
                // #i42643#
                // For PDF export, every draw operation for bitmaps takes a
                // noticeable amount of place (~50 characters). Thus, optimize
                // between tile bitmap size and number of drawing operations here.

                //                  A_out
                // n_chars = k1 *  ---------- + k2 * A_bitmap
                //                  A_bitmap

                // minimum n_chars is obtained for (derive for  A_bitmap,
                // set to 0, take positive solution):
                //                   k1
                // A_bitmap = Sqrt( ---- A_out )
                //                   k2

                // where k1 is the number of chars per draw operation, and
                // k2 is the number of chars per bitmap pixel.
                // This is approximately 50 and 7 for current PDF writer, respectively.

                const double    k1( 50 );
                const double    k2( 7 );
                const Size      aSize( aAlignedPaintRect.SSize() );
                const double    Abitmap( k1/k2 * static_cast<double>(aSize.Width())*aSize.Height() );

                pGraphicObj->DrawTiled( pOutDev,
                                        aAlignedPaintRect.SVRect(),
                                        aGrf.SSize(),
                                        Size( aPaintOffset.X(), aPaintOffset.Y() ),
                                        GraphicManagerDrawFlags::STANDARD,
                                        std::max( 128, static_cast<int>( sqrt(sqrt( Abitmap)) + .5 ) ) );
            }
            // reset clipping at output device
            pOutDev->Pop();
            // set <bDraw> and <bRetouche> to false, indicating that background
            // graphic and background are already drawn.
            bDraw = bRetouche = false;
        }
        break;

    case GPOS_NONE:
        bDraw = false;
        break;

    default: OSL_ENSURE( !pOutDev, "new Graphic position?" );
    }

    /// init variable <bGrfBackgrdAlreadDrawn> to indicate, if background of
    /// graphic is already drawn or not.
    bool bGrfBackgrdAlreadyDrawn = false;
    if ( bRetouche )
    {
        pOutDev->Push( PushFlags::FILLCOLOR|PushFlags::LINECOLOR );
        pOutDev->SetLineColor();

        // check, if a existing background graphic (not filling the complete
        // background) is transparent drawn and the background color is
        // "no fill" respectively "auto fill", if background transparency
        // has to be considered.
        // If YES, memorize transparency of background graphic.
        // check also, if background graphic bitmap is transparent.
        bool bTransparentGrfWithNoFillBackgrd = false;
        sal_Int32 nGrfTransparency = 0;
        bool bGrfIsTransparent = false;
        if ( (ePos != GPOS_NONE) &&
             (ePos != GPOS_TILED) && (ePos != GPOS_AREA)
           )
        {
            GraphicObject *pGrf = const_cast<GraphicObject*>(pBrush->GetGraphicObject());
            if ( bConsiderBackgroundTransparency )
            {
                GraphicAttr aGrfAttr = pGrf->GetAttr();
                if ( (aGrfAttr.GetTransparency() != 0) &&
                     (pBrush->GetColor() == COL_TRANSPARENT)
                   )
                {
                    bTransparentGrfWithNoFillBackgrd = true;
                    nGrfTransparency = aGrfAttr.GetTransparency();
                }
            }
            if ( pGrf->IsTransparent() )
            {
                bGrfIsTransparent = true;
            }
        }

        // to get color of brush, check background color against COL_TRANSPARENT ("no fill"/"auto fill")
        // instead of checking, if transparency is not set.
        const Color aColor( pBrush &&
                            ( !(pBrush->GetColor() == COL_TRANSPARENT) ||
                              gProp.bSFlyMetafile )
                    ? pBrush->GetColor()
                    : aGlobalRetoucheColor );

        // determine, if background region have to be
        //     drawn transparent.
        //     background region has to be drawn transparent, if
        //         background transparency have to be considered
        //     AND
        //       ( background color is transparent OR
        //         background graphic is transparent and background color is "no fill"
        //       )

        enum DrawStyle {
            Default,
            Transparent,
        } eDrawStyle = Default;

        if (bConsiderBackgroundTransparency &&
                ( ( aColor.GetTransparency() != 0) ||
                bTransparentGrfWithNoFillBackgrd ) )
        {
            eDrawStyle = Transparent;
        }

        // #i75614# reset draw mode in high contrast mode in order to get fill color set
        const DrawModeFlags nOldDrawMode = pOutDev->GetDrawMode();
        if ( gProp.pSGlobalShell->GetWin() &&
             Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
        {
            pOutDev->SetDrawMode( DrawModeFlags::Default );
        }

        // OD 06.08.2002 #99657# - if background region has to be drawn
        // transparent, set only the RGB values of the background color as
        // the fill color for the output device.
        switch (eDrawStyle)
        {
            case Transparent:
            {
                if( pOutDev->GetFillColor() != aColor.GetRGBColor() )
                    pOutDev->SetFillColor( aColor.GetRGBColor() );
                break;
            }
            default:
            {
                if( pOutDev->GetFillColor() != aColor )
                    pOutDev->SetFillColor( aColor );
                break;
            }
        }

        // #i75614#
        // restore draw mode
        pOutDev->SetDrawMode( nOldDrawMode );

        // OD 02.09.2002 #99657#
        switch (eDrawStyle)
        {
            case Transparent:
            {
                // background region have to be drawn transparent.
                // Thus, create a poly-polygon from the region and draw it with
                // the corresponding transparency percent.
                tools::PolyPolygon aDrawPoly( rOut.SVRect() );
                if ( aGrf.HasArea() )
                {
                    if ( !bGrfIsTransparent )
                    {
                        // subtract area of background graphic from draw area
                        // OD 08.10.2002 #103898# - consider only that part of the
                        // graphic area that is overlapping with draw area.
                        SwRect aTmpGrf = aGrf;
                        aTmpGrf.Intersection( rOut );
                        if ( aTmpGrf.HasArea() )
                        {
                            tools::Polygon aGrfPoly( aTmpGrf.SVRect() );
                            aDrawPoly.Insert( aGrfPoly );
                        }
                    }
                    else
                        bGrfBackgrdAlreadyDrawn = true;
                }
                // calculate transparency percent:
                // ( <transparency value[0x01..0xFF]>*100 + 0x7F ) / 0xFF
                // If there is a background graphic with a background color "no fill"/"auto fill",
                // the transparency value is taken from the background graphic,
                // otherwise take the transparency value from the color.
                sal_Int8 nTransparencyPercent = static_cast<sal_Int8>(
                  (( bTransparentGrfWithNoFillBackgrd ? nGrfTransparency : aColor.GetTransparency()
                   )*100 + 0x7F)/0xFF);
                // draw poly-polygon transparent
                pOutDev->DrawTransparent( aDrawPoly, nTransparencyPercent );

                break;
            }
            case Default:
            default:
            {
                SwRegionRects aRegion( rOut, 4 );
                if ( !bGrfIsTransparent )
                    aRegion -= aGrf;
                else
                    bGrfBackgrdAlreadyDrawn = true;
                // loop rectangles of background region, which has to be drawn
                for( size_t i = 0; i < aRegion.size(); ++i )
                {
                    pOutDev->DrawRect( aRegion[i].SVRect() );
                }
            }
        }
        pOutDev ->Pop();
    }

    if( bDraw && aGrf.IsOver( rOut ) )
        // OD 02.09.2002 #99657#
        // add parameter <bGrfBackgrdAlreadyDrawn>
        lcl_DrawGraphic( *pBrush, pOutDev, rSh, aGrf, rOut, bGrfNum, gProp,
                         bGrfBackgrdAlreadyDrawn );

    if( bReplaceGrfNum )
    {
        const BitmapEx& rBmp = rSh.GetReplacementBitmap(false);
        vcl::Font aTmp( pOutDev->GetFont() );
        Graphic::DrawEx( pOutDev, aEmptyOUStr, aTmp, rBmp, rOrg.Pos(), rOrg.SSize() );
    }
}

/**
 * Local helper for SwRootFrame::PaintSwFrame(..) - Adjust given rectangle to pixel size
 *
 * By OD at 27.09.2002 for #103636#
 * In order to avoid paint errors caused by multiple alignments (e.g. ::SwAlignRect(..))
 * and other changes to the to be painted rectangle, this method is called for the
 * rectangle to be painted in order to adjust it to the pixel it is overlapping
*/
static void lcl_AdjustRectToPixelSize( SwRect& io_aSwRect, const vcl::RenderContext &aOut )
{
    // local constant object of class <Size> to determine number of Twips
    // representing a pixel.
    const Size aTwipToPxSize( aOut.PixelToLogic( Size( 1,1 )) );

    // local object of class <Rectangle> in Twip coordinates
    // calculated from given rectangle aligned to pixel centers.
    const tools::Rectangle aPxCenterRect = aOut.PixelToLogic(
            aOut.LogicToPixel( io_aSwRect.SVRect() ) );

    // local constant object of class <Rectangle> representing given rectangle
    // in pixel.
    const tools::Rectangle aOrgPxRect = aOut.LogicToPixel( io_aSwRect.SVRect() );

    // calculate adjusted rectangle from pixel centered rectangle.
    // Due to rounding differences <aPxCenterRect> doesn't exactly represents
    // the Twip-centers. Thus, adjust borders by half of pixel width/height plus 1.
    // Afterwards, adjust calculated Twip-positions of the all borders.
    tools::Rectangle aSizedRect = aPxCenterRect;
    aSizedRect.AdjustLeft( -(aTwipToPxSize.Width()/2 + 1) );
    aSizedRect.AdjustRight( aTwipToPxSize.Width()/2 + 1 );
    aSizedRect.AdjustTop( -(aTwipToPxSize.Height()/2 + 1) );
    aSizedRect.AdjustBottom(aTwipToPxSize.Height()/2 + 1);

    // adjust left()
    while ( aOut.LogicToPixel(aSizedRect).Left() < aOrgPxRect.Left() )
    {
        aSizedRect.AdjustLeft( 1 );
    }
    // adjust right()
    while ( aOut.LogicToPixel(aSizedRect).Right() > aOrgPxRect.Right() )
    {
        aSizedRect.AdjustRight( -1 );
    }
    // adjust top()
    while ( aOut.LogicToPixel(aSizedRect).Top() < aOrgPxRect.Top() )
    {
        aSizedRect.AdjustTop( 1 );
    }
    // adjust bottom()
    while ( aOut.LogicToPixel(aSizedRect).Bottom() > aOrgPxRect.Bottom() )
    {
        aSizedRect.AdjustBottom( -1 );
    }

    io_aSwRect = SwRect( aSizedRect );

#if OSL_DEBUG_LEVEL > 0
    tools::Rectangle aTestOrgPxRect = aOut.LogicToPixel( io_aSwRect.SVRect() );
    tools::Rectangle aTestNewPxRect = aOut.LogicToPixel( aSizedRect );
    OSL_ENSURE( aTestOrgPxRect == aTestNewPxRect,
            "Error in lcl_AlignRectToPixelSize(..): Adjusted rectangle has incorrect position or size");
    // check Left()
    aSizedRect.AdjustLeft( -1 );
    aTestNewPxRect = aOut.LogicToPixel( aSizedRect );
    OSL_ENSURE( aTestOrgPxRect.Left() >= (aTestNewPxRect.Left()+1),
            "Error in lcl_AlignRectToPixelSize(..): Left() not correct adjusted");
    aSizedRect.AdjustLeft( 1 );
    // check Right()
    aSizedRect.AdjustRight( 1 );
    aTestNewPxRect = aOut.LogicToPixel( aSizedRect );
    OSL_ENSURE( aTestOrgPxRect.Right() <= (aTestNewPxRect.Right()-1),
            "Error in lcl_AlignRectToPixelSize(..): Right() not correct adjusted");
    aSizedRect.AdjustRight( -1 );
    // check Top()
    aSizedRect.AdjustTop( -1 );
    aTestNewPxRect = aOut.LogicToPixel( aSizedRect );
    OSL_ENSURE( aTestOrgPxRect.Top() >= (aTestNewPxRect.Top()+1),
            "Error in lcl_AlignRectToPixelSize(..): Top() not correct adjusted");
    aSizedRect.AdjustTop( 1 );
    // check Bottom()
    aSizedRect.AdjustBottom( 1 );
    aTestNewPxRect = aOut.LogicToPixel( aSizedRect );
    OSL_ENSURE( aTestOrgPxRect.Bottom() <= (aTestNewPxRect.Bottom()-1),
            "Error in lcl_AlignRectToPixelSize(..): Bottom() not correct adjusted");
    aSizedRect.AdjustBottom( -1 );
#endif
}

// FUNCTIONS USED FOR COLLAPSING TABLE BORDER LINES START

struct SwLineEntry
{
    SwTwips mnKey;
    SwTwips mnStartPos;
    SwTwips mnEndPos;

    svx::frame::Style maAttribute;

    enum OverlapType { NO_OVERLAP, OVERLAP1, OVERLAP2, OVERLAP3 };

public:
    SwLineEntry( SwTwips nKey,
                 SwTwips nStartPos,
                 SwTwips nEndPos,
                 const svx::frame::Style& rAttribute );

    OverlapType Overlaps( const SwLineEntry& rComp ) const;
};

SwLineEntry::SwLineEntry( SwTwips nKey,
                          SwTwips nStartPos,
                          SwTwips nEndPos,
                          const svx::frame::Style& rAttribute )
    :   mnKey( nKey ),
        mnStartPos( nStartPos ),
        mnEndPos( nEndPos ),
        maAttribute( rAttribute )
{
}

/*

 1. ----------    rOld
       ---------- rNew

 2. ----------    rOld
    ------------- rNew

 3.    -------    rOld
    ------------- rNew

 4. ------------- rOld
       ---------- rNew

 5. ----------    rOld
       ----       rNew

 6. ----------    rOld
    ----------    rNew

 7. ------------- rOld
    ----------    rNew

 8.    ---------- rOld
    ------------- rNew

 9.    ---------- rOld
    ----------    rNew
*/

SwLineEntry::OverlapType SwLineEntry::Overlaps( const SwLineEntry& rNew )  const
{
    SwLineEntry::OverlapType eRet = OVERLAP3;

    if ( mnStartPos >= rNew.mnEndPos || mnEndPos <= rNew.mnStartPos )
        eRet = NO_OVERLAP;

    // 1, 2, 3
    else if ( mnEndPos < rNew.mnEndPos )
        eRet = OVERLAP1;

    // 4, 5, 6, 7
    else if ( mnStartPos <= rNew.mnStartPos && mnEndPos >= rNew.mnEndPos )
        eRet = OVERLAP2;

    // 8, 9
    return eRet;
}

struct lt_SwLineEntry
{
    bool operator()( const SwLineEntry& e1, const SwLineEntry& e2 ) const
    {
        return e1.mnStartPos < e2.mnStartPos;
    }
};

typedef std::set< SwLineEntry, lt_SwLineEntry > SwLineEntrySet;
typedef std::map< SwTwips, SwLineEntrySet > SwLineEntryMap;

class SwTabFramePainter
{
    SwLineEntryMap maVertLines;
    SwLineEntryMap maHoriLines;
    const SwTabFrame& mrTabFrame;

    void Insert( SwLineEntry&, bool bHori );
    void Insert(const SwFrame& rFrame, const SvxBoxItem& rBoxItem, const SwRect &rPaintArea);
    void HandleFrame(const SwLayoutFrame& rFrame, const SwRect& rPaintArea);
    void FindStylesForLine( const Point&,
                            const Point&,
                            svx::frame::Style*,
                            bool bHori ) const;

public:
    explicit SwTabFramePainter( const SwTabFrame& rTabFrame );

    void PaintLines( OutputDevice& rDev, const SwRect& rRect ) const;
};

SwTabFramePainter::SwTabFramePainter( const SwTabFrame& rTabFrame )
    : mrTabFrame( rTabFrame )
{
    SwRect aPaintArea = rTabFrame.GetUpper()->GetPaintArea();
    HandleFrame(rTabFrame, aPaintArea);
}

void SwTabFramePainter::HandleFrame(const SwLayoutFrame& rLayoutFrame, const SwRect& rPaintArea)
{
    // Add border lines of cell frames. Skip covered cells. Skip cells
    // in special row span row, which do not have a negative row span:
    if ( rLayoutFrame.IsCellFrame() && !rLayoutFrame.IsCoveredCell() )
    {
        const SwCellFrame* pThisCell = static_cast<const SwCellFrame*>(&rLayoutFrame);
        const SwRowFrame* pRowFrame = static_cast<const SwRowFrame*>(pThisCell->GetUpper());
        const long nRowSpan = pThisCell->GetTabBox()->getRowSpan();
        if ( !pRowFrame->IsRowSpanLine() || nRowSpan > 1 || nRowSpan < -1 )
        {
            SwBorderAttrAccess aAccess( SwFrame::GetCache(), &rLayoutFrame );
            const SwBorderAttrs& rAttrs = *aAccess.Get();
            const SvxBoxItem& rBox = rAttrs.GetBox();
            Insert(rLayoutFrame, rBox, rPaintArea);
        }
    }

    // Recurse into lower layout frames, but do not recurse into lower tabframes.
    const SwFrame* pLower = rLayoutFrame.Lower();
    while ( pLower )
    {
        const SwLayoutFrame* pLowerLayFrame = dynamic_cast<const SwLayoutFrame*>(pLower);
        if ( pLowerLayFrame && !pLowerLayFrame->IsTabFrame() )
            HandleFrame(*pLowerLayFrame, rPaintArea);

        pLower = pLower->GetNext();
    }
}

void SwTabFramePainter::PaintLines(OutputDevice& rDev, const SwRect& rRect) const
{
    // #i16816# tagged pdf support
    SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, nullptr, rDev );

    SwLineEntryMap::const_iterator aIter = maHoriLines.begin();
    bool bHori = true;

    // color for subsidiary lines:
    const Color& rCol( SwViewOption::GetTableBoundariesColor() );

    // high contrast mode:
    // overrides the color of non-subsidiary lines.
    const Color* pHCColor = nullptr;
    DrawModeFlags nOldDrawMode = rDev.GetDrawMode();
    if( gProp.pSGlobalShell->GetWin() &&
        Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
    {
        pHCColor = &SwViewOption::GetFontColor();
        rDev.SetDrawMode( DrawModeFlags::Default );
    }

    const SwFrame* pUpper = mrTabFrame.GetUpper();
    SwRect aUpper( pUpper->getFramePrintArea() );
    aUpper.Pos() += pUpper->getFrameArea().Pos();
    SwRect aUpperAligned( aUpper );
    ::SwAlignRect( aUpperAligned, gProp.pSGlobalShell, &rDev );
    drawinglayer::primitive2d::Primitive2DContainer aHorizontalSequence;
    drawinglayer::primitive2d::Primitive2DContainer aVerticalSequence;

    while ( true )
    {
        if ( bHori && aIter == maHoriLines.end() )
        {
            aIter = maVertLines.begin();
            bHori = false;
        }

        if ( !bHori && aIter == maVertLines.end() )
            break;

        const SwLineEntrySet& rEntrySet = (*aIter).second;
        for (SwLineEntrySet::const_iterator aSetIter = rEntrySet.begin();
                 aSetIter != rEntrySet.end(); ++aSetIter)
        {
            const SwLineEntry& rEntry = *aSetIter;
            const svx::frame::Style& rEntryStyle( (*aSetIter).maAttribute );

            Point aStart, aEnd;
            if ( bHori )
            {
                aStart.setX( rEntry.mnStartPos );
                aStart.setY( rEntry.mnKey );
                aEnd.setX( rEntry.mnEndPos );
                aEnd.setY( rEntry.mnKey );
            }
            else
            {
                aStart.setX( rEntry.mnKey );
                aStart.setY( rEntry.mnStartPos );
                aEnd.setX( rEntry.mnKey );
                aEnd.setY( rEntry.mnEndPos );
            }

            svx::frame::Style aStyles[ 7 ];
            aStyles[ 0 ] = rEntryStyle;
            FindStylesForLine( aStart, aEnd, aStyles, bHori );
            SwRect aRepaintRect( aStart, aEnd );

            // the repaint rectangle has to be moved a bit for the centered lines:
            SwTwips nRepaintRectSize = !rEntryStyle.GetWidth() ? 1 : rEntryStyle.GetWidth();
            if ( bHori )
            {
                aRepaintRect.Height( 2 * nRepaintRectSize );
                aRepaintRect.Pos().AdjustY( -nRepaintRectSize );

                // To decide on visibility it is also necessary to expand the RepaintRect
                // to left/right according existing BorderLine overlap matchings, else there
                // will be repaint errors when scrolling in e.t TripleLine BorderLines.
                // aStyles[1] == aLFromT, aStyles[3] == aLFromB, aStyles[4] == aRFromT, aStyles[6] == aRFromB
                if(aStyles[1].IsUsed() || aStyles[3].IsUsed() || aStyles[4].IsUsed() || aStyles[6].IsUsed())
                {
                    const double fLineWidthMaxLeft(std::max(aStyles[1].GetWidth(), aStyles[3].GetWidth()));
                    const double fLineWidthMaxRight(std::max(aStyles[4].GetWidth(), aStyles[6].GetWidth()));
                    aRepaintRect.Width(aRepaintRect.Width() + (fLineWidthMaxLeft + fLineWidthMaxRight));
                    aRepaintRect.Pos().AdjustX( -fLineWidthMaxLeft );
                }
            }
            else
            {
                aRepaintRect.Width( 2 * nRepaintRectSize );
                aRepaintRect.Pos().AdjustX( -nRepaintRectSize );

                // Accordingly to horizontal case, but for top/bottom
                // aStyles[3] == aTFromR, aStyles[1] == aTFromL, aStyles[6] == aBFromR, aStyles[4] == aBFromL
                if(aStyles[3].IsUsed() || aStyles[1].IsUsed() || aStyles[6].IsUsed() || aStyles[4].IsUsed())
                {
                    const double fLineWidthMaxTop(std::max(aStyles[3].GetWidth(), aStyles[1].GetWidth()));
                    const double fLineWidthMaxBottom(std::max(aStyles[6].GetWidth(), aStyles[4].GetWidth()));
                    aRepaintRect.Height(aRepaintRect.Height() + (fLineWidthMaxTop + fLineWidthMaxBottom));
                    aRepaintRect.Pos().AdjustY( -fLineWidthMaxTop );
                }
            }

            if (!rRect.IsOver(aRepaintRect))
            {
                continue;
            }

            // subsidiary lines
            const Color* pTmpColor = nullptr;
            if (0 == aStyles[ 0 ].GetWidth())
            {
                if (isTableBoundariesEnabled() && gProp.pSGlobalShell->GetWin())
                    aStyles[ 0 ].Set( rCol, rCol, rCol, false, 1, 0, 0 );
                else
                    aStyles[0].SetType(SvxBorderLineStyle::NONE);
            }
            else
                pTmpColor = pHCColor;

            // The (twip) positions will be adjusted to meet these requirements:
            // 1. The y coordinates are located in the middle of the pixel grid
            // 2. The x coordinated are located at the beginning of the pixel grid
            // This is done, because the horizontal lines are painted "at
            // beginning", whereas the vertical lines are painted "centered".
            // By making the line sizes a multiple of one pixel size, we can
            // assure that all lines having the same twip size have the same
            // pixel size, independent of their position on the screen.
            Point aPaintStart = rDev.PixelToLogic( rDev.LogicToPixel(aStart) );
            Point aPaintEnd = rDev.PixelToLogic( rDev.LogicToPixel(aEnd) );

            if (gProp.pSGlobalShell->GetWin())
            {
                // The table borders do not use SwAlignRect, but all the other frames do.
                // Therefore we tweak the outer borders a bit to achieve that the outer
                // borders match the subsidiary lines of the upper:
                if (aStart.X() == aUpper.Left())
                    aPaintStart.setX( aUpperAligned.Left() );
                else if (aStart.X() == aUpper.Right_())
                    aPaintStart.setX( aUpperAligned.Right_() );
                if (aStart.Y() == aUpper.Top())
                    aPaintStart.setY( aUpperAligned.Top() );
                else if (aStart.Y() == aUpper.Bottom_())
                    aPaintStart.setY( aUpperAligned.Bottom_() );

                if (aEnd.X() == aUpper.Left())
                    aPaintEnd.setX( aUpperAligned.Left() );
                else if (aEnd.X() == aUpper.Right_())
                    aPaintEnd.setX( aUpperAligned.Right_() );
                if (aEnd.Y() == aUpper.Top())
                    aPaintEnd.setY( aUpperAligned.Top() );
                else if (aEnd.Y() == aUpper.Bottom_())
                    aPaintEnd.setY( aUpperAligned.Bottom_() );
            }

            if(aStyles[0].IsUsed())
            {
                if (bHori)
                {
                    const basegfx::B2DPoint aOrigin(aPaintStart.X(), aPaintStart.Y());
                    const basegfx::B2DVector aX(basegfx::B2DPoint(aPaintEnd.X(), aPaintEnd.Y()) - aOrigin);

                    if(!aX.equalZero())
                    {
                        const basegfx::B2DVector aY(basegfx::getNormalizedPerpendicular(aX));
                        svx::frame::StyleVectorTable aStartVector;

                        aStartVector.add(aStyles[ 1 ], aX, -aY, true); // aLFromT
                        aStartVector.add(aStyles[ 2 ], aX, -aX, true); // aLFromL
                        aStartVector.add(aStyles[ 3 ], aX, aY, false); // aLFromB
                        aStartVector.sort();

                        svx::frame::StyleVectorTable aEndVector;
                        const basegfx::B2DVector aAxis(-aX);

                        aEndVector.add(aStyles[ 4 ], aAxis, -aY, true); // aRFromT
                        aEndVector.add(aStyles[ 5 ], aAxis, aX, false); // aRFromR
                        aEndVector.add(aStyles[ 6 ], aAxis, aY, false); // aRFromB
                        aEndVector.sort();

                        CreateBorderPrimitives(
                            aHorizontalSequence,
                            aOrigin,
                            aX,
                            aStyles[ 0 ],
                            aStartVector,
                            aEndVector,
                            pTmpColor
                        );
                    }
                }
                else // vertical
                {
                    const basegfx::B2DPoint aOrigin(aPaintStart.X(), aPaintStart.Y());
                    const basegfx::B2DVector aX(basegfx::B2DPoint(aPaintEnd.X(), aPaintEnd.Y()) - aOrigin);

                    if(!aX.equalZero())
                    {
                        const basegfx::B2DVector aY(basegfx::getNormalizedPerpendicular(aX));
                        svx::frame::StyleVectorTable aStartVector;

                        aStartVector.add(aStyles[ 3 ], aX, -aY, false); // aTFromR
                        aStartVector.add(aStyles[ 2 ], aX, -aX, true); // aTFromT
                        aStartVector.add(aStyles[ 1 ], aX, aY, true); // aTFromL
                        aStartVector.sort();

                        svx::frame::StyleVectorTable aEndVector;
                        const basegfx::B2DVector aAxis(-aX);

                        aEndVector.add(aStyles[ 6 ], aAxis, -aY, false); // aBFromR
                        aEndVector.add(aStyles[ 5 ], aAxis, aX, false); // aBFromB
                        aEndVector.add(aStyles[ 4 ], aAxis, aY, true); // aBFromL
                        aEndVector.sort();

                        CreateBorderPrimitives(
                            aVerticalSequence,
                            aOrigin,
                            aX,
                            aStyles[ 0 ],
                            aStartVector,
                            aEndVector,
                            pTmpColor
                        );
                    }
                }
            }
        }
        ++aIter;
    }

    // to stay compatible, create order as it was formally. Also try to
    // merge primitives as far as possible
    drawinglayer::primitive2d::Primitive2DContainer aSequence;

    svx::frame::HelperMergeInB2DPrimitiveArray(aHorizontalSequence, aSequence);
    svx::frame::HelperMergeInB2DPrimitiveArray(aVerticalSequence, aSequence);

    // paint
    mrTabFrame.ProcessPrimitives(aSequence);

    // restore output device:
    rDev.SetDrawMode( nOldDrawMode );
}

/**
 * Finds the lines that join the line defined by (StartPoint, EndPoint) in either
 * StartPoint or Endpoint. The styles of these lines are required for DR's magic
 * line painting functions
 */
void SwTabFramePainter::FindStylesForLine( const Point& rStartPoint,
                                         const Point& rEndPoint,
                                         svx::frame::Style* pStyles,
                                         bool bHori ) const
{
    // pStyles[ 1 ] = bHori ? aLFromT : TFromL
    // pStyles[ 2 ] = bHori ? aLFromL : TFromT,
    // pStyles[ 3 ] = bHori ? aLFromB : TFromR,
    // pStyles[ 4 ] = bHori ? aRFromT : BFromL,
    // pStyles[ 5 ] = bHori ? aRFromR : BFromB,
    // pStyles[ 6 ] = bHori ? aRFromB : BFromR,

    SwLineEntryMap::const_iterator aMapIter = maVertLines.find( rStartPoint.X() );
    OSL_ENSURE( aMapIter != maVertLines.end(), "FindStylesForLine: Error" );
    const SwLineEntrySet& rVertSet = (*aMapIter).second;
    SwLineEntrySet::const_iterator aIter = rVertSet.begin();

    while ( aIter != rVertSet.end() )
    {
        const SwLineEntry& rEntry = *aIter;
        if ( bHori )
        {
            if ( rStartPoint.Y() == rEntry.mnStartPos )
                pStyles[ 3 ] = rEntry.maAttribute;
            else if ( rStartPoint.Y() == rEntry.mnEndPos )
                pStyles[ 1 ] = rEntry.maAttribute;
        }
        else
        {
            if ( rStartPoint.Y() == rEntry.mnEndPos )
                pStyles[ 2 ] = rEntry.maAttribute;
            else if ( rEndPoint.Y() == rEntry.mnStartPos )
                pStyles[ 5 ] = rEntry.maAttribute;
        }
        ++aIter;
    }

    aMapIter = maHoriLines.find( rStartPoint.Y() );
    OSL_ENSURE( aMapIter != maHoriLines.end(), "FindStylesForLine: Error" );
    const SwLineEntrySet& rHoriSet = (*aMapIter).second;
    aIter = rHoriSet.begin();

    while ( aIter != rHoriSet.end() )
    {
        const SwLineEntry& rEntry = *aIter;
        if ( bHori )
        {
            if ( rStartPoint.X() == rEntry.mnEndPos )
                pStyles[ 2 ] = rEntry.maAttribute;
            else if ( rEndPoint.X() == rEntry.mnStartPos )
                pStyles[ 5 ] = rEntry.maAttribute;
        }
        else
        {
            if ( rStartPoint.X() == rEntry.mnEndPos )
                pStyles[ 1 ] = rEntry.maAttribute;
            else if ( rStartPoint.X() == rEntry.mnStartPos )
                pStyles[ 3 ] = rEntry.maAttribute;
        }
        ++aIter;
    }

    if ( bHori )
    {
        aMapIter = maVertLines.find( rEndPoint.X() );
        OSL_ENSURE( aMapIter != maVertLines.end(), "FindStylesForLine: Error" );
        const SwLineEntrySet& rVertSet2 = (*aMapIter).second;
        aIter = rVertSet2.begin();

        while ( aIter != rVertSet2.end() )
        {
            const SwLineEntry& rEntry = *aIter;
            if ( rEndPoint.Y() == rEntry.mnStartPos )
                pStyles[ 6 ] = rEntry.maAttribute;
            else if ( rEndPoint.Y() == rEntry.mnEndPos )
                pStyles[ 4 ] = rEntry.maAttribute;
            ++aIter;
        }
    }
    else
    {
        aMapIter = maHoriLines.find( rEndPoint.Y() );
        OSL_ENSURE( aMapIter != maHoriLines.end(), "FindStylesForLine: Error" );
        const SwLineEntrySet& rHoriSet2 = (*aMapIter).second;
        aIter = rHoriSet2.begin();

        while ( aIter != rHoriSet2.end() )
        {
            const SwLineEntry& rEntry = *aIter;
            if ( rEndPoint.X() == rEntry.mnEndPos )
                pStyles[ 4 ] = rEntry.maAttribute;
            else if ( rEndPoint.X() == rEntry.mnStartPos )
                pStyles[ 6 ] = rEntry.maAttribute;
            ++aIter;
        }
    }
}

/**
 * Special case: #i9860#
 * first line in follow table without repeated headlines
 */
static bool lcl_IsFirstRowInFollowTableWithoutRepeatedHeadlines(
        SwTabFrame const& rTabFrame, SwFrame const& rFrame, SvxBoxItem const& rBoxItem)
{
    SwRowFrame const*const pThisRowFrame =
        dynamic_cast<const SwRowFrame*>(rFrame.GetUpper());
    return (pThisRowFrame
        && (pThisRowFrame->GetUpper() == &rTabFrame)
        && rTabFrame.IsFollow()
        && !rTabFrame.GetTable()->GetRowsToRepeat()
        &&  (  !pThisRowFrame->GetPrev()
            || static_cast<const SwRowFrame*>(pThisRowFrame->GetPrev())
                    ->IsRowSpanLine())
        && !rBoxItem.GetTop()
        && rBoxItem.GetBottom());
}

void SwTabFramePainter::Insert(const SwFrame& rFrame, const SvxBoxItem& rBoxItem, const SwRect& rPaintArea)
{
    // build 4 line entries for the 4 borders:
    SwRect aBorderRect = rFrame.getFrameArea();

    aBorderRect.Intersection(rPaintArea);

    bool const bBottomAsTop(lcl_IsFirstRowInFollowTableWithoutRepeatedHeadlines(
                mrTabFrame, rFrame, rBoxItem));
    bool const bVert = mrTabFrame.IsVertical();
    bool const bR2L  = mrTabFrame.IsRightToLeft();

    // no scaling needed, it's all in the primitives and the target device
    svx::frame::Style aL(rBoxItem.GetLeft(), 1.0);
    svx::frame::Style aR(rBoxItem.GetRight(), 1.0);
    svx::frame::Style aT(rBoxItem.GetTop(), 1.0);
    svx::frame::Style aB(rBoxItem.GetBottom(), 1.0);

    aR.MirrorSelf();
    aB.MirrorSelf();

    const SwTwips nLeft   = aBorderRect.Left_();
    const SwTwips nRight  = aBorderRect.Right_();
    const SwTwips nTop    = aBorderRect.Top_();
    const SwTwips nBottom = aBorderRect.Bottom_();

    aL.SetRefMode( svx::frame::RefMode::Centered );
    aR.SetRefMode( svx::frame::RefMode::Centered );
    aT.SetRefMode( !bVert ? svx::frame::RefMode::Begin : svx::frame::RefMode::End );
    aB.SetRefMode( !bVert ? svx::frame::RefMode::Begin : svx::frame::RefMode::End );

    SwLineEntry aLeft  (nLeft,   nTop,  nBottom,
            bVert ? aB                         : (bR2L ? aR : aL));
    SwLineEntry aRight (nRight,  nTop,  nBottom,
            bVert ? (bBottomAsTop ? aB : aT) : (bR2L ? aL : aR));
    SwLineEntry aTop   (nTop,    nLeft, nRight,
            bVert ? aL                         : (bBottomAsTop ? aB : aT));
    SwLineEntry aBottom(nBottom, nLeft, nRight,
            bVert ? aR                         : aB);

    Insert( aLeft, false );
    Insert( aRight, false );
    Insert( aTop, true );
    Insert( aBottom, true );
}

void SwTabFramePainter::Insert( SwLineEntry& rNew, bool bHori )
{
    // get all lines from structure, that have key entry of pLE
    SwLineEntryMap* pLine2 = bHori ? &maHoriLines : &maVertLines;
    const SwTwips nKey = rNew.mnKey;
    SwLineEntryMap::iterator aMapIter = pLine2->find( nKey );

    SwLineEntrySet* pLineSet = aMapIter != pLine2->end() ? &((*aMapIter).second) : nullptr;
    if ( !pLineSet )
    {
        SwLineEntrySet aNewSet;
        (*pLine2)[ nKey ] = aNewSet;
        pLineSet = &(*pLine2)[ nKey ];
    }
    SwLineEntrySet::iterator aIter = pLineSet->begin();

    while ( aIter != pLineSet->end() && rNew.mnStartPos < rNew.mnEndPos )
    {
        const SwLineEntry& rOld = *aIter;
        const SwLineEntry::OverlapType nOverlapType = rOld.Overlaps( rNew );

        const svx::frame::Style& rOldAttr = rOld.maAttribute;
        const svx::frame::Style& rNewAttr = rNew.maAttribute;
        const svx::frame::Style& rCmpAttr = std::max(rNewAttr, rOldAttr);

        if ( SwLineEntry::OVERLAP1 == nOverlapType )
        {
            OSL_ENSURE( rNew.mnStartPos >= rOld.mnStartPos, "Overlap type 3? How this?" );

            // new left segment
            const SwLineEntry aLeft( nKey, rOld.mnStartPos, rNew.mnStartPos, rOldAttr );

            // new middle segment
            const SwLineEntry aMiddle( nKey, rNew.mnStartPos, rOld.mnEndPos, rCmpAttr );

            // new right segment
            rNew.mnStartPos = rOld.mnEndPos;

            // update current lines set
            pLineSet->erase( aIter );
            if ( aLeft.mnStartPos   < aLeft.mnEndPos   ) pLineSet->insert( aLeft );
            if ( aMiddle.mnStartPos < aMiddle.mnEndPos ) pLineSet->insert( aMiddle );

            aIter = pLineSet->begin();

            continue; // start over
        }
        else if ( SwLineEntry::OVERLAP2 == nOverlapType )
        {
            // new left segment
            const SwLineEntry aLeft( nKey, rOld.mnStartPos, rNew.mnStartPos, rOldAttr );

            // new middle segment
            const SwLineEntry aMiddle( nKey, rNew.mnStartPos, rNew.mnEndPos, rCmpAttr );

            // new right segment
            const SwLineEntry aRight( nKey, rNew.mnEndPos, rOld.mnEndPos, rOldAttr );

            // update current lines set
            pLineSet->erase( aIter );
            if ( aLeft.mnStartPos < aLeft.mnEndPos ) pLineSet->insert( aLeft );
            if ( aMiddle.mnStartPos < aMiddle.mnEndPos ) pLineSet->insert( aMiddle );
            if ( aRight.mnStartPos < aRight.mnEndPos ) pLineSet->insert( aRight );

            rNew.mnStartPos = rNew.mnEndPos; // rNew should not be inserted!

            break; // we are finished
        }
        else if ( SwLineEntry::OVERLAP3 == nOverlapType )
        {
            // new left segment
            const SwLineEntry aLeft( nKey, rNew.mnStartPos, rOld.mnStartPos, rNewAttr );

            // new middle segment
            const SwLineEntry aMiddle( nKey, rOld.mnStartPos, rNew.mnEndPos, rCmpAttr );

            // new right segment
            const SwLineEntry aRight( nKey, rNew.mnEndPos, rOld.mnEndPos, rOldAttr );

            // update current lines set
            pLineSet->erase( aIter );
            if ( aLeft.mnStartPos < aLeft.mnEndPos ) pLineSet->insert( aLeft );
            if ( aMiddle.mnStartPos < aMiddle.mnEndPos ) pLineSet->insert( aMiddle );
            if ( aRight.mnStartPos < aRight.mnEndPos ) pLineSet->insert( aRight );

            rNew.mnStartPos = rNew.mnEndPos; // rNew should not be inserted!

            break; // we are finished
        }

        ++aIter;
    }

    if ( rNew.mnStartPos < rNew.mnEndPos ) // insert rest
        pLineSet->insert( rNew );
}

/**
 * FUNCTIONS USED FOR COLLAPSING TABLE BORDER LINES END
 * --> OD #i76669#
 */
namespace
{
    class SwViewObjectContactRedirector : public sdr::contact::ViewObjectContactRedirector
    {
        private:
            const SwViewShell& mrViewShell;

        public:
            explicit SwViewObjectContactRedirector( const SwViewShell& rSh )
                : mrViewShell( rSh )
            {};

            virtual drawinglayer::primitive2d::Primitive2DContainer createRedirectedPrimitive2DSequence(
                                    const sdr::contact::ViewObjectContact& rOriginal,
                                    const sdr::contact::DisplayInfo& rDisplayInfo) override
            {
                bool bPaint( true );

                SdrObject* pObj = rOriginal.GetViewContact().TryToGetSdrObject();
                if ( pObj )
                {
                    bPaint = SwFlyFrame::IsPaint( pObj, &mrViewShell );
                }

                if ( !bPaint )
                {
                    return drawinglayer::primitive2d::Primitive2DContainer();
                }

                return sdr::contact::ViewObjectContactRedirector::createRedirectedPrimitive2DSequence(
                                                        rOriginal, rDisplayInfo );
            }
    };

} // end of anonymous namespace
// <--

/**
 * Paint once for every visible page which is touched by Rect
 *
 * 1. Paint borders and backgrounds
 * 2. Paint the draw layer (frames and drawing objects) that is
 *    below the document (hell)
 * 3. Paint the document content (text)
 * 4. Paint the draw layer that is above the document
|*/
void SwRootFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const& rRect, SwPrintData const*const pPrintData) const
{
    OSL_ENSURE( Lower() && Lower()->IsPageFrame(), "Lower of root is no page." );

    PROTOCOL( this, PROT::FileInit, DbgAction::NONE, nullptr)

    bool bResetRootPaint = false;
    SwViewShell *pSh = mpCurrShell;

    if ( pSh->GetWin() )
    {
        if ( pSh->GetOut() == pSh->GetWin() && !pSh->GetWin()->IsVisible() )
        {
            return;
        }
        if (SwRootFrame::s_isInPaint)
        {
            SwPaintQueue::Add( pSh, rRect );
            return;
        }
    }
    else
        SwRootFrame::s_isInPaint = bResetRootPaint = true;

    SwSavePaintStatics *pStatics = nullptr;
    if ( gProp.pSGlobalShell )
        pStatics = new SwSavePaintStatics();
    gProp.pSGlobalShell = pSh;

    if( !pSh->GetWin() )
        gProp.pSProgress = SfxProgress::GetActiveProgress( static_cast<SfxObjectShell*>(pSh->GetDoc()->GetDocShell()) );

    ::SwCalcPixStatics( pSh->GetOut() );
    aGlobalRetoucheColor = pSh->Imp()->GetRetoucheColor();

    //Trigger an action to clear things up if needed.
    //Using this trick we can ensure that all values are valid in all paints -
    //no problems, no special case(s).
    // #i92745#
    // Extend check on certain states of the 'current' <SwViewShell> instance to
    // all existing <SwViewShell> instances.
    bool bPerformLayoutAction( true );
    {
        for(SwViewShell& rTmpViewShell : pSh->GetRingContainer())
        {
            if ( rTmpViewShell.IsInEndAction() ||
                 rTmpViewShell.IsPaintInProgress() ||
                 ( rTmpViewShell.Imp()->IsAction() &&
                   rTmpViewShell.Imp()->GetLayAction().IsActionInProgress() ) )
            {
                bPerformLayoutAction = false;
            }

            if(!bPerformLayoutAction)
                break;
        }
    }
    if ( bPerformLayoutAction )
    {
        const_cast<SwRootFrame*>(this)->ResetTurbo();
        SwLayAction aAction( const_cast<SwRootFrame*>(this), pSh->Imp() );
        aAction.SetPaint( false );
        aAction.SetComplete( false );
        aAction.SetReschedule( gProp.pSProgress != nullptr );
        aAction.Action(&rRenderContext);
        ResetTurboFlag();
        if ( !pSh->ActionPend() )
            pSh->Imp()->DelRegion();
    }

    SwRect aRect( rRect );
    aRect.Intersection( pSh->VisArea() );

    const bool bExtraData = ::IsExtraData( GetFormat()->GetDoc() );

    gProp.pSLines.reset(new SwLineRects); // Container for borders.

    // #104289#. During painting, something (OLE) can
    // load the linguistic, which in turn can cause a reformat
    // of the document. Dangerous! We better set this flag to
    // avoid the reformat.
    const bool bOldAction = IsCallbackActionEnabled();
    const_cast<SwRootFrame*>(this)->SetCallbackActionEnabled( false );

    const SwPageFrame *pPage = pSh->Imp()->GetFirstVisPage(&rRenderContext);

    const bool bBookMode = gProp.pSGlobalShell->GetViewOptions()->IsViewLayoutBookMode();
    if ( bBookMode && pPage->GetPrev() && static_cast<const SwPageFrame*>(pPage->GetPrev())->IsEmptyPage() )
        pPage = static_cast<const SwPageFrame*>(pPage->GetPrev());

    // #i68597#
    const bool bGridPainting(pSh->GetWin() && pSh->Imp()->HasDrawView() && pSh->Imp()->GetDrawView()->IsGridVisible());

    // Hide all page break controls before showing them again
    SwWrtShell* pWrtSh = dynamic_cast< SwWrtShell* >( gProp.pSGlobalShell );
    if ( pWrtSh )
    {
        SwEditWin& rEditWin = pWrtSh->GetView().GetEditWin();
        SwFrameControlsManager& rMngr = rEditWin.GetFrameControlsManager();
        const SwPageFrame* pHiddenPage = pPage;
        while ( pHiddenPage->GetPrev() != nullptr )
        {
            pHiddenPage = static_cast< const SwPageFrame* >( pHiddenPage->GetPrev() );
            SwFrameControlPtr pControl = rMngr.GetControl( PageBreak, pHiddenPage );
            if ( pControl.get() )
                pControl->ShowAll( false );
        }
    }

    // #i76669#
    SwViewObjectContactRedirector aSwRedirector( *pSh );

    while ( pPage )
    {
        const bool bPaintRightShadow =  pPage->IsRightShadowNeeded();
        const bool bPaintLeftShadow = pPage->IsLeftShadowNeeded();
        const bool bRightSidebar = pPage->SidebarPosition() == sw::sidebarwindows::SidebarPosition::RIGHT;

        if ( !pPage->IsEmptyPage() )
        {
            SwRect aPaintRect;
            SwPageFrame::GetBorderAndShadowBoundRect( pPage->getFrameArea(), pSh, &rRenderContext, aPaintRect,
                bPaintLeftShadow, bPaintRightShadow, bRightSidebar );

            if ( aRect.IsOver( aPaintRect ) )
            {
                if ( pSh->GetWin() )
                {
                    gProp.pSSubsLines.reset(new SwSubsRects);
                    gProp.pSSpecSubsLines.reset(new SwSubsRects);
                }
                gProp.pBLines.reset(new BorderLines);

                aPaintRect.Intersection_( aRect );

                if ( bExtraData &&
                     pSh->GetWin() && pSh->IsInEndAction() )
                {
                    // enlarge paint rectangle to complete page width, subtract
                    // current paint area and invalidate the resulting region.
                    SwRectFnSet aRectFnSet(pPage);
                    SwRect aPageRectTemp( aPaintRect );
                    aRectFnSet.SetLeftAndWidth( aPageRectTemp,
                         aRectFnSet.GetLeft(pPage->getFrameArea()),
                         aRectFnSet.GetWidth(pPage->getFrameArea()) );
                    aPageRectTemp.Intersection_( pSh->VisArea() );
                    vcl::Region aPageRectRegion( aPageRectTemp.SVRect() );
                    aPageRectRegion.Exclude( aPaintRect.SVRect() );
                    pSh->GetWin()->Invalidate( aPageRectRegion, InvalidateFlags::Children );
                }

                // #i80793#
                // enlarge paint rectangle for objects overlapping the same pixel
                // in all cases and before the DrawingLayer overlay is initialized.
                lcl_AdjustRectToPixelSize( aPaintRect, *(pSh->GetOut()) );

                // #i68597#
                // moved paint pre-process for DrawingLayer overlay here since the above
                // code dependent from bExtraData may expand the PaintRect
                {
                    // #i75172# if called from SwViewShell::ImplEndAction it should no longer
                    // really be used but handled by SwViewShell::ImplEndAction already
                    const vcl::Region aDLRegion(aPaintRect.SVRect());
                    pSh->DLPrePaint2(aDLRegion);
                }

                if(OUTDEV_WINDOW == gProp.pSGlobalShell->GetOut()->GetOutDevType())
                {
                    // OD 27.09.2002 #103636# - changed method SwLayVout::Enter(..)
                    // 2nd parameter is no longer <const> and will be set to the
                    // rectangle the virtual output device is calculated from <aPaintRect>,
                    // if the virtual output is used.
                    s_pVout->Enter(pSh, aPaintRect, !s_isNoVirDev);

                    // OD 27.09.2002 #103636# - adjust paint rectangle to pixel size
                    // Thus, all objects overlapping on pixel level with the unadjusted
                    // paint rectangle will be considered in the paint.
                    lcl_AdjustRectToPixelSize( aPaintRect, *(pSh->GetOut()) );
                }

                // maybe this can be put in the above scope. Since we are not sure, just leave it ATM
                s_pVout->SetOrgRect( aPaintRect );

                // OD 29.08.2002 #102450#
                // determine background color of page for <PaintLayer> method
                // calls, paint <hell> or <heaven>
                const Color aPageBackgrdColor(pPage->GetDrawBackgrdColor());

                pPage->PaintBaBo( aPaintRect, pPage );

                if ( pSh->Imp()->HasDrawView() )
                {
                    gProp.pSLines->LockLines( true );
                    const IDocumentDrawModelAccess& rIDDMA = pSh->getIDocumentDrawModelAccess();
                    pSh->Imp()->PaintLayer( rIDDMA.GetHellId(),
                                            pPrintData,
                                            *pPage, pPage->getFrameArea(),
                                            &aPageBackgrdColor,
                                            pPage->IsRightToLeft(),
                                            &aSwRedirector );
                    gProp.pSLines->PaintLines( pSh->GetOut(), gProp );
                    gProp.pSLines->LockLines( false );
                }

                if ( pSh->GetDoc()->GetDocumentSettingManager().get( DocumentSettingId::BACKGROUND_PARA_OVER_DRAWINGS ) )
                    pPage->PaintBaBo( aPaintRect, pPage, /*bOnlyTextBackground=*/true );

                if( pSh->GetWin() )
                {
                    // collect sub-lines
                    pPage->RefreshSubsidiary( aPaintRect );
                    // paint special sub-lines
                    gProp.pSSpecSubsLines->PaintSubsidiary( pSh->GetOut(), nullptr, gProp );
                }

                pPage->PaintSwFrame( rRenderContext, aPaintRect );

                // no paint of page border and shadow, if writer is in place mode.
                if( pSh->GetWin() && pSh->GetDoc()->GetDocShell() &&
                    !pSh->GetDoc()->GetDocShell()->IsInPlaceActive() )
                {
                    SwPageFrame::PaintBorderAndShadow( pPage->getFrameArea(), pSh, bPaintLeftShadow, bPaintRightShadow, bRightSidebar );
                    SwPageFrame::PaintNotesSidebar( pPage->getFrameArea(), pSh, pPage->GetPhyPageNum(), bRightSidebar);
                }

                gProp.pSLines->PaintLines( pSh->GetOut(), gProp );
                if ( pSh->GetWin() )
                {
                    gProp.pSSubsLines->PaintSubsidiary( pSh->GetOut(), gProp.pSLines.get(), gProp );
                    gProp.pSSubsLines.reset();
                    gProp.pSSpecSubsLines.reset();
                }
                // fdo#42750: delay painting these until after subsidiary lines
                // fdo#45562: delay painting these until after hell layer
                // fdo#47717: but do it before heaven layer
                ProcessPrimitives(gProp.pBLines->GetBorderLines_Clear());

                if ( pSh->Imp()->HasDrawView() )
                {
                    // OD 29.08.2002 #102450# - add 3rd parameter
                    // OD 09.12.2002 #103045# - add 4th parameter for horizontal text direction.
                    pSh->Imp()->PaintLayer( pSh->GetDoc()->getIDocumentDrawModelAccess().GetHeavenId(),
                                            pPrintData,
                                            *pPage, pPage->getFrameArea(),
                                            &aPageBackgrdColor,
                                            pPage->IsRightToLeft(),
                                            &aSwRedirector );
                }

                if ( bExtraData )
                    pPage->RefreshExtraData( aPaintRect );

                gProp.pBLines.reset();
                s_pVout->Leave();

                // #i68597#
                // needed to move grid painting inside Begin/EndDrawLayer bounds and to change
                // output rect for it accordingly
                if(bGridPainting)
                {
                    SdrPaintView* pPaintView = pSh->Imp()->GetDrawView();
                    SdrPageView* pPageView = pPaintView->GetSdrPageView();
                    pPageView->DrawPageViewGrid(*pSh->GetOut(), aPaintRect.SVRect(), SwViewOption::GetTextGridColor() );
                }

                // #i68597#
                // moved paint post-process for DrawingLayer overlay here, see above
                {
                    pSh->DLPostPaint2(true);
                }
            }

            pPage->PaintDecorators( );
            pPage->PaintBreak();
        }
        else if ( bBookMode && pSh->GetWin() && !pSh->GetDoc()->GetDocShell()->IsInPlaceActive() )
        {
            // paint empty page
            SwRect aPaintRect;
            SwRect aEmptyPageRect( pPage->getFrameArea() );

            // code from vprint.cxx
            const SwPageFrame& rFormatPage = pPage->GetFormatPage();
            aEmptyPageRect.SSize() = rFormatPage.getFrameArea().SSize();

            SwPageFrame::GetBorderAndShadowBoundRect( aEmptyPageRect, pSh, &rRenderContext, aPaintRect,
                bPaintLeftShadow, bPaintRightShadow, bRightSidebar );
            aPaintRect.Intersection_( aRect );

            if ( aRect.IsOver( aEmptyPageRect ) )
            {
                // #i75172# if called from SwViewShell::ImplEndAction it should no longer
                // really be used but handled by SwViewShell::ImplEndAction already
                {
                    const vcl::Region aDLRegion(aPaintRect.SVRect());
                    pSh->DLPrePaint2(aDLRegion);
                }

                if( pSh->GetOut()->GetFillColor() != aGlobalRetoucheColor )
                    pSh->GetOut()->SetFillColor( aGlobalRetoucheColor );

                pSh->GetOut()->SetLineColor(); // OD 20.02.2003 #107369# - no line color
                // OD 20.02.2003 #107369# - use aligned page rectangle
                {
                    SwRect aTmpPageRect( aEmptyPageRect );
                    ::SwAlignRect( aTmpPageRect, pSh, &rRenderContext );
                    aEmptyPageRect = aTmpPageRect;
                }

                pSh->GetOut()->DrawRect( aEmptyPageRect.SVRect() );

                // paint empty page text
                const vcl::Font& rEmptyPageFont = SwPageFrame::GetEmptyPageFont();
                const vcl::Font aOldFont( pSh->GetOut()->GetFont() );

                pSh->GetOut()->SetFont( rEmptyPageFont );
                pSh->GetOut()->DrawText( aEmptyPageRect.SVRect(), SwResId( STR_EMPTYPAGE ),
                                    DrawTextFlags::VCenter |
                                    DrawTextFlags::Center |
                                    DrawTextFlags::Clip );

                pSh->GetOut()->SetFont( aOldFont );
                // paint shadow and border for empty page
                // OD 19.02.2003 #107369# - use new method to paint page border and
                // shadow
                SwPageFrame::PaintBorderAndShadow( aEmptyPageRect, pSh, bPaintLeftShadow, bPaintRightShadow, bRightSidebar );
                SwPageFrame::PaintNotesSidebar( aEmptyPageRect, pSh, pPage->GetPhyPageNum(), bRightSidebar);

                {
                    pSh->DLPostPaint2(true);
                }
            }
        }

        OSL_ENSURE( !pPage->GetNext() || pPage->GetNext()->IsPageFrame(),
                "Neighbour of page is not a page." );
        pPage = static_cast<const SwPageFrame*>(pPage->GetNext());
    }

    gProp.pSLines.reset();

    if ( bResetRootPaint )
        SwRootFrame::s_isInPaint = false;
    if ( pStatics )
        delete pStatics;
    else
    {
        gProp.pSProgress = nullptr;
        gProp.pSGlobalShell = nullptr;
    }

    const_cast<SwRootFrame*>(this)->SetCallbackActionEnabled( bOldAction );
}

static void lcl_EmergencyFormatFootnoteCont( SwFootnoteContFrame *pCont )
{
    vcl::RenderContext* pRenderContext = pCont->getRootFrame()->GetCurrShell()->GetOut();

    //It's possible that the Cont will get destroyed.
    SwContentFrame *pCnt = pCont->ContainsContent();
    while ( pCnt && pCnt->IsInFootnote() )
    {
        pCnt->Calc(pRenderContext);
        pCnt = pCnt->GetNextContentFrame();
    }
}

class SwShortCut
{
    SwRectDist fnCheck;
    long nLimit;
public:
    SwShortCut( const SwFrame& rFrame, const SwRect& rRect );
    bool Stop( const SwRect& rRect ) const
        { return (rRect.*fnCheck)( nLimit ) > 0; }
};

SwShortCut::SwShortCut( const SwFrame& rFrame, const SwRect& rRect )
{
    bool bVert = rFrame.IsVertical();
    bool bR2L = rFrame.IsRightToLeft();
    if( rFrame.IsNeighbourFrame() && bVert == bR2L )
    {
        if( bVert )
        {
            fnCheck = &SwRect::GetBottomDistance;
            nLimit = rRect.Top();
        }
        else
        {
            fnCheck = &SwRect::GetLeftDistance;
            nLimit = rRect.Left() + rRect.Width();
        }
    }
    else if( bVert == rFrame.IsNeighbourFrame() )
    {
        fnCheck = &SwRect::GetTopDistance;
        nLimit = rRect.Top() + rRect.Height();
    }
    else
    {
        if ( rFrame.IsVertLR() )
        {
               fnCheck = &SwRect::GetLeftDistance;
               nLimit = rRect.Right();
        }
        else
        {
            fnCheck = &SwRect::GetRightDistance;
            nLimit = rRect.Left();
        }
    }
}

void SwLayoutFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const& rRect, SwPrintData const*const) const
{
    // #i16816# tagged pdf support
    Frame_Info aFrameInfo( *this );
    SwTaggedPDFHelper aTaggedPDFHelper( nullptr, &aFrameInfo, nullptr, rRenderContext );

    const SwFrame *pFrame = Lower();
    if ( !pFrame )
        return;

    SwShortCut aShortCut( *pFrame, rRect );
    bool bCnt = pFrame->IsContentFrame();
    if ( bCnt )
        pFrame->Calc(&rRenderContext);

    if ( pFrame->IsFootnoteContFrame() )
    {
        ::lcl_EmergencyFormatFootnoteCont( const_cast<SwFootnoteContFrame*>(static_cast<const SwFootnoteContFrame*>(pFrame)) );
        pFrame = Lower();
    }

    const SwPageFrame *pPage = nullptr;
    const bool bWin   = gProp.pSGlobalShell->GetWin() != nullptr;

    while ( IsAnLower( pFrame ) )
    {
        SwRect aPaintRect( pFrame->GetPaintArea() );
        if( aShortCut.Stop( aPaintRect ) )
            break;
        if ( bCnt && gProp.pSProgress )
            gProp.pSProgress->Reschedule();

        //We need to retouch if a frame explicitly requests it.
        //First do the retouch, because this could flatten the borders.
        if ( pFrame->IsRetouche() )
        {
            if ( pFrame->IsRetoucheFrame() && bWin && !pFrame->GetNext() )
            {   if ( !pPage )
                    pPage = FindPageFrame();
               pFrame->Retouch( pPage, rRect );
            }
            pFrame->ResetRetouche();
        }

        if ( rRect.IsOver( aPaintRect ) )
        {
            if ( bCnt && pFrame->IsCompletePaint() &&
                 !rRect.IsInside( aPaintRect ) && Application::AnyInput( VclInputFlags::KEYBOARD ) )
            {
                //fix(8104): It may happen, that the processing wasn't complete
                //but some parts of the paragraph were still repainted.
                //This could lead to the situation, that other parts of the
                //paragraph won't be repainted at all. The only solution seems
                //to be an invalidation of the window.
                //To not make it too severe the rectangle is limited by
                //painting the desired part and only invalidating the
                //remaining paragraph parts.
                if ( aPaintRect.Left()  == rRect.Left() &&
                     aPaintRect.Right() == rRect.Right() )
                {
                    aPaintRect.Bottom( rRect.Top() - 1 );
                    if ( aPaintRect.Height() > 0 )
                        gProp.pSGlobalShell->InvalidateWindows(aPaintRect);
                    aPaintRect.Top( rRect.Bottom() + 1 );
                    aPaintRect.Bottom( pFrame->getFrameArea().Bottom() );
                    if ( aPaintRect.Height() > 0 )
                        gProp.pSGlobalShell->InvalidateWindows(aPaintRect);
                    aPaintRect.Top( pFrame->getFrameArea().Top() );
                    aPaintRect.Bottom( pFrame->getFrameArea().Bottom() );
                }
                else
                {
                    gProp.pSGlobalShell->InvalidateWindows( aPaintRect );
                    pFrame = pFrame->GetNext();
                    if ( pFrame )
                    {
                        bCnt = pFrame->IsContentFrame();
                        if ( bCnt )
                            pFrame->Calc(&rRenderContext);
                    }
                    continue;
                }
            }
            pFrame->ResetCompletePaint();
            aPaintRect.Intersection_( rRect );

            pFrame->PaintSwFrame( rRenderContext, aPaintRect );

            if ( Lower() && Lower()->IsColumnFrame() )
            {
                //Paint the column separator line if needed. The page is
                //responsible for the page frame - not the upper.
                const SwFrameFormat *pFormat = GetUpper() && GetUpper()->IsPageFrame()
                                            ? GetUpper()->GetFormat()
                                            : GetFormat();
                const SwFormatCol &rCol = pFormat->GetCol();
                if ( rCol.GetLineAdj() != COLADJ_NONE )
                {
                    if ( !pPage )
                        pPage = pFrame->FindPageFrame();

                    PaintColLines( aPaintRect, rCol, pPage );
                }
            }
        }
        if ( !bCnt && pFrame->GetNext() && pFrame->GetNext()->IsFootnoteContFrame() )
            ::lcl_EmergencyFormatFootnoteCont( const_cast<SwFootnoteContFrame*>(static_cast<const SwFootnoteContFrame*>(pFrame->GetNext())) );

        pFrame = pFrame->GetNext();

        if ( pFrame )
        {
            bCnt = pFrame->IsContentFrame();
            if ( bCnt )
                pFrame->Calc(&rRenderContext);
        }
    }
}

static drawinglayer::primitive2d::Primitive2DContainer lcl_CreateDashedIndicatorPrimitive(
        const basegfx::B2DPoint& rStart, const basegfx::B2DPoint& rEnd,
        basegfx::BColor aColor )
{
    drawinglayer::primitive2d::Primitive2DContainer aSeq( 1 );

    std::vector< double > aStrokePattern;
    basegfx::B2DPolygon aLinePolygon;
    aLinePolygon.append(rStart);
    aLinePolygon.append(rEnd);

    const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
    if ( rSettings.GetHighContrastMode( ) )
    {
        // Only a solid line in high contrast mode
        aColor = rSettings.GetDialogTextColor().getBColor();
    }
    else
    {
        // Get a color for the contrast
        basegfx::BColor aHslLine = basegfx::utils::rgb2hsl( aColor );
        double nLuminance = aHslLine.getZ() * 2.5;
        if ( nLuminance == 0 )
            nLuminance = 0.5;
        else if ( nLuminance >= 1.0 )
            nLuminance = aHslLine.getZ() * 0.4;
        aHslLine.setZ( nLuminance );
        const basegfx::BColor aOtherColor = basegfx::utils::hsl2rgb( aHslLine );

        // Compute the plain line
        drawinglayer::primitive2d::PolygonHairlinePrimitive2D * pPlainLine =
            new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(
                    aLinePolygon, aOtherColor );

        aSeq[0] = drawinglayer::primitive2d::Primitive2DReference( pPlainLine );

        // Dashed line in twips
        aStrokePattern.push_back( 40 );
        aStrokePattern.push_back( 40 );

        aSeq.resize( 2 );
    }

    // Compute the dashed line primitive
    drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D * pLine =
            new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D (
                basegfx::B2DPolyPolygon( aLinePolygon ),
                drawinglayer::attribute::LineAttribute( aColor ),
                drawinglayer::attribute::StrokeAttribute( aStrokePattern ) );

    aSeq[ aSeq.size( ) - 1 ] = drawinglayer::primitive2d::Primitive2DReference( pLine );

    return aSeq;
}

void SwPageFrame::PaintBreak( ) const
{
    if ( gProp.pSGlobalShell->GetOut()->GetOutDevType() != OUTDEV_PRINTER  &&
         !gProp.pSGlobalShell->GetViewOptions()->IsPDFExport() &&
         !gProp.pSGlobalShell->GetViewOptions()->IsReadonly() &&
         !gProp.pSGlobalShell->IsPreview() )
    {
        const SwFrame* pBodyFrame = Lower();
        while ( pBodyFrame && !pBodyFrame->IsBodyFrame() )
            pBodyFrame = pBodyFrame->GetNext();

        if ( pBodyFrame )
        {
            const SwLayoutFrame* pLayBody = static_cast< const SwLayoutFrame* >( pBodyFrame );
            const SwFlowFrame *pFlowFrame = pLayBody->ContainsContent();

            // Test if the first node is a table
            const SwFrame* pFirstFrame = pLayBody->Lower();
            if ( pFirstFrame && pFirstFrame->IsTabFrame() )
                pFlowFrame = static_cast< const SwTabFrame* >( pFirstFrame );

            SwWrtShell* pWrtSh = dynamic_cast< SwWrtShell* >( gProp.pSGlobalShell );
            if ( pWrtSh )
            {
                SwEditWin& rEditWin = pWrtSh->GetView().GetEditWin();
                SwFrameControlsManager& rMngr = rEditWin.GetFrameControlsManager();

                if ( pFlowFrame && pFlowFrame->IsPageBreak( true ) )
                    rMngr.SetPageBreakControl( this );
                else
                    rMngr.RemoveControlsByType( PageBreak, this );
            }
        }
        SwLayoutFrame::PaintBreak( );
    }
}

void SwColumnFrame::PaintBreak( ) const
{
    if ( gProp.pSGlobalShell->GetOut()->GetOutDevType() != OUTDEV_PRINTER  &&
         !gProp.pSGlobalShell->GetViewOptions()->IsPDFExport() &&
         !gProp.pSGlobalShell->GetViewOptions()->IsReadonly() &&
         !gProp.pSGlobalShell->IsPreview() )
    {
        const SwFrame* pBodyFrame = Lower();
        while ( pBodyFrame && !pBodyFrame->IsBodyFrame() )
            pBodyFrame = pBodyFrame->GetNext();

        if ( pBodyFrame )
        {
            const SwContentFrame *pCnt = static_cast< const SwLayoutFrame* >( pBodyFrame )->ContainsContent();
            if ( pCnt && pCnt->IsColBreak( true ) )
            {
                // Paint the break only if:
                //    * Not in header footer edition, to avoid conflicts with the
                //      header/footer marker
                //    * Non-printing characters are shown, as this is more consistent
                //      with other formatting marks
                if ( !gProp.pSGlobalShell->IsShowHeaderFooterSeparator( Header ) &&
                     !gProp.pSGlobalShell->IsShowHeaderFooterSeparator( Footer ) &&
                      gProp.pSGlobalShell->GetViewOptions()->IsLineBreak() )
                {
                    SwRect aRect( pCnt->getFramePrintArea() );
                    aRect.Pos() += pCnt->getFrameArea().Pos();

                    // Draw the line
                    basegfx::B2DPoint aStart( double( aRect.Left() ), aRect.Top() );
                    basegfx::B2DPoint aEnd( double( aRect.Right() ), aRect.Top() );
                    double nWidth = aRect.Width();
                    if ( IsVertical( ) )
                    {
                        aStart = basegfx::B2DPoint( double( aRect.Right() ), double( aRect.Top() ) );
                        aEnd = basegfx::B2DPoint( double( aRect.Right() ), double( aRect.Bottom() ) );
                        nWidth = aRect.Height();
                    }

                    basegfx::BColor aLineColor = SwViewOption::GetPageBreakColor().getBColor();

                    drawinglayer::primitive2d::Primitive2DContainer aSeq =
                        lcl_CreateDashedIndicatorPrimitive( aStart, aEnd, aLineColor );

                    // Add the text above
                    OUString aBreakText = SwResId(STR_COLUMN_BREAK);

                    basegfx::B2DVector aFontSize;
                    OutputDevice* pOut = gProp.pSGlobalShell->GetOut();
                    vcl::Font aFont = pOut->GetSettings().GetStyleSettings().GetToolFont();
                    aFont.SetFontHeight( 8 * 20 );
                    pOut->SetFont( aFont );
                    drawinglayer::attribute::FontAttribute aFontAttr = drawinglayer::primitive2d::getFontAttributeFromVclFont(
                            aFontSize, aFont, IsRightToLeft(), false );

                    tools::Rectangle aTextRect;
                    pOut->GetTextBoundRect( aTextRect, aBreakText );
                    long nTextOff = ( nWidth - aTextRect.GetWidth() ) / 2;

                    basegfx::B2DHomMatrix aTextMatrix( basegfx::utils::createScaleTranslateB2DHomMatrix(
                                aFontSize.getX(), aFontSize.getY(),
                                aRect.Left() + nTextOff, aRect.Top() ) );
                    if ( IsVertical() )
                    {
                        aTextMatrix = basegfx::B2DHomMatrix( basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix (
                                aFontSize.getX(), aFontSize.getY(), 0.0, M_PI_2,
                                aRect.Right(), aRect.Top() + nTextOff ) );
                    }

                    drawinglayer::primitive2d::TextSimplePortionPrimitive2D * pText =
                            new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
                                aTextMatrix,
                                aBreakText, 0, aBreakText.getLength(),
                                std::vector< double >(),
                                aFontAttr,
                                lang::Locale(),
                                aLineColor );
                    aSeq.push_back( drawinglayer::primitive2d::Primitive2DReference( pText ) );

                    ProcessPrimitives( aSeq );
                }
            }
        }
    }
}

void SwLayoutFrame::PaintBreak( ) const
{
    const SwFrame* pFrame = Lower();
    while ( pFrame )
    {
        if ( pFrame->IsLayoutFrame() )
            static_cast< const SwLayoutFrame*>( pFrame )->PaintBreak( );
        pFrame = pFrame->GetNext();
    }
}

void SwPageFrame::PaintDecorators( ) const
{
    SwWrtShell* pWrtSh = dynamic_cast< SwWrtShell* >( gProp.pSGlobalShell );
    if ( pWrtSh )
    {
        SwEditWin& rEditWin = pWrtSh->GetView().GetEditWin();

        const SwLayoutFrame* pBody = FindBodyCont();
        if ( pBody )
        {
            SwRect aBodyRect( pBody->getFrameArea() );

            if ( gProp.pSGlobalShell->GetOut()->GetOutDevType() != OUTDEV_PRINTER &&
                 !gProp.pSGlobalShell->GetViewOptions()->IsPDFExport() &&
                 !gProp.pSGlobalShell->IsPreview() &&
                 !gProp.pSGlobalShell->GetViewOptions()->IsReadonly() &&
                 !gProp.pSGlobalShell->GetViewOptions()->getBrowseMode() &&
                 ( gProp.pSGlobalShell->IsShowHeaderFooterSeparator( Header ) ||
                   gProp.pSGlobalShell->IsShowHeaderFooterSeparator( Footer ) ) )
            {
                bool bRtl = AllSettings::GetLayoutRTL();
                const SwRect& rVisArea = gProp.pSGlobalShell->VisArea();
                long nXOff = std::min( aBodyRect.Right(), rVisArea.Right() );
                if ( bRtl )
                    nXOff = std::max( aBodyRect.Left(), rVisArea.Left() );

                // Header
                if ( gProp.pSGlobalShell->IsShowHeaderFooterSeparator( Header ) )
                {
                    const SwFrame* pHeaderFrame = Lower();
                    if ( !pHeaderFrame->IsHeaderFrame() )
                        pHeaderFrame = nullptr;

                    long nHeaderYOff = aBodyRect.Top();
                    Point nOutputOff = rEditWin.LogicToPixel( Point( nXOff, nHeaderYOff ) );
                    rEditWin.GetFrameControlsManager().SetHeaderFooterControl( this, Header, nOutputOff );
                }

                // Footer
                if ( gProp.pSGlobalShell->IsShowHeaderFooterSeparator( Footer ) )
                {
                    const SwFrame* pFootnoteContFrame = Lower();
                    while ( pFootnoteContFrame )
                    {
                        if ( pFootnoteContFrame->IsFootnoteContFrame() )
                            aBodyRect.AddBottom( pFootnoteContFrame->getFrameArea().Bottom() - aBodyRect.Bottom() );
                        pFootnoteContFrame = pFootnoteContFrame->GetNext();
                    }

                    long nFooterYOff = aBodyRect.Bottom();
                    Point nOutputOff = rEditWin.LogicToPixel( Point( nXOff, nFooterYOff ) );
                    rEditWin.GetFrameControlsManager().SetHeaderFooterControl( this, Footer, nOutputOff );
                }
            }
        }
    }
}

/**
 * For feature #99657#
 *
 * OD 12.08.2002
 * determines, if background of fly frame has to be drawn transparent
 * declaration found in /core/inc/flyfrm.cxx
 *
 * OD 08.10.2002 #103898# - If the background of the fly frame itself is not
 * transparent and the background is inherited from its parent/grandparent,
 * the background brush, used for drawing, has to be investigated for transparency.
 *
 * @return true, if background is transparent drawn
*/
bool SwFlyFrame::IsBackgroundTransparent() const
{
    bool bBackgroundTransparent = GetFormat()->IsBackgroundTransparent();
    if ( !bBackgroundTransparent &&
         GetFormat()->IsBackgroundBrushInherited() )
    {
        const SvxBrushItem* pBackgrdBrush = nullptr;
        const Color* pSectionTOXColor = nullptr;
        SwRect aDummyRect;
        drawinglayer::attribute::SdrAllFillAttributesHelperPtr aFillAttributes;

        if ( GetBackgroundBrush( aFillAttributes, pBackgrdBrush, pSectionTOXColor, aDummyRect, false, /*bConsiderTextBox=*/false) )
        {
            if ( pSectionTOXColor &&
                 (pSectionTOXColor->GetTransparency() != 0) &&
                 (*pSectionTOXColor != COL_TRANSPARENT) )
            {
                bBackgroundTransparent = true;
            }
            else if(aFillAttributes.get() && aFillAttributes->isUsed())
            {
                bBackgroundTransparent = aFillAttributes->isTransparent();
            }
            else if ( pBackgrdBrush )
            {
                if ( (pBackgrdBrush->GetColor().GetTransparency() != 0) &&
                     (pBackgrdBrush->GetColor() != COL_TRANSPARENT) )
                {
                    bBackgroundTransparent = true;
                }
                else
                {
                    const GraphicObject *pTmpGrf =
                            pBackgrdBrush->GetGraphicObject();
                    if ( pTmpGrf &&
                         (pTmpGrf->GetAttr().GetTransparency() != 0)
                       )
                    {
                        bBackgroundTransparent = true;
                    }
                }
            }
        }
    }

    return bBackgroundTransparent;
};

bool SwFlyFrame::IsPaint( SdrObject *pObj, const SwViewShell *pSh )
{
    SdrObjUserCall *pUserCall;

    if ( nullptr == ( pUserCall = GetUserCall(pObj) ) )
        return true;

    //Attribute dependent, don't paint for printer or Preview
    bool bPaint =  gProp.pSFlyOnlyDraw ||
                       static_cast<SwContact*>(pUserCall)->GetFormat()->GetPrint().GetValue();
    if ( !bPaint )
        bPaint = pSh->GetWin() && !pSh->IsPreview();

    if ( bPaint )
    {
        //The paint may be prevented by the superior Flys.
        SwFrame *pAnch = nullptr;
        if ( dynamic_cast< const SwFlyDrawObj *>( pObj ) !=  nullptr ) // i#117962#
        {
            bPaint = false;
        }
        if ( dynamic_cast< const SwVirtFlyDrawObj *>( pObj ) !=  nullptr )
        {
            SwFlyFrame *pFly = static_cast<SwVirtFlyDrawObj*>(pObj)->GetFlyFrame();
            if ( gProp.pSFlyOnlyDraw && gProp.pSFlyOnlyDraw == pFly )
                return true;

            //Try to avoid displaying the intermediate stage, Flys which don't
            //overlap with the page on which they are anchored won't be
            //painted.
            //HACK: exception: printing of frames in tables, those can overlap
            //a page once in a while when dealing with oversized tables (HTML).
            SwPageFrame *pPage = pFly->FindPageFrame();
            if ( pPage && pPage->getFrameArea().IsOver( pFly->getFrameArea() ) )
            {
                    pAnch = pFly->AnchorFrame();
            }

        }
        else
        {
            // OD 13.10.2003 #i19919# - consider 'virtual' drawing objects
            // OD 2004-03-29 #i26791#
            SwDrawContact* pDrawContact = dynamic_cast<SwDrawContact*>(pUserCall);
            pAnch = pDrawContact ? pDrawContact->GetAnchorFrame(pObj) : nullptr;
            if ( pAnch )
            {
                if ( !pAnch->isFrameAreaPositionValid() )
                    pAnch = nullptr;
                else if ( sal_IntPtr(pSh->GetOut()) == sal_IntPtr(pSh->getIDocumentDeviceAccess().getPrinter( false )))
                {
                    //HACK: we have to omit some of the objects for printing,
                    //otherwise they would be printed twice.
                    //The objects should get printed if the TableHack is active
                    //right now. Afterwards they must not be printed if the
                    //page over which they float position wise gets printed.
                    const SwPageFrame *pPage = pAnch->FindPageFrame();
                    if ( !pPage->getFrameArea().IsOver( pObj->GetCurrentBoundRect() ) )
                        pAnch = nullptr;
                }
            }
            else
            {
                // OD 02.07.2003 #108784# - debug assert
                if ( dynamic_cast< const SdrObjGroup *>( pObj ) ==  nullptr )
                {
                    OSL_FAIL( "<SwFlyFrame::IsPaint(..)> - paint of drawing object without anchor frame!?" );
                }
            }
        }
        if ( pAnch )
        {
            if ( pAnch->IsInFly() )
                bPaint = SwFlyFrame::IsPaint( pAnch->FindFlyFrame()->GetVirtDrawObj(),
                                            pSh );
            else if ( gProp.pSFlyOnlyDraw )
                bPaint = false;
        }
        else
            bPaint = false;
    }
    return bPaint;
}

void SwCellFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const& rRect, SwPrintData const*const) const
{
    if ( GetLayoutRowSpan() >= 1 )
        SwLayoutFrame::PaintSwFrame( rRenderContext, rRect );
}

struct BorderLinesGuard
{
    explicit BorderLinesGuard() : m_pBorderLines(std::move(gProp.pBLines))
    {
        gProp.pBLines.reset(new BorderLines);
    }
    ~BorderLinesGuard()
    {
        gProp.pBLines = std::move(m_pBorderLines);
    }
private:
    std::unique_ptr<BorderLines> m_pBorderLines;
};

void SwFlyFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const& rRect, SwPrintData const*const) const
{
    //optimize thumbnail generation and store procedure to improve odt saving performance, #i120030#
    SwViewShell *pShell = getRootFrame()->GetCurrShell();
    if (pShell && pShell->GetDoc() && pShell->GetDoc()->GetDocShell())
    {
        bool bInGenerateThumbnail = pShell->GetDoc()->GetDocShell()->IsInGenerateAndStoreThumbnail();
        if (bInGenerateThumbnail)
        {
            SwRect aVisRect = pShell->VisArea();
            if (!aVisRect.IsOver(getFrameArea()))
                return;
        }
    }

    //because of the overlapping of frames and drawing objects the flys have to
    //paint their borders (and those of the internal ones) directly.
    //e.g. #33066#
    gProp.pSLines->LockLines(true);
    BorderLinesGuard blg; // this should not paint borders added from PaintBaBo

    SwRect aRect( rRect );
    aRect.Intersection_( getFrameArea() );

    rRenderContext.Push( PushFlags::CLIPREGION );
    rRenderContext.SetClipRegion();
    const SwPageFrame* pPage = FindPageFrame();

    const SwNoTextFrame *pNoText = Lower() && Lower()->IsNoTextFrame()
                                                ? static_cast<const SwNoTextFrame*>(Lower()) : nullptr;

    bool bIsChart = false; //#i102950# don't paint additional borders for charts
    //check whether we have a chart
    if(pNoText)
    {
        const SwNoTextNode* pNoTNd = dynamic_cast<const SwNoTextNode*>(pNoText->GetNode());
        if( pNoTNd )
        {
            SwOLENode* pOLENd = const_cast<SwOLENode*>(pNoTNd->GetOLENode());
            if( pOLENd && pOLENd->GetOLEObj().GetObject().IsChart() )
                bIsChart = true;
        }
    }

    {
        bool bContour = GetFormat()->GetSurround().IsContour();
        tools::PolyPolygon aPoly;
        if ( bContour )
        {
            // OD 16.04.2003 #i13147# - add 2nd parameter with value <true>
            // to indicate that method is called for paint in order to avoid
            // load of the intrinsic graphic.
            bContour = GetContour( aPoly, true );
        }

        // #i47804# - distinguish complete background paint
        // and margin paint.
        // paint complete background for Writer text fly frames
        bool bPaintCompleteBack( !pNoText );
        // paint complete background for transparent graphic and contour,
        // if own background color exists.
        const bool bIsGraphicTransparent = pNoText && pNoText->IsTransparent();
        if ( !bPaintCompleteBack &&
             ( bIsGraphicTransparent|| bContour ) )
        {
            const SwFrameFormat* pSwFrameFormat = dynamic_cast< const SwFrameFormat* >(GetFormat());

            if (pSwFrameFormat && pSwFrameFormat->supportsFullDrawingLayerFillAttributeSet())
            {
                // check for transparency
                const drawinglayer::attribute::SdrAllFillAttributesHelperPtr aFillAttributes(pSwFrameFormat->getSdrAllFillAttributesHelper());

                // check if the new fill attributes are used
                if(aFillAttributes.get() && aFillAttributes->isUsed())
                {
                    bPaintCompleteBack = true;
                }
            }
            else
            {
                SvxBrushItem aBack = GetFormat()->makeBackgroundBrushItem();
                // OD 07.08.2002 #99657# #GetTransChg#
                //     to determine, if background has to be painted, by checking, if
                //     background color is not COL_TRANSPARENT ("no fill"/"auto fill")
                //     or a background graphic exists.
                bPaintCompleteBack = !(aBack.GetColor() == COL_TRANSPARENT) ||
                                     aBack.GetGraphicPos() != GPOS_NONE;
            }
        }
        // paint of margin needed.
        const bool bPaintMarginOnly( !bPaintCompleteBack &&
                                     getFramePrintArea().SSize() != getFrameArea().SSize() );

        // #i47804# - paint background of parent fly frame
        // for transparent graphics in layer Hell, if parent fly frame isn't
        // in layer Hell. It's only painted the intersection between the
        // parent fly frame area and the paint area <aRect>
        const IDocumentDrawModelAccess& rIDDMA = GetFormat()->getIDocumentDrawModelAccess();

        if (bIsGraphicTransparent &&
            GetFormat()->GetDoc()->getIDocumentSettingAccess().get(DocumentSettingId::SUBTRACT_FLYS) &&
            GetVirtDrawObj()->GetLayer() == rIDDMA.GetHellId() &&
            GetAnchorFrame()->FindFlyFrame() )
        {
            const SwFlyFrame* pParentFlyFrame = GetAnchorFrame()->FindFlyFrame();
            if ( pParentFlyFrame->GetDrawObj()->GetLayer() !=
                                            rIDDMA.GetHellId() )
            {
                SwFlyFrame* pOldRet = gProp.pSRetoucheFly2;
                gProp.pSRetoucheFly2 = const_cast<SwFlyFrame*>(this);

                SwBorderAttrAccess aAccess( SwFrame::GetCache(), pParentFlyFrame );
                const SwBorderAttrs &rAttrs = *aAccess.Get();
                SwRect aPaintRect( aRect );
                aPaintRect.Intersection_( pParentFlyFrame->getFrameArea() );
                pParentFlyFrame->PaintSwFrameBackground( aPaintRect, pPage, rAttrs );

                gProp.pSRetoucheFly2 = pOldRet;
            }
        }

        if ( bPaintCompleteBack || bPaintMarginOnly )
        {
            //#24926# JP 01.02.96, PaintBaBo is here partially so PaintSwFrameShadowAndBorder
            //receives the original Rect but PaintSwFrameBackground only the limited
            //one.

            // OD 2004-04-23 #116347#
            rRenderContext.Push( PushFlags::FILLCOLOR|PushFlags::LINECOLOR );
            rRenderContext.SetLineColor();

            pPage = FindPageFrame();

            SwBorderAttrAccess aAccess( SwFrame::GetCache(), static_cast<SwFrame const *>(this) );
            const SwBorderAttrs &rAttrs = *aAccess.Get();

            // paint background
            {
                SwRegionRects aRegion( aRect );
                // #i80822#
                // suppress painting of background in printing area for
                // non-transparent graphics.
                if ( bPaintMarginOnly ||
                     ( pNoText && !bIsGraphicTransparent ) )
                {
                    //What we actually want to paint is the small stripe between
                    //PrtArea and outer border.
                    SwRect aTmp( getFramePrintArea() ); aTmp += getFrameArea().Pos();
                    aRegion -= aTmp;
                }
                if ( bContour )
                {
                    rRenderContext.Push();
                    // #i80822#
                    // apply clip region under the same conditions, which are
                    // used in <SwNoTextFrame::PaintSwFrame(..)> to set the clip region
                    // for painting the graphic/OLE. Thus, the clip region is
                    // also applied for the PDF export.
                    SwViewShell *pSh = getRootFrame()->GetCurrShell();

                    if ( !rRenderContext.GetConnectMetaFile() || !pSh || !pSh->GetWin() )
                    {
                        rRenderContext.SetClipRegion(vcl::Region(aPoly));
                    }

                    for ( size_t i = 0; i < aRegion.size(); ++i )
                    {
                        PaintSwFrameBackground( aRegion[i], pPage, rAttrs, false, true );
                    }

                    rRenderContext.Pop();
                }
                else
                {
                    for ( size_t i = 0; i < aRegion.size(); ++i )
                    {
                        PaintSwFrameBackground( aRegion[i], pPage, rAttrs, false, true );
                    }
                }
            }

            // OD 06.08.2002 #99657# - paint border before painting background
            // paint border
            PaintSwFrameShadowAndBorder(rRect, pPage, rAttrs);

            rRenderContext.Pop();
        }
    }

    // OD 19.12.2002 #106318# - fly frame will paint it's subsidiary lines and
    // the subsidiary lines of its lowers on its own, due to overlapping with
    // other fly frames or other objects.
    if( gProp.pSGlobalShell->GetWin()
        && !bIsChart ) //#i102950# don't paint additional borders for charts
    {
        bool bSubsLineRectsCreated;
        if ( gProp.pSSubsLines )
        {
            // Lock already existing subsidiary lines
            gProp.pSSubsLines->LockLines( true );
            bSubsLineRectsCreated = false;
        }
        else
        {
            // create new subsidiary lines
            gProp.pSSubsLines.reset(new SwSubsRects);
            bSubsLineRectsCreated = true;
        }

        bool bSpecSubsLineRectsCreated;
        if ( gProp.pSSpecSubsLines )
        {
            // Lock already existing special subsidiary lines
            gProp.pSSpecSubsLines->LockLines( true );
            bSpecSubsLineRectsCreated = false;
        }
        else
        {
            // create new special subsidiary lines
            gProp.pSSpecSubsLines.reset(new SwSubsRects);
            bSpecSubsLineRectsCreated = true;
        }
        // Add subsidiary lines of fly frame and its lowers
        RefreshLaySubsidiary( pPage, aRect );
        // paint subsidiary lines of fly frame and its lowers
        gProp.pSSpecSubsLines->PaintSubsidiary( &rRenderContext, nullptr, gProp );
        gProp.pSSubsLines->PaintSubsidiary(&rRenderContext, gProp.pSLines.get(), gProp);
        if ( !bSubsLineRectsCreated )
            // unlock subsidiary lines
            gProp.pSSubsLines->LockLines( false );
        else
        {
            // delete created subsidiary lines container
            gProp.pSSubsLines.reset();
        }

        if ( !bSpecSubsLineRectsCreated )
            // unlock special subsidiary lines
            gProp.pSSpecSubsLines->LockLines( false );
        else
        {
            // delete created special subsidiary lines container
            gProp.pSSpecSubsLines.reset();
        }
    }

    SwLayoutFrame::PaintSwFrame( rRenderContext, aRect );

    Validate();

    // OD 19.12.2002 #106318# - first paint lines added by fly frame paint
    // and then unlock other lines.
    gProp.pSLines->PaintLines( &rRenderContext, gProp );
    gProp.pSLines->LockLines( false );
    // have to paint frame borders added in heaven layer here...
    ProcessPrimitives(gProp.pBLines->GetBorderLines_Clear());

    rRenderContext.Pop();

    if ( gProp.pSProgress && pNoText )
        gProp.pSProgress->Reschedule();
}

void SwTabFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const& rRect, SwPrintData const*const) const
{
    const SwViewOption* pViewOption = gProp.pSGlobalShell->GetViewOptions();
    if (pViewOption->IsTable())
    {
        // #i29550#
        if ( IsCollapsingBorders() )
        {
            SwBorderAttrAccess aAccess( SwFrame::GetCache(), static_cast<SwFrame const *>(this) );
            const SwBorderAttrs &rAttrs = *aAccess.Get();

            // paint shadow
            if ( rAttrs.GetShadow().GetLocation() != SvxShadowLocation::NONE )
            {
                SwRect aRect;
                ::lcl_CalcBorderRect( aRect, this, rAttrs, true, gProp );
                PaintShadow( rRect, aRect, rAttrs );
            }

            SwTabFramePainter aHelper(*this);
            aHelper.PaintLines(rRenderContext, rRect);
        }

        SwLayoutFrame::PaintSwFrame( rRenderContext, rRect );
    }
    // OD 10.01.2003 #i6467# - no light grey rectangle for page preview
    else if ( gProp.pSGlobalShell->GetWin() && !gProp.pSGlobalShell->IsPreview() )
    {
        // OD 10.01.2003 #i6467# - intersect output rectangle with table frame
        SwRect aTabRect( getFramePrintArea() );
        aTabRect.Pos() += getFrameArea().Pos();
        SwRect aTabOutRect( rRect );
        aTabOutRect.Intersection( aTabRect );
        SwViewOption::DrawRect( &rRenderContext, aTabOutRect, COL_LIGHTGRAY );
    }
    const_cast<SwTabFrame*>(this)->ResetComplete();
}

/**
 * Paint border shadow
 *
 * @param[in]       rRect       aligned rect to clip the result
 * @param[in,out]   rOutRect    full painting area as input
 *                              painting area reduced by shadow space for border and background as output
 * @param[in]       rShadow     includes shadow attributes
 * @param[in]       bDrawFullShadowRectangle    paint full rect of shadow
 * @param[in]       bTop        paint top part of the shadow
 * @param[in]       bBottom     paint bottom part of the shadow
 * @param[in]       bLeft       paint left part of the shadow
 * @param[in]       bRight      paint right part of the shadow
**/
static void lcl_PaintShadow( const SwRect& rRect, SwRect& rOutRect,
    const SvxShadowItem& rShadow, const bool bDrawFullShadowRectangle,
    const bool bTop, const bool bBottom,
    const bool bLeft, const bool bRight,
    SwPaintProperties const & properties)
{
    const long nWidth  = ::lcl_AlignWidth ( rShadow.GetWidth(), properties );
    const long nHeight = ::lcl_AlignHeight( rShadow.GetWidth(), properties );

    SwRects aRegion;
    SwRect aOut( rOutRect );

    switch ( rShadow.GetLocation() )
    {
        case SvxShadowLocation::BottomRight:
            {
                if ( bDrawFullShadowRectangle )
                {
                    // OD 06.08.2002 #99657# - draw full shadow rectangle
                    aOut.Top( rOutRect.Top() + nHeight );
                    aOut.Left( rOutRect.Left() + nWidth );
                    aRegion.push_back( aOut );
                }
                else
                {
                    if( bBottom )
                    {
                        aOut.Top( rOutRect.Bottom() - nHeight );
                        if( bLeft )
                            aOut.Left( rOutRect.Left() + nWidth );
                        aRegion.push_back( aOut );
                    }
                    if( bRight )
                    {
                        aOut.Left( rOutRect.Right() - nWidth );
                        if( bTop )
                            aOut.Top( rOutRect.Top() + nHeight );
                        else
                            aOut.Top( rOutRect.Top() );
                        if( bBottom )
                            aOut.Bottom( rOutRect.Bottom() - nHeight );
                        aRegion.push_back( aOut );
                    }
                }

                if( bRight )
                    rOutRect.Right( rOutRect.Right() - nWidth );
                if( bBottom )
                    rOutRect.Bottom( rOutRect.Bottom()- nHeight );
            }
            break;
        case SvxShadowLocation::TopLeft:
            {
                if ( bDrawFullShadowRectangle )
                {
                    // OD 06.08.2002 #99657# - draw full shadow rectangle
                    aOut.Bottom( rOutRect.Bottom() - nHeight );
                    aOut.Right( rOutRect.Right() - nWidth );
                    aRegion.push_back( aOut );
                }
                else
                {
                    if( bTop )
                    {
                        aOut.Bottom( rOutRect.Top() + nHeight );
                        if( bRight )
                            aOut.Right( rOutRect.Right() - nWidth );
                        aRegion.push_back( aOut );
                    }
                    if( bLeft )
                    {
                        aOut.Right( rOutRect.Left() + nWidth );
                        if( bBottom )
                            aOut.Bottom( rOutRect.Bottom() - nHeight );
                        else
                            aOut.Bottom( rOutRect.Bottom() );
                        if( bTop )
                            aOut.Top( rOutRect.Top() + nHeight );
                        aRegion.push_back( aOut );
                    }
                }

                if( bLeft )
                    rOutRect.Left( rOutRect.Left() + nWidth );
                if( bTop )
                    rOutRect.Top( rOutRect.Top() + nHeight );
            }
            break;
        case SvxShadowLocation::TopRight:
            {
                if ( bDrawFullShadowRectangle )
                {
                    // OD 06.08.2002 #99657# - draw full shadow rectangle
                    aOut.Bottom( rOutRect.Bottom() - nHeight);
                    aOut.Left( rOutRect.Left() + nWidth );
                    aRegion.push_back( aOut );
                }
                else
                {
                    if( bTop )
                    {
                        aOut.Bottom( rOutRect.Top() + nHeight );
                        if( bLeft )
                            aOut.Left( rOutRect.Left() + nWidth );
                        aRegion.push_back( aOut );
                    }
                    if( bRight )
                    {
                        aOut.Left( rOutRect.Right() - nWidth );
                        if( bBottom )
                            aOut.Bottom( rOutRect.Bottom() - nHeight );
                        else
                            aOut.Bottom( rOutRect.Bottom() );
                        if( bTop )
                            aOut.Top( rOutRect.Top() + nHeight );
                        aRegion.push_back( aOut );
                    }
                }

                if( bRight )
                    rOutRect.Right( rOutRect.Right() - nWidth );
                if( bTop )
                    rOutRect.Top( rOutRect.Top() + nHeight );
            }
            break;
        case SvxShadowLocation::BottomLeft:
            {
                if ( bDrawFullShadowRectangle )
                {
                    // OD 06.08.2002 #99657# - draw full shadow rectangle
                    aOut.Top( rOutRect.Top() + nHeight );
                    aOut.Right( rOutRect.Right() - nWidth );
                    aRegion.push_back( aOut );
                }
                else
                {
                    if( bBottom )
                    {
                        aOut.Top( rOutRect.Bottom()- nHeight );
                        if( bRight )
                            aOut.Right( rOutRect.Right() - nWidth );
                        aRegion.push_back( aOut );
                    }
                    if( bLeft )
                    {
                        aOut.Right( rOutRect.Left() + nWidth );
                        if( bTop )
                            aOut.Top( rOutRect.Top() + nHeight );
                        else
                            aOut.Top( rOutRect.Top() );
                        if( bBottom )
                            aOut.Bottom( rOutRect.Bottom() - nHeight );
                        aRegion.push_back( aOut );
                    }
                }

                if( bLeft )
                    rOutRect.Left( rOutRect.Left() + nWidth );
                if( bBottom )
                    rOutRect.Bottom( rOutRect.Bottom() - nHeight );
            }
            break;
        default:
            assert(false);
            break;
    }

    vcl::RenderContext *pOut = properties.pSGlobalShell->GetOut();

    DrawModeFlags nOldDrawMode = pOut->GetDrawMode();
    Color aShadowColor( rShadow.GetColor().GetRGBColor() );
    if( !aRegion.empty() && properties.pSGlobalShell->GetWin() &&
        Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
    {
        // In high contrast mode, the output device has already set the
        // DrawModeFlags::SettingsFill flag. This causes the SetFillColor function
        // to ignore the setting of a new color. Therefore we have to reset
        // the drawing mode
        pOut->SetDrawMode( DrawModeFlags::Default );
        aShadowColor = SwViewOption::GetFontColor();
    }

    if ( pOut->GetFillColor() != aShadowColor )
        pOut->SetFillColor( aShadowColor );

    pOut->SetLineColor();

    pOut->SetDrawMode( nOldDrawMode );

    for (SwRect & rOut : aRegion)
    {
        aOut = rOut;
        if ( rRect.IsOver( aOut ) && aOut.Height() > 0 && aOut.Width() > 0 )
        {
            aOut.Intersection_( rRect );
            pOut->DrawRect( aOut.SVRect() );
        }
    }
}

/**
 * Paints a shadow if the format requests so.
 *
 * The shadow is always painted on the outer edge of the OutRect.
 * If needed, the OutRect is shrunk so the painting of the border can be
 * done on it.
 *
 * @note: draw full shadow rectangle for frames with transparent drawn backgrounds (OD 23.08.2002 #99657#)
 */
void SwFrame::PaintShadow( const SwRect& rRect, SwRect& rOutRect,
                         const SwBorderAttrs &rAttrs ) const
{
    SvxShadowItem rShadow = rAttrs.GetShadow();

    const bool bCnt    = IsContentFrame();
    const bool bTop    = !bCnt || rAttrs.GetTopLine  ( *(this) );
    const bool bBottom = !bCnt || rAttrs.GetBottomLine( *(this) );

    if( IsVertical() )
    {
        switch( rShadow.GetLocation() )
        {
            case SvxShadowLocation::BottomRight: rShadow.SetLocation(SvxShadowLocation::BottomLeft);  break;
            case SvxShadowLocation::TopLeft:     rShadow.SetLocation(SvxShadowLocation::TopRight);    break;
            case SvxShadowLocation::TopRight:    rShadow.SetLocation(SvxShadowLocation::BottomRight); break;
            case SvxShadowLocation::BottomLeft:  rShadow.SetLocation(SvxShadowLocation::TopLeft);     break;
            default: break;
        }
    }

    // OD 23.08.2002 #99657# - determine, if full shadow rectangle have to
    //     be drawn or only two shadow rectangles beside the frame.
    //     draw full shadow rectangle, if frame background is drawn transparent.
    //     Status Quo:
    //         SwLayoutFrame can have transparent drawn backgrounds. Thus,
    //         "asked" their frame format.
    const bool bDrawFullShadowRectangle =
            ( IsLayoutFrame() &&
              static_cast<const SwLayoutFrame*>(this)->GetFormat()->IsBackgroundTransparent()
            );

    SwRectFnSet aRectFnSet(this);
    ::lcl_ExtendLeftAndRight( rOutRect, *(this), rAttrs, aRectFnSet.FnRect() );

    lcl_PaintShadow(rRect, rOutRect, rShadow, bDrawFullShadowRectangle, bTop, bBottom, true, true, gProp);
}

void SwFrame::PaintBorderLine( const SwRect& rRect,
                             const SwRect& rOutRect,
                             const SwPageFrame * pPage,
                             const Color *pColor,
                             const SvxBorderLineStyle nStyle ) const
{
    if ( !rOutRect.IsOver( rRect ) )
        return;

    SwRect aOut( rOutRect );
    aOut.Intersection_( rRect );

    const SwTabFrame *pTab = IsCellFrame() ? FindTabFrame() : nullptr;
    SubColFlags nSubCol = ( IsCellFrame() || IsRowFrame() )
                          ? SubColFlags::Tab
                          : ( IsInSct()
                              ? SubColFlags::Sect
                              : ( IsInFly() ? SubColFlags::Fly : SubColFlags::Page ) );
    if( pColor && gProp.pSGlobalShell->GetWin() &&
        Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
    {
        pColor = &SwViewOption::GetFontColor();
    }

    if (pPage->GetSortedObjs() &&
        pPage->GetFormat()->GetDoc()->getIDocumentSettingAccess().get(DocumentSettingId::SUBTRACT_FLYS))
    {
        SwRegionRects aRegion( aOut, 4 );
        basegfx::utils::B2DClipState aClipState;
        ::lcl_SubtractFlys( this, pPage, aOut, aRegion, aClipState, gProp );
        for ( size_t i = 0; i < aRegion.size(); ++i )
            gProp.pSLines->AddLineRect( aRegion[i], pColor, nStyle, pTab, nSubCol, gProp );
    }
    else
        gProp.pSLines->AddLineRect( aOut, pColor, nStyle, pTab, nSubCol, gProp );
}

namespace drawinglayer
{
    namespace primitive2d
    {
        class SW_DLLPUBLIC SwBorderRectanglePrimitive2D : public BufferedDecompositionPrimitive2D
        {
        private:
            /// the transformation defining the geometry of this BorderRectangle
            basegfx::B2DHomMatrix       maB2DHomMatrix;

            /// the four styles to be used
            svx::frame::Style           maStyleTop;
            svx::frame::Style           maStyleRight;
            svx::frame::Style           maStyleBottom;
            svx::frame::Style           maStyleLeft;

        protected:
            /// local decomposition.
            virtual void create2DDecomposition(
                Primitive2DContainer& rContainer,
                const geometry::ViewInformation2D& rViewInformation) const override;

        public:
            /// constructor
            SwBorderRectanglePrimitive2D(
                const basegfx::B2DHomMatrix& rB2DHomMatrix,
                const svx::frame::Style& rStyleTop,
                const svx::frame::Style& rStyleRight,
                const svx::frame::Style& rStyleBottom,
                const svx::frame::Style& rStyleLeft);

            /// data read access
            const basegfx::B2DHomMatrix& getB2DHomMatrix() const { return maB2DHomMatrix; }
            const svx::frame::Style& getStyleTop() const { return maStyleTop; }
            const svx::frame::Style& getStyleRight() const { return maStyleRight; }
            const svx::frame::Style& getStyleBottom() const { return maStyleBottom; }
            const svx::frame::Style& getStyleLeft() const { return maStyleLeft; }

            /// 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
            DeclPrimitive2DIDBlock()
        };

        void SwBorderRectanglePrimitive2D::create2DDecomposition(
            Primitive2DContainer& rContainer,
            const geometry::ViewInformation2D& /*rViewInformation*/) const
        {
            basegfx::B2DPoint aTopLeft(getB2DHomMatrix() * basegfx::B2DPoint(0.0, 0.0));
            basegfx::B2DPoint aTopRight(getB2DHomMatrix() * basegfx::B2DPoint(1.0, 0.0));
            basegfx::B2DPoint aBottomLeft(getB2DHomMatrix() * basegfx::B2DPoint(0.0, 1.0));
            basegfx::B2DPoint aBottomRight(getB2DHomMatrix() * basegfx::B2DPoint(1.0, 1.0));

            if(getStyleTop().IsUsed())
            {
                // move top left/right inwards half border width
                basegfx::B2DVector aDown(getB2DHomMatrix() * basegfx::B2DVector(0.0, 1.0));
                aDown.setLength(getStyleTop().GetWidth() * 0.5);
                aTopLeft += aDown;
                aTopRight += aDown;
            }

            if(getStyleBottom().IsUsed())
            {
                // move bottom left/right inwards half border width
                basegfx::B2DVector aUp(getB2DHomMatrix() * basegfx::B2DVector(0.0, -1.0));
                aUp.setLength(getStyleBottom().GetWidth() * 0.5);
                aBottomLeft += aUp;
                aBottomRight += aUp;
            }

            if(getStyleLeft().IsUsed())
            {
                // move left top/bottom inwards half border width
                basegfx::B2DVector aRight(getB2DHomMatrix() * basegfx::B2DVector(1.0, 0.0));
                aRight.setLength(getStyleLeft().GetWidth() * 0.5);
                aTopLeft += aRight;
                aBottomLeft += aRight;
            }

            if(getStyleRight().IsUsed())
            {
                // move right top/bottom inwards half border width
                basegfx::B2DVector aLeft(getB2DHomMatrix() * basegfx::B2DVector(-1.0, 0.0));
                aLeft.setLength(getStyleRight().GetWidth() * 0.5);
                aTopRight += aLeft;
                aBottomRight += aLeft;
            }

            // go round-robin, from TopLeft to TopRight, down, left and back up. That
            // way, the borders will not need to be mirrored in any way
            if(getStyleTop().IsUsed())
            {
                // create BorderPrimitive(s) for top border
                const basegfx::B2DVector aVector(aTopRight - aTopLeft);
                svx::frame::StyleVectorTable aStartStyleVectorTable;
                svx::frame::StyleVectorTable aEndStyleVectorTable;

                if(getStyleLeft().IsUsed())
                {
                    aStartStyleVectorTable.add(getStyleLeft(), aVector, basegfx::B2DVector(aBottomLeft - aTopLeft), false);
                }

                if(getStyleRight().IsUsed())
                {
                    aEndStyleVectorTable.add(getStyleRight(), -aVector, basegfx::B2DVector(aBottomRight - aTopRight), false);
                }

                CreateBorderPrimitives(
                    rContainer,
                    aTopLeft,
                    aVector,
                    getStyleTop(),
                    aStartStyleVectorTable,
                    aEndStyleVectorTable,
                    nullptr);
            }

            if(getStyleRight().IsUsed())
            {
                // create BorderPrimitive(s) for right border
                const basegfx::B2DVector aVector(aBottomRight - aTopRight);
                svx::frame::StyleVectorTable aStartStyleVectorTable;
                svx::frame::StyleVectorTable aEndStyleVectorTable;

                if(getStyleTop().IsUsed())
                {
                    aStartStyleVectorTable.add(getStyleTop(), aVector, basegfx::B2DVector(aTopLeft - aTopRight), false);
                }

                if(getStyleBottom().IsUsed())
                {
                    aEndStyleVectorTable.add(getStyleBottom(), -aVector, basegfx::B2DVector(aBottomLeft - aBottomRight), false);
                }

                CreateBorderPrimitives(
                    rContainer,
                    aTopRight,
                    aVector,
                    getStyleRight(),
                    aStartStyleVectorTable,
                    aEndStyleVectorTable,
                    nullptr);
            }

            if(getStyleBottom().IsUsed())
            {
                // create BorderPrimitive(s) for bottom border
                const basegfx::B2DVector aVector(aBottomLeft - aBottomRight);
                svx::frame::StyleVectorTable aStartStyleVectorTable;
                svx::frame::StyleVectorTable aEndStyleVectorTable;

                if(getStyleRight().IsUsed())
                {
                    aStartStyleVectorTable.add(getStyleRight(), aVector, basegfx::B2DVector(aTopRight - aBottomRight), false);
                }

                if(getStyleLeft().IsUsed())
                {
                    aEndStyleVectorTable.add(getStyleLeft(), -aVector, basegfx::B2DVector(aTopLeft - aBottomLeft), false);
                }

                CreateBorderPrimitives(
                    rContainer,
                    aBottomRight,
                    aVector,
                    getStyleBottom(),
                    aStartStyleVectorTable,
                    aEndStyleVectorTable,
                    nullptr);
            }

            if(getStyleLeft().IsUsed())
            {
                // create BorderPrimitive(s) for left border
                const basegfx::B2DVector aVector(aTopLeft - aBottomLeft);
                svx::frame::StyleVectorTable aStartStyleVectorTable;
                svx::frame::StyleVectorTable aEndStyleVectorTable;

                if(getStyleBottom().IsUsed())
                {
                    aStartStyleVectorTable.add(getStyleBottom(), aVector, basegfx::B2DVector(aBottomRight - aBottomLeft), false);
                }

                if(getStyleTop().IsUsed())
                {
                    aEndStyleVectorTable.add(getStyleTop(), -aVector, basegfx::B2DVector(aTopRight - aTopLeft), false);
                }

                CreateBorderPrimitives(
                    rContainer,
                    aBottomLeft,
                    aVector,
                    getStyleLeft(),
                    aStartStyleVectorTable,
                    aEndStyleVectorTable,
                    nullptr);
            }
        }

        SwBorderRectanglePrimitive2D::SwBorderRectanglePrimitive2D(
            const basegfx::B2DHomMatrix& rB2DHomMatrix,
            const svx::frame::Style& rStyleTop,
            const svx::frame::Style& rStyleRight,
            const svx::frame::Style& rStyleBottom,
            const svx::frame::Style& rStyleLeft)
        :   BufferedDecompositionPrimitive2D(),
            maB2DHomMatrix(rB2DHomMatrix),
            maStyleTop(rStyleTop),
            maStyleRight(rStyleRight),
            maStyleBottom(rStyleBottom),
            maStyleLeft(rStyleLeft)
        {
        }

        bool SwBorderRectanglePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
        {
            if(BasePrimitive2D::operator==(rPrimitive))
            {
                const SwBorderRectanglePrimitive2D& rCompare = static_cast<const SwBorderRectanglePrimitive2D&>(rPrimitive);

                return (getB2DHomMatrix() == rCompare.getB2DHomMatrix() &&
                    getStyleTop() == rCompare.getStyleTop() &&
                    getStyleRight() == rCompare.getStyleRight() &&
                    getStyleBottom() == rCompare.getStyleBottom() &&
                    getStyleLeft() == rCompare.getStyleLeft());
            }

            return false;
        }

        basegfx::B2DRange SwBorderRectanglePrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
        {
            basegfx::B2DRange aRetval(0.0, 0.0, 1.0, 1.0);

            aRetval.transform(getB2DHomMatrix());
            return aRetval;
        }

        // provide unique ID
        ImplPrimitive2DIDBlock(SwBorderRectanglePrimitive2D, PRIMITIVE2D_ID_SWBORDERRECTANGLERIMITIVE)

    } // end of namespace primitive2d
} // end of namespace drawinglayer

void PaintCharacterBorder(
    const SwFont& rFont,
    const SwRect& rPaintArea,
    const bool bVerticalLayout,
    const bool bJoinWithPrev,
    const bool bJoinWithNext )
{
    SwRect aAlignedRect(rPaintArea);
    SwAlignRect(aAlignedRect, gProp.pSGlobalShell, gProp.pSGlobalShell->GetOut());

    bool bTop = true;
    bool bBottom = true;
    bool bLeft = true;
    bool bRight = true;

    switch( rFont.GetOrientation(bVerticalLayout) )
    {
        case 0 :
            bLeft = !bJoinWithPrev;
            bRight = !bJoinWithNext;
            break;
        case 900 :
            bBottom = !bJoinWithPrev;
            bTop = !bJoinWithNext;
            break;
        case 1800 :
            bRight = !bJoinWithPrev;
            bLeft = !bJoinWithNext;
            break;
        case 2700 :
            bTop = !bJoinWithPrev;
            bBottom = !bJoinWithNext;
            break;
    }

    // Paint shadow (reduce painting rect)
    {
        const SvxShadowItem aShadow(
            0, &rFont.GetShadowColor(), rFont.GetShadowWidth(),
            rFont.GetAbsShadowLocation(bVerticalLayout));

        if( aShadow.GetLocation() != SvxShadowLocation::NONE )
        {
            lcl_PaintShadow( rPaintArea, aAlignedRect, aShadow,
                             false, bTop, bBottom, bLeft, bRight, gProp);
        }
    }

    const basegfx::B2DHomMatrix aBorderTransform(
        basegfx::utils::createScaleTranslateB2DHomMatrix(
            aAlignedRect.Width(), aAlignedRect.Height(),
            aAlignedRect.Left(), aAlignedRect.Top()));
    const svx::frame::Style aStyleTop(bTop ? rFont.GetAbsTopBorder(bVerticalLayout).get_ptr() : nullptr, 1.0);
    const svx::frame::Style aStyleRight(bRight ? rFont.GetAbsRightBorder(bVerticalLayout).get_ptr() : nullptr, 1.0);
    const svx::frame::Style aStyleBottom(bBottom ? rFont.GetAbsBottomBorder(bVerticalLayout).get_ptr() : nullptr, 1.0);
    const svx::frame::Style aStyleLeft(bLeft ? rFont.GetAbsLeftBorder(bVerticalLayout).get_ptr() : nullptr, 1.0);
    drawinglayer::primitive2d::Primitive2DContainer aBorderLineTarget;

    aBorderLineTarget.append(
        drawinglayer::primitive2d::Primitive2DReference(
            new drawinglayer::primitive2d::SwBorderRectanglePrimitive2D(
                aBorderTransform,
                aStyleTop,
                aStyleRight,
                aStyleBottom,
                aStyleLeft)));
    gProp.pBLines->AddBorderLines(aBorderLineTarget);
}

/// #i15844#
static const SwFrame* lcl_HasNextCell( const SwFrame& rFrame )
{
    OSL_ENSURE( rFrame.IsCellFrame(),
            "lcl_HasNextCell( const SwFrame& rFrame ) should be called with SwCellFrame" );

    const SwFrame* pTmpFrame = &rFrame;
    do
    {
        if ( pTmpFrame->GetNext() )
            return pTmpFrame->GetNext();

        pTmpFrame = pTmpFrame->GetUpper()->GetUpper();
    }
    while ( pTmpFrame->IsCellFrame() );

    return nullptr;
}

/**
 * Determine cell frame, from which the border attributes
 * for paint of top/bottom border has to be used.
 *
 * OD 21.02.2003 #b4779636#, #107692#
 *
 * @param _pCellFrame
 * input parameter - constant pointer to cell frame for which the cell frame
 * for the border attributes has to be determined.
 *
 * @param _rCellBorderAttrs
 * input parameter - constant reference to the border attributes of cell frame
 * <_pCellFrame>.
 *
 * @param _bTop
 * input parameter - boolean, that controls, if cell frame for top border or
 * for bottom border has to be determined.
 *
 * @return constant pointer to cell frame, for which the border attributes has
 * to be used
 */
static const SwFrame* lcl_GetCellFrameForBorderAttrs( const SwFrame*         _pCellFrame,
                                           const SwBorderAttrs& _rCellBorderAttrs,
                                           const bool           _bTop )
{
    OSL_ENSURE( _pCellFrame, "No cell frame available, dying soon" );

    // determine, if cell frame is at bottom/top border of a table frame and
    // the table frame has/is a follow.
    const SwFrame* pTmpFrame = _pCellFrame;
    bool bCellAtBorder = true;
    bool bCellAtLeftBorder = !_pCellFrame->GetPrev();
    bool bCellAtRightBorder = !_pCellFrame->GetNext();
    while( !pTmpFrame->IsRowFrame() || !pTmpFrame->GetUpper()->IsTabFrame() )
    {
        pTmpFrame = pTmpFrame->GetUpper();
        if ( pTmpFrame->IsRowFrame() &&
             (_bTop ? pTmpFrame->GetPrev() : pTmpFrame->GetNext())
           )
        {
            bCellAtBorder = false;
        }
        if ( pTmpFrame->IsCellFrame() )
        {
            if ( pTmpFrame->GetPrev() )
            {
                bCellAtLeftBorder = false;
            }
            if ( pTmpFrame->GetNext() )
            {
                bCellAtRightBorder = false;
            }
        }
    }
    OSL_ENSURE( pTmpFrame && pTmpFrame->IsRowFrame(), "No RowFrame available" );

    const SwLayoutFrame* pParentRowFrame = static_cast<const SwLayoutFrame*>(pTmpFrame);
    const SwTabFrame* pParentTabFrame =
            static_cast<const SwTabFrame*>(pParentRowFrame->GetUpper());

    const bool bCellNeedsAttribute = bCellAtBorder &&
                                     ( _bTop ?
                                      // bCellInFirstRowWithMaster
                                       ( !pParentRowFrame->GetPrev() &&
                                         pParentTabFrame->IsFollow() &&
                                         0 == pParentTabFrame->GetTable()->GetRowsToRepeat() ) :
                                      // bCellInLastRowWithFollow
                                       ( !pParentRowFrame->GetNext() &&
                                         pParentTabFrame->GetFollow() )
                                     );

    const SwFrame* pRet = _pCellFrame;
    if ( bCellNeedsAttribute )
    {
        // determine, if cell frame has no borders inside the table.
        const SwFrame* pNextCell = nullptr;
        bool bNoBordersInside = false;

        if ( bCellAtLeftBorder && ( nullptr != ( pNextCell = lcl_HasNextCell( *_pCellFrame ) ) ) )
        {
            SwBorderAttrAccess aAccess( SwFrame::GetCache(), pNextCell );
            const SwBorderAttrs &rBorderAttrs = *aAccess.Get();
            const SvxBoxItem& rBorderBox = rBorderAttrs.GetBox();
            bCellAtRightBorder = !lcl_HasNextCell( *pNextCell );
            bNoBordersInside =
                ( !rBorderBox.GetTop()    || !pParentRowFrame->GetPrev() ) &&
                  !rBorderBox.GetLeft() &&
                ( !rBorderBox.GetRight()  || bCellAtRightBorder ) &&
                ( !rBorderBox.GetBottom() || !pParentRowFrame->GetNext() );
        }
        else
        {
            const SvxBoxItem& rBorderBox = _rCellBorderAttrs.GetBox();
            bNoBordersInside =
                ( !rBorderBox.GetTop()    || !pParentRowFrame->GetPrev() ) &&
                ( !rBorderBox.GetLeft()   || bCellAtLeftBorder ) &&
                ( !rBorderBox.GetRight()  || bCellAtRightBorder ) &&
                ( !rBorderBox.GetBottom() || !pParentRowFrame->GetNext() );
        }

        if ( bNoBordersInside )
        {
            if ( _bTop && !_rCellBorderAttrs.GetBox().GetTop() )
            {
                //-hack
                // Cell frame has no top border and no border inside the table, but
                // it is at the top border of a table frame, which is a follow.
                // Thus, use border attributes of cell frame in first row of complete table.
                // First, determine first table frame of complete table.
                SwTabFrame* pMasterTabFrame = pParentTabFrame->FindMaster( true );
                // determine first row of complete table.
                const SwFrame* pFirstRow = pMasterTabFrame->GetLower();
                // return first cell in first row
                SwFrame* pLowerCell = const_cast<SwFrame*>(pFirstRow->GetLower());
                while ( !pLowerCell->IsCellFrame() ||
                        ( pLowerCell->GetLower() && pLowerCell->GetLower()->IsRowFrame() )
                      )
                {
                    pLowerCell = pLowerCell->GetLower();
                }
                OSL_ENSURE( pLowerCell && pLowerCell->IsCellFrame(), "No CellFrame available" );
                pRet = pLowerCell;
            }
            else if ( !_bTop && !_rCellBorderAttrs.GetBox().GetBottom() )
            {
                //-hack
                // Cell frame has no bottom border and no border inside the table,
                // but it is at the bottom border of a table frame, which has a follow.
                // Thus, use border attributes of cell frame in last row of complete table.
                // First, determine last table frame of complete table.
                SwTabFrame* pLastTabFrame = const_cast<SwTabFrame*>(pParentTabFrame->GetFollow());
                while ( pLastTabFrame->GetFollow() )
                {
                    pLastTabFrame = pLastTabFrame->GetFollow();
                }
                // determine last row of complete table.
                SwFrame* pLastRow = pLastTabFrame->GetLastLower();
                // return first bottom border cell in last row
                SwFrame* pLowerCell = pLastRow->GetLower();
                while ( !pLowerCell->IsCellFrame() ||
                        ( pLowerCell->GetLower() && pLowerCell->GetLower()->IsRowFrame() )
                      )
                {
                    if ( pLowerCell->IsRowFrame() )
                    {
                        while ( pLowerCell->GetNext() )
                        {
                            pLowerCell = pLowerCell->GetNext();
                        }
                    }
                    pLowerCell = pLowerCell->GetLower();
                }
                OSL_ENSURE( pLowerCell && pLowerCell->IsCellFrame(), "No CellFrame available" );
                pRet = pLowerCell;
            }
        }
    }

    return pRet;
}

drawinglayer::processor2d::BaseProcessor2D * SwFrame::CreateProcessor2D( ) const
{
    basegfx::B2DRange aViewRange;

    SdrPage *pDrawPage = getRootFrame()->GetCurrShell()->Imp()->GetPageView()->GetPage();
    const drawinglayer::geometry::ViewInformation2D aNewViewInfos(
            basegfx::B2DHomMatrix(  ),
            getRootFrame()->GetCurrShell()->GetOut()->GetViewTransformation(),
            aViewRange,
            GetXDrawPageForSdrPage( pDrawPage ),
            0.0,
            uno::Sequence< beans::PropertyValue >() );

    return  drawinglayer::processor2d::createBaseProcessor2DFromOutputDevice(
                    *getRootFrame()->GetCurrShell()->GetOut(),
                    aNewViewInfos );
}

void SwFrame::ProcessPrimitives( const drawinglayer::primitive2d::Primitive2DContainer& rSequence ) const
{
    drawinglayer::processor2d::BaseProcessor2D * pProcessor2D = CreateProcessor2D();

    if ( pProcessor2D )
    {
        pProcessor2D->process( rSequence );
        delete pProcessor2D;
    }
}

/// Paints shadows and borders
void SwFrame::PaintSwFrameShadowAndBorder(
    const SwRect& rRect,
    const SwPageFrame* /*pPage*/,
    const SwBorderAttrs& rAttrs) const
{
    // There's nothing (Row,Body,Footnote,Root,Column,NoText) need to do here
    if (GetType() & (SwFrameType::NoTxt|SwFrameType::Row|SwFrameType::Body|SwFrameType::Ftn|SwFrameType::Column|SwFrameType::Root))
        return;

    if (IsCellFrame() && !gProp.pSGlobalShell->GetViewOptions()->IsTable())
        return;

    // #i29550#
    if ( IsTabFrame() || IsCellFrame() || IsRowFrame() )
    {
        const SwTabFrame* pTabFrame = FindTabFrame();
        if ( pTabFrame->IsCollapsingBorders() )
            return;

        if ( pTabFrame->GetTable()->IsNewModel() && ( !IsCellFrame() || IsCoveredCell() ) )
            return;
    }

    const bool bLine = rAttrs.IsLine();
    const bool bShadow = rAttrs.GetShadow().GetLocation() != SvxShadowLocation::NONE;

    // - flag to control,
    //-hack has to be used.
    const bool bb4779636HackActive = true;

    const SwFrame* pCellFrameForBottomBorderAttrs = nullptr;
    const SwFrame* pCellFrameForTopBorderAttrs = nullptr;
    bool         bFoundCellForTopOrBorderAttrs = false;
    if ( bb4779636HackActive && IsCellFrame() )
    {
        pCellFrameForBottomBorderAttrs = lcl_GetCellFrameForBorderAttrs( this, rAttrs, false );
        if ( pCellFrameForBottomBorderAttrs != this )
            bFoundCellForTopOrBorderAttrs = true;
        pCellFrameForTopBorderAttrs = lcl_GetCellFrameForBorderAttrs( this, rAttrs, true );
        if ( pCellFrameForTopBorderAttrs != this )
            bFoundCellForTopOrBorderAttrs = true;
    }

    // - add condition <bFoundCellForTopOrBorderAttrs>
    //-hack
    if ( !(bLine || bShadow || bFoundCellForTopOrBorderAttrs) )
        return;

    //If the rectangle is completely inside the PrtArea, no border needs to
    //be painted.
    //For the PrtArea the aligned value needs to be used, otherwise it could
    //happen, that some parts won't be processed.
    SwRect aRect( getFramePrintArea() );
    aRect += getFrameArea().Pos();
    ::SwAlignRect( aRect, gProp.pSGlobalShell, gProp.pSGlobalShell->GetOut() );
    // OD 27.09.2002 #103636# - new local boolean variable in order to
    // suspend border paint under special cases - see below.
    // NOTE: This is a fix for the implementation of feature #99657#.
    bool bDrawOnlyShadowForTransparentFrame = false;
    if ( aRect.IsInside( rRect ) )
    {
        // OD 27.09.2002 #103636# - paint shadow, if background is transparent.
        // Because of introduced transparent background for fly frame #99657#,
        // the shadow have to be drawn if the background is transparent,
        // in spite the fact that the paint rectangle <rRect> lies fully
        // in the printing area.
        // NOTE to chosen solution:
        //     On transparent background, continue processing, but suspend
        //     drawing of border by setting <bDrawOnlyShadowForTransparentFrame>
        //     to true.
        if ( IsLayoutFrame() &&
             static_cast<const SwLayoutFrame*>(this)->GetFormat()->IsBackgroundTransparent() )
        {
             bDrawOnlyShadowForTransparentFrame = true;
        }
        else
        {
            return;
        }
    }

    ::lcl_CalcBorderRect( aRect, this, rAttrs, true, gProp );
    rAttrs.SetGetCacheLine( true );

    if(bShadow)
    {
        PaintShadow(rRect, aRect, rAttrs);
    }

    // OD 27.09.2002 #103636# - suspend drawing of border
    // add condition < NOT bDrawOnlyShadowForTransparentFrame > - see above
    // - add condition <bFoundCellForTopOrBorderAttrs>
    //-hack.
    if((bLine || bFoundCellForTopOrBorderAttrs) && !bDrawOnlyShadowForTransparentFrame)
    {
        // define SvxBorderLine(s) to use
        const SvxBoxItem& rBox(rAttrs.GetBox());
        const SvxBorderLine* pLeftBorder(rBox.GetLeft());
        const SvxBorderLine* pRightBorder(rBox.GetRight());
        const SvxBorderLine* pTopBorder(rBox.GetTop());
        const SvxBorderLine* pBottomBorder(rBox.GetBottom());

        // if R2L, exchange Right/Left
        const bool bR2L(IsCellFrame() && IsRightToLeft());

        if(bR2L)
        {
            std::swap(pLeftBorder, pRightBorder);
        }

        // if ContentFrame and joined Prev/Next, reset top/bottom as needed
        if(IsContentFrame())
        {
            if(rAttrs.JoinedWithPrev(*this))
            {
                    pTopBorder = nullptr;
            }

            if(rAttrs.JoinedWithNext(*this))
            {
                    pBottomBorder = nullptr;
            }
        }

        // necessary to replace TopBorder?
        if((!IsContentFrame() || rAttrs.GetTopLine(*this)) && IsCellFrame() && pCellFrameForTopBorderAttrs != this)
        {
            SwBorderAttrAccess aAccess(SwFrame::GetCache(), pCellFrameForTopBorderAttrs);
            pTopBorder = aAccess.Get()->GetBox().GetTop();
        }

        // necessary to replace BottomBorder?
        if((!IsContentFrame() || rAttrs.GetBottomLine(*this)) && IsCellFrame() && pCellFrameForBottomBorderAttrs != this)
        {
            SwBorderAttrAccess aAccess(SwFrame::GetCache(), pCellFrameForBottomBorderAttrs);
            pBottomBorder = aAccess.Get()->GetBox().GetBottom();
        }

        if(nullptr != pLeftBorder || nullptr != pRightBorder || nullptr != pTopBorder || nullptr != pBottomBorder)
        {
            // now we have all SvxBorderLine(s) sorted out, create geometry
            const basegfx::B2DHomMatrix aBorderTransform(
                basegfx::utils::createScaleTranslateB2DHomMatrix(
                    aRect.Width(), aRect.Height(),
                    aRect.Left(), aRect.Top()));
            const svx::frame::Style aStyleTop(pTopBorder, 1.0);
            const svx::frame::Style aStyleRight(pRightBorder, 1.0);
            const svx::frame::Style aStyleBottom(pBottomBorder, 1.0);
            const svx::frame::Style aStyleLeft(pLeftBorder, 1.0);
            drawinglayer::primitive2d::Primitive2DContainer aBorderLineTarget;

            aBorderLineTarget.append(
                drawinglayer::primitive2d::Primitive2DReference(
                    new drawinglayer::primitive2d::SwBorderRectanglePrimitive2D(
                        aBorderTransform,
                        aStyleTop,
                        aStyleRight,
                        aStyleBottom,
                        aStyleLeft)));
            gProp.pBLines->AddBorderLines(aBorderLineTarget);
        }
    }

    rAttrs.SetGetCacheLine( false );
}

/**
 * Special implementation because of the footnote line
 *
 * Currently only the top frame needs to be taken into account
 * Other lines and shadows are set aside
 */
void SwFootnoteContFrame::PaintSwFrameShadowAndBorder(
    const SwRect& rRect,
    const SwPageFrame* pPage,
    const SwBorderAttrs&) const
{
    //If the rectangle is completely inside the PrtArea, no border needs to
    //be painted.
    SwRect aRect( getFramePrintArea() );
    aRect.Pos() += getFrameArea().Pos();
    if ( !aRect.IsInside( rRect ) )
        PaintLine( rRect, pPage );
}

/// Paint footnote lines.
void SwFootnoteContFrame::PaintLine( const SwRect& rRect,
                              const SwPageFrame *pPage ) const
{
    //The length of the line is derived from the percentual indication on the
    //PageDesc. The position is also stated on the PageDesc.
    //The pen can directly be taken from the PageDesc.

    if ( !pPage )
        pPage = FindPageFrame();
    const SwPageFootnoteInfo &rInf = pPage->GetPageDesc()->GetFootnoteInfo();

    SwRectFnSet aRectFnSet(this);
    SwTwips nPrtWidth = aRectFnSet.GetWidth(getFramePrintArea());
    Fraction aFract( nPrtWidth, 1 );
    const SwTwips nWidth = static_cast<long>(aFract *= rInf.GetWidth());

    SwTwips nX = aRectFnSet.GetPrtLeft(*this);
    switch ( rInf.GetAdj() )
    {
        case css::text::HorizontalAdjust_CENTER:
            nX += nPrtWidth/2 - nWidth/2; break;
        case css::text::HorizontalAdjust_RIGHT:
            nX += nPrtWidth - nWidth; break;
        case css::text::HorizontalAdjust_LEFT:
            /* do nothing */; break;
        default:
            SAL_WARN("sw.core", "New adjustment for footnote lines?");
            assert(false);
    }
    SwTwips nLineWidth = rInf.GetLineWidth();
    const SwRect aLineRect = aRectFnSet.IsVert() ?
        SwRect( Point(getFrameArea().Left()+getFrameArea().Width()-rInf.GetTopDist()-nLineWidth,
                      nX), Size( nLineWidth, nWidth ) )
            : SwRect( Point( nX, getFrameArea().Pos().Y() + rInf.GetTopDist() ),
                            Size( nWidth, rInf.GetLineWidth()));
    if ( aLineRect.HasArea() && rInf.GetLineStyle() != SvxBorderLineStyle::NONE)
        PaintBorderLine( rRect, aLineRect , pPage, &rInf.GetLineColor(),
                rInf.GetLineStyle() );
}

/// Paints the separator line for inside columns
void SwLayoutFrame::PaintColLines( const SwRect &rRect, const SwFormatCol &rFormatCol,
                                 const SwPageFrame *pPage ) const
{
    const SwFrame *pCol = Lower();
    if ( !pCol || !pCol->IsColumnFrame() )
        return;

    SwRectFn fnRect = pCol->IsVertical() ? ( pCol->IsVertLR() ? fnRectVertL2R : fnRectVert ) : fnRectHori;

    SwRect aLineRect = getFramePrintArea();
    aLineRect += getFrameArea().Pos();

    SwTwips nTop = ((aLineRect.*fnRect->fnGetHeight)()*rFormatCol.GetLineHeight())
                   / 100 - (aLineRect.*fnRect->fnGetHeight)();
    SwTwips nBottom = 0;

    switch ( rFormatCol.GetLineAdj() )
    {
        case COLADJ_CENTER:
            nBottom = nTop / 2; nTop -= nBottom; break;
        case COLADJ_TOP:
            nBottom = nTop; nTop = 0; break;
        case COLADJ_BOTTOM:
            break;
        default:
            OSL_ENSURE( false, "New adjustment for column lines?" );
    }

    if( nTop )
        (aLineRect.*fnRect->fnSubTop)( nTop );
    if( nBottom )
        (aLineRect.*fnRect->fnAddBottom)( nBottom );

    SwTwips nPenHalf = rFormatCol.GetLineWidth();
    (aLineRect.*fnRect->fnSetWidth)( nPenHalf );
    nPenHalf /= 2;

    //We need to be a bit generous here, to not lose something.
    SwRect aRect( rRect );
    (aRect.*fnRect->fnSubLeft)( nPenHalf + gProp.nSPixelSzW );
    (aRect.*fnRect->fnAddRight)( nPenHalf + gProp.nSPixelSzW );
    SwRectGet fnGetX = IsRightToLeft() ? fnRect->fnGetLeft : fnRect->fnGetRight;
    while ( pCol->GetNext() )
    {
        (aLineRect.*fnRect->fnSetPosX)
            ( (pCol->getFrameArea().*fnGetX)() - nPenHalf );
        if ( aRect.IsOver( aLineRect ) )
            PaintBorderLine( aRect, aLineRect , pPage, &rFormatCol.GetLineColor(),
                   rFormatCol.GetLineStyle() );
        pCol = pCol->GetNext();
    }
}

void SwPageFrame::PaintGrid( OutputDevice const * pOut, SwRect const &rRect ) const
{
    if( !m_bHasGrid || gProp.pSRetoucheFly || gProp.pSRetoucheFly2 )
        return;
    SwTextGridItem const*const pGrid(GetGridItem(this));
    if( pGrid && ( OUTDEV_PRINTER != pOut->GetOutDevType() ?
        pGrid->GetDisplayGrid() : pGrid->GetPrintGrid() ) )
    {
        const SwLayoutFrame* pBody = FindBodyCont();
        if( pBody )
        {
            SwRect aGrid( pBody->getFramePrintArea() );
            aGrid += pBody->getFrameArea().Pos();

            SwRect aInter( aGrid );
            aInter.Intersection( rRect );
            if( aInter.HasArea() )
            {
                bool bGrid = pGrid->GetRubyTextBelow();
                bool bCell = GRID_LINES_CHARS == pGrid->GetGridType();
                long nGrid = pGrid->GetBaseHeight();
                const SwDoc* pDoc = GetFormat()->GetDoc();
                long nGridWidth = GetGridWidth(*pGrid, *pDoc);
                long nRuby = pGrid->GetRubyHeight();
                long nSum = nGrid + nRuby;
                const Color *pCol = &pGrid->GetColor();

                SwTwips nRight = aInter.Left() + aInter.Width();
                SwTwips nBottom = aInter.Top() + aInter.Height();
                if( IsVertical() )
                {
                    SwTwips nOrig = aGrid.Left() + aGrid.Width();
                    SwTwips nY = nOrig + nSum *
                                 ( ( nOrig - aInter.Left() ) / nSum );
                    SwRect aTmp( Point( nY, aInter.Top() ),
                                Size( 1, aInter.Height() ) );
                    SwTwips nX = aGrid.Top() + nGrid *
                                ( ( aInter.Top() - aGrid.Top() )/ nGrid );
                    if( nX < aInter.Top() )
                        nX += nGrid;
                    SwTwips nGridBottom = aGrid.Top() + aGrid.Height();
                    bool bLeft = aGrid.Top() >= aInter.Top();
                    bool bRight = nGridBottom <= nBottom;
                    bool bBorder = bLeft || bRight;
                    while( nY > nRight )
                    {
                        aTmp.Pos().setX( nY );
                        if( bGrid )
                        {
                            nY -= nGrid;
                            SwTwips nPosY = std::max( aInter.Left(), nY );
                            SwTwips nHeight = std::min(nRight, aTmp.Pos().X())-nPosY;
                            if( nHeight > 0 )
                            {
                                if( bCell )
                                {
                                    SwRect aVert( Point( nPosY, nX ),
                                                Size( nHeight, 1 ) );
                                    while( aVert.Top() <= nBottom )
                                    {
                                        PaintBorderLine(rRect,aVert,this,pCol);
                                        aVert.Pos().AdjustY(nGrid );
                                    }
                                }
                                else if( bBorder )
                                {
                                    SwRect aVert( Point( nPosY, aGrid.Top() ),
                                                  Size( nHeight, 1 ) );
                                    if( bLeft )
                                        PaintBorderLine(rRect,aVert,this,pCol);
                                    if( bRight )
                                    {
                                        aVert.Pos().setY( nGridBottom );
                                        PaintBorderLine(rRect,aVert,this,pCol);
                                    }
                                }
                            }
                        }
                        else
                        {
                            nY -= nRuby;
                            if( bBorder )
                            {
                                SwTwips nPos = std::max( aInter.Left(), nY );
                                SwTwips nW = std::min(nRight, aTmp.Pos().X()) - nPos;
                                SwRect aVert( Point( nPos, aGrid.Top() ),
                                              Size( nW, 1 ) );
                                if( nW > 0 )
                                {
                                    if( bLeft )
                                        PaintBorderLine(rRect,aVert,this,pCol);
                                    if( bRight )
                                    {
                                        aVert.Pos().setY( nGridBottom );
                                        PaintBorderLine(rRect,aVert,this,pCol);
                                    }
                                }
                            }
                        }
                        bGrid = !bGrid;
                    }
                    while( nY >= aInter.Left() )
                    {
                        aTmp.Pos().setX( nY );
                        PaintBorderLine( rRect, aTmp, this, pCol);
                        if( bGrid )
                        {
                            nY -= nGrid;
                            SwTwips nHeight = aTmp.Pos().X()
                                              - std::max(aInter.Left(), nY );
                            if( nHeight > 0 )
                            {
                                if( bCell )
                                {
                                    SwRect aVert( Point(aTmp.Pos().X()-nHeight,
                                                  nX ), Size( nHeight, 1 ) );
                                    while( aVert.Top() <= nBottom )
                                    {
                                        PaintBorderLine(rRect,aVert,this,pCol);
                                        aVert.Pos().AdjustY(nGrid );
                                    }
                                }
                                else if( bBorder )
                                {
                                    SwRect aVert( Point(aTmp.Pos().X()-nHeight,
                                            aGrid.Top() ), Size( nHeight, 1 ) );
                                    if( bLeft )
                                        PaintBorderLine(rRect,aVert,this,pCol);
                                    if( bRight )
                                    {
                                        aVert.Pos().setY( nGridBottom );
                                        PaintBorderLine(rRect,aVert,this,pCol);
                                    }
                                }
                            }
                        }
                        else
                        {
                            nY -= nRuby;
                            if( bBorder )
                            {
                                SwTwips nPos = std::max( aInter.Left(), nY );
                                SwTwips nW = std::min(nRight, aTmp.Pos().X()) - nPos;
                                SwRect aVert( Point( nPos, aGrid.Top() ),
                                              Size( nW, 1 ) );
                                if( nW > 0 )
                                {
                                    if( bLeft )
                                        PaintBorderLine(rRect,aVert,this,pCol);
                                    if( bRight )
                                    {
                                        aVert.Pos().setY( nGridBottom );
                                        PaintBorderLine(rRect,aVert,this,pCol);
                                    }
                                }
                            }
                        }
                        bGrid = !bGrid;
                    }
                }
                else
                {
                    SwTwips nOrig = aGrid.Top();
                    SwTwips nY = nOrig + nSum *( (aInter.Top()-nOrig)/nSum );
                    SwRect aTmp( Point( aInter.Left(), nY ),
                                Size( aInter.Width(), 1 ) );
                    //for textgrid refactor
                    SwTwips nX = aGrid.Left() + nGridWidth *
                        ( ( aInter.Left() - aGrid.Left() )/ nGridWidth );
                    if( nX < aInter.Left() )
                        nX += nGridWidth;
                    SwTwips nGridRight = aGrid.Left() + aGrid.Width();
                    bool bLeft = aGrid.Left() >= aInter.Left();
                    bool bRight = nGridRight <= nRight;
                    bool bBorder = bLeft || bRight;
                    while( nY < aInter.Top() )
                    {
                        aTmp.Pos().setY(nY);
                        if( bGrid )
                        {
                            nY += nGrid;
                            SwTwips nPosY = std::max( aInter.Top(), aTmp.Pos().getY() );
                            SwTwips nHeight = std::min(nBottom, nY ) - nPosY;
                            if( nHeight )
                            {
                                if( bCell )
                                {
                                    SwRect aVert( Point( nX, nPosY ),
                                                Size( 1, nHeight ) );
                                    while( aVert.Left() <= nRight )
                                    {
                                        PaintBorderLine(rRect,aVert,this,pCol);
                                        aVert.Pos().AdjustX(nGridWidth );  //for textgrid refactor
                                    }
                                }
                                else if ( bBorder )
                                {
                                    SwRect aVert( Point( aGrid.Left(), nPosY ),
                                                Size( 1, nHeight ) );
                                    if( bLeft )
                                        PaintBorderLine(rRect,aVert,this,pCol);
                                    if( bRight )
                                    {
                                        aVert.Pos().setX( nGridRight );
                                        PaintBorderLine(rRect,aVert,this,pCol);
                                    }
                                }
                            }
                        }
                        else
                        {
                            nY += nRuby;
                            if( bBorder )
                            {
                                SwTwips nPos = std::max(aInter.Top(),aTmp.Pos().getY());
                                SwTwips nH = std::min( nBottom, nY ) - nPos;
                                SwRect aVert( Point( aGrid.Left(), nPos ),
                                            Size( 1, nH ) );
                                if( nH > 0 )
                                {
                                    if( bLeft )
                                        PaintBorderLine(rRect,aVert,this,pCol);
                                    if( bRight )
                                    {
                                        aVert.Pos().setX(nGridRight);
                                        PaintBorderLine(rRect,aVert,this,pCol);
                                    }
                                }
                            }
                        }
                        bGrid = !bGrid;
                    }
                    while( nY <= nBottom )
                    {
                        aTmp.Pos().setY(nY);
                        PaintBorderLine( rRect, aTmp, this, pCol);
                        if( bGrid )
                        {
                            nY += nGrid;
                            SwTwips nHeight = std::min(nBottom, nY) - aTmp.Pos().getY();
                            if( nHeight )
                            {
                                if( bCell )
                                {
                                    SwRect aVert( Point( nX, aTmp.Pos().getY() ),
                                                Size( 1, nHeight ) );
                                    while( aVert.Left() <= nRight )
                                    {
                                        PaintBorderLine( rRect, aVert, this, pCol);
                                        aVert.Pos().setX(aVert.Pos().getX() + nGridWidth);  //for textgrid refactor
                                    }
                                }
                                else if( bBorder )
                                {
                                    SwRect aVert( Point( aGrid.Left(),
                                        aTmp.Pos().getY() ), Size( 1, nHeight ) );
                                    if( bLeft )
                                        PaintBorderLine(rRect,aVert,this,pCol);
                                    if( bRight )
                                    {
                                        aVert.Pos().setX(nGridRight);
                                        PaintBorderLine(rRect,aVert,this,pCol);
                                    }
                                }
                            }
                        }
                        else
                        {
                            nY += nRuby;
                            if( bBorder )
                            {
                                SwTwips nPos = std::max(aInter.Top(),aTmp.Pos().Y());
                                SwTwips nH = std::min( nBottom, nY ) - nPos;
                                SwRect aVert( Point( aGrid.Left(), nPos ),
                                            Size( 1, nH ) );
                                if( nH > 0 )
                                {
                                    if( bLeft )
                                        PaintBorderLine(rRect,aVert,this,pCol);
                                    if( bRight )
                                    {
                                        aVert.Pos().setX(nGridRight);
                                        PaintBorderLine(rRect,aVert,this,pCol);
                                    }
                                }
                            }
                        }
                        bGrid = !bGrid;
                    }
                }
            }
        }
    }
}

/**
 * Paint margin area of a page
 *
 * OD 20.11.2002 for #104598#:
 * implement paint of margin area; margin area will be painted for a
 * view shell with a window and if the document is not in online layout.
 *
 * @param _rOutputRect
 * input parameter - constant instance reference of the rectangle, for
 * which an output has to be generated.
 *
 * @param _pViewShell
 * input parameter - instance of the view shell, on which the output
 * has to be generated.
 */
void SwPageFrame::PaintMarginArea( const SwRect& _rOutputRect,
                                 SwViewShell const * _pViewShell ) const
{
    if (  _pViewShell->GetWin() && !_pViewShell->GetViewOptions()->getBrowseMode() )
    {
        // Simplified paint with DrawingLayer FillStyle
        SwRect aPgRect = getFrameArea();
        aPgRect.Intersection_( _rOutputRect );

        if(!aPgRect.IsEmpty())
        {
            OutputDevice *pOut = _pViewShell->GetOut();

            if(pOut->GetFillColor() != aGlobalRetoucheColor)
            {
                pOut->SetFillColor(aGlobalRetoucheColor);
            }

            pOut->DrawRect(aPgRect.SVRect());
        }
    }
}

const sal_Int8 SwPageFrame::mnShadowPxWidth = 9;

bool SwPageFrame::IsRightShadowNeeded() const
{
    const SwViewShell *pSh = getRootFrame()->GetCurrShell();
    const bool bIsLTR = getRootFrame()->IsLeftToRightViewLayout();

    // We paint the right shadow if we're not in book mode
    // or if we've no sibling or are the last page of the "row"
    return !pSh || (!pSh->GetViewOptions()->IsViewLayoutBookMode()) || !GetNext()
        || (this == Lower())  || (bIsLTR && OnRightPage())
        || (!bIsLTR && !OnRightPage());

}

bool SwPageFrame::IsLeftShadowNeeded() const
{
    const SwViewShell *pSh = getRootFrame()->GetCurrShell();
    const bool bIsLTR = getRootFrame()->IsLeftToRightViewLayout();

    // We paint the left shadow if we're not in book mode
    // or if we've no sibling or are the last page of the "row"
    return !pSh || (!pSh->GetViewOptions()->IsViewLayoutBookMode()) || !GetPrev()
        || (bIsLTR && !OnRightPage())
        || (!bIsLTR && OnRightPage());
}

/**
 * Determine rectangle for bottom page shadow
 * for #i9719#
 */
/*static*/ void SwPageFrame::GetHorizontalShadowRect( const SwRect& _rPageRect,
                                                const SwViewShell*    _pViewShell,
                                                OutputDevice const * pRenderContext,
                                                SwRect&       _orHorizontalShadowRect,
                                                bool bPaintLeftShadow,
                                                bool bPaintRightShadow,
                                                bool bRightSidebar )
{
    const SwPostItMgr *pMgr = _pViewShell->GetPostItMgr();
    SwRect aAlignedPageRect( _rPageRect );
    ::SwAlignRect( aAlignedPageRect, _pViewShell, pRenderContext );
    SwRect aPagePxRect = pRenderContext->LogicToPixel( aAlignedPageRect.SVRect() );

    long lShadowAdjustment = mnShadowPxWidth - 1; // TODO: extract this

    _orHorizontalShadowRect.Chg(
                    Point( aPagePxRect.Left() + (bPaintLeftShadow ? lShadowAdjustment : 0), 0 ),
                    Size( aPagePxRect.Width() - ( (bPaintLeftShadow ? lShadowAdjustment : 0) + (bPaintRightShadow ? lShadowAdjustment : 0) ),
                        mnShadowPxWidth ) );

    if(pMgr && pMgr->ShowNotes() && pMgr->HasNotes())
    {
        // Notes are displayed, we've to extend borders
        SwTwips aSidebarTotalWidth = pMgr->GetSidebarWidth(true) + pMgr->GetSidebarBorderWidth(true);
        if(bRightSidebar)
            _orHorizontalShadowRect.Right( _orHorizontalShadowRect.Right() + aSidebarTotalWidth );
        else
            _orHorizontalShadowRect.Left( _orHorizontalShadowRect.Left() - aSidebarTotalWidth );
    }
}

enum PaintArea {LEFT, RIGHT, TOP, BOTTOM};
#define BORDER_TILE_SIZE 512

/// Wrapper around pOut->DrawBitmapEx.
static void lcl_paintBitmapExToRect(vcl::RenderContext *pOut, const Point& aPoint, const Size& aSize, const BitmapEx& rBitmapEx, PaintArea eArea)
{
    // The problem is that if we get called multiple times and the color is
    // partly transparent, then the result will get darker and darker. To avoid
    // this, always paint the background color before doing the real paint.
    tools::Rectangle aRect(aPoint, aSize);

    switch (eArea)
    {
        case LEFT: aRect.SetLeft( aRect.Right() - 1 ); break;
        case RIGHT: aRect.SetRight( aRect.Left() + 1 ); break;
        case TOP: aRect.SetTop( aRect.Bottom() - 1 ); break;
        case BOTTOM: aRect.SetBottom( aRect.Top() + 1 ); break;
    }

    pOut->SetFillColor(SwViewOption::GetAppBackgroundColor());
    pOut->SetLineColor();
    pOut->DrawRect(pOut->PixelToLogic(aRect));

    // Tiled render if necessary
    tools::Rectangle aComplete(aPoint, aSize);
    Size aTileSize(BORDER_TILE_SIZE, BORDER_TILE_SIZE);

    long iterX = eArea != RIGHT && eArea != LEFT ? BORDER_TILE_SIZE : 0;
    long iterY = eArea == RIGHT || eArea == LEFT ? BORDER_TILE_SIZE : 0;

    for (tools::Rectangle aTile = tools::Rectangle(aPoint, aTileSize); true; aTile.Move(iterX, iterY))
    {
        tools::Rectangle aRender = aComplete.GetIntersection(aTile);
        if (aRender.IsEmpty())
            break;
        pOut->DrawBitmapEx(pOut->PixelToLogic(aRender.TopLeft()),
                           pOut->PixelToLogic(aRender.GetSize()),
                           Point(0, 0), aRender.GetSize(),
                           rBitmapEx);
    }

}

/**
 * Paint page border and shadow
 *
 * for #i9719#
 * implement paint of page border and shadow
*/
/*static*/ void SwPageFrame::PaintBorderAndShadow( const SwRect& _rPageRect,
                                                 const SwViewShell*    _pViewShell,
                                                 bool bPaintLeftShadow,
                                                 bool bPaintRightShadow,
                                                 bool bRightSidebar )
{
    // No shadow in prefs
    if (!SwViewOption::IsShadow())
        return;

    // #i16816# tagged pdf support
    SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, nullptr, *_pViewShell->GetOut() );

    static vcl::DeleteOnDeinit<drawinglayer::primitive2d::DiscreteShadow> shadowMaskObj(
        new drawinglayer::primitive2d::DiscreteShadow(
            vcl::bitmap::loadFromName(BMP_PAGE_SHADOW_MASK,
                                      ImageLoadFlags::IgnoreDarkTheme | ImageLoadFlags::IgnoreScalingFactor)));

    drawinglayer::primitive2d::DiscreteShadow& shadowMask = *shadowMaskObj.get();
    static vcl::DeleteOnDeinit< BitmapEx > aPageTopRightShadowObj( new BitmapEx );
    static vcl::DeleteOnDeinit< BitmapEx > aPageBottomRightShadowObj( new BitmapEx );
    static vcl::DeleteOnDeinit< BitmapEx > aPageBottomLeftShadowObj( new BitmapEx );
    static vcl::DeleteOnDeinit< BitmapEx > aPageBottomShadowBaseObj( new BitmapEx );
    static vcl::DeleteOnDeinit< BitmapEx > aPageRightShadowBaseObj( new BitmapEx );
    static vcl::DeleteOnDeinit< BitmapEx > aPageTopShadowBaseObj( new BitmapEx );
    static vcl::DeleteOnDeinit< BitmapEx > aPageTopLeftShadowObj( new BitmapEx );
    static vcl::DeleteOnDeinit< BitmapEx > aPageLeftShadowBaseObj( new BitmapEx );
    BitmapEx& aPageTopRightShadow = *aPageTopRightShadowObj.get();
    BitmapEx& aPageBottomRightShadow = *aPageBottomRightShadowObj.get();
    BitmapEx& aPageBottomLeftShadow = *aPageBottomLeftShadowObj.get();
    BitmapEx& aPageBottomShadow = *aPageBottomShadowBaseObj.get();
    BitmapEx& aPageRightShadow = *aPageRightShadowBaseObj.get();
    BitmapEx& aPageTopShadow = *aPageTopShadowBaseObj.get();
    BitmapEx& aPageTopLeftShadow = *aPageTopLeftShadowObj.get();
    BitmapEx& aPageLeftShadow = *aPageLeftShadowBaseObj.get();
    static Color aShadowColor( COL_AUTO );

    SwRect aAlignedPageRect( _rPageRect );
    ::SwAlignRect( aAlignedPageRect, _pViewShell, _pViewShell->GetOut() );
    SwRect aPagePxRect = _pViewShell->GetOut()->LogicToPixel( aAlignedPageRect.SVRect() );

    if (aShadowColor != SwViewOption::GetShadowColor())
    {
        aShadowColor = SwViewOption::GetShadowColor();

        AlphaMask aMask( shadowMask.getBottomRight().GetBitmap() );
        Bitmap aFilledSquare( aMask.GetSizePixel(), 24 );
        aFilledSquare.Erase( aShadowColor );
        aPageBottomRightShadow = BitmapEx( aFilledSquare, aMask );

        aMask = AlphaMask( shadowMask.getBottomLeft().GetBitmap() );
        aFilledSquare = Bitmap( aMask.GetSizePixel(), 24 );
        aFilledSquare.Erase( aShadowColor );
        aPageBottomLeftShadow = BitmapEx( aFilledSquare, aMask );

        aMask = AlphaMask( shadowMask.getBottom().GetBitmap() );
        aFilledSquare = Bitmap( aMask.GetSizePixel(), 24 );
        aFilledSquare.Erase( aShadowColor );
        aPageBottomShadow = BitmapEx( aFilledSquare, aMask );

        aMask = AlphaMask( shadowMask.getTop().GetBitmap() );
        aFilledSquare = Bitmap( aMask.GetSizePixel(), 24 );
        aFilledSquare.Erase( aShadowColor );
        aPageTopShadow = BitmapEx( aFilledSquare, aMask );

        aMask = AlphaMask( shadowMask.getTopRight().GetBitmap() );
        aFilledSquare = Bitmap( aMask.GetSizePixel(), 24 );
        aFilledSquare.Erase( aShadowColor );
        aPageTopRightShadow = BitmapEx( aFilledSquare, aMask );

        aMask = AlphaMask( shadowMask.getRight().GetBitmap() );
        aFilledSquare = Bitmap( aMask.GetSizePixel(), 24 );
        aFilledSquare.Erase( aShadowColor );
        aPageRightShadow = BitmapEx( aFilledSquare, aMask );

        aMask = AlphaMask( shadowMask.getTopLeft().GetBitmap() );
        aFilledSquare = Bitmap( aMask.GetSizePixel(), 24 );
        aFilledSquare.Erase( aShadowColor );
        aPageTopLeftShadow = BitmapEx( aFilledSquare, aMask );

        aMask = AlphaMask( shadowMask.getLeft().GetBitmap() );
        aFilledSquare = Bitmap( aMask.GetSizePixel(), 24 );
        aFilledSquare.Erase( aShadowColor );
        aPageLeftShadow = BitmapEx( aFilledSquare, aMask );
    }

    SwRect aPaintRect;
    OutputDevice *pOut = _pViewShell->GetOut();

    SwPageFrame::GetHorizontalShadowRect( _rPageRect, _pViewShell, pOut, aPaintRect, bPaintLeftShadow, bPaintRightShadow, bRightSidebar );

    // Right shadow & corners
    if ( bPaintRightShadow )
    {
        pOut->DrawBitmapEx( pOut->PixelToLogic( Point( aPaintRect.Right(), aPagePxRect.Bottom() + 1 - (aPageBottomRightShadow.GetSizePixel().Height() - mnShadowPxWidth) ) ),
            aPageBottomRightShadow );
        pOut->DrawBitmapEx( pOut->PixelToLogic( Point( aPaintRect.Right(), aPagePxRect.Top() - mnShadowPxWidth ) ),
            aPageTopRightShadow );

        if (aPagePxRect.Height() > 2 * mnShadowPxWidth)
        {
            const long nWidth = aPageRightShadow.GetSizePixel().Width();
            const long nHeight = aPagePxRect.Height() - 2 * (mnShadowPxWidth - 1);
            if (aPageRightShadow.GetSizePixel().Height() < BORDER_TILE_SIZE)
                aPageRightShadow.Scale(Size(nWidth, BORDER_TILE_SIZE), BmpScaleFlag::Fast);

            lcl_paintBitmapExToRect(pOut,
                    Point(aPaintRect.Right() + mnShadowPxWidth, aPagePxRect.Top() + mnShadowPxWidth - 1),
                    Size(nWidth, nHeight),
                    aPageRightShadow, RIGHT);
        }
    }

    // Left shadows and corners
    if(bPaintLeftShadow)
    {
        const long lLeft = aPaintRect.Left() - aPageBottomLeftShadow.GetSizePixel().Width();
        pOut->DrawBitmapEx( pOut->PixelToLogic( Point( lLeft,
            aPagePxRect.Bottom() + 1 + mnShadowPxWidth - aPageBottomLeftShadow.GetSizePixel().Height() ) ), aPageBottomLeftShadow );
        pOut->DrawBitmapEx( pOut->PixelToLogic( Point( lLeft, aPagePxRect.Top() - mnShadowPxWidth ) ), aPageTopLeftShadow );
        if (aPagePxRect.Height() > 2 * mnShadowPxWidth)
        {
            const long nWidth = aPageLeftShadow.GetSizePixel().Width();
            const long nHeight = aPagePxRect.Height() - 2 * (mnShadowPxWidth - 1);
            if (aPageLeftShadow.GetSizePixel().Height() < BORDER_TILE_SIZE)
                aPageLeftShadow.Scale(Size(nWidth, BORDER_TILE_SIZE), BmpScaleFlag::Fast);

            lcl_paintBitmapExToRect(pOut,
                    Point(lLeft, aPagePxRect.Top() + mnShadowPxWidth - 1),
                    Size(nWidth, nHeight),
                    aPageLeftShadow, LEFT);
        }
    }

    // Bottom shadow
    const long nBottomHeight = aPageBottomShadow.GetSizePixel().Height();
    if (aPageBottomShadow.GetSizePixel().Width() < BORDER_TILE_SIZE)
        aPageBottomShadow.Scale(Size(BORDER_TILE_SIZE, nBottomHeight), BmpScaleFlag::Fast);

    lcl_paintBitmapExToRect(pOut,
            Point(aPaintRect.Left(), aPagePxRect.Bottom() + 2),
            Size(aPaintRect.Width(), nBottomHeight),
            aPageBottomShadow, BOTTOM);

    // Top shadow
    const long nTopHeight = aPageTopShadow.GetSizePixel().Height();
    if (aPageTopShadow.GetSizePixel().Width() < BORDER_TILE_SIZE)
        aPageTopShadow.Scale(Size(BORDER_TILE_SIZE, nTopHeight), BmpScaleFlag::Fast);

    lcl_paintBitmapExToRect(pOut,
            Point(aPaintRect.Left(), aPagePxRect.Top() - mnShadowPxWidth),
            Size(aPaintRect.Width(), nTopHeight),
            aPageTopShadow, TOP);
}

/**
 * mod #i6193# paint sidebar for notes
 * IMPORTANT: if you change the rects here, also change SwPostItMgr::ScrollbarHit
 */
/*static*/void SwPageFrame::PaintNotesSidebar(const SwRect& _rPageRect, SwViewShell* _pViewShell, sal_uInt16 nPageNum, bool bRight)
{
    //TODO: cut out scrollbar area and arrows out of sidepane rect, otherwise it could flicker when pressing arrow buttons
    if (!_pViewShell )
        return;

    SwRect aPageRect( _rPageRect );
    SwAlignRect( aPageRect, _pViewShell, _pViewShell->GetOut() );

    const SwPostItMgr *pMgr = _pViewShell->GetPostItMgr();
    if (pMgr && pMgr->ShowNotes() && pMgr->HasNotes())  // do not show anything in print preview
    {
        sal_Int32 nScrollerHeight = pMgr->GetSidebarScrollerHeight();
        const tools::Rectangle &aVisRect = _pViewShell->VisArea().SVRect();
        //draw border and sidepane
        _pViewShell->GetOut()->SetLineColor();
        if (!bRight)
        {
            _pViewShell->GetOut()->SetFillColor(COL_NOTES_SIDEPANE_BORDER);
            _pViewShell->GetOut()->DrawRect(tools::Rectangle(Point(aPageRect.Left()-pMgr->GetSidebarBorderWidth(),aPageRect.Top()),Size(pMgr->GetSidebarBorderWidth(),aPageRect.Height())))    ;
            if (Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
                _pViewShell->GetOut()->SetFillColor(COL_BLACK);
            else
                _pViewShell->GetOut()->SetFillColor(COL_NOTES_SIDEPANE);
            _pViewShell->GetOut()->DrawRect(tools::Rectangle(Point(aPageRect.Left()-pMgr->GetSidebarWidth()-pMgr->GetSidebarBorderWidth(),aPageRect.Top()),Size(pMgr->GetSidebarWidth(),aPageRect.Height())))  ;
        }
        else
        {
            _pViewShell->GetOut()->SetFillColor(COL_NOTES_SIDEPANE_BORDER);
            SwRect aSidebarBorder(aPageRect.TopRight(),Size(pMgr->GetSidebarBorderWidth(),aPageRect.Height()));
            _pViewShell->GetOut()->DrawRect(aSidebarBorder.SVRect());
            if (Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
                _pViewShell->GetOut()->SetFillColor(COL_BLACK);
            else
                _pViewShell->GetOut()->SetFillColor(COL_NOTES_SIDEPANE);
            SwRect aSidebar(Point(aPageRect.Right()+pMgr->GetSidebarBorderWidth(),aPageRect.Top()),Size(pMgr->GetSidebarWidth(),aPageRect.Height()));
            _pViewShell->GetOut()->DrawRect(aSidebar.SVRect());
        }
        if (pMgr->ShowScrollbar(nPageNum))
        {
            // draw scrollbar area and arrows
            Point aPointBottom;
            Point aPointTop;
            aPointBottom = !bRight ? Point(aPageRect.Left() - pMgr->GetSidebarWidth() - pMgr->GetSidebarBorderWidth() + _pViewShell->GetOut()->PixelToLogic(Size(2,0)).Width(),aPageRect.Bottom()- _pViewShell->GetOut()->PixelToLogic(Size(0,2+pMgr->GetSidebarScrollerHeight())).Height()) :
                                    Point(aPageRect.Right() + pMgr->GetSidebarBorderWidth() + _pViewShell->GetOut()->PixelToLogic(Size(2,0)).Width(),aPageRect.Bottom()- _pViewShell->GetOut()->PixelToLogic(Size(0,2+pMgr->GetSidebarScrollerHeight())).Height());
            aPointTop = !bRight ?    Point(aPageRect.Left() - pMgr->GetSidebarWidth() + _pViewShell->GetOut()->PixelToLogic(Size(2,0)).Width(),aPageRect.Top() + _pViewShell->GetOut()->PixelToLogic(Size(0,2)).Height()) :
                                Point(aPageRect.Right() + pMgr->GetSidebarBorderWidth() + _pViewShell->GetOut()->PixelToLogic(Size(2,0)).Width(),aPageRect.Top() + _pViewShell->GetOut()->PixelToLogic(Size(0,2)).Height());
            Size aSize(pMgr->GetSidebarWidth() - _pViewShell->GetOut()->PixelToLogic(Size(4,0)).Width(), _pViewShell->GetOut()->PixelToLogic(Size(0,nScrollerHeight)).Height()) ;
            tools::Rectangle aRectBottom(aPointBottom,aSize);
            tools::Rectangle aRectTop(aPointTop,aSize);

            if (aRectBottom.IsOver(aVisRect))
            {

                if (Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
                {
                    _pViewShell->GetOut()->SetLineColor(COL_WHITE);
                    _pViewShell->GetOut()->SetFillColor(COL_BLACK);
                }
                else
                {
                    _pViewShell->GetOut()->SetLineColor(COL_BLACK);
                    _pViewShell->GetOut()->SetFillColor(COL_NOTES_SIDEPANE_SCROLLAREA);
                }
                _pViewShell->GetOut()->DrawRect(aRectBottom);
                _pViewShell->GetOut()->DrawLine(aPointBottom + Point(pMgr->GetSidebarWidth()/3,0), aPointBottom + Point(pMgr->GetSidebarWidth()/3 , _pViewShell->GetOut()->PixelToLogic(Size(0,nScrollerHeight)).Height()));

                _pViewShell->GetOut()->SetLineColor();
                Point aMiddleFirst(aPointBottom + Point(pMgr->GetSidebarWidth()/6,_pViewShell->GetOut()->PixelToLogic(Size(0,nScrollerHeight)).Height()/2));
                Point aMiddleSecond(aPointBottom + Point(pMgr->GetSidebarWidth()/3*2,_pViewShell->GetOut()->PixelToLogic(Size(0,nScrollerHeight)).Height()/2));
                PaintNotesSidebarArrows(aMiddleFirst,aMiddleSecond,_pViewShell,pMgr->GetArrowColor(KEY_PAGEUP,nPageNum), pMgr->GetArrowColor(KEY_PAGEDOWN,nPageNum));
            }
            if (aRectTop.IsOver(aVisRect))
            {
                if (Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
                {
                    _pViewShell->GetOut()->SetLineColor(COL_WHITE);
                    _pViewShell->GetOut()->SetFillColor(COL_BLACK);
                }
                else
                {
                    _pViewShell->GetOut()->SetLineColor(COL_BLACK);
                    _pViewShell->GetOut()->SetFillColor(COL_NOTES_SIDEPANE_SCROLLAREA);
                }
                _pViewShell->GetOut()->DrawRect(aRectTop);
                _pViewShell->GetOut()->DrawLine(aPointTop + Point(pMgr->GetSidebarWidth()/3*2,0), aPointTop + Point(pMgr->GetSidebarWidth()/3*2 , _pViewShell->GetOut()->PixelToLogic(Size(0,nScrollerHeight)).Height()));

                _pViewShell->GetOut()->SetLineColor();
                Point aMiddleFirst(aPointTop + Point(pMgr->GetSidebarWidth()/3,_pViewShell->GetOut()->PixelToLogic(Size(0,nScrollerHeight)).Height()/2));
                Point aMiddleSecond(aPointTop + Point(pMgr->GetSidebarWidth()/6*5,_pViewShell->GetOut()->PixelToLogic(Size(0,nScrollerHeight)).Height()/2));
                PaintNotesSidebarArrows(aMiddleFirst,aMiddleSecond,_pViewShell, pMgr->GetArrowColor(KEY_PAGEUP,nPageNum), pMgr->GetArrowColor(KEY_PAGEDOWN,nPageNum));
            }
        }
    }
}

/*static*/ void SwPageFrame::PaintNotesSidebarArrows(const Point &aMiddleFirst, const Point &aMiddleSecond, SwViewShell const * _pViewShell, const Color& rColorUp, const Color& rColorDown)
{
    tools::Polygon aTriangleUp(3);
    tools::Polygon aTriangleDown(3);

    aTriangleUp.SetPoint(aMiddleFirst + Point(0,_pViewShell->GetOut()->PixelToLogic(Size(0,-3)).Height()),0);
    aTriangleUp.SetPoint(aMiddleFirst + Point(_pViewShell->GetOut()->PixelToLogic(Size(-3,0)).Width(),_pViewShell->GetOut()->PixelToLogic(Size(0,3)).Height()),1);
    aTriangleUp.SetPoint(aMiddleFirst + Point(_pViewShell->GetOut()->PixelToLogic(Size(3,0)).Width(),_pViewShell->GetOut()->PixelToLogic(Size(0,3)).Height()),2);

    aTriangleDown.SetPoint(aMiddleSecond + Point(_pViewShell->GetOut()->PixelToLogic(Size(-3,0)).Width(),_pViewShell->GetOut()->PixelToLogic(Size(0,-3)).Height()),0);
    aTriangleDown.SetPoint(aMiddleSecond + Point(_pViewShell->GetOut()->PixelToLogic(Size(+3,0)).Width(),_pViewShell->GetOut()->PixelToLogic(Size(0,-3)).Height()),1);
    aTriangleDown.SetPoint(aMiddleSecond + Point(0,_pViewShell->GetOut()->PixelToLogic(Size(0,3)).Height()),2);

    _pViewShell->GetOut()->SetFillColor(rColorUp);
    _pViewShell->GetOut()->DrawPolygon(aTriangleUp);
    _pViewShell->GetOut()->SetFillColor(rColorDown);
    _pViewShell->GetOut()->DrawPolygon(aTriangleDown);
}

/**
 * Get bound rectangle of border and shadow for repaints
 *
 * for #i9719#
 */
/*static*/ void SwPageFrame::GetBorderAndShadowBoundRect( const SwRect& _rPageRect,
                                                        const SwViewShell*    _pViewShell,
                                                        OutputDevice const * pRenderContext,
                                                        SwRect& _orBorderAndShadowBoundRect,
                                                        bool bLeftShadow,
                                                        bool bRightShadow,
                                                        bool bRightSidebar
                                                      )
{
    SwRect aAlignedPageRect( _rPageRect );
    ::SwAlignRect( aAlignedPageRect, _pViewShell, pRenderContext );
    SwRect aPagePxRect = pRenderContext->LogicToPixel( aAlignedPageRect.SVRect() );
    aPagePxRect.Bottom( aPagePxRect.Bottom() + mnShadowPxWidth + 1 );
    aPagePxRect.Top( aPagePxRect.Top() - mnShadowPxWidth - 1 );

    SwRect aTmpRect;

    // Always ask for full shadow since we want a bounding rect
    // including at least the page frame
    SwPageFrame::GetHorizontalShadowRect( _rPageRect, _pViewShell, pRenderContext, aTmpRect, false, false, bRightSidebar );

    if(bLeftShadow) aPagePxRect.Left( aTmpRect.Left() - mnShadowPxWidth - 1);
    if(bRightShadow) aPagePxRect.Right( aTmpRect.Right() + mnShadowPxWidth + 1);

    _orBorderAndShadowBoundRect = pRenderContext->PixelToLogic( aPagePxRect.SVRect() );
}

SwRect SwPageFrame::GetBoundRect(OutputDevice const * pOutputDevice) const
{
    const SwViewShell *pSh = getRootFrame()->GetCurrShell();
    SwRect aPageRect( getFrameArea() );
    SwRect aResult;

    if(!pSh) {
        return SwRect( Point(0, 0), Size(0, 0) );
    }

    SwPageFrame::GetBorderAndShadowBoundRect( aPageRect, pSh, pOutputDevice, aResult,
        IsLeftShadowNeeded(), IsRightShadowNeeded(), SidebarPosition() ==  sw::sidebarwindows::SidebarPosition::RIGHT );
    return aResult;
}

/*static*/ SwTwips SwPageFrame::GetSidebarBorderWidth( const SwViewShell* _pViewShell )
{
    const SwPostItMgr* pPostItMgr = _pViewShell ? _pViewShell->GetPostItMgr() : nullptr;
    const SwTwips nRet = pPostItMgr && pPostItMgr->HasNotes() && pPostItMgr->ShowNotes() ? pPostItMgr->GetSidebarWidth() + pPostItMgr->GetSidebarBorderWidth() : 0;
    return nRet;
}

void SwFrame::PaintBaBo( const SwRect& rRect, const SwPageFrame *pPage,
                         const bool bOnlyTextBackground ) const
{
    if ( !pPage )
        pPage = FindPageFrame();

    OutputDevice *pOut = gProp.pSGlobalShell->GetOut();

    // #i16816# tagged pdf support
    SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, nullptr, *pOut );

    // OD 2004-04-23 #116347#
    pOut->Push( PushFlags::FILLCOLOR|PushFlags::LINECOLOR );
    pOut->SetLineColor();

    SwBorderAttrAccess aAccess( SwFrame::GetCache(), this );
    const SwBorderAttrs &rAttrs = *aAccess.Get();

    // OD 20.11.2002 #104598# - take care of page margin area
    // Note: code move from <SwFrame::PaintSwFrameBackground(..)> to new method
    // <SwPageFrame::Paintmargin(..)>.
    if ( IsPageFrame() && !bOnlyTextBackground)
    {
        static_cast<const SwPageFrame*>(this)->PaintMarginArea( rRect, gProp.pSGlobalShell );
    }

    // paint background
    {
        PaintSwFrameBackground( rRect, pPage, rAttrs, false, true/*bLowerBorder*/, bOnlyTextBackground );
    }

    // OD 06.08.2002 #99657# - paint border before painting background
    // paint grid for page frame and paint border
    if (!bOnlyTextBackground)
    {
        SwRect aRect( rRect );

        if( IsPageFrame() )
        {
            static_cast<const SwPageFrame*>(this)->PaintGrid( pOut, aRect );
        }

        PaintSwFrameShadowAndBorder(aRect, pPage, rAttrs);
    }

    pOut->Pop();
}

static bool lcl_compareFillAttributes(const drawinglayer::attribute::SdrAllFillAttributesHelperPtr& pA, const drawinglayer::attribute::SdrAllFillAttributesHelperPtr& pB)
{
    if (pA == pB)
        return true;
    if (!pA || !pB)
        return false;
    return pA->getFillAttribute() == pB->getFillAttribute();
}

/// OD 05.09.2002 #102912#
/// Do not paint background for fly frames without a background brush by
/// calling <PaintBaBo> at the page or at the fly frame its anchored
void SwFrame::PaintSwFrameBackground( const SwRect &rRect, const SwPageFrame *pPage,
                             const SwBorderAttrs & rAttrs,
                             const bool bLowerMode,
                             const bool bLowerBorder,
                             const bool bOnlyTextBackground ) const
{
    // OD 20.01.2003 #i1837# - no paint of table background, if corresponding
    // option is *not* set.
    if( IsTabFrame() &&
        !gProp.pSGlobalShell->GetViewOptions()->IsTable() )
    {
        return;
    }

    // nothing to do for covered table cells:
    if( IsCellFrame() && IsCoveredCell() )
        return;

    SwViewShell *pSh = gProp.pSGlobalShell;

    // #i16816# tagged pdf support
    SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, nullptr, *pSh->GetOut() );

    const SvxBrushItem* pItem;
    // OD 05.09.2002 #102912#
    // temporary background brush for a fly frame without a background brush
    SvxBrushItem* pTmpBackBrush = nullptr;
    const Color* pCol;
    SwRect aOrigBackRect;
    const bool bPageFrame = IsPageFrame();
    bool bLowMode = true;
    drawinglayer::attribute::SdrAllFillAttributesHelperPtr aFillAttributes;

    bool bBack = GetBackgroundBrush( aFillAttributes, pItem, pCol, aOrigBackRect, bLowerMode, /*bConsiderTextBox=*/false );
    //- Output if a separate background is used.
    bool bNoFlyBackground = !gProp.bSFlyMetafile && !bBack && IsFlyFrame();
    if ( bNoFlyBackground )
    {
        // OD 05.09.2002 #102912# - Fly frame has no background.
        // Try to find background brush at parents, if previous call of
        // <GetBackgroundBrush> disabled this option with the parameter <bLowerMode>
        if ( bLowerMode )
        {
            bBack = GetBackgroundBrush( aFillAttributes, pItem, pCol, aOrigBackRect, false, /*bConsiderTextBox=*/false );
        }
        // If still no background found for the fly frame, initialize the
        // background brush <pItem> with global retouche color and set <bBack>
        // to true, that fly frame will paint its background using this color.
        if ( !bBack )
        {
            // OD 10.01.2003 #i6467# - on print output, pdf output and
            // in embedded mode not editing color COL_WHITE is used instead of
            // the global retouche color.
            if ( pSh->GetOut()->GetOutDevType() == OUTDEV_PRINTER ||
                 pSh->GetViewOptions()->IsPDFExport() ||
                 ( pSh->GetDoc()->GetDocShell()->GetCreateMode() == SfxObjectCreateMode::EMBEDDED &&
                   !pSh->GetDoc()->GetDocShell()->IsInPlaceActive()
                 )
               )
            {
                pTmpBackBrush = new SvxBrushItem( COL_WHITE, RES_BACKGROUND );

                //UUU
                aFillAttributes.reset(new drawinglayer::attribute::SdrAllFillAttributesHelper(COL_WHITE));
            }
            else
            {
                pTmpBackBrush = new SvxBrushItem( aGlobalRetoucheColor, RES_BACKGROUND);

                //UUU
                aFillAttributes.reset(new drawinglayer::attribute::SdrAllFillAttributesHelper(aGlobalRetoucheColor));
            }

            pItem = pTmpBackBrush;
            bBack = true;
        }
    }

    SwRect aPaintRect( getFrameArea() );
    if( IsTextFrame() || IsSctFrame() )
        aPaintRect = UnionFrame( true );

    // bOnlyTextBackground means background that's on top of background shapes,
    // this includes both text and cell frames.
    if ( (!bOnlyTextBackground || IsTextFrame() || IsCellFrame()) && aPaintRect.IsOver( rRect ) )
    {
        if ( bBack || bPageFrame || !bLowerMode )
        {
            const bool bBrowse = pSh->GetViewOptions()->getBrowseMode();
            SwRect aRect;
            if ( (bPageFrame && bBrowse) ||
                 (IsTextFrame() && getFramePrintArea().SSize() == getFrameArea().SSize()) )
            {
                aRect = getFrameArea();
                ::SwAlignRect( aRect, gProp.pSGlobalShell, gProp.pSGlobalShell->GetOut() );
            }
            else
            {
                ::lcl_CalcBorderRect( aRect, this, rAttrs, false, gProp);
                if ( (IsTextFrame() || IsTabFrame()) && GetPrev() )
                {
                    if ( GetPrev()->GetAttrSet()->GetBackground() == GetAttrSet()->GetBackground() &&
                         lcl_compareFillAttributes(GetPrev()->getSdrAllFillAttributesHelper(), getSdrAllFillAttributesHelper()))
                    {
                        aRect.Top( getFrameArea().Top() );
                    }
                }
            }
            aRect.Intersection( rRect );

            OutputDevice *pOut = pSh->GetOut();

            if ( aRect.HasArea() )
            {
                SvxBrushItem* pNewItem = nullptr;

                if( pCol )
                {
                    pNewItem = new SvxBrushItem( *pCol, RES_BACKGROUND );
                    pItem = pNewItem;
                    aFillAttributes.reset(new drawinglayer::attribute::SdrAllFillAttributesHelper(*pCol));
                }

                SwRegionRects aRegion( aRect );
                basegfx::B2DPolygon aB2DPolygon{tools::Polygon(aRect.SVRect()).getB2DPolygon()};
                basegfx::utils::B2DClipState aClipState{basegfx::B2DPolyPolygon(aB2DPolygon)};
                if (pPage->GetSortedObjs() &&
                    pSh->GetDoc()->getIDocumentSettingAccess().get(DocumentSettingId::SUBTRACT_FLYS))
                {
                    ::lcl_SubtractFlys( this, pPage, aRect, aRegion, aClipState, gProp );
                }

                // OD 06.08.2002 #99657# - determine, if background transparency
                //     have to be considered for drawing.
                //     --> Status Quo: background transparency have to be
                //        considered for fly frames
                const bool bConsiderBackgroundTransparency = IsFlyFrame();
                bool bDone(false);

                // #i125189# We are also done when the new DrawingLayer FillAttributes are used
                // or the FillStyle is set (different from drawing::FillStyle_NONE)
                if(pOut && aFillAttributes.get())
                {
                    if(aFillAttributes->isUsed())
                    {
                        // check if really something is painted
                        bDone = DrawFillAttributes(aFillAttributes, aOrigBackRect, aRegion, aClipState, *pOut);
                    }

                    if(!bDone)
                    {
                        // if not, still a FillStyle could be set but the transparency is at 100%,
                        // thus need to check the model data itself for FillStyle (do not rely on
                        // SdrAllFillAttributesHelper since it already contains optimized information,
                        // e.g. transparency leads to no fill)
                        const drawing::FillStyle eFillStyle(GetAttrSet()->Get(XATTR_FILLSTYLE).GetValue());

                        if(drawing::FillStyle_NONE != eFillStyle)
                        {
                            bDone = true;
                        }
                    }
                }

                if(!bDone)
                {
                    for (size_t i = 0; i < aRegion.size(); ++i)
                    {
                        if (1 < aRegion.size())
                        {
                            ::SwAlignRect( aRegion[i], gProp.pSGlobalShell, gProp.pSGlobalShell->GetOut() );
                            if( !aRegion[i].HasArea() )
                              continue;
                        }
                    // OD 06.08.2002 #99657# - add 6th parameter to indicate, if
                    //     background transparency have to be considered
                    //     Set missing 5th parameter to the default value GRFNUM_NO
                    //         - see declaration in /core/inc/frmtool.hxx.
                            ::DrawGraphic(
                                pItem,
                                pOut,
                                aOrigBackRect,
                                aRegion[i],
                                GRFNUM_NO,
                                bConsiderBackgroundTransparency );
                    }
                }
                if( pCol )
                    delete pNewItem;
            }
        }
        else
            bLowMode = bLowerMode;
    }

    // OD 05.09.2002 #102912#
    // delete temporary background brush.
    delete pTmpBackBrush;

    //Now process lower and his neighbour.
    //We end this as soon as a Frame leaves the chain and therefore is not a lower
    //of me anymore
    const SwFrame *pFrame = GetLower();
    if ( pFrame )
    {
        SwRect aFrameRect;
        SwRect aRect( GetPaintArea() );
        aRect.Intersection_( rRect );
        SwRect aBorderRect( aRect );
        SwShortCut aShortCut( *pFrame, aBorderRect );
        do
        {   if ( gProp.pSProgress )
                gProp.pSProgress->Reschedule();

            aFrameRect = pFrame->GetPaintArea();
            if ( aFrameRect.IsOver( aBorderRect ) )
            {
                SwBorderAttrAccess aAccess( SwFrame::GetCache(), pFrame );
                const SwBorderAttrs &rTmpAttrs = *aAccess.Get();
                if ( ( pFrame->IsLayoutFrame() && bLowerBorder ) || aFrameRect.IsOver( aRect ) )
                {
                    pFrame->PaintSwFrameBackground( aRect, pPage, rTmpAttrs, bLowMode,
                                           bLowerBorder, bOnlyTextBackground );
                }

                if ( bLowerBorder )
                {
                    pFrame->PaintSwFrameShadowAndBorder( aBorderRect, pPage, rTmpAttrs );
                }
            }
            pFrame = pFrame->GetNext();
        } while ( pFrame && pFrame->GetUpper() == this &&
                  !aShortCut.Stop( aFrameRect ) );
    }
}

/// Refreshes all subsidiary lines of a page.
void SwPageFrame::RefreshSubsidiary( const SwRect &rRect ) const
{
    if ( IS_SUBS || isTableBoundariesEnabled() || IS_SUBS_SECTION || IS_SUBS_FLYS )
    {
        if ( rRect.HasArea() )
        {
            //During paint using the root, the array is controlled from there.
            //Otherwise we'll handle it for our self.
            bool bDelSubs = false;
            if ( !gProp.pSSubsLines )
            {
                gProp.pSSubsLines.reset(new SwSubsRects);
                // OD 20.12.2002 #106318# - create container for special subsidiary lines
                gProp.pSSpecSubsLines.reset(new SwSubsRects);
                bDelSubs = true;
            }

            RefreshLaySubsidiary( this, rRect );

            if ( bDelSubs )
            {
                // OD 20.12.2002 #106318# - paint special subsidiary lines
                // and delete its container
                gProp.pSSpecSubsLines->PaintSubsidiary( gProp.pSGlobalShell->GetOut(), nullptr, gProp );
                gProp.pSSpecSubsLines.reset();

                gProp.pSSubsLines->PaintSubsidiary(gProp.pSGlobalShell->GetOut(), gProp.pSLines.get(), gProp);
                gProp.pSSubsLines.reset();
            }
        }
    }
}

void SwLayoutFrame::RefreshLaySubsidiary( const SwPageFrame *pPage,
                                        const SwRect &rRect ) const
{
    const bool bSubsOpt   = IS_SUBS;
    if ( bSubsOpt )
        PaintSubsidiaryLines( pPage, rRect );

    const SwFrame *pLow = Lower();
    if( !pLow )
        return;
    SwShortCut aShortCut( *pLow, rRect );
    while( pLow && !aShortCut.Stop( pLow->getFrameArea() ) )
    {
        if ( pLow->getFrameArea().IsOver( rRect ) && pLow->getFrameArea().HasArea() )
        {
            if ( pLow->IsLayoutFrame() )
                static_cast<const SwLayoutFrame*>(pLow)->RefreshLaySubsidiary( pPage, rRect);
            else if ( pLow->GetDrawObjs() )
            {
                const SwSortedObjs& rObjs = *(pLow->GetDrawObjs());
                for (SwAnchoredObject* pAnchoredObj : rObjs)
                {
                    if ( pPage->GetFormat()->GetDoc()->getIDocumentDrawModelAccess().IsVisibleLayerId(
                                    pAnchoredObj->GetDrawObj()->GetLayer() ) &&
                         dynamic_cast< const SwFlyFrame *>( pAnchoredObj ) !=  nullptr )
                    {
                        const SwFlyFrame *pFly =
                                    static_cast<const SwFlyFrame*>(pAnchoredObj);
                        if ( pFly->IsFlyInContentFrame() && pFly->getFrameArea().IsOver( rRect ) )
                        {
                            if ( !pFly->Lower() || !pFly->Lower()->IsNoTextFrame() ||
                                 !static_cast<const SwNoTextFrame*>(pFly->Lower())->HasAnimation())
                                pFly->RefreshLaySubsidiary( pPage, rRect );
                        }
                    }
                }
            }
        }
        pLow = pLow->GetNext();
    }
}

/**
 * Subsidiary lines to paint the PrtAreas
 * Only the LayoutFrames which directly contain Content
 * Paints the desired line and pays attention to not overpaint any flys
 */
static void lcl_RefreshLine( const SwLayoutFrame *pLay,
                                  const SwPageFrame *pPage,
                                  const Point &rP1,
                                  const Point &rP2,
                                  const SubColFlags nSubColor,
                                  SwLineRects* pSubsLines )
{
    //In which direction do we loop? Can only be horizontal or vertical.
    OSL_ENSURE( ((rP1.X() == rP2.X()) || (rP1.Y() == rP2.Y())),
            "Sloped subsidiary lines are not allowed." );

    const bool bHori = rP1.Y() == rP2.Y();

    // use pointers to member function in order to unify flow
    typedef long (Point:: *pmfPtGet)() const;
    typedef void (Point:: *pmfPtSet)(long);
    const pmfPtGet pDirPtX = &Point::X;
    const pmfPtGet pDirPtY = &Point::Y;
    const pmfPtGet pDirPt = bHori ? pDirPtX : pDirPtY;
    const pmfPtSet pDirPtSetX = &Point::setX;
    const pmfPtSet pDirPtSetY = &Point::setY;
    const pmfPtSet pDirPtSet = bHori ? pDirPtSetX : pDirPtSetY;

    Point aP1( rP1 );
    Point aP2( rP2 );

    while ( (aP1.*pDirPt)() < (aP2.*pDirPt)() )
    {
        //If the starting point lies in a fly, it is directly set behind the
        //fly.
        //The end point moves to the start if the end point lies in a fly or we
        //have a fly between starting point and end point.
        // In this way, every position is output one by one.

        //If I'm a fly I'll only avoid those flys which are places 'above' me;
        //this means those who are behind me in the array.
        //Even if I'm inside a fly or inside a fly inside a fly a.s.o I won't
        //avoid any of those flys.
        SwOrderIter aIter( pPage );
        const SwFlyFrame *pMyFly = pLay->FindFlyFrame();
        if ( pMyFly )
        {
            aIter.Current( pMyFly->GetVirtDrawObj() );
            while ( nullptr != (pMyFly = pMyFly->GetAnchorFrame()->FindFlyFrame()) )
            {
                if ( aIter()->GetOrdNum() > pMyFly->GetVirtDrawObj()->GetOrdNum() )
                    aIter.Current( pMyFly->GetVirtDrawObj() );
            }
        }
        else
            aIter.Bottom();

        while ( aIter() )
        {
            const SwVirtFlyDrawObj *pObj = static_cast<const SwVirtFlyDrawObj*>(aIter());
            const SwFlyFrame *pFly = pObj ? pObj->GetFlyFrame() : nullptr;

            //I certainly won't avoid myself, even if I'm placed _inside_ the
            //fly I won't avoid it.
            if ( !pFly || (pFly == pLay || pFly->IsAnLower( pLay )) )
            {
                aIter.Next();
                continue;
            }

            // OD 19.12.2002 #106318# - do *not* consider fly frames with
            // a transparent background.
            // OD 2004-02-12 #110582#-2 - do *not* consider fly frame, which
            // belongs to a invisible layer
            if ( pFly->IsBackgroundTransparent() ||
                 !pFly->GetFormat()->GetDoc()->getIDocumentDrawModelAccess().IsVisibleLayerId( pObj->GetLayer() ) )
            {
                aIter.Next();
                continue;
            }

            //Is the Obj placed on the line
            const long nP1OthPt = !bHori ? rP1.X() : rP1.Y();
            const tools::Rectangle &rBound = pObj->GetCurrentBoundRect();
            const Point aDrPt( rBound.TopLeft() );
            const long nDrOthPt = !bHori ? aDrPt.X() : aDrPt.Y();
            const Size  aDrSz( rBound.GetSize() );
            const long nDrOthSz = !bHori ? aDrSz.Width() : aDrSz.Height();

            if ( nP1OthPt >= nDrOthPt && nP1OthPt <= nDrOthPt + nDrOthSz )
            {
                const long nDrDirPt = bHori ? aDrPt.X() : aDrPt.Y();
                const long nDrDirSz = bHori ? aDrSz.Width() : aDrSz.Height();

                if ( (aP1.*pDirPt)() >= nDrDirPt && (aP1.*pDirPt)() <= nDrDirPt + nDrDirSz )
                    (aP1.*pDirPtSet)( nDrDirPt + nDrDirSz );

                if ( (aP2.*pDirPt)() >= nDrDirPt && (aP1.*pDirPt)() < (nDrDirPt - 1) )
                    (aP2.*pDirPtSet)( nDrDirPt - 1 );
            }
            aIter.Next();
        }

        if ( (aP1.*pDirPt)() < (aP2.*pDirPt)() )
        {
            SwRect aRect( aP1, aP2 );
            // OD 18.11.2002 #99672# - use parameter <pSubsLines> instead of
            // global variable <gProp.pSSubsLines>.
            pSubsLines->AddLineRect( aRect, nullptr, SvxBorderLineStyle::SOLID,
                    nullptr, nSubColor, gProp );
        }
        aP1 = aP2;
        (aP1.*pDirPtSet)( (aP1.*pDirPt)() + 1 );
        aP2 = rP2;
    }
}

static drawinglayer::primitive2d::Primitive2DContainer lcl_CreatePageAreaDelimiterPrimitives(
        const SwRect& rRect )
{
    drawinglayer::primitive2d::Primitive2DContainer aSeq( 4 );

    basegfx::BColor aLineColor = SwViewOption::GetDocBoundariesColor().getBColor();
    double nLineLength = 200.0; // in Twips

    Point aPoints[] = { rRect.TopLeft(), rRect.TopRight(), rRect.BottomRight(), rRect.BottomLeft() };
    double const aXOffDirs[] = { -1.0, 1.0, 1.0, -1.0 };
    double const aYOffDirs[] = { -1.0, -1.0, 1.0, 1.0 };

    // Actually loop over the corners to create the two lines
    for ( int i = 0; i < 4; i++ )
    {
        basegfx::B2DVector aHorizVector( aXOffDirs[i], 0.0 );
        basegfx::B2DVector aVertVector( 0.0, aYOffDirs[i] );

        basegfx::B2DPoint aBPoint( aPoints[i].getX(), aPoints[i].getY() );

        basegfx::B2DPolygon aPolygon;
        aPolygon.append( aBPoint + aHorizVector * nLineLength );
        aPolygon.append( aBPoint );
        aPolygon.append( aBPoint + aVertVector * nLineLength );

        drawinglayer::primitive2d::PolygonHairlinePrimitive2D* pLine =
            new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(
                    aPolygon, aLineColor );
        aSeq[i] = drawinglayer::primitive2d::Primitive2DReference( pLine );
    }

    return aSeq;
}

static drawinglayer::primitive2d::Primitive2DContainer lcl_CreateRectangleDelimiterPrimitives (
        const SwRect& rRect )
{
    drawinglayer::primitive2d::Primitive2DContainer aSeq( 1 );
    basegfx::BColor aLineColor = SwViewOption::GetDocBoundariesColor().getBColor();

    basegfx::B2DPolygon aPolygon;
    aPolygon.append( basegfx::B2DPoint( rRect.Left(), rRect.Top() ) );
    aPolygon.append( basegfx::B2DPoint( rRect.Right(), rRect.Top() ) );
    aPolygon.append( basegfx::B2DPoint( rRect.Right(), rRect.Bottom() ) );
    aPolygon.append( basegfx::B2DPoint( rRect.Left(), rRect.Bottom() ) );
    aPolygon.setClosed( true );

    drawinglayer::primitive2d::PolygonHairlinePrimitive2D* pLine =
        new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(
                aPolygon, aLineColor );
    aSeq[0] = drawinglayer::primitive2d::Primitive2DReference( pLine );

    return aSeq;
}

static drawinglayer::primitive2d::Primitive2DContainer lcl_CreateColumnAreaDelimiterPrimitives(
        const SwRect& rRect )
{
    drawinglayer::primitive2d::Primitive2DContainer aSeq( 4 );

    basegfx::BColor aLineColor = SwViewOption::GetDocBoundariesColor().getBColor();
    double nLineLength = 100.0; // in Twips

    Point aPoints[] = { rRect.TopLeft(), rRect.TopRight(), rRect.BottomRight(), rRect.BottomLeft() };
    double const aXOffDirs[] = { 1.0, -1.0, -1.0, 1.0 };
    double const aYOffDirs[] = { 1.0, 1.0, -1.0, -1.0 };

    // Actually loop over the corners to create the two lines
    for ( int i = 0; i < 4; i++ )
    {
        basegfx::B2DVector aHorizVector( aXOffDirs[i], 0.0 );
        basegfx::B2DVector aVertVector( 0.0, aYOffDirs[i] );

        basegfx::B2DPoint aBPoint( aPoints[i].getX(), aPoints[i].getY() );

        basegfx::B2DPolygon aPolygon;
        aPolygon.append( aBPoint + aHorizVector * nLineLength );
        aPolygon.append( aBPoint );
        aPolygon.append( aBPoint + aVertVector * nLineLength );

        drawinglayer::primitive2d::PolygonHairlinePrimitive2D* pLine =
            new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(
                    aPolygon, aLineColor );
        aSeq[i] = drawinglayer::primitive2d::Primitive2DReference( pLine );
    }

    return aSeq;
}

void SwPageFrame::PaintSubsidiaryLines( const SwPageFrame *,
                                        const SwRect & ) const
{
    if ( !gProp.pSGlobalShell->IsHeaderFooterEdit() )
    {
        const SwFrame* pLay = Lower();
        const SwFrame* pFootnoteCont = nullptr;
        const SwFrame* pPageBody = nullptr;
        while ( pLay && !( pFootnoteCont && pPageBody ) )
        {
            if ( pLay->IsFootnoteContFrame( ) )
                pFootnoteCont = pLay;
            if ( pLay->IsBodyFrame() )
                pPageBody = pLay;
            pLay = pLay->GetNext();
        }

        SwRect aArea( pPageBody->getFrameArea() );
        if ( pFootnoteCont )
            aArea.AddBottom( pFootnoteCont->getFrameArea().Bottom() - aArea.Bottom() );

        if ( !gProp.pSGlobalShell->GetViewOptions()->IsViewMetaChars( ) )
            ProcessPrimitives( lcl_CreatePageAreaDelimiterPrimitives( aArea ) );
        else
            ProcessPrimitives( lcl_CreateRectangleDelimiterPrimitives( aArea ) );
    }
}

void SwColumnFrame::PaintSubsidiaryLines( const SwPageFrame *,
                                        const SwRect & ) const
{
    const SwFrame* pLay = Lower();
    const SwFrame* pFootnoteCont = nullptr;
    const SwFrame* pColBody = nullptr;
    while ( pLay && !( pFootnoteCont && pColBody ) )
    {
        if ( pLay->IsFootnoteContFrame( ) )
            pFootnoteCont = pLay;
        if ( pLay->IsBodyFrame() )
            pColBody = pLay;
        pLay = pLay->GetNext();
    }

    SwRect aArea( pColBody->getFrameArea() );

    // #i3662# - enlarge top of column body frame's printing area
    // in sections to top of section frame.
    const bool bColInSection =  GetUpper()->IsSctFrame();
    if ( bColInSection )
    {
        if ( IsVertical() )
            aArea.Right( GetUpper()->getFrameArea().Right() );
        else
            aArea.Top( GetUpper()->getFrameArea().Top() );
    }

    if ( pFootnoteCont )
        aArea.AddBottom( pFootnoteCont->getFrameArea().Bottom() - aArea.Bottom() );

    ::SwAlignRect( aArea, gProp.pSGlobalShell, gProp.pSGlobalShell->GetOut() );

    if ( !gProp.pSGlobalShell->GetViewOptions()->IsViewMetaChars( ) )
        ProcessPrimitives( lcl_CreateColumnAreaDelimiterPrimitives( aArea ) );
    else
        ProcessPrimitives( lcl_CreateRectangleDelimiterPrimitives( aArea ) );
}

void SwSectionFrame::PaintSubsidiaryLines( const SwPageFrame * pPage,
                                        const SwRect & rRect ) const
{
    const bool bNoLowerColumn = !Lower() || !Lower()->IsColumnFrame();
    if ( bNoLowerColumn )
    {
        SwLayoutFrame::PaintSubsidiaryLines( pPage, rRect );
    }
}

/**
 * The SwBodyFrame doesn't print any subsidiary line: it's bounds are painted
 * either by the parent page or the parent column frame.
 */
void SwBodyFrame::PaintSubsidiaryLines( const SwPageFrame *,
                                        const SwRect & ) const
{
}

void SwHeadFootFrame::PaintSubsidiaryLines( const SwPageFrame *, const SwRect & ) const
{
    if ( gProp.pSGlobalShell->IsHeaderFooterEdit() )
    {
        SwRect aArea( getFramePrintArea() );
        aArea.Pos() += getFrameArea().Pos();
        if ( !gProp.pSGlobalShell->GetViewOptions()->IsViewMetaChars( ) )
            ProcessPrimitives( lcl_CreatePageAreaDelimiterPrimitives( aArea ) );
        else
            ProcessPrimitives( lcl_CreateRectangleDelimiterPrimitives( aArea ) );
    }
}

/**
 * This method is overridden in order to have no subsidiary lines
 * around the footnotes.
 */
void SwFootnoteFrame::PaintSubsidiaryLines( const SwPageFrame *,
                                        const SwRect & ) const
{
}

/**
 * This method is overridden in order to have no subsidiary lines
 * around the footnotes containers.
 */
void SwFootnoteContFrame::PaintSubsidiaryLines( const SwPageFrame *,
                                        const SwRect & ) const
{
}

void SwLayoutFrame::PaintSubsidiaryLines( const SwPageFrame *pPage,
                                        const SwRect &rRect ) const
{
    bool bNewTableModel = false;

    // #i29550#
    if ( IsTabFrame() || IsCellFrame() || IsRowFrame() )
    {
        const SwTabFrame* pTabFrame = FindTabFrame();
        if ( pTabFrame->IsCollapsingBorders() )
            return;

        bNewTableModel = pTabFrame->GetTable()->IsNewModel();
        // in the new table model, we have an early return for all cell-related
        // frames, except from non-covered table cells
        if ( bNewTableModel )
            if ( IsTabFrame() ||
                 IsRowFrame() ||
                 ( IsCellFrame() && IsCoveredCell() ) )
                return;
    }

    const bool bFlys = pPage->GetSortedObjs() != nullptr;

    const bool bCell = IsCellFrame();
    // use frame area for cells
    // OD 13.02.2003 #i3662# - for section use also frame area
    const bool bUseFrameArea = bCell || IsSctFrame();
    SwRect aOriginal( bUseFrameArea ? getFrameArea() : getFramePrintArea() );
    if ( !bUseFrameArea )
        aOriginal.Pos() += getFrameArea().Pos();

    ::SwAlignRect( aOriginal, gProp.pSGlobalShell, gProp.pSGlobalShell->GetOut() );

    if ( !aOriginal.IsOver( rRect ) )
        return;

    SwRect aOut( aOriginal );
    aOut.Intersection_( rRect );

    const SwTwips nRight = aOut.Right();
    const SwTwips nBottom= aOut.Bottom();

    const Point aRT( nRight, aOut.Top() );
    const Point aRB( nRight, nBottom );
    const Point aLB( aOut.Left(), nBottom );

    SubColFlags nSubColor = ( bCell || IsRowFrame() )
                            ? SubColFlags::Tab
                            : ( IsInSct()
                                ? SubColFlags::Sect
                                : ( IsInFly() ? SubColFlags::Fly : SubColFlags::Page ) );

    // OD 18.11.2002 #99672# - collect body, header, footer, footnote and section
    // sub-lines in <pSpecSubsLine> array.
    const bool bSpecialSublines = IsBodyFrame() || IsHeaderFrame() || IsFooterFrame() ||
                                  IsFootnoteFrame() || IsSctFrame();
    SwLineRects *const pUsedSubsLines = bSpecialSublines
            ? gProp.pSSpecSubsLines.get() : gProp.pSSubsLines.get();

    // NOTE: for cell frames only left and right (horizontal layout) respectively
    //      top and bottom (vertical layout) lines painted.
    // NOTE2: this does not hold for the new table model!!! We paint the top border
    // of each non-covered table cell.
    const bool bVert = IsVertical();
    if ( bFlys )
    {
        // OD 14.11.2002 #104822# - add control for drawing left and right lines
        if ( !bCell || bNewTableModel || !bVert )
        {
            if ( aOriginal.Left() == aOut.Left() )
                ::lcl_RefreshLine( this, pPage, aOut.Pos(), aLB, nSubColor, pUsedSubsLines );
            // OD 14.11.2002 #104821# - in vertical layout set page/column break at right
            if ( aOriginal.Right() == nRight )
                ::lcl_RefreshLine( this, pPage, aRT, aRB, nSubColor, pUsedSubsLines );
        }
        // OD 14.11.2002 #104822# - adjust control for drawing top and bottom lines
        if ( !bCell || bNewTableModel || bVert )
        {
            if ( aOriginal.Top() == aOut.Top() )
                // OD 14.11.2002 #104821# - in horizontal layout set page/column break at top
                ::lcl_RefreshLine( this, pPage, aOut.Pos(), aRT, nSubColor, pUsedSubsLines );
            if ( aOriginal.Bottom() == nBottom )
                ::lcl_RefreshLine( this, pPage, aLB, aRB, nSubColor,
                                   pUsedSubsLines );
        }
    }
    else
    {
        // OD 14.11.2002 #104822# - add control for drawing left and right lines
        if ( !bCell || bNewTableModel || !bVert )
        {
            if ( aOriginal.Left() == aOut.Left() )
            {
                const SwRect aRect( aOut.Pos(), aLB );
                pUsedSubsLines->AddLineRect( aRect, nullptr,
                        SvxBorderLineStyle::SOLID, nullptr, nSubColor, gProp );
            }
            // OD 14.11.2002 #104821# - in vertical layout set page/column break at right
            if ( aOriginal.Right() == nRight )
            {
                const SwRect aRect( aRT, aRB );
                pUsedSubsLines->AddLineRect( aRect, nullptr,
                        SvxBorderLineStyle::SOLID, nullptr, nSubColor, gProp );
            }
        }
        // OD 14.11.2002 #104822# - adjust control for drawing top and bottom lines
        if ( !bCell || bNewTableModel || bVert )
        {
            if ( aOriginal.Top() == aOut.Top() )
            {
                // OD 14.11.2002 #104821# - in horizontal layout set page/column break at top
                const SwRect aRect( aOut.Pos(), aRT );
                pUsedSubsLines->AddLineRect( aRect, nullptr,
                        SvxBorderLineStyle::SOLID, nullptr, nSubColor, gProp );
            }
            if ( aOriginal.Bottom() == nBottom )
            {
                const SwRect aRect( aLB, aRB );
                pUsedSubsLines->AddLineRect( aRect, nullptr,
                        SvxBorderLineStyle::SOLID, nullptr, nSubColor, gProp );
            }
        }
    }
}

/**
 * Refreshes all extra data (line breaks a.s.o) of the page. Basically only those objects
 * are considered which horizontally overlap the Rect.
 */
void SwPageFrame::RefreshExtraData( const SwRect &rRect ) const
{
    const SwLineNumberInfo &rInfo = GetFormat()->GetDoc()->GetLineNumberInfo();
    bool bLineInFly = (rInfo.IsPaintLineNumbers() && rInfo.IsCountInFlys())
        || static_cast<sal_Int16>(SW_MOD()->GetRedlineMarkPos()) != text::HoriOrientation::NONE;

    SwRect aRect( rRect );
    ::SwAlignRect( aRect, gProp.pSGlobalShell, gProp.pSGlobalShell->GetOut() );
    if ( aRect.HasArea() )
    {
        SwLayoutFrame::RefreshExtraData( aRect );

        if ( bLineInFly && GetSortedObjs() )
            for (SwAnchoredObject* pAnchoredObj : *GetSortedObjs())
            {
                if ( auto pFly = dynamic_cast< const SwFlyFrame *>( pAnchoredObj ) )
                {
                    if ( pFly->getFrameArea().Top() <= aRect.Bottom() &&
                         pFly->getFrameArea().Bottom() >= aRect.Top() )
                        pFly->RefreshExtraData( aRect );
                }
            }
    }
}

void SwLayoutFrame::RefreshExtraData( const SwRect &rRect ) const
{

    const SwLineNumberInfo &rInfo = GetFormat()->GetDoc()->GetLineNumberInfo();
    bool bLineInBody = rInfo.IsPaintLineNumbers(),
             bLineInFly  = bLineInBody && rInfo.IsCountInFlys(),
             bRedLine = static_cast<sal_Int16>(SW_MOD()->GetRedlineMarkPos())!=text::HoriOrientation::NONE;

    const SwContentFrame *pCnt = ContainsContent();
    while ( pCnt && IsAnLower( pCnt ) )
    {
        if ( pCnt->IsTextFrame() && ( bRedLine ||
             ( !pCnt->IsInTab() &&
               ((bLineInBody && pCnt->IsInDocBody()) ||
               (bLineInFly  && pCnt->IsInFly())) ) ) &&
             pCnt->getFrameArea().Top() <= rRect.Bottom() &&
             pCnt->getFrameArea().Bottom() >= rRect.Top() )
        {
            static_cast<const SwTextFrame*>(pCnt)->PaintExtraData( rRect );
        }
        if ( bLineInFly && pCnt->GetDrawObjs() )
            for (SwAnchoredObject* pAnchoredObj : *pCnt->GetDrawObjs())
            {
                if ( auto pFly = dynamic_cast< const SwFlyFrame *>( pAnchoredObj ) )
                {
                    if ( pFly->IsFlyInContentFrame() &&
                         pFly->getFrameArea().Top() <= rRect.Bottom() &&
                         pFly->getFrameArea().Bottom() >= rRect.Top() )
                        pFly->RefreshExtraData( rRect );
                }
        }
        pCnt = pCnt->GetNextContentFrame();
    }
}

/**
 * For #102450#
 * Determine the color, that is respectively will be drawn as background
 * for the page frame.
 * Using existing method SwFrame::GetBackgroundBrush to determine the color
 * that is set at the page frame respectively is parent. If none is found
 * return the global retouche color
 *
 * @return Color
 */
const Color SwPageFrame::GetDrawBackgrdColor() const
{
    const SvxBrushItem* pBrushItem;
    const Color* pDummyColor;
    SwRect aDummyRect;
    drawinglayer::attribute::SdrAllFillAttributesHelperPtr aFillAttributes;

    if ( GetBackgroundBrush( aFillAttributes, pBrushItem, pDummyColor, aDummyRect, true, /*bConsiderTextBox=*/false) )
    {
        if(aFillAttributes.get() && aFillAttributes->isUsed())
        {
            // let SdrAllFillAttributesHelper do the average color calculation
            return Color(aFillAttributes->getAverageColor(aGlobalRetoucheColor.getBColor()));
        }
        else if(pBrushItem)
        {
            OUString referer;
            SwViewShell * sh1 = getRootFrame()->GetCurrShell();
            if (sh1 != nullptr) {
                SfxObjectShell * sh2 = sh1->GetDoc()->GetPersist();
                if (sh2 != nullptr && sh2->HasName()) {
                    referer = sh2->GetMedium()->GetName();
                }
            }
            const Graphic* pGraphic = pBrushItem->GetGraphic(referer);

            if(pGraphic)
            {
                // #29105# when a graphic is set, it may be possible to calculate a single
                // color which looks good in all places of the graphic. Since it is
                // planned to have text edit on the overlay one day and the fallback
                // to aGlobalRetoucheColor returns something useful, just use that
                // for now.
            }
            else
            {
                // not a graphic, use (hopefully) initialized color
                return pBrushItem->GetColor();
            }
        }
    }

    return aGlobalRetoucheColor;
}

/// create/return font used to paint the "empty page" string
const vcl::Font& SwPageFrame::GetEmptyPageFont()
{
    static vcl::Font* pEmptyPgFont = nullptr;
    if ( nullptr == pEmptyPgFont )
    {
        pEmptyPgFont = new vcl::Font;
        pEmptyPgFont->SetFontSize( Size( 0, 80 * 20 )); // == 80 pt
        pEmptyPgFont->SetWeight( WEIGHT_BOLD );
        pEmptyPgFont->SetStyleName( aEmptyOUStr );
        pEmptyPgFont->SetFamilyName("Helvetica");
        pEmptyPgFont->SetFamily( FAMILY_SWISS );
        pEmptyPgFont->SetTransparent( true );
        pEmptyPgFont->SetColor( COL_GRAY );
    }

    return *pEmptyPgFont;
}

/**
 * Retouch for a section
 *
 * Retouch will only be done, if the Frame is the last one in his chain.
 * The whole area of the upper which is located below the Frame will be
 * cleared using PaintSwFrameBackground.
 */
void SwFrame::Retouch( const SwPageFrame * pPage, const SwRect &rRect ) const
{
    if ( gProp.bSFlyMetafile )
        return;

    OSL_ENSURE( GetUpper(), "Retouche try without Upper." );
    OSL_ENSURE( getRootFrame()->GetCurrShell() && gProp.pSGlobalShell->GetWin(), "Retouche on a printer?" );

    SwRect aRetouche( GetUpper()->GetPaintArea() );
    aRetouche.Top( getFrameArea().Top() + getFrameArea().Height() );
    aRetouche.Intersection( gProp.pSGlobalShell->VisArea() );

    if ( aRetouche.HasArea() )
    {
        //Omit the passed Rect. To do this, we unfortunately need a region to
        //cut out.
        SwRegionRects aRegion( aRetouche );
        aRegion -= rRect;
        SwViewShell *pSh = getRootFrame()->GetCurrShell();

        // #i16816# tagged pdf support
        SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, nullptr, *pSh->GetOut() );

        for ( size_t i = 0; i < aRegion.size(); ++i )
        {
            const SwRect &rRetouche = aRegion[i];

            GetUpper()->PaintBaBo( rRetouche, pPage );

            //Hell and Heaven need to be refreshed too.
            //To avoid recursion my retouch flag needs to be reset first!
            ResetRetouche();
            if ( rRetouche.HasArea() )
            {
                const Color aPageBackgrdColor(pPage->GetDrawBackgrdColor());
                const IDocumentDrawModelAccess& rIDDMA = pSh->getIDocumentDrawModelAccess();
                // --> OD #i76669#
                SwViewObjectContactRedirector aSwRedirector( *pSh );
                // <--

                pSh->Imp()->PaintLayer( rIDDMA.GetHellId(), nullptr,
                                        *pPage, rRetouche, &aPageBackgrdColor,
                                        pPage->IsRightToLeft(),
                                        &aSwRedirector );
                pSh->Imp()->PaintLayer( rIDDMA.GetHeavenId(), nullptr,
                                        *pPage, rRetouche, &aPageBackgrdColor,
                                        pPage->IsRightToLeft(),
                                        &aSwRedirector );
            }

            SetRetouche();

            //Because we leave all paint areas, we need to refresh the
            //subsidiary lines.
            pPage->RefreshSubsidiary( rRetouche );
        }
    }
    if ( SwViewShell::IsLstEndAction() )
        ResetRetouche();
}

/**
 * Determine the background brush for the frame:
 * the background brush is taken from it-self or from its parent (anchor/upper).
 * Normally, the background brush is taken, which has no transparent color or
 * which has a background graphic. But there are some special cases:
 * (1) No background brush is taken from a page frame, if view option "IsPageBack"
 *     isn't set.
 * (2) Background brush from a index section is taken under special conditions.
 *     In this case parameter <rpCol> is set to the index shading color.
 * (3) New (OD 20.08.2002) - Background brush is taken, if on background drawing
 *     of the frame transparency is considered and its color is not "no fill"/"auto fill"
 *
 * Old description in German:
 * Returns the Backgroundbrush for the area of the Frame.
 * The Brush is defined by the Frame or by an upper, the first Brush is
 * used. If no Brush is defined for a Frame, false is returned.
 *
 * @param rpBrush
 * output parameter - constant reference pointer the found background brush
 *
 * @param rpFillStyle
 * output parameter - constant reference pointer the found background fill style
 *
 * @param rpFillGradient
 * output parameter - constant reference pointer the found background fill gradient
 *
 * @param rpCol
 * output parameter - constant reference pointer to the color of the index shading
 * set under special conditions, if background brush is taken from an index section.
 *
 * @param rOrigRect
 * in-/output parameter - reference to the rectangle the background brush is
 * considered for - adjusted to the frame, from which the background brush is
 * taken.
 *
 * @parem bLowerMode
 * input parameter - boolean indicating, if background brush should *not* be
 * taken from parent.
 *
 * @param bConsiderTextBox
 * consider the TextBox of this fly frame (if there is any) when determining
 * the background color, useful for automatic font color.
 *
 * @return true, if a background brush for the frame is found
 */
bool SwFrame::GetBackgroundBrush(
    drawinglayer::attribute::SdrAllFillAttributesHelperPtr& rFillAttributes,
    const SvxBrushItem* & rpBrush,
    const Color*& rpCol,
    SwRect &rOrigRect,
    bool bLowerMode,
    bool bConsiderTextBox ) const
{
    const SwFrame *pFrame = this;
    SwViewShell *pSh = getRootFrame()->GetCurrShell();
    const SwViewOption *pOpt = pSh->GetViewOptions();
    rpBrush = nullptr;
    rpCol = nullptr;
    do
    {
        if ( pFrame->IsPageFrame() && !pOpt->IsPageBack() )
            return false;

        if (pFrame->supportsFullDrawingLayerFillAttributeSet())
        {
            bool bHandledTextBox = false;
            if (pFrame->IsFlyFrame() && bConsiderTextBox)
            {
                const SwFlyFrame* pFlyFrame = static_cast<const SwFlyFrame*>(pFrame);
                SwFrameFormat* pShape
                    = SwTextBoxHelper::getOtherTextBoxFormat(pFlyFrame->GetFormat(), RES_FLYFRMFMT);
                if (pShape)
                {
                    SdrObject* pObject = pShape->FindRealSdrObject();
                    if (pObject)
                    {
                        // Work with the fill attributes of the shape of the fly frame.
                        rFillAttributes.reset(
                            new drawinglayer::attribute::SdrAllFillAttributesHelper(
                                pObject->GetMergedItemSet()));
                        bHandledTextBox = true;
                    }
                }
            }

            if (!bHandledTextBox)
                rFillAttributes = pFrame->getSdrAllFillAttributesHelper();
        }
        const SvxBrushItem &rBack = pFrame->GetAttrSet()->GetBackground();

        if( pFrame->IsSctFrame() )
        {
            const SwSection* pSection = static_cast<const SwSectionFrame*>(pFrame)->GetSection();
            // OD 20.08.2002 #99657# #GetTransChg#
            //     Note: If frame <pFrame> is a section of the index and
            //         it its background color is "no fill"/"auto fill" and
            //         it has no background graphic and
            //         we are not in the page preview and
            //         we are not in read-only mode and
            //         option "index shadings" is set and
            //         the output is not the printer
            //         then set <rpCol> to the color of the index shading
            if( pSection && (   TOX_HEADER_SECTION == pSection->GetType() ||
                                TOX_CONTENT_SECTION == pSection->GetType() ) &&
                (rBack.GetColor() == COL_TRANSPARENT) &&
                rBack.GetGraphicPos() == GPOS_NONE &&
                !pOpt->IsPagePreview() &&
                !pOpt->IsReadonly() &&
                // #114856# Form view
                !pOpt->IsFormView() &&
                SwViewOption::IsIndexShadings() &&
                !pOpt->IsPDFExport() &&
                pSh->GetOut()->GetOutDevType() != OUTDEV_PRINTER )
            {
                rpCol = &SwViewOption::GetIndexShadingsColor();
            }
        }

        // OD 20.08.2002 #99657#
        //     determine, if background draw of frame <pFrame> considers transparency
        //     --> Status Quo: background transparency have to be
        //                     considered for fly frames
        const bool bConsiderBackgroundTransparency = pFrame->IsFlyFrame();

        // #i125189# Do not base the decision for using the parent's fill style for this
        // frame when the new DrawingLayer FillAttributes are used on the SdrAllFillAttributesHelper
        // information. There the data is already optimized to no fill in the case that the
        // transparence is at 100% while no fill is the criteria for derivation
        bool bNewDrawingLayerFillStyleIsUsedAndNotNoFill(false);

        if(rFillAttributes.get())
        {
            // the new DrawingLayer FillStyle is used
            if(rFillAttributes->isUsed())
            {
                // it's not drawing::FillStyle_NONE
                bNewDrawingLayerFillStyleIsUsedAndNotNoFill = true;
            }
            else
            {
                // maybe optimized already when 100% transparency is used somewhere, need to test
                // XFillStyleItem directly from the model data
                const drawing::FillStyle eFillStyle(pFrame->GetAttrSet()->Get(XATTR_FILLSTYLE).GetValue());

                if(drawing::FillStyle_NONE != eFillStyle)
                {
                    bNewDrawingLayerFillStyleIsUsedAndNotNoFill = true;
                }
            }
        }

        // OD 20.08.2002 #99657#
        //     add condition:
        //     If <bConsiderBackgroundTransparency> is set - see above -,
        //     return brush of frame <pFrame>, if its color is *not* "no fill"/"auto fill"
        if (
            // #i125189# Done when the new DrawingLayer FillAttributes are used and
            // not drawing::FillStyle_NONE (see above)
            bNewDrawingLayerFillStyleIsUsedAndNotNoFill ||

            // done when SvxBrushItem is used
            !rBack.GetColor().GetTransparency() || rBack.GetGraphicPos() != GPOS_NONE ||

            // done when direct color is forced
            rpCol ||

            // done when consider BG transparency and color is not completely transparent
            (bConsiderBackgroundTransparency && (rBack.GetColor() != COL_TRANSPARENT))
           )
        {
            rpBrush = &rBack;
            if ( pFrame->IsPageFrame() && pSh->GetViewOptions()->getBrowseMode() )
            {
                rOrigRect = pFrame->getFrameArea();
            }
            else
            {
                if ( pFrame->getFrameArea().SSize() != pFrame->getFramePrintArea().SSize() )
                {
                    SwBorderAttrAccess aAccess( SwFrame::GetCache(), pFrame );
                    const SwBorderAttrs &rAttrs = *aAccess.Get();
                    ::lcl_CalcBorderRect( rOrigRect, pFrame, rAttrs, false, gProp );
                }
                else
                {
                    rOrigRect = pFrame->getFramePrintArea();
                    rOrigRect += pFrame->getFrameArea().Pos();
                }
            }

            return true;
        }

        if ( bLowerMode )
        {
            // Do not try to get background brush from parent (anchor/upper)
            return false;
        }

        // get parent frame - anchor or upper - for next loop
        if ( pFrame->IsFlyFrame() )
        {
            // OD 20.08.2002 - use "static_cast" instead of "old C-cast"
            pFrame = static_cast<const SwFlyFrame*>(pFrame)->GetAnchorFrame();
        }
        else
        {
            pFrame = pFrame->GetUpper();
        }
    } while ( pFrame );

    return false;
}

void SetOutDevAndWin( SwViewShell *pSh, OutputDevice *pO,
                      vcl::Window *pW, sal_uInt16 nZoom )
{
    pSh->mpOut = pO;
    pSh->mpWin = pW;
    pSh->mpOpt->SetZoom( nZoom );
}

Graphic SwFrameFormat::MakeGraphic( ImageMap* )
{
    return Graphic();
}

Graphic SwFlyFrameFormat::MakeGraphic( ImageMap* pMap )
{
    Graphic aRet;
    //search any Fly!
    SwIterator<SwFrame,SwFormat> aIter( *this );
    SwFrame *pFirst = aIter.First();
    SwViewShell *const pSh =
        pFirst ? pFirst->getRootFrame()->GetCurrShell() : nullptr;
    if (nullptr != pSh)
    {
        SwViewShell *pOldGlobal = gProp.pSGlobalShell;
        gProp.pSGlobalShell = pSh;

        bool bNoteURL = pMap &&
            SfxItemState::SET != GetAttrSet().GetItemState( RES_URL );
        if( bNoteURL )
        {
            OSL_ENSURE( !pNoteURL, "MakeGraphic: pNoteURL already used? " );
            pNoteURL = new SwNoteURL;
        }
        SwFlyFrame *pFly = static_cast<SwFlyFrame*>(pFirst);

        OutputDevice *pOld = pSh->GetOut();
        ScopedVclPtrInstance< VirtualDevice > pDev( *pOld );
        pDev->EnableOutput( false );

        GDIMetaFile aMet;
        MapMode aMap( pOld->GetMapMode().GetMapUnit() );
        pDev->SetMapMode( aMap );
        aMet.SetPrefMapMode( aMap );

        ::SwCalcPixStatics( pSh->GetOut() );
        aMet.SetPrefSize( pFly->getFrameArea().SSize() );

        aMet.Record( pDev.get() );
        pDev->SetLineColor();
        pDev->SetFillColor();
        pDev->SetFont( pOld->GetFont() );

        //Enlarge the rectangle if needed, so the border is painted too.
        SwRect aOut( pFly->getFrameArea() );
        SwBorderAttrAccess aAccess( SwFrame::GetCache(), pFly );
        const SwBorderAttrs &rAttrs = *aAccess.Get();
        if ( rAttrs.CalcRightLine() )
            aOut.SSize().AdjustWidth(2*gProp.nSPixelSzW );
        if ( rAttrs.CalcBottomLine() )
            aOut.SSize().AdjustHeight(2*gProp.nSPixelSzH );

        // #i92711# start Pre/PostPaint encapsulation before pOut is changed to the buffering VDev
        const vcl::Region aRepaintRegion(aOut.SVRect());
        pSh->DLPrePaint2(aRepaintRegion);

        vcl::Window *pWin = pSh->GetWin();
        sal_uInt16 nZoom = pSh->GetViewOptions()->GetZoom();
        ::SetOutDevAndWin( pSh, pDev, nullptr, 100 );
        gProp.bSFlyMetafile = true;
        gProp.pSFlyMetafileOut = pWin;

        SwViewShellImp *pImp = pSh->Imp();
        gProp.pSFlyOnlyDraw = pFly;
        gProp.pSLines.reset(new SwLineRects);

        // OD 09.12.2002 #103045# - determine page, fly frame is on
        const SwPageFrame* pFlyPage = pFly->FindPageFrame();
        const Color aPageBackgrdColor(pFlyPage->GetDrawBackgrdColor());
        const IDocumentDrawModelAccess& rIDDMA = pSh->getIDocumentDrawModelAccess();
        // --> OD #i76669#
        SwViewObjectContactRedirector aSwRedirector( *pSh );
        // <--
        pImp->PaintLayer( rIDDMA.GetHellId(), nullptr,
                          *pFlyPage, aOut, &aPageBackgrdColor,
                          pFlyPage->IsRightToLeft(),
                          &aSwRedirector );
        gProp.pSLines->PaintLines( pDev, gProp );
        if ( pFly->IsFlyInContentFrame() )
            pFly->PaintSwFrame( *pDev, aOut );
        gProp.pSLines->PaintLines( pDev, gProp );
        // OD 30.08.2002 #102450# - add 3rd parameter
        pImp->PaintLayer( rIDDMA.GetHeavenId(), nullptr,
                          *pFlyPage, aOut, &aPageBackgrdColor,
                          pFlyPage->IsRightToLeft(),
                          &aSwRedirector );
        gProp.pSLines->PaintLines( pDev, gProp );
        gProp.pSLines.reset();
        gProp.pSFlyOnlyDraw = nullptr;

        gProp.pSFlyMetafileOut = nullptr;
        gProp.bSFlyMetafile = false;
        ::SetOutDevAndWin( pSh, pOld, pWin, nZoom );

        // #i92711# end Pre/PostPaint encapsulation when pOut is back and content is painted
           pSh->DLPostPaint2(true);

        aMet.Stop();
        aMet.Move( -pFly->getFrameArea().Left(), -pFly->getFrameArea().Top() );
        aRet = Graphic( aMet );

        if( bNoteURL )
        {
            OSL_ENSURE( pNoteURL, "MakeGraphic: Good Bye, NoteURL." );
            delete pNoteURL;
            pNoteURL = nullptr;
        }
        gProp.pSGlobalShell = pOldGlobal;
    }
    return aRet;
}

Graphic SwDrawFrameFormat::MakeGraphic( ImageMap* )
{
    Graphic aRet;
    SwDrawModel* pMod = getIDocumentDrawModelAccess().GetDrawModel();
    if ( pMod )
    {
        SdrObject *pObj = FindSdrObject();
        std::unique_ptr<SdrView> pView( new SdrView( *pMod ) );
        SdrPageView *pPgView = pView->ShowSdrPage(pView->GetModel()->GetPage(0));
        pView->MarkObj( pObj, pPgView );
        aRet = pView->GetMarkedObjBitmapEx();
        pView->HideSdrPage();
    }
    return aRet;
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
