/* -*- 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 <utility>
#include <vcl/alpha.hxx>
#include <vcl/canvastools.hxx>
#include <tools/lazydelete.hxx>
#include <sfx2/docfile.hxx>
#include <sfx2/printer.hxx>
#include <sfx2/progress.hxx>
#include <sfx2/StylePreviewRenderer.hxx>
#include <editeng/brushitem.hxx>
#include <editeng/prntitem.hxx>
#include <editeng/boxitem.hxx>
#include <editeng/shaditem.hxx>
#include <svx/ctredlin.hxx>
#include <svx/framelink.hxx>
#include <svx/svdouno.hxx>
#include <drawdoc.hxx>
#include <tgrditem.hxx>
#include <calbck.hxx>
#include <fmtsrnd.hxx>
#include <fmtclds.hxx>
#include <fmturl.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 <frmatr.hxx>
#include <frmtool.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 <swfont.hxx>

#include <svx/sdr/primitive2d/sdrframeborderprimitive2d.hxx>
#include <svx/sdr/contact/viewobjectcontact.hxx>
#include <svx/sdr/contact/viewcontact.hxx>
#include <DocumentSettingManager.hxx>
#include <IDocumentDeviceAccess.hxx>
#include <IDocumentDrawModelAccess.hxx>

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

#include <svx/sdr/attribute/sdrallfillattributeshelper.hxx>

#include <svtools/borderhelper.hxx>

#include <bitmaps.hlst>
#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
#include <drawinglayer/primitive2d/PolyPolygonStrokePrimitive2D.hxx>
#include <drawinglayer/primitive2d/discreteshadowprimitive2d.hxx>
#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
#include <drawinglayer/primitive2d/textprimitive2d.hxx>
#include <drawinglayer/primitive2d/textlayoutdevice.hxx>
#include <drawinglayer/processor2d/baseprocessor2d.hxx>
#include <drawinglayer/processor2d/processor2dtools.hxx>
#include <svx/unoapi.hxx>
#include <svx/svdpagv.hxx>
#include <svx/xfillit0.hxx>
#include <basegfx/matrix/b2dhommatrixtools.hxx>
#include <basegfx/color/bcolortools.hxx>
#include <basegfx/utils/b2dclipstate.hxx>
#include <sal/log.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>
#include <comphelper/configuration.hxx>
#include <comphelper/lok.hxx>
#include <svtools/optionsdrawinglayer.hxx>
#include <vcl/GraphicLoader.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>

#include <svl/style.hxx>
#include <ndtxt.hxx>
#include <unotools/configmgr.hxx>
#include <vcl/hatch.hxx>

using namespace ::editeng;
using namespace ::com::sun::star;

namespace {

struct SwPaintProperties;

//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> {};
}

namespace {

// Classes collecting the border lines and help lines
class SwLineRect : public SwRect
{
    Color m_aColor;
    SvxBorderLineStyle m_nStyle;
    const SwTabFrame* m_pTabFrame;
    SubColFlags m_nSubColor; //colorize subsidiary lines
    bool m_bPainted; //already painted?
    sal_uInt8 m_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 m_aColor; }
    SvxBorderLineStyle GetStyle() const { return m_nStyle; }
    const SwTabFrame* GetTab() const { return m_pTabFrame; }
    void SetPainted() { m_bPainted = true; }
    void Lock(bool bLock)
    {
        if (bLock)
            ++m_nLock;
        else if (m_nLock)
            --m_nLock;
    }
    bool IsPainted() const { return m_bPainted; }
    bool IsLocked() const { return m_nLock != 0; }
    SubColFlags GetSubColor() const { return m_nSubColor; }

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

}

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

namespace {

class SwLineRects
{
public:
    std::vector<SwLineRect> m_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 m_nLastCount; //avoid unnecessary cycles in PaintLines
    SwLineRects()
        : m_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 m_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(drawinglayer::primitive2d::Primitive2DContainer&& rContainer);
    drawinglayer::primitive2d::Primitive2DContainer GetBorderLines_Clear()
    {
        drawinglayer::primitive2d::Primitive2DContainer lines;
        lines.swap(m_Lines);
        return lines;
    }
};

}

// Default zoom factor
const double aEdgeScale = 0.5;

//To optimize the expensive RetouchColor determination
Color aGlobalRetoucheColor;

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

namespace {

/**
 * 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
    tools::Long                nSPixelSzW;
    tools::Long                nSPixelSzH;
    tools::Long                nSHalfPixelSzW;
    tools::Long                nSHalfPixelSzH;
    tools::Long                nSMinDistPixelW;
    tools::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;

static bool isSubsidiaryLinesEnabled()
{
    return !gProp.pSGlobalShell->GetViewOptions()->IsPagePreview() &&
           !gProp.pSGlobalShell->GetViewOptions()->IsReadonly() &&
           !gProp.pSGlobalShell->GetViewOptions()->IsFormView() &&
           gProp.pSGlobalShell->GetViewOptions()->IsShowBoundaries();
}

/**
 * Set borders alignment statics
 * Adjustment for 'small' twip-to-pixel relations:
 * For 'small' twip-to-pixel relations (less than 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());
}

namespace {

/**
 * 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(drawinglayer::primitive2d::Primitive2DContainer&& rContainer)
{
    if(!rContainer.empty())
    {
        m_Lines.append(std::move(rContainer));
    }
}

SwLineRect::SwLineRect(const SwRect& rRect, const Color* pCol, const SvxBorderLineStyle nStyl,
                       const SwTabFrame* pT, const SubColFlags nSCol)
    : SwRect(rRect)
    , m_nStyle(nStyl)
    , m_pTabFrame(pT)
    , m_nSubColor(nSCol)
    , m_bPainted(false)
    , m_nLock(0)
{
    if ( pCol != nullptr )
        m_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 tools::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 tools::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 = m_aLineRects.rbegin(); it != m_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;
        }
    }
    m_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 tools::Long nAdd = 20;

    std::vector<SwLineRect*> aCheck;

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

        aCheck.clear();

        const bool bVert = rL1.Height() > rL1.Width();
        tools::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 = m_aLineRects.begin(); it2 != m_aLineRects.end(); ++it2)
        {
            SwLineRect &rL2 = *it2;
            if ( rL2.GetTab() != rL1.GetTab() ||
                 rL2.IsPainted()              ||
                 rL2.IsLocked()               ||
                 (bVert == (rL2.Height() > rL2.Width())) )
                continue;

            tools::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.Contains( aIns ) )
                                continue;
                            m_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.Contains( aIns ) )
                                continue;
                            m_aLineRects.emplace_back(aIns, &rL1.GetColor(),
                                                      SvxBorderLineStyle::SOLID, rL1.GetTab(),
                                                      SubColFlags::Tab);
                            if ( isFull() )
                            {
                                assert(i > 0);
                                --i;
                                k = aCheck.size();
                                break;
                            }
                        }
                        if ( rL1.Right() > pLB->Right() )
                            rL1.Left( pLB->Left() );
                        else
                            bRemove = true;
                    }
                }
            }
        }
        if ( bRemove )
        {
            m_aLineRects.erase(m_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 < m_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(m_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.AddLeft  ( - (properties.nSPixelSzW+properties.nSHalfPixelSzW) );
            aSubsRect.AddRight ( properties.nSPixelSzW+properties.nSHalfPixelSzW );
        }
        else
        {
            aSubsRect.AddTop   ( - (properties.nSPixelSzH+properties.nSHalfPixelSzH) );
            aSubsRect.AddBottom( properties.nSPixelSzH+properties.nSHalfPixelSzH );
        }
        for (const_iterator itK = rRects.m_aLineRects.begin(); itK != rRects.m_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.Overlaps( rLine ) )
            {
                if ( bVerticalSubs ) // Vertical?
                {
                    if ( aSubsRect.Left()  <= rLine.Right() &&
                         aSubsRect.Right() >= rLine.Left() )
                    {
                        tools::Long nTmp = rLine.Top()-(properties.nSPixelSzH+1);
                        if ( aSubsLineRect.Top() < nTmp )
                        {
                            SwRect aNewSubsRect( aSubsLineRect );
                            aNewSubsRect.Bottom( nTmp );
                            m_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 );
                            m_aLineRects.emplace_back(aNewSubsRect, nullptr,
                                                      aSubsLineRect.GetStyle(), nullptr,
                                                      aSubsLineRect.GetSubColor());
                        }
                        m_aLineRects.erase(m_aLineRects.begin() + i);
                        --i;
                        break;
                    }
                }
                else // Horizontal
                {
                    if ( aSubsRect.Top() <= rLine.Bottom() &&
                         aSubsRect.Bottom() >= rLine.Top() )
                    {
                        tools::Long nTmp = rLine.Left()-(properties.nSPixelSzW+1);
                        if ( aSubsLineRect.Left() < nTmp )
                        {
                            SwRect aNewSubsRect( aSubsLineRect );
                            aNewSubsRect.Right( nTmp );
                            m_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 );
                            m_aLineRects.emplace_back(aNewSubsRect, nullptr,
                                                      aSubsLineRect.GetStyle(), nullptr,
                                                      aSubsLineRect.GetSubColor());
                        }
                        m_aLineRects.erase(m_aLineRects.begin() + i);
                        --i;
                        break;
                    }
                }
            }
        }
    }
}

void SwLineRects::LockLines( bool bLock )
{
    for (SwLineRect& rLRect : m_aLineRects)
        rLRect.Lock(bLock);
}

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

    // Discriminate vertically stretched rect from horizontally stretched
    // and restrict minimum nHalfLWidth to 1
    tools::Long nHalfLWidth = std::max( std::min( rLRect.Width(  ), rLRect.Height(  ) ) / 2, tools::Long(1) );

    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 (m_aLineRects.size() == m_nLastCount)
        return;

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

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

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

    for (size_t i = 0; i < m_aLineRects.size(); ++i)
    {
        SwLineRect& rLRect = m_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 < m_aLineRects.size(); ++i)
        {
            SwLineRect& rLRect = m_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();
        }
    }
    m_nLastCount = nMinCount;
}

void SwSubsRects::PaintSubsidiary( OutputDevice *pOut,
                                   const SwLineRects *pRects,
                                   SwPaintProperties const & properties )
{
    if (m_aLineRects.empty())
        return;

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

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

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

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

    if (m_aLineRects.empty())
        return;

    auto popIt = pOut->ScopedPush(vcl::PushFlags::FILLCOLOR | vcl::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 (SwLineRect& rLRect : m_aLineRects)
    {
        // Add condition <!rLRect.IsLocked()> to prevent paint of locked subsidiary lines.
        if ( !rLRect.IsPainted() &&
             !rLRect.IsLocked() )
        {
            const Color *pCol = nullptr;
            SwViewShell *pShell = properties.pSGlobalShell;
            const SwViewOption *pOpt = pShell->GetViewOptions();
            switch ( rLRect.GetSubColor() )
            {
                case SubColFlags::Page: pCol = &pOpt->GetDocBoundariesColor(); break;
                case SubColFlags::Tab: pCol = &pOpt->GetTableBoundariesColor(); break;
                case SubColFlags::Fly:
                case SubColFlags::Sect: pCol = &pOpt->GetSectionBoundColor(); break;
            }

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

            rLRect.SetPainted();
        }
    }

    pOut->SetDrawMode( nOldDrawMode );
}

// 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.AddTop( 1 );
    }

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

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

    if ( rRect.Right() < aPxCenterRect.Right() )
    {
        // 'leave pixel overlapping on right'
        aAlignedPxRect.AddRight( - 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 = SwRect(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 tools::Long lcl_AlignWidth( const tools::Long nWidth, SwPaintProperties const & properties )
{
    if ( nWidth )
    {
        const tools::Long nW = nWidth % properties.nSPixelSzW;

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

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

        if ( !nH || nH > properties.nSHalfPixelSzH )
            return std::max(tools::Long(1), 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();

        SwRectFnSet fnRect(*pFrame);

        const SvxBoxItem &rBox = rAttrs.GetBox();
        const bool bTop = 0 != fnRect.GetTopMargin(*pFrame);
        if ( bTop || rBox.GetTop() )
        {
            SwTwips nDiff = rBox.GetTop() ?
                rBox.CalcLineSpace( SvxBoxItemLine::TOP, /*bEvenIfNoLine=*/false, /*bAllowNegative=*/true ) :
                rBox.GetDistance( SvxBoxItemLine::TOP );
            if( nDiff )
                fnRect.SubTop(rRect, nDiff);
        }

        const bool bBottom = 0 != fnRect.GetBottomMargin(*pFrame);
        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 ) :
                    rBox.GetDistance( SvxBoxItemLine::BOTTOM );
            }
            if( nDiff )
                fnRect.AddBottom(rRect, nDiff);
        }

        if ( rBox.GetLeft() )
            fnRect.SubLeft(rRect, rBox.CalcLineSpace(SvxBoxItemLine::LEFT));
        else
            fnRect.SubLeft(rRect, rBox.GetDistance(SvxBoxItemLine::LEFT));

        if ( rBox.GetRight() )
            fnRect.AddRight(rRect, rBox.CalcLineSpace(SvxBoxItemLine::RIGHT));
        else
            fnRect.AddRight(rRect, rBox.GetDistance(SvxBoxItemLine::RIGHT));

        if ( bShadow && rAttrs.GetShadow().GetLocation() != SvxShadowLocation::NONE )
        {
            const SvxShadowItem &rShadow = rAttrs.GetShadow();
            if ( bTop )
                fnRect.SubTop(rRect, rShadow.CalcShadowSpace(SvxShadowItemSide::TOP));
            fnRect.SubLeft(rRect, rShadow.CalcShadowSpace(SvxShadowItemSide::LEFT));
            if ( bBottom )
                fnRect.AddBottom(rRect, rShadow.CalcShadowSpace(SvxShadowItemSide::BOTTOM));
            fnRect.AddRight(rRect, 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;

        const SwFlyFrame *pFly = pAnchoredObj->DynCastFlyFrame();
        if (!pFly)
            continue;

        if (pSelfFly == pFly || gProp.pSRetoucheFly == pFly || !rRect.Overlaps(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)
            continue;

        /// Change internal order of condition
        ///    first check "!bHell", then "..->Lower()" and "..->IsNoTextFrame()"
        ///    have not to be performed, if frame is in "Hell"
        const SwFrame* pLower = pFly->Lower();
        if (!bHell && pLower && pLower->IsNoTextFrame() &&
               (static_cast<SwNoTextFrame const*>(pLower)->IsTransparent() ||
                static_cast<SwNoTextFrame const*>(pLower)->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_implDrawGraphicBackground(const SvxBrushItem& _rBackgrdBrush,
                                       vcl::RenderContext& _rOut,
                                       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.IsTransparent() )
    ///     background color is transparent --> draw transparent.
    {
        bDrawTransparent = true;
        nTransparencyPercent = ((255 - aColor.GetAlpha())*100 + 0x7F)/0xFF;
    }
    else if ( (_rGraphicObj.GetAttr().IsTransparent()) &&
                (_rBackgrdBrush.GetColor() == COL_TRANSPARENT) )
    ///     graphic is drawn transparent and background color is
    ///     "no fill"/"auto fill" --> draw transparent
    {
        bDrawTransparent = true;
        nTransparencyPercent = 100 - (_rGraphicObj.GetAttr().GetAlpha() * 100 + 127) / 255;
    }

    if ( bDrawTransparent )
    {
        /// draw background transparent
        if( _rOut.GetFillColor() != aColor.GetRGBColor() )
            _rOut.SetFillColor( aColor.GetRGBColor() );
        tools::PolyPolygon aPoly( _rAlignedPaintRect.SVRect() );
        _rOut.DrawTransparent( aPoly, nTransparencyPercent );
    }
    else
    {
        /// draw background opaque
        if ( _rOut.GetFillColor() != aColor )
            _rOut.SetFillColor( aColor );
        _rOut.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 an inline, checking the conditions and calling method
 * method <lcl_implDrawGraphicBackground(..)> for the intrinsic drawing.
 *
 * @param _rBackgrdBrush
 * background brush contain the color the background has to be drawn.
 *
 * @param _rOut
 * 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 void lcl_DrawGraphicBackground( const SvxBrushItem& _rBackgrdBrush,
                                    OutputDevice& _rOut,
                                    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_implDrawGraphicBackground( _rBackgrdBrush, _rOut, _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_DrawGraphicBackground>
 *
 * Also, change type of <bGrfNum> and <bClip> from <bool> to <bool>
 */
static void lcl_DrawGraphic( const SvxBrushItem& rBrush, vcl::RenderContext &rOutDev,
                      const 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, &rOutDev );

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

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

    OUString aOriginURL = pGrf->GetGraphic().getOriginURL();
    if (pGrf->GetGraphic().GetType() == GraphicType::Default && !aOriginURL.isEmpty())
    {
        Graphic aGraphic = vcl::graphic::loadFromURL(aOriginURL);
        pGrf->SetGraphic(aGraphic);
    }

    // Outsource drawing of background with a background color
    ::lcl_DrawGraphicBackground( rBrush, rOutDev, 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, rOutDev );

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

    paintGraphicUsingPrimitivesHelper(
        rOutDev,
        *pGrf,
        pGrf->GetAttr(),
        aGraphicTransform,
        OUString(),
        OUString(),
        OUString());

    if ( bNotInside )
        rOutDev.Pop();
}

bool DrawFillAttributes(
    const drawinglayer::attribute::SdrAllFillAttributesHelperPtr& rFillAttributes,
    const SwRect& rOriginalLayoutRect,
    const SwRegionRects& rPaintRegion,
    const basegfx::utils::B2DClipState& rClipState,
    vcl::RenderContext& rOut)
{
    if(rFillAttributes && 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()))
        {
            // 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 (comphelper::IsFuzzing() || SvtOptionsDrawinglayer::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 const double fExpandX = 0.55;
                static const 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, drawinglayer::primitive2d::Primitive2DContainer(rSequence));
                    pPrimitives = &primitives;
                }
                assert(pPrimitives && pPrimitives->size());

                drawinglayer::geometry::ViewInformation2D aViewInformation2D;
                aViewInformation2D.setViewTransformation(rOut.GetViewTransformation());
                aViewInformation2D.setViewport(aPaintRange);

                std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor(drawinglayer::processor2d::createProcessor2DFromOutputDevice(
                    rOut,
                    aViewInformation2D) );
                pProcessor->process(*pPrimitives);
                return true;
            }
        }
    }

    return false;
}

void DrawGraphic(
    const SvxBrushItem *pBrush,
    vcl::RenderContext &rOutDev,
    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.Contains( 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, &rOutDev );
            // draw background color for aligned paint rectangle
            lcl_DrawGraphicBackground( *pBrush, rOutDev, 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
            auto popIt = rOutDev.ScopedPush(vcl::PushFlags::CLIPREGION);
            rOutDev.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( rOutDev,
                                        aAlignedPaintRect.SVRect(),
                                        aGrf.SSize(),
                                        Size( aPaintOffset.X(), aPaintOffset.Y() ),
                                        std::max( 128, static_cast<int>( sqrt(sqrt( Abitmap)) + .5 ) ) );
            }
            // 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( false, "new Graphic position?" );
    }

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

        // check, if an 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.IsTransparent()) &&
                     (pBrush->GetColor() == COL_TRANSPARENT)
                   )
                {
                    bTransparentGrfWithNoFillBackgrd = true;
                    nGrfTransparency = 255 - aGrfAttr.GetAlpha();
                }
            }
            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.IsTransparent()) ||
                bTransparentGrfWithNoFillBackgrd ) )
        {
            eDrawStyle = Transparent;
        }

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

        // 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( rOutDev.GetFillColor() != aColor.GetRGBColor() )
                    rOutDev.SetFillColor( aColor.GetRGBColor() );
                break;
            }
            default:
            {
                if( rOutDev.GetFillColor() != aColor )
                {
                    if (aColor.IsTransparent() && !rOutDev.HasAlpha())
                        rOutDev.SetFillColor();
                    else
                        rOutDev.SetFillColor( aColor );
                }
                break;
            }
        }

        // #i75614#
        // restore draw mode
        rOutDev.SetDrawMode( nOldDrawMode );

        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
                        // 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 : (255 - aColor.GetAlpha())
                   )*100 + 0x7F)/0xFF);
                // draw poly-polygon transparent
                rOutDev.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 )
                {
                    rOutDev.DrawRect( aRegion[i].SVRect() );
                }
            }
        }
    }

    if( bDraw && aGrf.Overlaps( rOut ) )
        lcl_DrawGraphic( *pBrush, rOutDev, rSh, aGrf, rOut, bGrfNum, gProp,
                         bGrfBackgrdAlreadyDrawn );

    if( bReplaceGrfNum )
    {
        const Bitmap& rBmp = rSh.GetReplacementBitmap();
        vcl::Font aTmp( rOutDev.GetFont() );
        Graphic::DrawEx(rOutDev, OUString(), 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

namespace {

struct SwLineEntry
{
    SwTwips mnKey;
    SwTwips mnStartPos;
    SwTwips mnEndPos;
    SwTwips mnLimitedEndPos;
    bool mbOuter;

    svx::frame::Style maAttribute;

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

    enum class VerticalType { LEFT, RIGHT };

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

    OverlapType Overlaps( const SwLineEntry& rComp ) const;

    /**
     * Assuming that this entry is for a Word-style covering cell and the border matching eType is
     * set, limit the end position of this border in case covered cells have no borders set.
     */
    void LimitVerticalEndPos(const SwFrame& rFrame, VerticalType eType);
};

}

SwLineEntry::SwLineEntry( SwTwips nKey,
                          SwTwips nStartPos,
                          SwTwips nEndPos,
                          bool bOuter,
                          const svx::frame::Style& rAttribute )
    :   mnKey( nKey ),
        mnStartPos( nStartPos ),
        mnEndPos( nEndPos ),
        mnLimitedEndPos(0),
        mbOuter(bOuter),
        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)
        eRet = OVERLAP2;

    // 8, 9
    return eRet;
}

void SwLineEntry::LimitVerticalEndPos(const SwFrame& rFrame, VerticalType eType)
{
    if (!rFrame.IsCellFrame())
    {
        return;
    }

    const auto& rCellFrame = static_cast<const SwCellFrame&>(rFrame);
    std::vector<const SwCellFrame*> aCoveredCells = rCellFrame.GetCoveredCells();
    // Iterate in reverse order, so we can stop at the first cell that has a border. This can
    // determine what is the minimal end position that is safe to use as a limit.
    for (auto it = aCoveredCells.rbegin(); it != aCoveredCells.rend(); ++it)
    {
        const SwCellFrame* pCoveredCell = *it;
        SwBorderAttrAccess aAccess( SwFrame::GetCache(), pCoveredCell );
        const SwBorderAttrs& rAttrs = *aAccess.Get();
        const SvxBoxItem& rBox = rAttrs.GetBox();
        if (eType == VerticalType::LEFT && rBox.GetLeft())
        {
            break;
        }

        if (eType == VerticalType::RIGHT && rBox.GetRight())
        {
            break;
        }

        mnLimitedEndPos = pCoveredCell->getFrameArea().Top();
    }
}

namespace {

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;

namespace {

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);

    /// Inserts top border at the top of a follow table.
    void InsertFollowTopBorder(const SwFrame& rFrame, const SvxBoxItem& rBoxItem,
                               bool bWordTableCell, SwTwips nTop, SwTwips nLeft, SwTwips nRight,
                               bool bTopIsOuter);
    void InsertMasterBottomBorder(const SwFrame& rFrame, const SvxBoxItem& rBoxItem,
                                  bool bWordTableCell, SwTwips nBottom, SwTwips nLeft, SwTwips nRight,
                                  bool bBottomIsOuter);

    void HandleFrame(const SwLayoutFrame& rFrame, const SwRect& rPaintArea);
    void FindStylesForLine( Point&,
                            Point&,
                            svx::frame::Style*,
                            bool bHori,
                            bool bOuter ) 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 tools::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 )
    {
        if (pLower->IsLayoutFrame() && !pLower->IsTabFrame())
        {
            const SwLayoutFrame* pLowerLayFrame = static_cast<const SwLayoutFrame*>(pLower);
            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( gProp.pSGlobalShell->GetViewOptions()->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 = &gProp.pSGlobalShell->GetViewOptions()->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 );

    // prepare SdrFrameBorderDataVector
    drawinglayer::primitive2d::SdrFrameBorderDataVector aData;

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

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

        const SwLineEntrySet& rEntrySet = (*aIter).second;
        for (const SwLineEntry& rEntry : rEntrySet)
        {
            const svx::frame::Style& rEntryStyle( rEntry.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, rEntry.mbOuter);

            if (!bHori && rEntry.mnLimitedEndPos)
            {
                aEnd.setY(rEntry.mnLimitedEndPos);
            }

            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.Overlaps(aRepaintRect))
            {
                continue;
            }

            // subsidiary lines
            const Color* pTmpColor = nullptr;
            if (0 == aStyles[ 0 ].GetWidth())
            {
                if (isSubsidiaryLinesEnabled() &&
                    gProp.pSGlobalShell->GetViewOptions()->IsTableBoundaries() &&
                    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));
                        aData.emplace_back(
                            aOrigin,
                            aX,
                            aStyles[0],
                            pTmpColor);
                        drawinglayer::primitive2d::SdrFrameBorderData& rInstance(aData.back());

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

                        rInstance.addSdrConnectStyleData(false, aStyles[4], -aY, true); // aRFromT
                        rInstance.addSdrConnectStyleData(false, aStyles[5], aX, false); // aRFromR
                        rInstance.addSdrConnectStyleData(false, aStyles[6], aY, false); // aRFromB
                    }
                }
                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));
                        aData.emplace_back(
                            aOrigin,
                            aX,
                            aStyles[0],
                            pTmpColor);
                        drawinglayer::primitive2d::SdrFrameBorderData& rInstance(aData.back());

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

                        rInstance.addSdrConnectStyleData(false, aStyles[6], -aY, false); // aBFromR
                        rInstance.addSdrConnectStyleData(false, aStyles[5], aX, false); // aBFromB
                        rInstance.addSdrConnectStyleData(false, aStyles[4], aY, true); // aBFromL
                    }
                }
            }
        }
        ++aIter;
    }

    // create instance of SdrFrameBorderPrimitive2D if
    // SdrFrameBorderDataVector is used
    if(!aData.empty())
    {
        drawinglayer::primitive2d::Primitive2DContainer aSequence;
        aSequence.append(
                new drawinglayer::primitive2d::SdrFrameBorderPrimitive2D(
                    std::move(aData),
                    true));    // force visualization to minimal one discrete unit (pixel)
        // 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( Point& rStartPoint,
                                         Point& rEndPoint,
                                         svx::frame::Style* pStyles,
                                         bool bHori, bool bOuter ) const
{
    // For example, aLFromB means: this vertical line intersects my horizontal line at its left end,
    // from bottom.
    // 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,

    bool bWordTableCell = false;
    SwViewShell* pShell = mrTabFrame.getRootFrame()->GetCurrShell();
    if (pShell)
    {
        const IDocumentSettingAccess& rIDSA = pShell->GetDoc()->getIDocumentSettingAccess();
        bWordTableCell = rIDSA.get(DocumentSettingId::TABLE_ROW_KEEP);
    }

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

    for ( const SwLineEntry& rEntry : rVertSet )
    {
        if ( bHori )
        {
            if ( rStartPoint.Y() == rEntry.mnStartPos )
                pStyles[ 3 ] = rEntry.maAttribute;
            else if ( rStartPoint.Y() == rEntry.mnEndPos )
                pStyles[ 1 ] = rEntry.maAttribute;

            if (bWordTableCell && rStartPoint.X() == rEntry.mnKey && !bOuter && rEntry.mbOuter)
            {
                rStartPoint.AdjustX(rEntry.maAttribute.GetWidth());
            }
        }
        else
        {
            if ( rStartPoint.Y() == rEntry.mnEndPos )
                pStyles[ 2 ] = rEntry.maAttribute;
            else if ( rEndPoint.Y() == rEntry.mnStartPos )
                pStyles[ 5 ] = rEntry.maAttribute;
        }
    }

    aMapIter = maHoriLines.find( rStartPoint.Y() );
    assert(aMapIter != maHoriLines.end() && "FindStylesForLine: Error");
    const SwLineEntrySet& rHoriSet = aMapIter->second;

    for ( const SwLineEntry& rEntry : rHoriSet )
    {
        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;

            if (bWordTableCell && rStartPoint.Y() == rEntry.mnKey && !bOuter && rEntry.mbOuter)
            {
                rStartPoint.AdjustY(rEntry.maAttribute.GetWidth());
            }
        }
    }

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

        for ( const SwLineEntry& rEntry : rVertSet2 )
        {
            if ( rEndPoint.Y() == rEntry.mnStartPos )
                pStyles[ 6 ] = rEntry.maAttribute;
            else if ( rEndPoint.Y() == rEntry.mnEndPos )
                pStyles[ 4 ] = rEntry.maAttribute;

            if (bWordTableCell && rEndPoint.X() == rEntry.mnKey && !bOuter && rEntry.mbOuter)
            {
                rEndPoint.AdjustX(-rEntry.maAttribute.GetWidth());
            }
        }
    }
    else
    {
        aMapIter = maHoriLines.find( rEndPoint.Y() );
        assert(aMapIter != maHoriLines.end() && "FindStylesForLine: Error");
        const SwLineEntrySet& rHoriSet2 = (*aMapIter).second;

        for ( const SwLineEntry& rEntry : rHoriSet2 )
        {
            if ( rEndPoint.X() == rEntry.mnEndPos )
                pStyles[ 4 ] = rEntry.maAttribute;
            else if ( rEndPoint.X() == rEntry.mnStartPos )
                pStyles[ 6 ] = rEntry.maAttribute;

            if (bWordTableCell && rEndPoint.Y() == rEntry.mnKey && !bOuter && rEntry.mbOuter)
            {
                rEndPoint.AdjustY(-rEntry.maAttribute.GetWidth());
            }
        }
    }
}

/**
 * Special case: #i9860#
 * first line in follow table without repeated headlines
 * Special case: tdf#150308
 * first visible line of a table with preceding hidden deleted rows
 */
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()
            // tdf#150308 first table row isn't equal to the table row of the first
            // row frame of the first table frame: there are invisible deleted rows
            // in Hide Changes mode before the first visible table row
            || rTabFrame.GetTable()->GetTabLines().front() != pThisRowFrame->GetTabLine() )
        && !rTabFrame.GetTable()->GetRowsToRepeat()
        &&  (  !pThisRowFrame->GetPrev()
            || static_cast<const SwRowFrame*>(pThisRowFrame->GetPrev())
                    ->IsRowSpanLine())
        && !rBoxItem.GetTop()
        && rBoxItem.GetBottom());
}

/**
 * Special case:
 * last visible cell of a table row followed with a hidden deleted cell,
 * and the right border of the visible cell was painted by its left border
 */
static bool lcl_IsLastVisibleCellBeforeHiddenCellAtTheEndOfRow(
        SwFrame const& rFrame, SvxBoxItem const& rBoxItem)
{
    SwRowFrame const*const pThisRowFrame =
        dynamic_cast<const SwRowFrame*>(rFrame.GetUpper());
    const SwCellFrame* pThisCell = static_cast<const SwCellFrame*>(&rFrame);

    return pThisRowFrame
        // last visible cell
        && !rFrame.GetNext()
        // it has only left border
        && !rBoxItem.GetRight()
        && rBoxItem.GetLeft()
        // last visible table cell isn't equal to the last cell:
        // there are invisible deleted cells in Hide Changes mode
        && pThisRowFrame->GetTabLine()->GetTabBoxes().back() != pThisCell->GetTabBox();
}

void SwTabFramePainter::InsertFollowTopBorder(const SwFrame& rFrame, const SvxBoxItem& rBoxItem,
                                              bool bWordTableCell, SwTwips nTop, SwTwips nLeft,
                                              SwTwips nRight, bool bTopIsOuter)
{
    // Figure out which cell to copy.
    int nCol = 0;
    const SwFrame* pCell = &rFrame;
    while (pCell)
    {
        if (!pCell->GetPrev())
        {
            break;
        }

        ++nCol;
        pCell = pCell->GetPrev();
    }

    auto pThisRow = dynamic_cast<const SwRowFrame*>(rFrame.GetUpper());
    if (!pThisRow || pThisRow->GetUpper() != &mrTabFrame)
    {
        return;
    }

    if (!mrTabFrame.IsFollow() || mrTabFrame.GetTable()->GetRowsToRepeat())
    {
        return;
    }

    if (pThisRow->GetPrev() || rBoxItem.GetTop() || rBoxItem.GetBottom())
    {
        return;
    }

    // This is then a first row in a follow table, without repeated headlines.
    auto pLastRow = dynamic_cast<const SwRowFrame*>(mrTabFrame.GetLastLower());
    if (!pLastRow || pLastRow == pThisRow)
    {
        return;
    }

    const SwFrame* pLastCell = pLastRow->GetLower();
    if (!pLastCell)
    {
        return;
    }

    for (int i = 0; i < nCol; ++i)
    {
        if (!pLastCell->GetNext())
        {
            // Reference row has merged cells, work with the last possible one.
            break;
        }

        pLastCell = pLastCell->GetNext();
    }

    SwBorderAttrAccess aAccess(SwFrame::GetCache(), pLastCell);
    const SwBorderAttrs& rAttrs = *aAccess.Get();
    const SvxBoxItem& rLastBoxItem = rAttrs.GetBox();
    if (!rLastBoxItem.GetBottom())
    {
        return;
    }

    // The matching (same column) row in the last row has a bottom border for us.
    svx::frame::Style aFollowB(rLastBoxItem.GetBottom(), 1.0);
    aFollowB.SetWordTableCell(bWordTableCell);
    SwLineEntry aFollowTop(nTop, nLeft, nRight, bTopIsOuter, aFollowB);
    aFollowB.SetRefMode(svx::frame::RefMode::Begin);
    Insert(aFollowTop, true);
}

void SwTabFramePainter::InsertMasterBottomBorder(const SwFrame& rFrame, const SvxBoxItem& rBoxItem,
                                              bool bWordTableCell, SwTwips nBottom, SwTwips nLeft,
                                              SwTwips nRight, bool bBottomIsOuter)
{
    // Figure out which cell to copy.
    int nCol = 0;
    const SwFrame* pCell = &rFrame;
    while (pCell)
    {
        if (!pCell->GetPrev())
        {
            break;
        }

        ++nCol;
        pCell = pCell->GetPrev();
    }

    auto pThisRow = dynamic_cast<const SwRowFrame*>(rFrame.GetUpper());
    if (!pThisRow || pThisRow->GetUpper() != &mrTabFrame)
    {
        return;
    }

    if (mrTabFrame.IsFollow() || !mrTabFrame.GetFollow())
    {
        return;
    }

    // This is a master table that is split.
    if (pThisRow->GetNext() || rBoxItem.GetTop() || rBoxItem.GetBottom())
    {
        return;
    }

    // This is then a last row in a master table.
    auto pFirstRow = dynamic_cast<const SwRowFrame*>(mrTabFrame.GetLower());
    if (!pFirstRow || pFirstRow == pThisRow)
    {
        return;
    }

    const SwFrame* pFirstCell = pFirstRow->GetLower();
    if (!pFirstCell)
    {
        return;
    }

    for (int i = 0; i < nCol; ++i)
    {
        if (!pFirstCell->GetNext())
        {
            // Reference row has merged cells, work with the last possible one.
            break;
        }

        pFirstCell = pFirstCell->GetNext();
    }

    SwBorderAttrAccess aAccess(SwFrame::GetCache(), pFirstCell);
    const SwBorderAttrs& rAttrs = *aAccess.Get();
    const SvxBoxItem& rFirstBoxItem = rAttrs.GetBox();
    if (!rFirstBoxItem.GetTop())
    {
        return;
    }

    // The matching (same column) row in the first row has a top border for us.
    svx::frame::Style aMasterT(rFirstBoxItem.GetTop(), 1.0);
    aMasterT.SetWordTableCell(bWordTableCell);
    SwLineEntry aMasterBottom(nBottom, nLeft, nRight, bBottomIsOuter, aMasterT);
    aMasterT.SetRefMode(svx::frame::RefMode::Begin);
    Insert(aMasterBottom, true);
}

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 bLeftAsRight(lcl_IsLastVisibleCellBeforeHiddenCellAtTheEndOfRow(
                rFrame, rBoxItem));

    bool const bVert = mrTabFrame.IsVertical();
    bool const bR2L  = mrTabFrame.IsRightToLeft();

    bool bWordTableCell = false;
    SwViewShell* pShell = rFrame.getRootFrame()->GetCurrShell();
    if (pShell)
    {
        const IDocumentSettingAccess& rIDSA = pShell->GetDoc()->getIDocumentSettingAccess();
        bWordTableCell = rIDSA.get(DocumentSettingId::TABLE_ROW_KEEP);
    }

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

    // First cell in a row.
    bool bLeftIsOuter = rFrame.IsCellFrame() && rFrame.GetUpper()->GetLower() == &rFrame;
    // Last cell in a row.
    bool bRightIsOuter = rFrame.IsCellFrame() && rFrame.GetNext() == nullptr;
    // First row in a table.
    bool bTopIsOuter = rFrame.IsCellFrame() && rFrame.GetUpper()->GetUpper()->GetLower() == rFrame.GetUpper();
    // Last row in a table.
    bool bBottomIsOuter = rFrame.IsCellFrame() && rFrame.GetUpper()->GetNext() == nullptr;

    aR.MirrorSelf();
    if (!bWordTableCell || !bBottomIsOuter)
    {
        // Outer horizontal lines are never mirrored in Word.
        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 );

    if (bWordTableCell && bLeftIsOuter)
    {
        // Outer vertical lines are always mirrored in Word.
        aL.MirrorSelf();
    }

    SwLineEntry aLeft  (nLeft,   nTop,  nBottom, bLeftIsOuter,
            bVert ? aB                         : (bR2L ? aR : aL));
    if (bWordTableCell && rBoxItem.GetLeft())
    {
        aLeft.LimitVerticalEndPos(rFrame, SwLineEntry::VerticalType::LEFT);
    }

    SwLineEntry aRight (nRight,  nTop,  nBottom, bRightIsOuter,
            bVert ? (bBottomAsTop ? aB : aT) : ((bR2L || bLeftAsRight) ? aL : aR));
    if (bWordTableCell && rBoxItem.GetRight())
    {
        aRight.LimitVerticalEndPos(rFrame, SwLineEntry::VerticalType::RIGHT);
    }

    SwLineEntry aTop   (nTop,    nLeft, nRight, bTopIsOuter,
            bVert ? aL                         : (bBottomAsTop ? aB : aT));

    SwLineEntry aBottom(nBottom, nLeft, nRight, bBottomIsOuter,
            bVert ? aR                         : aB);

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

    InsertMasterBottomBorder(rFrame, rBoxItem, bWordTableCell, nBottom, nLeft, nRight,
                             bBottomIsOuter);
    InsertFollowTopBorder(rFrame, rBoxItem, bWordTableCell, nTop, nLeft, nRight, bTopIsOuter);
}

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 )
    {
        (*pLine2)[nKey] = SwLineEntrySet();
        pLineSet = &(*pLine2)[ nKey ];
    }
    SwLineEntrySet::iterator aIter = pLineSet->begin();

    bool bWordTableCell = false;
    SwViewShell* pShell = mrTabFrame.getRootFrame()->GetCurrShell();
    if (pShell)
    {
        const IDocumentSettingAccess& rIDSA = pShell->GetDoc()->getIDocumentSettingAccess();
        bWordTableCell = rIDSA.get(DocumentSettingId::TABLE_ROW_KEEP);
    }
    bool bR2L = mrTabFrame.IsRightToLeft();
    while ( aIter != pLineSet->end() && rNew.mnStartPos < rNew.mnEndPos )
    {
        const SwLineEntry& rOld = *aIter;

        // The bWordTableCell code only works for LTR at the moment, avoid it for RTL.
        if (rOld.mnLimitedEndPos || (bWordTableCell && (rOld.mbOuter != rNew.mbOuter) && !bR2L))
        {
            // Don't merge with this line entry as it ends sooner than mnEndPos.
            ++aIter;
            continue;
        }

        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, rOld.mbOuter, rOldAttr);

            // new middle segment
            const SwLineEntry aMiddle(nKey, rNew.mnStartPos, rOld.mnEndPos, rOld.mbOuter, 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, rOld.mbOuter, rOldAttr);

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

            // new right segment
            const SwLineEntry aRight(nKey, rNew.mnEndPos, rOld.mnEndPos, rOld.mbOuter, 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, rOld.mbOuter, rNewAttr);

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

            // new right segment
            const SwLineEntry aRight(nKey, rNew.mnEndPos, rOld.mnEndPos, rOld.mbOuter, 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 );
}


/**
 * 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, PaintFrameMode) 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()->GetOutDev() && !pSh->GetWin()->IsVisible() )
        {
            return;
        }
        if (SwRootFrame::s_isInPaint)
        {
            SwPaintQueue::Add( pSh, rRect );
            return;
        }
    }
    else
        SwRootFrame::s_isInPaint = bResetRootPaint = true;

    const IDocumentSettingAccess& rIDSA = pSh->GetDoc()->getIDocumentSettingAccess();
    bool isPaintHellOverHF = rIDSA.get(DocumentSettingId::PAINT_HELL_OVER_HEADER_FOOTER) && pSh->Imp()->HasDrawView();
    std::unique_ptr<SwSavePaintStatics> pStatics;
    if ( gProp.pSGlobalShell )
        pStatics.reset(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();

    // Copy rRect; for one, rRect could become dangling during the below action, and for another it
    // needs to be copied to aRect anyway as that is modified further down below:
    SwRect aRect( rRect );

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

    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);

    // #126222. The positions of headers and footers of the previous
    // pages have to be updated, else these headers and footers could
    // get visible at a wrong position.
    const SwPageFrame *pPageDeco = static_cast<const SwPageFrame*>(pPage->GetPrev());
    while (pPageDeco)
    {
        pPageDeco->PaintDecorators();
        OSL_ENSURE(!pPageDeco->GetPrev() || pPageDeco->GetPrev()->IsPageFrame(),
            "Neighbour of page is not a page.");
        pPageDeco = static_cast<const SwPageFrame*>(pPageDeco->GetPrev());
    }

    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( FrameControlType::PageBreak, pHiddenPage );
            if ( pControl )
                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.Overlaps( 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())
                {
                    // 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);

                    // 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 );

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

                pPage->PaintBaBo( aPaintRect, pPage );

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

                if (isPaintHellOverHF)
                {
                    if ( pSh->GetDoc()->GetDocumentSettingManager().get( DocumentSettingId::BACKGROUND_PARA_OVER_DRAWINGS ) )
                        pPage->PaintBaBo( aPaintRect, pPage, /*bOnlyTextBackground=*/true, PAINT_HEADER_FOOTER );
                    pPage->PaintSwFrame(rRenderContext, aPaintRect, PAINT_HEADER_FOOTER);
                    gProp.pSLines->LockLines( true );
                    const IDocumentDrawModelAccess& rIDDMA = pSh->getIDocumentDrawModelAccess();
                    pSh->Imp()->PaintLayer( rIDDMA.GetHeaderFooterHellId(),
                                            *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, isPaintHellOverHF ? PAINT_NON_HEADER_FOOTER : PAINT_ALL );
                if( pSh->GetWin() )
                {
                    // collect sub-lines
                    pPage->RefreshSubsidiary( aPaintRect );
                    // paint special sub-lines
                    gProp.pSSpecSubsLines->PaintSubsidiary( pSh->GetOut(), nullptr, gProp );
                }
                pPage->PaintSwFrame( rRenderContext, aPaintRect, isPaintHellOverHF ? PAINT_NON_HEADER_FOOTER : PAINT_ALL);

                // no paint of page border and shadow, if writer is in place mode.
                SwDocShell* pShell = pSh->GetDoc()->GetDocShell();
                if( pSh->GetWin() && pShell && !pShell->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
                {
                    SwTaggedPDFHelper tag(nullptr, nullptr, nullptr, rRenderContext);
                    ProcessPrimitives(gProp.pBLines->GetBorderLines_Clear());
                }

                if ( pSh->Imp()->HasDrawView() )
                {
                    pSh->Imp()->PaintLayer( pSh->GetDoc()->getIDocumentDrawModelAccess().GetHeavenId(),
                                            *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(), pSh->GetViewOptions()->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.Overlaps( 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 );
                // No line color
                pSh->GetOut()->SetLineColor();
                // 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
                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 )
        pStatics.reset();
    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();
    }
}

namespace {

class SwShortCut
{
    SwRectDist m_fnCheck;
    tools::Long m_nLimit;

public:
    SwShortCut( const SwFrame& rFrame, const SwRect& rRect );
    bool Stop(const SwRect& rRect) const { return (rRect.*m_fnCheck)(m_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 )
        {
            m_fnCheck = &SwRect::GetBottomDistance;
            m_nLimit = rRect.Top();
        }
        else
        {
            m_fnCheck = &SwRect::GetLeftDistance;
            m_nLimit = rRect.Left() + rRect.Width();
        }
    }
    else if( bVert == rFrame.IsNeighbourFrame() )
    {
        m_fnCheck = &SwRect::GetTopDistance;
        m_nLimit = rRect.Top() + rRect.Height();
    }
    else
    {
        if ( rFrame.IsVertLR() )
        {
            m_fnCheck = &SwRect::GetLeftDistance;
            m_nLimit = rRect.Right();
        }
        else
        {
            m_fnCheck = &SwRect::GetRightDistance;
            m_nLimit = rRect.Left();
        }
    }
}

void SwLayoutFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const& rRect, PaintFrameMode ePaintFrameMode) const
{
    if (!getFramePrintArea().HasArea() && !IsRowFrame())
    {   // tdf#163032 row frame may contain rowspan>1 cell that must be painted
        return; // do not paint hidden frame
    }

    // #i16816# tagged pdf support
    Frame_Info aFrameInfo(*this, false);
    SwTaggedPDFHelper aTaggedPDFHelper( nullptr,
        PAINT_HEADER_FOOTER == ePaintFrameMode ? nullptr : &aFrameInfo,
        nullptr, rRenderContext );
    ::std::optional<SwTaggedPDFHelper> oTaggedLink;
    if (IsFlyFrame())
    {
        // tdf#154939 Link nested inside Figure
        auto const pItem(GetFormat()->GetAttrSet().GetItemIfSet(RES_URL));
        if (pItem && !pItem->GetURL().isEmpty())
        {
            Frame_Info linkInfo(*this, true);
            oTaggedLink.emplace(nullptr, &linkInfo, nullptr, rRenderContext);
        }
    }

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

    pFrame = SkipFrame(pFrame, ePaintFrameMode);
    if (!pFrame)
        return;

    SwFrameDeleteGuard g(const_cast<SwLayoutFrame*>(this)); // lock because Calc() and recursion
    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;
    bool bWin = gProp.pSGlobalShell->GetWin() != nullptr;
    if (comphelper::LibreOfficeKit::isTiledPainting())
        // Tiled rendering is similar to printing in this case: painting transparently multiple
        // times will result in darker colors: avoid that.
        bWin = false;

    while ( IsAnLower( pFrame ) )
    {
        assert(pFrame);
        SwRect aPaintRect( pFrame->GetPaintArea() );
        if( aShortCut.Stop( aPaintRect ) )
            break;
        if ( bCnt && gProp.pSProgress )
           SfxProgress::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.Overlaps( aPaintRect ) )
        {
            if ( bCnt && pFrame->IsCompletePaint() &&
                 !(comphelper::LibreOfficeKit::isActive() && comphelper::LibreOfficeKit::isTiledPainting()) &&
                 !rRect.Contains( 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();
        pFrame = SkipFrame(pFrame, ePaintFrameMode);

        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
        aSeq[0] =
            new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(
                    aLinePolygon, aOtherColor );

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

        aSeq.resize( 2 );
    }

    // Compute the dashed line primitive
    aSeq[ aSeq.size( ) - 1 ] =
            new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D (
                basegfx::B2DPolyPolygon( aLinePolygon ),
                drawinglayer::attribute::LineAttribute( aColor ),
                drawinglayer::attribute::StrokeAttribute( std::move(aStrokePattern) ) );


    return aSeq;
}

void SwPageFrame::PaintBreak( ) const
{
    if ( gProp.pSGlobalShell->GetOut()->GetOutDevType() == OUTDEV_PRINTER  ||
         gProp.pSGlobalShell->GetViewOptions()->IsPDFExport() ||
         gProp.pSGlobalShell->GetViewOptions()->IsReadonly() ||
         gProp.pSGlobalShell->IsPreview() )
        return;

    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( FrameControlType::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() )
        return;

    const SwFrame* pBodyFrame = Lower();
    while ( pBodyFrame && !pBodyFrame->IsBodyFrame() )
        pBodyFrame = pBodyFrame->GetNext();

    if ( !pBodyFrame )
        return;

    const SwContentFrame *pCnt = static_cast< const SwLayoutFrame* >( pBodyFrame )->ContainsContent();
    if ( !(pCnt && pCnt->IsColBreak( true )) )
        return;

    // 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( FrameControlType::Header ) &&
         !gProp.pSGlobalShell->IsShowHeaderFooterSeparator( FrameControlType::Footer ) &&
          gProp.pSGlobalShell->GetViewOptions()->IsLineBreak()) )
        return;

    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 = gProp.pSGlobalShell->GetViewOptions()->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 );
    tools::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::utils::createScaleShearXRotateTranslateB2DHomMatrix (
                aFontSize.getX(), aFontSize.getY(), 0.0, M_PI_2,
                aRect.Right(), aRect.Top() + nTextOff );
    }

    aSeq.push_back(
            new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
                aTextMatrix,
                aBreakText, 0, aBreakText.getLength(),
                std::vector< double >(),
                {},
                std::move(aFontAttr),
                lang::Locale(),
                aLineColor ) );

    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 )
        return;

    SwEditWin& rEditWin = pWrtSh->GetView().GetEditWin();

    const SwLayoutFrame* pBody = FindBodyCont();
    if ( !pBody )
        return;

    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( FrameControlType::Header ) ||
           gProp.pSGlobalShell->IsShowHeaderFooterSeparator( FrameControlType::Footer ) )) )
        return;

    bool bRtl = AllSettings::GetLayoutRTL();
    const SwRect& rVisArea = gProp.pSGlobalShell->VisArea();
    tools::Long nXOff = std::min( aBodyRect.Right(), rVisArea.Right() );
    if ( bRtl )
        nXOff = std::max( aBodyRect.Left(), rVisArea.Left() );

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

        tools::Long nHeaderYOff = aBodyRect.Top();
        Point nOutputOff = rEditWin.LogicToPixel( Point( nXOff, nHeaderYOff ) );
        rEditWin.GetFrameControlsManager().SetHeaderFooterControl( this, FrameControlType::Header, nOutputOff );
    }

    // Footer
    if ( !gProp.pSGlobalShell->IsShowHeaderFooterSeparator( FrameControlType::Footer ) )
        return;

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

    tools::Long nFooterYOff = aBodyRect.Bottom();
    Point nOutputOff = rEditWin.LogicToPixel( Point( nXOff, nFooterYOff ) );
    rEditWin.GetFrameControlsManager().SetHeaderFooterControl( this, FrameControlType::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* pBackgroundBrush = nullptr;
        std::optional<Color> xSectionTOXColor;
        SwRect aDummyRect;
        drawinglayer::attribute::SdrAllFillAttributesHelperPtr aFillAttributes;

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

    return bBackgroundTransparent;
};

static void lcl_PaintReplacement( const SwRect &rRect, const SwViewShell &rSh )
{
    const Bitmap& rBmp = const_cast<SwViewShell&>(rSh).GetReplacementBitmap();
    vcl::Font aFont(rSh.GetOut()->GetFont() );
    Graphic::DrawEx(*rSh.GetOut(), OUString(), aFont, rBmp, rRect.Pos(), rRect.SSize());
}

bool SwFlyFrame::IsPaint(SdrObject *pObj, const SwViewShell& rSh)
{
    SdrObjUserCall *pUserCall = GetUserCall(pObj);

    if ( nullptr == pUserCall )
        return true;

    if ( (!rSh.GetViewOptions()->IsDraw()
             && (dynamic_cast<SdrUnoObj*>(pObj) || dynamic_cast<SdrAttrObj*>(pObj) || dynamic_cast<SwFlyDrawObj*>(pObj)))
        || (!rSh.GetViewOptions()->IsGraphic() && dynamic_cast<SwVirtFlyDrawObj*>(pObj)) )
    {
        SwRect rBoundRect = GetBoundRectOfAnchoredObj( pObj );
        lcl_PaintReplacement(rBoundRect, rSh);
        return false;
    }
    assert(pObj);

    //Attribute dependent, don't paint for printer or Preview
    bool bPaint =  gProp.pSFlyOnlyDraw ||
                       static_cast<SwContact*>(pUserCall)->GetFormat()->GetPrint().GetValue();
    if ( !bPaint )
        bPaint = rSh.GetWin() && !rSh.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 ( auto pFlyDraw = dynamic_cast<SwVirtFlyDrawObj *>( pObj ) )
        {
            SwFlyFrame *pFly = pFlyDraw->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().Overlaps( pFly->getFrameArea() ) )
            {
                    pAnch = pFly->AnchorFrame();
            }

        }
        else
        {
            // Consider 'virtual' drawing objects
            SwDrawContact* pDrawContact = dynamic_cast<SwDrawContact*>(pUserCall);
            pAnch = pDrawContact ? pDrawContact->GetAnchorFrame(pObj) : nullptr;
            if ( pAnch )
            {
                if ( !pAnch->isFrameAreaPositionValid() )
                    pAnch = nullptr;
                else if ( rSh.GetOut() == rSh.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().Overlaps( SwRect(pObj->GetCurrentBoundRect()) ) )
                        pAnch = nullptr;
                }
            }
            else
            {
                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(), rSh);
            else if ( gProp.pSFlyOnlyDraw )
                bPaint = false;
        }
        else
            bPaint = false;
    }
    return bPaint;
}

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

namespace {

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;
};

}

// set strikethrough for deleted objects anchored to character
void SwFrame::SetDrawObjsAsDeleted( bool bDeleted )
{
    if ( SwSortedObjs *pObjs = GetDrawObjs() )
    {
        for (SwAnchoredObject* pAnchoredObj : *pObjs)
        {
            if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
            {
                pFly->SetDeleted(bDeleted);
            }
        }
    }
}

void SwFlyFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const& rRect, PaintFrameMode) 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)
        {
            const SwRect& aVisRect = pShell->VisArea();
            if (!aVisRect.Overlaps(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( vcl::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 )
        {
            // 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 SwFlyFrameFormat* pSwFrameFormat = 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 && aFillAttributes->isUsed())
                {
                    bPaintCompleteBack = true;
                }
            }
            else
            {
                std::unique_ptr<SvxBrushItem> aBack = GetFormat()->makeBackgroundBrushItem();
                //     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.

            auto popIt = rRenderContext.ScopedPush(vcl::PushFlags::FILLCOLOR | vcl::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 )
                {
                    auto popIt2 = rRenderContext.ScopedPush();
                    // #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 );
                    }
                }
                else
                {
                    for ( size_t i = 0; i < aRegion.size(); ++i )
                    {
                        PaintSwFrameBackground( aRegion[i], pPage, rAttrs, false, true );
                    }
                }
            }

            // paint border before painting background
            PaintSwFrameShadowAndBorder(rRect, pPage, rAttrs);
        }
    }

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

    {
        SwTaggedPDFHelper tag(nullptr, nullptr, nullptr, rRenderContext);
        // 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());
    }

    PaintDecorators();

    // crossing out for tracked deletion
    if ( GetAuthor() != std::string::npos && IsDeleted() )
    {
        tools::Long startX = aRect.Left(  ), endX = aRect.Right();
        tools::Long startY = aRect.Top(  ),  endY = aRect.Bottom();
        rRenderContext.SetLineColor( SwPostItMgr::GetColorAnchor(GetAuthor()) );
        rRenderContext.DrawLine(Point(startX, startY), Point(endX, endY));
        rRenderContext.DrawLine(Point(startX, endY), Point(endX, startY));
    }

    rRenderContext.Pop();

    if ( gProp.pSProgress && pNoText )
        SfxProgress::Reschedule();
}

void SwFlyFrame::PaintDecorators() const
{
    // Show the un-float button
    SwWrtShell* pWrtSh = dynamic_cast< SwWrtShell* >( gProp.pSGlobalShell );
    if ( pWrtSh )
    {
        UpdateUnfloatButton(pWrtSh, IsShowUnfloatButton(pWrtSh));
    }
}

SwView* SwTextFrame::GetView()
{
    SwWrtShell* pWrtSh = dynamic_cast<SwWrtShell*>(gProp.pSGlobalShell);

    if (!pWrtSh)
        return nullptr;

    return &pWrtSh->GetView();
}

void SwTextFrame::PaintParagraphStylesHighlighting() const
{
    // Maybe avoid the dynamic_cast and just use GetActiveWrtShell()
    // NO! Multiple windows will not display the highlighting correctly if GetActiveWrtShell is used.
    SwWrtShell* pWrtSh = dynamic_cast<SwWrtShell*>(gProp.pSGlobalShell);

    if (!pWrtSh)
        return;

    if (!pWrtSh->GetView().IsSpotlightParaStyles())
        return;

    vcl::RenderContext* pRenderContext = pWrtSh->GetOut();
    if (!pRenderContext)
        return;

    const SwTextFormatColl* pColl = GetTextNodeFirst()->GetTextColl();
    UIName sStyleName = pColl->GetName();

    Color nStyleColor;
    int nStyleNumber(-1);

    bool bSpotlightStyle;
    if (comphelper::LibreOfficeKit::isActive())
    {
        // For simplicity in kit mode, we render in the document "all styles"
        bSpotlightStyle = true;
        // Do this so these are stable across views regardless of an individual
        // user's selection mode in the style panel.
        nStyleNumber = pWrtSh->GetDoc()->GetTextFormatColls()->GetPos(pColl);
        nStyleColor = ColorHash(sStyleName.toString());
    }
    else
    {
        StylesSpotlightColorMap& rParaStylesColorMap
                = pWrtSh->GetView().GetStylesSpotlightParaColorMap();
        bSpotlightStyle = rParaStylesColorMap.contains(sStyleName.toString());
        if (bSpotlightStyle)
        {
            nStyleNumber = rParaStylesColorMap[sStyleName.toString()].second;
            nStyleColor = rParaStylesColorMap[sStyleName.toString()].first;
        }
    }

    //  draw styles highlighter
    if (bSpotlightStyle)
    {
        SwRect aFrameAreaRect(getFrameArea());

        if (IsRightToLeft())
        {
            aFrameAreaRect.AddRight(75);
            aFrameAreaRect.Left(aFrameAreaRect.Right() + 300);
        }
        else
        {
            aFrameAreaRect.AddLeft(-375);
            aFrameAreaRect.Right(aFrameAreaRect.Left() + 300);
        }

        const tools::Rectangle aRect = aFrameAreaRect.SVRect();

        vcl::Font aFont(OutputDevice::GetDefaultFont(DefaultFontType::UI_SANS, GetAppLanguage(),
                                                     GetDefaultFontFlags::OnlyOne, pRenderContext));
        aFont.SetFontSize(Size(0, 140 * pRenderContext->GetDPIScaleFactor()));
        aFont.SetUnderline(FontLineStyle::LINESTYLE_NONE);
        aFont.SetTransparent(false);
        aFont.SetWeight(WEIGHT_NORMAL);
        aFont.SetFamily(FontFamily::FAMILY_MODERN);
        aFont.SetColor(COL_BLACK);

        auto popIt = pRenderContext->ScopedPush(vcl::PushFlags::ALL);

        pRenderContext->SetFillColor(nStyleColor);
        pRenderContext->SetLineColor(nStyleColor);

        pRenderContext->DrawRect(aRect);

        // draw hatch pattern if paragraph has direct formatting
        if (SwDoc::HasParagraphDirectFormatting(SwPosition(*GetTextNodeForParaProps())))
        {
            Color aHatchColor(nStyleColor);
            // make hatch line color 41% darker than the fill color
            aHatchColor.ApplyTintOrShade(-4100);
            Hatch aHatch(HatchStyle::Single, aHatchColor, 50, 450_deg10);
            pRenderContext->DrawHatch(tools::PolyPolygon(aRect), aHatch);
        }

        pRenderContext->SetFont(aFont);
        pRenderContext->SetLayoutMode(vcl::text::ComplexTextLayoutFlags::Default);
        pRenderContext->SetTextFillColor(nStyleColor);
        pRenderContext->DrawText(aRect, OUString::number(nStyleNumber),
                                 DrawTextFlags::Center | DrawTextFlags::VCenter);
    }
}

void SwTextFrame::PaintOutlineContentVisibilityButton() const
{
    SwWrtShell* pWrtSh = dynamic_cast<SwWrtShell*>(gProp.pSGlobalShell);
    if (pWrtSh && pWrtSh->GetViewOptions()->IsShowOutlineContentVisibilityButton())
        UpdateOutlineContentVisibilityButton(pWrtSh);
}

void SwTabFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const& rRect, PaintFrameMode) const
{
    if (!getFramePrintArea().HasArea())
    {
        return; // do not paint hidden frame
    }
    const SwViewOption* pViewOption = gProp.pSGlobalShell->GetViewOptions();
    if (pViewOption->IsTable())
    {
        // tdf#77388 first paint the cell content to avoid of removing own border
        SwLayoutFrame::PaintSwFrame( rRenderContext, rRect );

        // #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);
        }
    }
    // #i6467# - no light grey rectangle for page preview
    else if ( gProp.pSGlobalShell->GetWin() && !gProp.pSGlobalShell->IsPreview() )
    {
        //  #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 tools::Long nWidth  = ::lcl_AlignWidth ( rShadow.GetWidth(), properties );
    const tools::Long nHeight = ::lcl_AlignHeight( rShadow.GetWidth(), properties );

    SwRects aRegion;
    SwRect aOut( rOutRect );

    switch ( rShadow.GetLocation() )
    {
        case SvxShadowLocation::BottomRight:
            {
                if ( bDrawFullShadowRectangle )
                {
                    // 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.AddRight(- nWidth );
                if( bBottom )
                    rOutRect.AddBottom(- nHeight );
            }
            break;
        case SvxShadowLocation::TopLeft:
            {
                if ( bDrawFullShadowRectangle )
                {
                    // 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.AddLeft( nWidth );
                if( bTop )
                    rOutRect.AddTop( nHeight );
            }
            break;
        case SvxShadowLocation::TopRight:
            {
                if ( bDrawFullShadowRectangle )
                {
                    // 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.AddRight( - nWidth );
                if( bTop )
                    rOutRect.AddTop( nHeight );
            }
            break;
        case SvxShadowLocation::BottomLeft:
            {
                if ( bDrawFullShadowRectangle )
                {
                    // 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.AddLeft( nWidth );
                if( bBottom )
                    rOutRect.AddBottom( - 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 = properties.pSGlobalShell->GetViewOptions()->GetFontColor();
    }

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

    pOut->SetLineColor();

    pOut->SetDrawMode( nOldDrawMode );

    for (const SwRect & rOut : aRegion)
    {
        aOut = rOut;
        if ( rRect.Overlaps( 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;
        }
    }

    //  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.Overlaps( 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() )
    {
        SwViewShell *pSh = getRootFrame()->GetCurrShell();
        const SwViewOption *pOpt = pSh->GetViewOptions();
        pColor = &pOpt->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::primitive2d
{
        namespace {

        class 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 Primitive2DReference create2DDecomposition(
                const geometry::ViewInformation2D& rViewInformation) const override;

        public:
            /// constructor
            SwBorderRectanglePrimitive2D(
                basegfx::B2DHomMatrix aB2DHomMatrix,
                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
            virtual sal_uInt32 getPrimitive2DID() const override;
        };

        }

        Primitive2DReference SwBorderRectanglePrimitive2D::create2DDecomposition(
            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));

            // prepare SdrFrameBorderDataVector
            drawinglayer::primitive2d::SdrFrameBorderDataVector aData;

            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);
                aData.emplace_back(
                    aTopLeft,
                    aVector,
                    getStyleTop(),
                    nullptr);
                drawinglayer::primitive2d::SdrFrameBorderData& rInstance(aData.back());

                if(getStyleLeft().IsUsed())
                {
                    rInstance.addSdrConnectStyleData(true, getStyleLeft(), basegfx::B2DVector(aBottomLeft - aTopLeft), false);
                }

                if(getStyleRight().IsUsed())
                {
                    rInstance.addSdrConnectStyleData(false, getStyleRight(), basegfx::B2DVector(aBottomRight - aTopRight), false);
                }
            }

            if(getStyleRight().IsUsed())
            {
                // create BorderPrimitive(s) for right border
                const basegfx::B2DVector aVector(aBottomRight - aTopRight);
                aData.emplace_back(
                    aTopRight,
                    aVector,
                    getStyleRight(),
                    nullptr);
                drawinglayer::primitive2d::SdrFrameBorderData& rInstance(aData.back());

                if(getStyleTop().IsUsed())
                {
                    rInstance.addSdrConnectStyleData(true, getStyleTop(), basegfx::B2DVector(aTopLeft - aTopRight), false);
                }

                if(getStyleBottom().IsUsed())
                {
                    rInstance.addSdrConnectStyleData(false, getStyleBottom(), basegfx::B2DVector(aBottomLeft - aBottomRight), false);
                }
            }

            if(getStyleBottom().IsUsed())
            {
                // create BorderPrimitive(s) for bottom border
                const basegfx::B2DVector aVector(aBottomLeft - aBottomRight);
                aData.emplace_back(
                    aBottomRight,
                    aVector,
                    getStyleBottom(),
                    nullptr);
                drawinglayer::primitive2d::SdrFrameBorderData& rInstance(aData.back());

                if(getStyleRight().IsUsed())
                {
                    rInstance.addSdrConnectStyleData(true, getStyleRight(), basegfx::B2DVector(aTopRight - aBottomRight), false);
                }

                if(getStyleLeft().IsUsed())
                {
                    rInstance.addSdrConnectStyleData(false, getStyleLeft(), basegfx::B2DVector(aTopLeft - aBottomLeft), false);
                }
            }

            if(getStyleLeft().IsUsed())
            {
                // create BorderPrimitive(s) for left border
                const basegfx::B2DVector aVector(aTopLeft - aBottomLeft);
                aData.emplace_back(
                    aBottomLeft,
                    aVector,
                    getStyleLeft(),
                    nullptr);
                drawinglayer::primitive2d::SdrFrameBorderData& rInstance(aData.back());

                if(getStyleBottom().IsUsed())
                {
                    rInstance.addSdrConnectStyleData(true, getStyleBottom(), basegfx::B2DVector(aBottomRight - aBottomLeft), false);
                }

                if(getStyleTop().IsUsed())
                {
                    rInstance.addSdrConnectStyleData(false, getStyleTop(), basegfx::B2DVector(aTopRight - aTopLeft), false);
                }
            }

            // create instance of SdrFrameBorderPrimitive2D if
            // SdrFrameBorderDataVector is used
            if(aData.empty())
                return nullptr;

            return new drawinglayer::primitive2d::SdrFrameBorderPrimitive2D(
                        std::move(aData),
                        true);    // force visualization to minimal one discrete unit (pixel)
        }

        SwBorderRectanglePrimitive2D::SwBorderRectanglePrimitive2D(
            basegfx::B2DHomMatrix aB2DHomMatrix,
            const svx::frame::Style& rStyleTop,
            const svx::frame::Style& rStyleRight,
            const svx::frame::Style& rStyleBottom,
            const svx::frame::Style& rStyleLeft)
        :   maB2DHomMatrix(std::move(aB2DHomMatrix)),
            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
        sal_uInt32 SwBorderRectanglePrimitive2D::getPrimitive2DID() const
        {
            return PRIMITIVE2D_ID_SWBORDERRECTANGLERIMITIVE;
        }

} // end of namespace drawinglayer::primitive2d

namespace {

editeng::SvxBorderLine const * get_ptr(std::optional<editeng::SvxBorderLine> const & opt) {
    return opt ? &*opt : nullptr;
}

}

void PaintCharacterBorder(
    const SwFont& rFont,
    const SwRect& rPaintArea,
    const bool bVerticalLayout,
    const bool bVerticalLayoutLRBT,
    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, bVerticalLayoutLRBT).get())
    {
        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, bVerticalLayoutLRBT));

        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 ? get_ptr(rFont.GetAbsTopBorder(bVerticalLayout, bVerticalLayoutLRBT)) : nullptr,
        1.0);
    const svx::frame::Style aStyleRight(
        bRight ? get_ptr(rFont.GetAbsRightBorder(bVerticalLayout, bVerticalLayoutLRBT)) : nullptr,
        1.0);
    const svx::frame::Style aStyleBottom(
        bBottom ? get_ptr(rFont.GetAbsBottomBorder(bVerticalLayout, bVerticalLayoutLRBT))
                : nullptr,
        1.0);
    const svx::frame::Style aStyleLeft(
        bLeft ? get_ptr(rFont.GetAbsLeftBorder(bVerticalLayout, bVerticalLayoutLRBT)) : nullptr,
        1.0);
    drawinglayer::primitive2d::Primitive2DContainer aBorderLineTarget;

    aBorderLineTarget.append(
            new drawinglayer::primitive2d::SwBorderRectanglePrimitive2D(
                aBorderTransform,
                aStyleTop,
                aStyleRight,
                aStyleBottom,
                aStyleLeft));
    gProp.pBLines->AddBorderLines(std::move(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;
}

std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> SwFrame::CreateProcessor2D( ) const
{
    basegfx::B2DRange aViewRange;

    SdrPage *pDrawPage = getRootFrame()->GetCurrShell()->Imp()->GetPageView()->GetPage();
    drawinglayer::geometry::ViewInformation2D aNewViewInfos;
    aNewViewInfos.setViewTransformation(getRootFrame()->GetCurrShell()->GetOut()->GetViewTransformation());
    aNewViewInfos.setViewport(aViewRange);
    aNewViewInfos.setVisualizedPage(GetXDrawPageForSdrPage( pDrawPage ));

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

void SwFrame::ProcessPrimitives( const drawinglayer::primitive2d::Primitive2DContainer& rSequence ) const
{
    std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor2D = CreateProcessor2D();
    if ( pProcessor2D )
    {
        pProcessor2D->process( rSequence );
    }
}

/// 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::Footnote|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;
    }

    if (!getFrameArea().HasArea())
    { // hidden text frame, may be called by upper from PaintSwFrameBackground()
        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() );
    // 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.Contains( rRect ) )
    {
        // 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);
    }

    // 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())
        {
            const SwFrame* pDirRefFrame(IsCellFrame() ? FindTabFrame() : this);
            const SwRectFnSet aRectFnSet(*pDirRefFrame);
            const SwRectFn _aRectFn(aRectFnSet.FnRect());

            if(rAttrs.JoinedWithPrev(*this))
            {
                // tdf#115296 re-add adaptation of vert distance to close the evtl.
                // existing gap to previous frame
                const SwFrame* pPrevFrame(GetPrev());
                (aRect.*_aRectFn->fnSetTop)( (pPrevFrame->*_aRectFn->fnGetPrtBottom)() );

                // ...and disable top border paint/creation
                pTopBorder = nullptr;
            }

            if(rAttrs.JoinedWithNext(*this))
            {
                // tdf#115296 re-add adaptation of vert distance to close the evtl.
                // existing gap to next frame
                const SwFrame* pNextFrame(GetNext());
                (aRect.*_aRectFn->fnSetBottom)( (pNextFrame->*_aRectFn->fnGetPrtTop)() );

                // ...and disable bottom border paint/creation
                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();
        }

        bool bWordBorder = false;
        SwViewShell* pShell = getRootFrame()->GetCurrShell();
        if (pShell)
        {
            const IDocumentSettingAccess& rIDSA = pShell->GetDoc()->getIDocumentSettingAccess();
            bWordBorder = rIDSA.get(DocumentSettingId::TABLE_ROW_KEEP);
        }
        bool bInWordTableCell = IsContentFrame() && GetUpper()->IsCellFrame() && bWordBorder;
        if (bInWordTableCell)
        {
            // Compat mode: don't paint bottom border if we know the bottom of the content was cut
            // off.
            auto pContentFrame = static_cast<const SwContentFrame*>(this);
            if (pContentFrame->IsUndersized())
            {
                pBottomBorder = nullptr;
            }
        }

        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);
            svx::frame::Style aStyleRight(pRightBorder, 1.0);

            // Right/bottom page borders are always mirrored in Word.
            if (IsPageFrame() && bWordBorder)
            {
                aStyleRight.MirrorSelf();
            }

            svx::frame::Style aStyleBottom(pBottomBorder, 1.0);

            if (IsPageFrame() && bWordBorder)
            {
                aStyleBottom.MirrorSelf();
            }

            const svx::frame::Style aStyleLeft(pLeftBorder, 1.0);
            drawinglayer::primitive2d::Primitive2DContainer aBorderLineTarget;

            drawinglayer::primitive2d::Primitive2DReference aRetval(
                    new drawinglayer::primitive2d::SwBorderRectanglePrimitive2D(
                        aBorderTransform,
                        aStyleTop,
                        aStyleRight,
                        aStyleBottom,
                        aStyleLeft));

            if (bInWordTableCell)
            {
                // Compat mode: cut off the borders which are outside of our own area.
                const SwRect& rClip = getFrameArea();
                basegfx::B2DRectangle aClip(rClip.Left(), rClip.Top(), rClip.Right(),
                                            rClip.Bottom());
                basegfx::B2DPolyPolygon aPolyPolygon(
                    basegfx::utils::createPolygonFromRect(aClip));
                const drawinglayer::primitive2d::Primitive2DReference xClipped(
                    new drawinglayer::primitive2d::MaskPrimitive2D(std::move(aPolyPolygon), { aRetval }));
                aRetval = xClipped;
            }

            aBorderLineTarget.append(aRetval);
            gProp.pBLines->AddBorderLines(std::move(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.Contains( 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 );
    aFract *= rInf.GetWidth();
    SwTwips nWidth = static_cast<tools::Long>(aFract);

    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();
    std::optional<SwRect> oLineRect;
    if (aRectFnSet.IsVert())
    {
        oLineRect.emplace(Point(getFrameArea().Left()+getFrameArea().Width()-rInf.GetTopDist()-nLineWidth,
                      nX), Size( nLineWidth, nWidth ) );
    }
    else
    {
        Point aPoint(nX, getFrameArea().Pos().Y() + rInf.GetTopDist());
        const IDocumentSettingAccess& rIDSA = GetFormat()->getIDocumentSettingAccess();
        if (rIDSA.get(DocumentSettingId::CONTINUOUS_ENDNOTES))
        {
            // Word style: instead of fixed value, upper spacing is 60% of all space.
            auto nPrintAreaTop = static_cast<double>(getFramePrintArea().Top());
            aPoint.setY(getFrameArea().Pos().Y() + nPrintAreaTop * 0.6);

            const SwFootnoteFrame* pEndnoteFrame = FindEndNote();
            bool bEndnoteContinuation = pEndnoteFrame && pEndnoteFrame->GetMaster();
            if (bEndnoteContinuation)
            {
                // Endnote continuation separator is print area wide.
                nWidth = nPrtWidth;
            }
            else
            {
                // Length is 2 inches, but don't paint outside the container frame.
                nWidth = o3tl::convert(2, o3tl::Length::in, o3tl::Length::twip);
                if (nWidth > nPrtWidth)
                {
                    nWidth = nPrtWidth;
                }
            }
        }
        oLineRect.emplace(aPoint, Size(nWidth, rInf.GetLineWidth()));
    }
    if ( oLineRect->HasArea() && rInf.GetLineStyle() != SvxBorderLineStyle::NONE)
        PaintBorderLine( rRect, *oLineRect , pPage, &rInf.GetLineColor(),
                rInf.GetLineStyle() );
}

void SwFootnoteContFrame::dumpAsXml(xmlTextWriterPtr writer) const
{
    (void)xmlTextWriterStartElement(writer, reinterpret_cast<const xmlChar*>("ftncont"));
    dumpAsXmlAttributes(writer);

    (void)xmlTextWriterStartElement(writer, BAD_CAST("infos"));
    dumpInfosAsXml(writer);
    (void)xmlTextWriterEndElement(writer);
    dumpChildrenAsXml(writer);

    (void)xmlTextWriterEndElement(writer);
}

/// 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;

    SwRectFnSet fnRect(*pCol);

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

    SwTwips nTop = (fnRect.GetHeight(aLineRect)*rFormatCol.GetLineHeight())
                   / 100 - fnRect.GetHeight(aLineRect);
    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 )
        fnRect.SubTop(aLineRect, nTop);
    if( nBottom )
        fnRect.AddBottom(aLineRect, nBottom);

    SwTwips nPenHalf = rFormatCol.GetLineWidth();
    fnRect.SetWidth(aLineRect, nPenHalf);
    nPenHalf /= 2;

    //We need to be a bit generous here, to not lose something.
    SwRect aRect( rRect );
    fnRect.SubLeft(aRect, nPenHalf + gProp.nSPixelSzW);
    fnRect.AddRight(aRect, nPenHalf + gProp.nSPixelSzW);
    while ( pCol->GetNext() )
    {
        fnRect.SetPosX(aLineRect, (IsRightToLeft() ? fnRect.GetLeft(pCol->getFrameArea())
                                                   : fnRect.GetRight(pCol->getFrameArea()))
                                      - nPenHalf);
        if ( aRect.Overlaps( 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() )) )
        return;

    const SwLayoutFrame* pBody = FindBodyCont();
    if( !pBody )
        return;

    SwRect aGrid( pBody->getFramePrintArea() );
    aGrid += pBody->getFrameArea().Pos();

    SwRect aInter( aGrid );
    aInter.Intersection( rRect );
    if( !aInter.HasArea() )
        return;

    bool bGrid = pGrid->GetRubyTextBelow();
    bool bCell = SwTextGrid::LinesAndChars == pGrid->GetGridType();
    tools::Long nGrid = pGrid->GetBaseHeight();
    const SwDoc& rDoc = GetFormat()->GetDoc();
    tools::Long nGridWidth = GetGridWidth(*pGrid, rDoc);
    tools::Long nRuby = pGrid->GetRubyHeight();
    tools::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( SwTwips(aInter.Left()), nY );
                SwTwips nHeight = std::min(nRight, SwTwips(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( SwTwips(aInter.Left()), nY );
                    SwTwips nW = std::min(nRight, SwTwips(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(SwTwips(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( SwTwips(aInter.Left()), nY );
                    SwTwips nW = std::min(nRight, SwTwips(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() )
        return;

    // 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::snShadowPxWidth = 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() ));

    tools::Long lShadowAdjustment = snShadowPxWidth - 1; // TODO: extract this

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

    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.AddRight( aSidebarTotalWidth );
        else
            _orHorizontalShadowRect.AddLeft( - aSidebarTotalWidth );
    }
}

namespace {

enum PaintArea {LEFT, RIGHT, TOP, BOTTOM};

}

#define BORDER_TILE_SIZE 512

/// Wrapper around pOut->DrawBitmap.
static void lcl_paintBitmapExToRect(vcl::RenderContext *pOut, const Point& aPoint, const Size& aSize, const Bitmap& rBitmap, PaintArea eArea)
{
    if(!comphelper::LibreOfficeKit::isActive())
    {
        // 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);

        if (!aRect.IsEmpty())
        {
            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::GetCurrentViewOptions().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);

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

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

}

/**
 * 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 (!_pViewShell->GetViewOptions()->IsShadow())
        return;

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

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

    drawinglayer::primitive2d::DiscreteShadow& shadowMask = *shadowMaskObj.get();
    static tools::DeleteOnDeinit< Bitmap > aPageTopRightShadowObj {};
    static tools::DeleteOnDeinit< Bitmap > aPageBottomRightShadowObj {};
    static tools::DeleteOnDeinit< Bitmap > aPageBottomLeftShadowObj  {};
    static tools::DeleteOnDeinit< Bitmap > aPageBottomShadowBaseObj  {};
    static tools::DeleteOnDeinit< Bitmap > aPageRightShadowBaseObj  {};
    static tools::DeleteOnDeinit< Bitmap > aPageTopShadowBaseObj  {};
    static tools::DeleteOnDeinit< Bitmap > aPageTopLeftShadowObj  {};
    static tools::DeleteOnDeinit< Bitmap > aPageLeftShadowBaseObj  {};
    Bitmap& aPageTopRightShadow = *aPageTopRightShadowObj.get();
    Bitmap& aPageBottomRightShadow = *aPageBottomRightShadowObj.get();
    Bitmap& aPageBottomLeftShadow = *aPageBottomLeftShadowObj.get();
    Bitmap& aPageBottomShadow = *aPageBottomShadowBaseObj.get();
    Bitmap& aPageRightShadow = *aPageRightShadowBaseObj.get();
    Bitmap& aPageTopShadow = *aPageTopShadowBaseObj.get();
    Bitmap& aPageTopLeftShadow = *aPageTopLeftShadowObj.get();
    Bitmap& 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 != _pViewShell->GetViewOptions()->GetShadowColor())
    {
        aShadowColor = _pViewShell->GetViewOptions()->GetShadowColor();

        AlphaMask aMask( shadowMask.getBottomRight().CreateColorBitmap() );
        Bitmap aFilledSquare(aMask.GetSizePixel(), vcl::PixelFormat::N24_BPP);
        aFilledSquare.Erase( aShadowColor );
        aPageBottomRightShadow = Bitmap( aFilledSquare, aMask );

        aMask = AlphaMask( shadowMask.getBottomLeft().CreateColorBitmap() );
        aFilledSquare = Bitmap(aMask.GetSizePixel(), vcl::PixelFormat::N24_BPP);
        aFilledSquare.Erase( aShadowColor );
        aPageBottomLeftShadow = Bitmap( aFilledSquare, aMask );

        aMask = AlphaMask( shadowMask.getBottom().CreateColorBitmap() );
        aFilledSquare = Bitmap(aMask.GetSizePixel(), vcl::PixelFormat::N24_BPP);
        aFilledSquare.Erase( aShadowColor );
        aPageBottomShadow = Bitmap( aFilledSquare, aMask );

        aMask = AlphaMask( shadowMask.getTop().CreateColorBitmap() );
        aFilledSquare = Bitmap(aMask.GetSizePixel(), vcl::PixelFormat::N24_BPP);
        aFilledSquare.Erase( aShadowColor );
        aPageTopShadow = Bitmap( aFilledSquare, aMask );

        aMask = AlphaMask( shadowMask.getTopRight().CreateColorBitmap() );
        aFilledSquare = Bitmap(aMask.GetSizePixel(), vcl::PixelFormat::N24_BPP);
        aFilledSquare.Erase( aShadowColor );
        aPageTopRightShadow = Bitmap( aFilledSquare, aMask );

        aMask = AlphaMask( shadowMask.getRight().CreateColorBitmap() );
        aFilledSquare = Bitmap(aMask.GetSizePixel(), vcl::PixelFormat::N24_BPP);
        aFilledSquare.Erase( aShadowColor );
        aPageRightShadow = Bitmap( aFilledSquare, aMask );

        aMask = AlphaMask( shadowMask.getTopLeft().CreateColorBitmap() );
        aFilledSquare = Bitmap(aMask.GetSizePixel(), vcl::PixelFormat::N24_BPP);
        aFilledSquare.Erase( aShadowColor );
        aPageTopLeftShadow = Bitmap( aFilledSquare, aMask );

        aMask = AlphaMask( shadowMask.getLeft().CreateColorBitmap() );
        aFilledSquare = Bitmap(aMask.GetSizePixel(), vcl::PixelFormat::N24_BPP);
        aFilledSquare.Erase( aShadowColor );
        aPageLeftShadow = Bitmap( aFilledSquare, aMask );
    }

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

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

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

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

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

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

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

    // Bottom shadow
    const tools::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 tools::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() - snShadowPxWidth),
            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
        return;

    sal_Int32 nScrollerHeight = pMgr->GetSidebarScrollerHeight();
    const tools::Rectangle aVisRect = _pViewShell->VisArea().SVRect();
    //draw border and sidepane
    _pViewShell->GetOut()->SetLineColor();
    if (!bRight)
    {
        _pViewShell->GetOut()->SetFillColor(_pViewShell->GetViewOptions()->GetDocBoundariesColor());
        _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(_pViewShell->GetViewOptions()->GetSectionBoundColor());
        _pViewShell->GetOut()->DrawRect(tools::Rectangle(Point(aPageRect.Left()-pMgr->GetSidebarWidth()-pMgr->GetSidebarBorderWidth(),aPageRect.Top()),Size(pMgr->GetSidebarWidth(),aPageRect.Height())))  ;
    }
    else
    {
        _pViewShell->GetOut()->SetFillColor(_pViewShell->GetViewOptions()->GetDocBoundariesColor());
        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(_pViewShell->GetViewOptions()->GetSectionBoundColor());
        SwRect aSidebar(Point(aPageRect.Right()+pMgr->GetSidebarBorderWidth(),aPageRect.Top()),Size(pMgr->GetSidebarWidth(),aPageRect.Height()));
        _pViewShell->GetOut()->DrawRect(aSidebar.SVRect());
    }
    if (!pMgr->ShowScrollbar(nPageNum))
        return;

    // 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.Overlaps(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_LIGHTGRAY);
        }
        _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.Overlaps(aVisRect))
        return;

    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_LIGHTGRAY);
    }
    _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.AddBottom( snShadowPxWidth + 1 );
    aPagePxRect.AddTop( - snShadowPxWidth - 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() - snShadowPxWidth - 1);
    if(bRightShadow) aPagePxRect.Right( aTmpRect.Right() + snShadowPxWidth + 1);

    _orBorderAndShadowBoundRect = SwRect(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;
}

const SwFrame* SwFrame::SkipFrame(const SwFrame* pFrame, PaintFrameMode ePaintFrameMode )
{
    if (ePaintFrameMode != PAINT_ALL)
    {
        if (ePaintFrameMode == PAINT_NON_HEADER_FOOTER)
        {
            while (pFrame && (pFrame->IsHeaderFrame() || pFrame->IsFooterFrame()))
                pFrame = pFrame->GetNext();
        }
        else
        {
            while ( pFrame && !pFrame->IsHeaderFrame() && !pFrame->IsFooterFrame())
                pFrame = pFrame->GetNext();
        }
    }
    return pFrame;
}

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

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

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

    auto popIt = pOut->ScopedPush(vcl::PushFlags::FILLCOLOR | vcl::PushFlags::LINECOLOR);
    pOut->SetLineColor();

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

    // 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, ePaintFrameMode );
    }

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

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

/// 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,
                             PaintFrameMode ePaintFrameMode) const
{
    // #i1837# - no paint of table background, if corresponding option is *not* set.
    SwViewShell *pSh = gProp.pSGlobalShell;
    if( IsTabFrame() &&
        !pSh->GetViewOptions()->IsTable() )
    {
        return;
    }

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

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

    const SvxBrushItem* pItem;
    // temporary background brush for a fly frame without a background brush
    std::unique_ptr<SvxBrushItem> pTmpBackBrush;
    std::optional<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 );

    // show track changes of table row
    if( IsRowFrame() && !getRootFrame()->IsHideRedlines() )
    {
        RedlineType eType = static_cast<const SwRowFrame*>(this)->GetTabLine()->GetRedlineType();
        if ( RedlineType::Delete == eType || RedlineType::Insert == eType )
        {
            pCol = RedlineType::Delete == eType ? COL_AUTHOR_TABLE_DEL : COL_AUTHOR_TABLE_INS;
            bBack = true;
        }
    }
    else if ( IsCellFrame() && !getRootFrame()->IsHideRedlines() )
    {
        RedlineType eType = static_cast<const SwCellFrame*>(this)->GetTabBox()->GetRedlineType();
        if ( RedlineType::Delete == eType || RedlineType::Insert == eType )
        {
            pCol = RedlineType::Delete == eType ? COL_AUTHOR_TABLE_DEL : COL_AUTHOR_TABLE_INS;
            bBack = true;
        }
    }

    if ( bBack && IsCellFrame() && !getRootFrame()->IsHideRedlines() &&
        // skip cell background to show the row colored according to its tracked change
        RedlineType::None != static_cast<const SwRowFrame*>(GetUpper())->GetTabLine()->GetRedlineType() )
    {
        return;
    }

    //- Output if a separate background is used.
    bool bNoFlyBackground = !gProp.bSFlyMetafile && !bBack && IsFlyFrame();
    if ( bNoFlyBackground )
    {
        // 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 )
        {
            //  #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.reset(new SvxBrushItem( COL_WHITE, RES_BACKGROUND ));

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

                //UUU
                aFillAttributes = std::make_shared<drawinglayer::attribute::SdrAllFillAttributesHelper>(aGlobalRetoucheColor);
            }

            pItem = pTmpBackBrush.get();
            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.Overlaps( 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
            {
                if (bPageFrame && GetAttrSet()->GetItem<SfxBoolItem>(RES_BACKGROUND_FULL_SIZE)->GetValue())
                {
                    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() )
            {
                std::unique_ptr<SvxBrushItem> pNewItem;

                if( pCol )
                {
                    pNewItem.reset(new SvxBrushItem( *pCol, RES_BACKGROUND ));
                    pItem = pNewItem.get();
                    aFillAttributes = std::make_shared<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 );
                }

                //  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 (aFillAttributes)
                {
                    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;
                        }
                    //  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 );
                    }
                }
            }
        }
        else
            bLowMode = bLowerMode;
    }

    // delete temporary background brush.
    pTmpBackBrush.reset();

    //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 )
        return;

    SwRect aFrameRect;
    SwRect aRect( GetPaintArea() );
    aRect.Intersection_( rRect );
    SwRect aBorderRect( aRect );

    pFrame = SkipFrame(pFrame, ePaintFrameMode);
    if (!pFrame)
        return;

    SwShortCut aShortCut( *pFrame, aBorderRect );
    do
    {   if ( gProp.pSProgress )
            SfxProgress::Reschedule();

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

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

/// Refreshes all subsidiary lines of a page.
void SwPageFrame::RefreshSubsidiary( const SwRect &rRect ) const
{
    if ( !(isSubsidiaryLinesEnabled() ||
           gProp.pSGlobalShell->GetViewOptions()->IsTextBoundaries() ||
           gProp.pSGlobalShell->GetViewOptions()->IsSectionBoundaries() ||
           gProp.pSGlobalShell->GetViewOptions()->IsTableBoundaries()) )
        return;

    if ( !rRect.HasArea() )
        return;

    //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);
        // create container for special subsidiary lines
        gProp.pSSpecSubsLines.reset(new SwSubsRects);
        bDelSubs = true;
    }

    RefreshLaySubsidiary( this, rRect );

    if ( bDelSubs )
    {
        // 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
{
    if (isSubsidiaryLinesEnabled())
        PaintSubsidiaryLines( pPage, rRect );

    const SwFrame *pLow = Lower();
    if( !pLow )
        return;
    SwShortCut aShortCut( *pLow, rRect );
    while( pLow && !aShortCut.Stop( pLow->getFrameArea() ) )
    {
        if ( pLow->getFrameArea().Overlaps( 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() ) )
                        if (auto pFly = pAnchoredObj->DynCastFlyFrame() )
                        {
                            if ( pFly->IsFlyInContentFrame() && pFly->getFrameArea().Overlaps( rRect ) )
                            {
                                SwFrame* pLower = pFly->Lower();
                                if ( !pLower || !pLower->IsNoTextFrame() ||
                                     !static_cast<const SwNoTextFrame*>(pLower)->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 tools::Long (Point::*pmfPtGet)() const;
    typedef void (Point::*pmfPtSet)(tools::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;
            }

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

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

            if ( nP1OthPt >= nDrOthPt && nP1OthPt <= nDrOthPt + nDrOthSz )
            {
                const tools::Long nDrDirPt = bHori ? aDrPt.X() : aDrPt.Y();
                const tools::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 );
            // 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 std::vector<basegfx::B2DPolygon> lcl_CreatePageAreaDelimiterPolygons(const SwRect& rRect, bool bHeaderFooter)
{
    std::vector<basegfx::B2DPolygon> aPolygons;

    // Hide text boundaries by default - cool#3491
    if (!bHeaderFooter && comphelper::LibreOfficeKit::isActive())
        return aPolygons;

    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 );

        aPolygons.emplace_back(aPolygon);
    }

    return aPolygons;
}

static drawinglayer::primitive2d::Primitive2DContainer lcl_CreateDelimiterPrimitives(
    const std::vector<basegfx::B2DPolygon>& rPolygons)
{
    drawinglayer::primitive2d::Primitive2DContainer aSeq(rPolygons.size());

    basegfx::BColor aLineColor = SwViewOption::GetCurrentViewOptions().GetDocBoundariesColor().getBColor();
    for (size_t i = 0; i < rPolygons.size(); ++i)
        aSeq[i] = new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(rPolygons[i], aLineColor);

    return aSeq;
}

static std::vector<basegfx::B2DPolygon> lcl_CreateRectangleDelimiterPolygons(const SwRect& rRect)
{
    std::vector<basegfx::B2DPolygon> aRet(1);
    aRet[0].append( basegfx::B2DPoint( rRect.Left(), rRect.Top() ) );
    aRet[0].append( basegfx::B2DPoint( rRect.Right(), rRect.Top() ) );
    aRet[0].append( basegfx::B2DPoint( rRect.Right(), rRect.Bottom() ) );
    aRet[0].append( basegfx::B2DPoint( rRect.Left(), rRect.Bottom() ) );
    aRet[0].setClosed( true );
    return aRet;
}

static drawinglayer::primitive2d::Primitive2DContainer lcl_CreateRectangleDelimiterPrimitives (
        const SwRect& rRect )
{
    return lcl_CreateDelimiterPrimitives(lcl_CreateRectangleDelimiterPolygons(rRect));
}

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

    basegfx::BColor aLineColor = SwViewOption::GetCurrentViewOptions().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 );

        aSeq[i] = new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(
                    std::move(aPolygon), aLineColor );
    }

    return aSeq;
}

std::vector<basegfx::B2DPolygon> SwPageFrame::GetSubsidiaryLinesPolygons(const SwViewShell& rViewShell) const
{
    std::vector<basegfx::B2DPolygon> aPolygons;

    if (!rViewShell.GetViewOptions()->IsTextBoundaries())
        return aPolygons;

    const SwFrame* pLay = Lower();
    const SwFrame* pFootnoteCont = nullptr;
    const SwFrame* pPageBody = nullptr;

    if (!pLay)
        return aPolygons;

    while ( pLay && !( pFootnoteCont && pPageBody ) )
    {
        if ( pLay->IsFootnoteContFrame( ) )
            pFootnoteCont = pLay;
        if ( pLay->IsBodyFrame() )
            pPageBody = pLay;
        pLay = pLay->GetNext();
    }

    if (!pPageBody)
        return aPolygons;

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

    if (aArea.IsEmpty())
        return aPolygons;

    if (!rViewShell.GetViewOptions()->IsTextBoundariesFull())
        aPolygons = lcl_CreatePageAreaDelimiterPolygons(aArea, false /* body */);
    else
        aPolygons = lcl_CreateRectangleDelimiterPolygons(aArea);

    return aPolygons;
}

void SwPageFrame::PaintSubsidiaryLines(const SwPageFrame*, const SwRect&) const
{
    if (gProp.pSGlobalShell->IsHeaderFooterEdit())
        return;

    std::vector<basegfx::B2DPolygon> aPolygons = GetSubsidiaryLinesPolygons(*gProp.pSGlobalShell);
    if (aPolygons.empty())
        return;

    ProcessPrimitives(lcl_CreateDelimiterPrimitives(aPolygons));
}

static void lclAddSubsidiaryLinesBounds(const std::vector<basegfx::B2DPolygon>& rPolygons, RectangleVector& rRects)
{
    for (const auto& rPolygon : rPolygons)
    {
        tools::Rectangle aRect(vcl::unotools::rectangleFromB2DRectangle(rPolygon.getB2DRange()));
        aRect.expand(1);
        if (basegfx::utils::isRectangle(rPolygon) && aRect.GetWidth() > 4 && aRect.GetHeight() > 4)
        {
            // turn hairline rectangle into four non-overlapping blocks that cover the borders
            rRects.emplace_back(tools::Rectangle(Point(aRect.Left(), aRect.Top()), Size(aRect.GetWidth(), 2)));
            rRects.emplace_back(tools::Rectangle(Point(aRect.Left(), aRect.Top() + 2), Size(2, aRect.GetHeight() - 4)));
            rRects.emplace_back(tools::Rectangle(Point(aRect.Right() - 2, aRect.Top() + 2), Size(2, aRect.GetHeight() - 4)));
            rRects.emplace_back(tools::Rectangle(Point(aRect.Left(), aRect.Top() + aRect.GetHeight() - 2), Size(aRect.GetWidth(), 2)));
        }
        else
            rRects.emplace_back(aRect);
    }
}

void SwPageFrame::AddSubsidiaryLinesBounds(const SwViewShell& rViewShell, RectangleVector& rRects) const
{
    lclAddSubsidiaryLinesBounds(GetSubsidiaryLinesPolygons(rViewShell), rRects);

    const SwFrame *pLow = Lower();
    while (pLow)
    {
        if (pLow->getFrameArea().HasArea())
        {
            if (pLow->IsHeaderFrame() || pLow->IsFooterFrame())
            {
                static_cast<const SwHeadFootFrame*>(pLow)->AddSubsidiaryLinesBounds(rViewShell, rRects);
            }
        }
        pLow = pLow->GetNext();
    }
}

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

    assert(pColBody && "presumably this is impossible");

    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()->IsTextBoundariesFull( ) )
        ProcessPrimitives( lcl_CreateColumnAreaDelimiterPrimitives( aArea ) );
    else
        ProcessPrimitives( lcl_CreateRectangleDelimiterPrimitives( aArea ) );
}

void SwSectionFrame::PaintSubsidiaryLines( const SwPageFrame * pPage,
                                        const SwRect & rRect ) const
{
    if (!gProp.pSGlobalShell->GetViewOptions()->IsSectionBoundaries())
        return;

    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
{
}

std::vector<basegfx::B2DPolygon> SwHeadFootFrame::GetSubsidiaryLinesPolygons(const SwViewShell& rViewShell) const
{
    std::vector<basegfx::B2DPolygon> aPolygons;

    if (!rViewShell.GetViewOptions()->IsTextBoundaries())
        return aPolygons;

    SwRect aArea( getFramePrintArea() );
    aArea.Pos() += getFrameArea().Pos();
    if (!rViewShell.GetViewOptions()->IsTextBoundariesFull( ))
        aPolygons = lcl_CreatePageAreaDelimiterPolygons(aArea, true /* header/footer*/);
    else
        aPolygons = lcl_CreateRectangleDelimiterPolygons(aArea);

    return aPolygons;
}

void SwHeadFootFrame::PaintSubsidiaryLines(const SwPageFrame*, const SwRect&) const
{
    if (!gProp.pSGlobalShell->IsHeaderFooterEdit())
        return;

    std::vector<basegfx::B2DPolygon> aPolygons = GetSubsidiaryLinesPolygons(*gProp.pSGlobalShell);
    if (aPolygons.empty())
        return;

    ProcessPrimitives(lcl_CreateDelimiterPrimitives(aPolygons));
}

void SwHeadFootFrame::AddSubsidiaryLinesBounds(const SwViewShell& rViewShell, RectangleVector& rRects) const
{
    lclAddSubsidiaryLinesBounds(GetSubsidiaryLinesPolygons(rViewShell), rRects);
}

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

    if ( (IsSctFrame() || IsFlyFrame()) &&
         !gProp.pSGlobalShell->GetViewOptions()->IsSectionBoundaries() )
        return;
    if ( IsTextFrame() &&
         !gProp.pSGlobalShell->GetViewOptions()->IsTextBoundaries() )
        return;

    //  #i3662# - use frame area for cells 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.Overlaps( 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 ) );

    // 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 )
    {
        // 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 );
            // in vertical layout set page/column break at right
            if ( aOriginal.Right() == nRight )
                ::lcl_RefreshLine( this, pPage, aRT, aRB, nSubColor, pUsedSubsLines );
        }
        // adjust control for drawing top and bottom lines
        if ( !bCell || bNewTableModel || bVert )
        {
            if ( aOriginal.Top() == aOut.Top() )
                // 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
    {
        // 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 );
            }
            // 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 );
            }
        }
        // adjust control for drawing top and bottom lines
        if ( !bCell || bNewTableModel || bVert )
        {
            if ( aOriginal.Top() == aOut.Top() )
            {
                // 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>(SwModule::get()->GetRedlineMarkPos()) != text::HoriOrientation::NONE;

    SwRect aRect( rRect );
    ::SwAlignRect( aRect, gProp.pSGlobalShell, gProp.pSGlobalShell->GetOut() );
    if ( !aRect.HasArea() )
        return;

    SwLayoutFrame::RefreshExtraData( aRect );

    if ( bLineInFly && GetSortedObjs() )
        for (SwAnchoredObject* pAnchoredObj : *GetSortedObjs())
        {
            if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
            {
                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>(SwModule::get()->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 = pAnchoredObj->DynCastFlyFrame() )
                {
                    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
 */
Color SwPageFrame::GetDrawBackgroundColor() const
{
    const SvxBrushItem* pBrushItem;
    std::optional<Color> xDummyColor;
    SwRect aDummyRect;
    drawinglayer::attribute::SdrAllFillAttributesHelperPtr aFillAttributes;

    if ( GetBackgroundBrush( aFillAttributes, pBrushItem, xDummyColor, aDummyRect, true, /*bConsiderTextBox=*/false) )
    {
        if(aFillAttributes && 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 aEmptyPgFont = []()
    {
        vcl::Font tmp;
        tmp.SetFontSize( Size( 0, 80 * 20 )); // == 80 pt
        tmp.SetWeight( WEIGHT_BOLD );
        tmp.SetStyleName(OUString());
        tmp.SetFamilyName(u"Noto Sans"_ustr);
        tmp.SetFamily( FAMILY_SWISS );
        tmp.SetTransparent( true );
        tmp.SetColor( COL_GRAY );
        return tmp;
    }();

    return aEmptyPgFont;
}

/**
 * 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->GetDrawBackgroundColor());
                const IDocumentDrawModelAccess& rIDDMA = pSh->getIDocumentDrawModelAccess();
                // --> OD #i76669#
                SwViewObjectContactRedirector aSwRedirector( *pSh );
                // <--

                pSh->Imp()->PaintLayer( rIDDMA.GetHellId(),
                                        *pPage, rRetouche, &aPageBackgrdColor,
                                        pPage->IsRightToLeft(),
                                        &aSwRedirector );
                pSh->Imp()->PaintLayer( rIDDMA.GetHeavenId(),
                                        *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 an 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.
 *
 * @param 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,
    std::optional<Color>& rxCol,
    SwRect &rOrigRect,
    bool bLowerMode,
    bool bConsiderTextBox ) const
{
    const SwFrame *pFrame = this;
    SwViewShell *pSh = getRootFrame()->GetCurrShell();
    const SwViewOption *pOpt = pSh->GetViewOptions();
    rpBrush = nullptr;
    rxCol.reset();
    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 =
                            std::make_shared<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();
            //     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 && (   SectionType::ToxHeader == pSection->GetType() ||
                                SectionType::ToxContent == pSection->GetType() ) &&
                (rBack.GetColor() == COL_TRANSPARENT) &&
                rBack.GetGraphicPos() == GPOS_NONE &&
                !pOpt->IsPagePreview() &&
                !pOpt->IsReadonly() &&
                // #114856# Form view
                !pOpt->IsFormView() &&
                pOpt->IsIndexShadings() &&
                !pOpt->IsPDFExport() &&
                pSh->GetOut()->GetOutDevType() != OUTDEV_PRINTER )
            {
                rxCol = pOpt->GetIndexShadingsColor();
            }
        }

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

        //     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().GetAlpha() == 255 || rBack.GetGraphicPos() != GPOS_NONE ||

            // done when direct color is forced
            rxCol ||

            // 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();
                ::SwAlignRect(rOrigRect, pSh, pSh->GetOut());
            }
            else
            {
                if (pFrame->IsPageFrame()
                    && pFrame->GetAttrSet()->GetItem<SfxBoolItem>(RES_BACKGROUND_FULL_SIZE)->GetValue())
                {
                    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() )
        {
            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*, const sal_uInt32 /*nMaximumQuadraticPixels*/, const std::optional<Size>& /*rTargetDPI*/ )
{
    return Graphic();
}

Graphic SwFlyFrameFormat::MakeGraphic( ImageMap* pMap, const sal_uInt32 /*nMaximumQuadraticPixels*/, const std::optional<Size>& /*rTargetDPI*/ )
{
    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 )
        {
            assert(!pNoteURL);
            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.AddWidth(2*gProp.nSPixelSzW );
        if ( rAttrs.CalcBottomLine() )
            aOut.AddHeight(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->GetOutDev();

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

        // determine page, fly frame is on
        const SwPageFrame* pFlyPage = pFly->FindPageFrame();
        const Color aPageBackgrdColor(pFlyPage->GetDrawBackgroundColor());
        const IDocumentDrawModelAccess& rIDDMA = pSh->getIDocumentDrawModelAccess();
        // --> OD #i76669#
        SwViewObjectContactRedirector aSwRedirector( *pSh );
        // <--
        pImp->PaintLayer( rIDDMA.GetHellId(),
                          *pFlyPage, aOut, &aPageBackgrdColor,
                          pFlyPage->IsRightToLeft(),
                          &aSwRedirector );
        gProp.pSLines->PaintLines( pDev, gProp );
        if ( pFly->IsFlyInContentFrame() )
            pFly->PaintSwFrame( *pDev, aOut );
        gProp.pSLines->PaintLines( pDev, gProp );
        pImp->PaintLayer( rIDDMA.GetHeavenId(),
                          *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 )
        {
            assert(pNoteURL);
            pNoteURL->FillImageMap(pMap, pFly->getFrameArea().Pos(), aMap);
            delete pNoteURL;
            pNoteURL = nullptr;
        }
        gProp.pSGlobalShell = pOldGlobal;
    }
    return aRet;
}

Graphic SwDrawFrameFormat::MakeGraphic( ImageMap*, const sal_uInt32 nMaximumQuadraticPixels, const std::optional<Size>& rTargetDPI )
{
    Graphic aRet;
    SwDrawModel* pMod = getIDocumentDrawModelAccess().GetDrawModel();
    if ( pMod )
    {
        SdrObject *pObj = FindSdrObject();
        SdrView aView( *pMod );
        SdrPageView *pPgView = aView.ShowSdrPage(aView.GetModel().GetPage(0));
        aView.MarkObj( pObj, pPgView );
        aRet = aView.GetMarkedObjBitmap(/*bNoVDevIfOneBmpMarked=*/false, nMaximumQuadraticPixels, rTargetDPI);
        aView.HideSdrPage();
    }
    return aRet;
}

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