/* -*- 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/canvastools.hxx>
#include <tools/lazydelete.hxx>
#include <sfx2/docfile.hxx>
#include <sfx2/printer.hxx>
#include <sfx2/progress.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 <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/viewobjectcontactredirector.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 isSubsidiaryLinesFlysEnabled()
{
    return !gProp.pSGlobalShell->GetViewOptions()->IsPagePreview() &&
           !gProp.pSGlobalShell->GetViewOptions()->IsReadonly() &&
           !gProp.pSGlobalShell->GetViewOptions()->IsFormView() &&
           gProp.pSGlobalShell->GetViewOptions()->IsObjectBoundaries();
}
//other subsidiary lines enabled?
static bool isSubsidiaryLinesEnabled()
{
    return !gProp.pSGlobalShell->GetViewOptions()->IsPagePreview() &&
           !gProp.pSGlobalShell->GetViewOptions()->IsReadonly() &&
           !gProp.pSGlobalShell->GetViewOptions()->IsFormView() &&
           !gProp.pSGlobalShell->GetViewOptions()->IsWhitespaceHidden() &&
           gProp.pSGlobalShell->GetViewOptions()->IsDocBoundaries();
}
//subsidiary lines for sections
static bool isSubsidiaryLinesForSectionsEnabled()
{
    return !gProp.pSGlobalShell->GetViewOptions()->IsPagePreview() &&
           !gProp.pSGlobalShell->GetViewOptions()->IsReadonly() &&
           !gProp.pSGlobalShell->GetViewOptions()->IsFormView() &&
           gProp.pSGlobalShell->GetViewOptions()->IsSectionBoundaries();
}
 
 
namespace {
 
bool isTableBoundariesEnabled()
{
    if (!gProp.pSGlobalShell->GetViewOptions()->IsTable())
        return false;
 
    if (gProp.pSGlobalShell->GetViewOptions()->IsPagePreview())
        return false;
 
    if (gProp.pSGlobalShell->GetViewOptions()->IsReadonly())
        return false;
 
    if (gProp.pSGlobalShell->GetViewOptions()->IsFormView())
        return false;
 
    return gProp.pSGlobalShell->GetViewOptions()->IsTableBoundaries();
}
 
}
 
/**
 * 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 );
 
    pOut->Push( 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;
    pOut->Pop();
 
}
 
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;
 
    pOut->Push( 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::Fly: pCol = &pOpt->GetObjectBoundariesColor(); break;
                case SubColFlags::Tab: pCol = &pOpt->GetTableBoundariesColor(); break;
                case SubColFlags::Sect: pCol = &pOpt->GetSectionBoundColor(); break;
            }
 
            if (pCol && pOut->GetFillColor() != *pCol)
                pOut->SetFillColor( *pCol );
            pOut->DrawRect( rLRect.SVRect() );
 
            rLRect.SetPainted();
        }
    }
 
    pOut->SetDrawMode( nOldDrawMode );
 
    pOut->Pop();
}
 
// Various functions that are use in this file.
 
/**
 * Function <SwAlignRect(..)> is also used outside this file
 *
 * Correction: adjust rectangle on pixel level in order to make sure,
 * that the border "leaves its original pixel", if it has to
 * No prior adjustments for odd relation between pixel and twip
 */
void SwAlignRect( SwRect &rRect, const SwViewShell *pSh, const vcl::RenderContext* pRenderContext )
{
    if( !rRect.HasArea() )
        return;
 
    // Make sure that view shell (parameter <pSh>) exists, if the output device
    // is taken from this view shell --> no output device, no alignment
    // Output device taken from view shell <pSh>, if <gProp.bSFlyMetafile> not set
    if ( !gProp.bSFlyMetafile && !pSh )
    {
        return;
    }
 
    const vcl::RenderContext *pOut = gProp.bSFlyMetafile ?
                        gProp.pSFlyMetafileOut.get() : pRenderContext;
 
    // Hold original rectangle in pixel
    const tools::Rectangle aOrgPxRect = pOut->LogicToPixel( rRect.SVRect() );
    // Determine pixel-center rectangle in twip
    const SwRect aPxCenterRect( pOut->PixelToLogic( aOrgPxRect ) );
 
    // Perform adjustments on pixel level.
    SwRect aAlignedPxRect( aOrgPxRect );
    if ( rRect.Top() > aPxCenterRect.Top() )
    {
        // 'leave pixel overlapping on top'
        aAlignedPxRect.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) ||
             /// Change internal order of condition
             ///    first check "!bHell", then "..->Lower()" and "..->IsNoTextFrame()"
             ///    have not to be performed, if frame is in "Hell"
             ( !bHell && pFly->Lower() && pFly->Lower()->IsNoTextFrame() &&
               (static_cast<SwNoTextFrame const*>(pFly->Lower())->IsTransparent() ||
                static_cast<SwNoTextFrame const*>(pFly->Lower())->HasAnimation() ||
                 pFly->GetFormat()->GetSurround().IsContour()
               )
             )
           )
            continue;
 
        // Own if-statements for transparent background/shadow of fly frames
        // in order to handle special conditions.
        if (pFly->IsBackgroundTransparent())
        {
            // Background <pFly> is transparent drawn. Thus normally, its region
            // have not to be subtracted from given region.
            // But, if method is called for a fly frame and
            // <pFly> is a direct lower of this fly frame and
            // <pFly> inherites its transparent background brush from its parent,
            // then <pFly> frame area have to be subtracted from given region.
            // NOTE: Because in Status Quo transparent backgrounds can only be
            //     assigned to fly frames, the handle of this special case
            //     avoids drawing of transparent areas more than once, if
            //     a fly frame inherites a transparent background from its
            //     parent fly frame.
            if (pFrame->IsFlyFrame() &&
                (pFly->GetAnchorFrame()->FindFlyFrame() == pFrame) &&
                pFly->GetFormat()->IsBackgroundBrushInherited()
               )
            {
                SwRect aRect;
                SwBorderAttrAccess aAccess( SwFrame::GetCache(), static_cast<SwFrame const *>(pFly) );
                const SwBorderAttrs &rAttrs = *aAccess.Get();
                ::lcl_CalcBorderRect( aRect, pFly, rAttrs, true, rProperties );
                rRegion -= aRect;
                rClipState.subtractRange(lcl_ShrinkFly(aRect));
                continue;
            }
            else
            {
                continue;
            }
        }
 
        if (bHell && pFly->GetAnchorFrame()->IsInFly())
        {
            //So the border won't get dismantled by the background of the other
            //Fly.
            SwRect aRect;
            SwBorderAttrAccess aAccess( SwFrame::GetCache(), static_cast<SwFrame const *>(pFly) );
            const SwBorderAttrs &rAttrs = *aAccess.Get();
            ::lcl_CalcBorderRect( aRect, pFly, rAttrs, true, rProperties );
            rRegion -= aRect;
            rClipState.subtractRange(lcl_ShrinkFly(aRect));
        }
        else
        {
            SwRect aRect( pFly->getFramePrintArea() );
            aRect += pFly->getFrameArea().Pos();
            rRegion -= aRect;
            rClipState.subtractRange(lcl_ShrinkFly(aRect));
        }
    }
    if (gProp.pSRetoucheFly == gProp.pSRetoucheFly2)
        gProp.pSRetoucheFly = nullptr;
}
 
static void lcl_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
            rOutDev.Push( 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 ) ) );
            }
            // reset clipping at output device
            rOutDev.Pop();
            // set <bDraw> and <bRetouche> to false, indicating that background
            // graphic and background are already drawn.
            bDraw = bRetouche = false;
        }
        break;
 
    case GPOS_NONE:
        bDraw = false;
        break;
 
    default: OSL_ENSURE( false, "new Graphic position?" );
    }
 
    /// init variable <bGrfBackgrdAlreadDrawn> to indicate, if background of
    /// graphic is already drawn or not.
    bool bGrfBackgrdAlreadyDrawn = false;
    if ( bRetouche )
    {
        rOutDev.Push( 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 )
                    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() );
                }
            }
        }
        rOutDev.Pop();
    }
 
    if( bDraw && aGrf.Overlaps( rOut ) )
        lcl_DrawGraphic( *pBrush, rOutDev, rSh, aGrf, rOut, bGrfNum, gProp,
                         bGrfBackgrdAlreadyDrawn );
 
    if( bReplaceGrfNum )
    {
        const BitmapEx& rBmp = rSh.GetReplacementBitmap(false);
        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 (isTableBoundariesEnabled() && gProp.pSGlobalShell->GetWin())
                    aStyles[ 0 ].Set( rCol, rCol, rCol, false, 1, 0, 0 );
                else
                    aStyles[0].SetType(SvxBorderLineStyle::NONE);
            }
            else
                pTmpColor = pHCColor;
 
            // The (twip) positions will be adjusted to meet these requirements:
            // 1. The y coordinates are located in the middle of the pixel grid
            // 2. The x coordinated are located at the beginning of the pixel grid
            // This is done, because the horizontal lines are painted "at
            // beginning", whereas the vertical lines are painted "centered".
            // By making the line sizes a multiple of one pixel size, we can
            // assure that all lines having the same twip size have the same
            // pixel size, independent of their position on the screen.
            Point aPaintStart = rDev.PixelToLogic( rDev.LogicToPixel(aStart) );
            Point aPaintEnd = rDev.PixelToLogic( rDev.LogicToPixel(aEnd) );
 
            if (gProp.pSGlobalShell->GetWin())
            {
                // The table borders do not use SwAlignRect, but all the other frames do.
                // Therefore we tweak the outer borders a bit to achieve that the outer
                // borders match the subsidiary lines of the upper:
                if (aStart.X() == aUpper.Left())
                    aPaintStart.setX( aUpperAligned.Left() );
                else if (aStart.X() == aUpper.Right_())
                    aPaintStart.setX( aUpperAligned.Right_() );
                if (aStart.Y() == aUpper.Top())
                    aPaintStart.setY( aUpperAligned.Top() );
                else if (aStart.Y() == aUpper.Bottom_())
                    aPaintStart.setY( aUpperAligned.Bottom_() );
 
                if (aEnd.X() == aUpper.Left())
                    aPaintEnd.setX( aUpperAligned.Left() );
                else if (aEnd.X() == aUpper.Right_())
                    aPaintEnd.setX( aUpperAligned.Right_() );
                if (aEnd.Y() == aUpper.Top())
                    aPaintEnd.setY( aUpperAligned.Top() );
                else if (aEnd.Y() == aUpper.Bottom_())
                    aPaintEnd.setY( aUpperAligned.Bottom_() );
            }
 
            if(aStyles[0].IsUsed())
            {
                if (bHori)
                {
                    const basegfx::B2DPoint aOrigin(aPaintStart.X(), aPaintStart.Y());
                    const basegfx::B2DVector aX(basegfx::B2DPoint(aPaintEnd.X(), aPaintEnd.Y()) - aOrigin);
 
                    if(!aX.equalZero())
                    {
                        const basegfx::B2DVector aY(basegfx::getNormalizedPerpendicular(aX));
                        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 );
}
 
/**
 * FUNCTIONS USED FOR COLLAPSING TABLE BORDER LINES END
 * --> OD #i76669#
 */
namespace
{
    class SwViewObjectContactRedirector : public sdr::contact::ViewObjectContactRedirector
    {
        private:
            const SwViewShell& mrViewShell;
 
        public:
            explicit SwViewObjectContactRedirector( const SwViewShell& rSh )
                : mrViewShell( rSh )
            {};
 
            virtual void createRedirectedPrimitive2DSequence(
                                    const sdr::contact::ViewObjectContact& rOriginal,
                                    const sdr::contact::DisplayInfo& rDisplayInfo,
                                    drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) override
            {
                bool bPaint( true );
 
                SdrObject* pObj = rOriginal.GetViewContact().TryToGetSdrObject();
                if ( pObj )
                {
                    bPaint = SwFlyFrame::IsPaint( pObj, &mrViewShell );
                }
 
                if ( !bPaint )
                {
                    return;
                }
 
                sdr::contact::ViewObjectContactRedirector::createRedirectedPrimitive2DSequence(
                                                        rOriginal, rDisplayInfo, rVisitor );
            }
    };
 
} // end of anonymous namespace
// <--
 
/**
 * Paint once for every visible page which is touched by Rect
 *
 * 1. Paint borders and backgrounds
 * 2. Paint the draw layer (frames and drawing objects) that is
 *    below the document (hell)
 * 3. Paint the document content (text)
 * 4. Paint the draw layer that is above the document
|*/
void SwRootFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const& rRect, 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(),
                                            /*pPrintData*/nullptr,
                                            *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(),
                                            /*pPrintData*/nullptr,
                                            *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(),
                                            /*pPrintData*/nullptr,
                                            *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->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;
};
 
bool SwFlyFrame::IsPaint( SdrObject *pObj, const SwViewShell *pSh )
{
    SdrObjUserCall *pUserCall = GetUserCall(pObj);
 
    if ( nullptr == pUserCall )
        return true;
 
    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 = pSh->GetWin() && !pSh->IsPreview();
 
    if ( bPaint )
    {
        //The paint may be prevented by the superior Flys.
        SwFrame *pAnch = nullptr;
        if ( dynamic_cast< const SwFlyDrawObj *>( pObj ) !=  nullptr ) // i#117962#
        {
            bPaint = false;
        }
        if ( 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 ( pSh->GetOut() == pSh->getIDocumentDeviceAccess().getPrinter( false ))
                {
                    //HACK: we have to omit some of the objects for printing,
                    //otherwise they would be printed twice.
                    //The objects should get printed if the TableHack is active
                    //right now. Afterwards they must not be printed if the
                    //page over which they float position wise gets printed.
                    const SwPageFrame *pPage = pAnch->FindPageFrame();
                    if ( !pPage->getFrameArea().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(),
                                            pSh );
            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.
 
            rRenderContext.Push( 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 )
                {
                    rRenderContext.Push();
                    // #i80822#
                    // apply clip region under the same conditions, which are
                    // used in <SwNoTextFrame::PaintSwFrame(..)> to set the clip region
                    // for painting the graphic/OLE. Thus, the clip region is
                    // also applied for the PDF export.
                    SwViewShell *pSh = getRootFrame()->GetCurrShell();
 
                    if ( !rRenderContext.GetConnectMetaFile() || !pSh || !pSh->GetWin() )
                    {
                        rRenderContext.SetClipRegion(vcl::Region(aPoly));
                    }
 
                    for ( size_t i = 0; i < aRegion.size(); ++i )
                    {
                        PaintSwFrameBackground( aRegion[i], pPage, rAttrs, false, true );
                    }
 
                    rRenderContext.Pop();
                }
                else
                {
                    for ( size_t i = 0; i < aRegion.size(); ++i )
                    {
                        PaintSwFrameBackground( aRegion[i], pPage, rAttrs, false, true );
                    }
                }
            }
 
            // paint border before painting background
            PaintSwFrameShadowAndBorder(rRect, pPage, rAttrs);
 
            rRenderContext.Pop();
        }
    }
 
    // 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;
 
    vcl::RenderContext* pRenderContext = pWrtSh->GetOut();
    if (!pRenderContext)
        return;
 
    StylesHighlighterColorMap& rParaStylesColorMap
            = pWrtSh->GetView().GetStylesHighlighterParaColorMap();
 
    if (rParaStylesColorMap.empty())
        return;
 
    //  draw styles highlighter
    OUString sStyleName = GetTextNodeFirst()->GetTextColl()->GetName();
    if (rParaStylesColorMap.contains(sStyleName))
    {
        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);
 
        pRenderContext->Push(vcl::PushFlags::ALL);
 
        pRenderContext->SetFillColor(rParaStylesColorMap[sStyleName].first);
        pRenderContext->SetLineColor(rParaStylesColorMap[sStyleName].first);
 
        pRenderContext->DrawRect(aRect);
 
        // draw hatch pattern if paragraph has direct formatting
        if (SwDoc::HasParagraphDirectFormatting(SwPosition(*GetTextNodeForParaProps())))
        {
            Color aHatchColor(rParaStylesColorMap[sStyleName].first);
            // 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(rParaStylesColorMap[sStyleName].first);
        pRenderContext->DrawText(aRect, OUString::number(rParaStylesColorMap[sStyleName].second),
                                 DrawTextFlags::Center | DrawTextFlags::VCenter);
 
        pRenderContext->Pop();
    }
}
 
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::Ftn|SwFrameType::Column|SwFrameType::Root))
        return;
 
    if (IsCellFrame() && !gProp.pSGlobalShell->GetViewOptions()->IsTable())
        return;
 
    // #i29550#
    if ( IsTabFrame() || IsCellFrame() || IsRowFrame() )
    {
        const SwTabFrame* pTabFrame = FindTabFrame();
        if ( pTabFrame->IsCollapsingBorders() )
            return;
 
        if ( pTabFrame->GetTable()->IsNewModel() && ( !IsCellFrame() || IsCoveredCell() ) )
            return;
    }
 
    const bool bLine = rAttrs.IsLine();
    const bool bShadow = rAttrs.GetShadow().GetLocation() != SvxShadowLocation::NONE;
 
    // - flag to control,
    //-hack has to be used.
    const bool bb4779636HackActive = true;
 
    const SwFrame* pCellFrameForBottomBorderAttrs = nullptr;
    const SwFrame* pCellFrameForTopBorderAttrs = nullptr;
    bool         bFoundCellForTopOrBorderAttrs = false;
    if ( bb4779636HackActive && IsCellFrame() )
    {
        pCellFrameForBottomBorderAttrs = lcl_GetCellFrameForBorderAttrs( this, rAttrs, false );
        if ( pCellFrameForBottomBorderAttrs != this )
            bFoundCellForTopOrBorderAttrs = true;
        pCellFrameForTopBorderAttrs = lcl_GetCellFrameForBorderAttrs( this, rAttrs, true );
        if ( pCellFrameForTopBorderAttrs != this )
            bFoundCellForTopOrBorderAttrs = true;
    }
 
    // - add condition <bFoundCellForTopOrBorderAttrs>
    //-hack
    if ( !(bLine || bShadow || bFoundCellForTopOrBorderAttrs) )
        return;
 
    //If the rectangle is completely inside the PrtArea, no border needs to
    //be painted.
    //For the PrtArea the aligned value needs to be used, otherwise it could
    //happen, that some parts won't be processed.
    SwRect aRect( getFramePrintArea() );
    aRect += getFrameArea().Pos();
    ::SwAlignRect( aRect, gProp.pSGlobalShell, gProp.pSGlobalShell->GetOut() );
    // 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 = GRID_LINES_CHARS == pGrid->GetGridType();
    tools::Long nGrid = pGrid->GetBaseHeight();
    const SwDoc* pDoc = GetFormat()->GetDoc();
    tools::Long nGridWidth = GetGridWidth(*pGrid, *pDoc);
    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->DrawBitmapEx.
static void lcl_paintBitmapExToRect(vcl::RenderContext *pOut, const Point& aPoint, const Size& aSize, const BitmapEx& rBitmapEx, 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->DrawBitmapEx(pOut->PixelToLogic(aRender.TopLeft()),
                           pOut->PixelToLogic(aRender.GetSize()),
                           Point(0, 0), aRender.GetSize(),
                           rBitmapEx);
    }
 
}
 
/**
 * Paint page border and shadow
 *
 * for #i9719#
 * implement paint of page border and shadow
*/
/*static*/ void SwPageFrame::PaintBorderAndShadow( const SwRect& _rPageRect,
                                                 const SwViewShell*    _pViewShell,
                                                 bool bPaintLeftShadow,
                                                 bool bPaintRightShadow,
                                                 bool bRightSidebar )
{
    // No shadow in prefs
    if (!_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< BitmapEx > aPageTopRightShadowObj {};
    static tools::DeleteOnDeinit< BitmapEx > aPageBottomRightShadowObj {};
    static tools::DeleteOnDeinit< BitmapEx > aPageBottomLeftShadowObj  {};
    static tools::DeleteOnDeinit< BitmapEx > aPageBottomShadowBaseObj  {};
    static tools::DeleteOnDeinit< BitmapEx > aPageRightShadowBaseObj  {};
    static tools::DeleteOnDeinit< BitmapEx > aPageTopShadowBaseObj  {};
    static tools::DeleteOnDeinit< BitmapEx > aPageTopLeftShadowObj  {};
    static tools::DeleteOnDeinit< BitmapEx > aPageLeftShadowBaseObj  {};
    BitmapEx& aPageTopRightShadow = *aPageTopRightShadowObj.get();
    BitmapEx& aPageBottomRightShadow = *aPageBottomRightShadowObj.get();
    BitmapEx& aPageBottomLeftShadow = *aPageBottomLeftShadowObj.get();
    BitmapEx& aPageBottomShadow = *aPageBottomShadowBaseObj.get();
    BitmapEx& aPageRightShadow = *aPageRightShadowBaseObj.get();
    BitmapEx& aPageTopShadow = *aPageTopShadowBaseObj.get();
    BitmapEx& aPageTopLeftShadow = *aPageTopLeftShadowObj.get();
    BitmapEx& aPageLeftShadow = *aPageLeftShadowBaseObj.get();
    static Color aShadowColor( COL_AUTO );
 
    SwRect aAlignedPageRect( _rPageRect );
    ::SwAlignRect( aAlignedPageRect, _pViewShell, _pViewShell->GetOut() );
    SwRect aPagePxRect(_pViewShell->GetOut()->LogicToPixel( aAlignedPageRect.SVRect() ));
 
    if (aShadowColor != _pViewShell->GetViewOptions()->GetShadowColor())
    {
        aShadowColor = _pViewShell->GetViewOptions()->GetShadowColor();
 
        AlphaMask aMask( shadowMask.getBottomRight().GetBitmap() );
        Bitmap aFilledSquare(aMask.GetSizePixel(), vcl::PixelFormat::N24_BPP);
        aFilledSquare.Erase( aShadowColor );
        aPageBottomRightShadow = BitmapEx( aFilledSquare, aMask );
 
        aMask = AlphaMask( shadowMask.getBottomLeft().GetBitmap() );
        aFilledSquare = Bitmap(aMask.GetSizePixel(), vcl::PixelFormat::N24_BPP);
        aFilledSquare.Erase( aShadowColor );
        aPageBottomLeftShadow = BitmapEx( aFilledSquare, aMask );
 
        aMask = AlphaMask( shadowMask.getBottom().GetBitmap() );
        aFilledSquare = Bitmap(aMask.GetSizePixel(), vcl::PixelFormat::N24_BPP);
        aFilledSquare.Erase( aShadowColor );
        aPageBottomShadow = BitmapEx( aFilledSquare, aMask );
 
        aMask = AlphaMask( shadowMask.getTop().GetBitmap() );
        aFilledSquare = Bitmap(aMask.GetSizePixel(), vcl::PixelFormat::N24_BPP);
        aFilledSquare.Erase( aShadowColor );
        aPageTopShadow = BitmapEx( aFilledSquare, aMask );
 
        aMask = AlphaMask( shadowMask.getTopRight().GetBitmap() );
        aFilledSquare = Bitmap(aMask.GetSizePixel(), vcl::PixelFormat::N24_BPP);
        aFilledSquare.Erase( aShadowColor );
        aPageTopRightShadow = BitmapEx( aFilledSquare, aMask );
 
        aMask = AlphaMask( shadowMask.getRight().GetBitmap() );
        aFilledSquare = Bitmap(aMask.GetSizePixel(), vcl::PixelFormat::N24_BPP);
        aFilledSquare.Erase( aShadowColor );
        aPageRightShadow = BitmapEx( aFilledSquare, aMask );
 
        aMask = AlphaMask( shadowMask.getTopLeft().GetBitmap() );
        aFilledSquare = Bitmap(aMask.GetSizePixel(), vcl::PixelFormat::N24_BPP);
        aFilledSquare.Erase( aShadowColor );
        aPageTopLeftShadow = BitmapEx( aFilledSquare, aMask );
 
        aMask = AlphaMask( shadowMask.getLeft().GetBitmap() );
        aFilledSquare = Bitmap(aMask.GetSizePixel(), vcl::PixelFormat::N24_BPP);
        aFilledSquare.Erase( aShadowColor );
        aPageLeftShadow = BitmapEx( aFilledSquare, aMask );
    }
 
    SwRect aPaintRect;
    OutputDevice *pOut = _pViewShell->GetOut();
 
    SwPageFrame::GetHorizontalShadowRect( _rPageRect, _pViewShell, pOut, aPaintRect, bPaintLeftShadow, bPaintRightShadow, bRightSidebar );
 
    // Right shadow & corners
    if ( bPaintRightShadow )
    {
        pOut->DrawBitmapEx( pOut->PixelToLogic( Point( aPaintRect.Right(), aPagePxRect.Bottom() + 1 - (aPageBottomRightShadow.GetSizePixel().Height() - snShadowPxWidth) ) ),
            aPageBottomRightShadow );
        pOut->DrawBitmapEx( 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->DrawBitmapEx( pOut->PixelToLogic( Point( lLeft,
            aPagePxRect.Bottom() + 1 + snShadowPxWidth - aPageBottomLeftShadow.GetSizePixel().Height() ) ), aPageBottomLeftShadow );
        pOut->DrawBitmapEx( 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()->GetObjectBoundariesColor());
        _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()->GetObjectBoundariesColor());
        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 );
 
    pOut->Push( 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);
    }
 
    pOut->Pop();
}
 
static bool lcl_compareFillAttributes(const drawinglayer::attribute::SdrAllFillAttributesHelperPtr& pA, const drawinglayer::attribute::SdrAllFillAttributesHelperPtr& pB)
{
    if (pA == pB)
        return true;
    if (!pA || !pB)
        return false;
    return pA->getFillAttribute() == pB->getFillAttribute();
}
 
/// 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() || isTableBoundariesEnabled()
        || isSubsidiaryLinesForSectionsEnabled() || isSubsidiaryLinesFlysEnabled()) )
        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
{
    const bool bSubsOpt = isSubsidiaryLinesEnabled()
                          || (IsSctFrame() && isSubsidiaryLinesForSectionsEnabled())
                          || (IsFlyFrame() && isSubsidiaryLinesFlysEnabled());
    if (bSubsOpt)
        PaintSubsidiaryLines( pPage, rRect );
 
    const SwFrame *pLow = Lower();
    if( !pLow )
        return;
    SwShortCut aShortCut( *pLow, rRect );
    while( pLow && !aShortCut.Stop( pLow->getFrameArea() ) )
    {
        if ( pLow->getFrameArea().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 ) )
                            {
                                if ( !pFly->Lower() || !pFly->Lower()->IsNoTextFrame() ||
                                     !static_cast<const SwNoTextFrame*>(pFly->Lower())->HasAnimation())
                                    pFly->RefreshLaySubsidiary( pPage, rRect );
                            }
                        }
                }
            }
        }
        pLow = pLow->GetNext();
    }
}
 
/**
 * Subsidiary lines to paint the PrtAreas
 * Only the LayoutFrames which directly contain Content
 * Paints the desired line and pays attention to not overpaint any flys
 */
static void lcl_RefreshLine( const SwLayoutFrame *pLay,
                                  const SwPageFrame *pPage,
                                  const Point &rP1,
                                  const Point &rP2,
                                  const SubColFlags nSubColor,
                                  SwLineRects* pSubsLines )
{
    //In which direction do we loop? Can only be horizontal or vertical.
    OSL_ENSURE( ((rP1.X() == rP2.X()) || (rP1.Y() == rP2.Y())),
            "Sloped subsidiary lines are not allowed." );
 
    const bool bHori = rP1.Y() == rP2.Y();
 
    // use pointers to member function in order to unify flow
    typedef 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()->IsDocBoundaries())
        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()->IsDocBoundaries())
        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 (!bCell && IsFlyFrame())
    {
        if (!gProp.pSGlobalShell->GetViewOptions()->IsObjectBoundaries())
            return;
 
        // if the frame is wrap none or wrap through, then text boundary lines have no meaning
        // (unless the frame itself contains text)
        const text::WrapTextMode aSurround = GetFormat()->GetSurround().GetSurround();
        if (GetFormat()->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR
            && (!Lower() || !Lower()->IsTextFrame())
            && (aSurround == text::WrapTextMode::WrapTextMode_THROUGH
                || aSurround == text::WrapTextMode::WrapTextMode_NONE))
        {
            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>(SW_MOD()->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>(SW_MOD()->GetRedlineMarkPos())!=text::HoriOrientation::NONE;
 
    const SwContentFrame *pCnt = ContainsContent();
    while ( pCnt && IsAnLower( pCnt ) )
    {
        if ( pCnt->IsTextFrame() && ( bRedLine ||
             ( !pCnt->IsInTab() &&
               ((bLineInBody && pCnt->IsInDocBody()) ||
               (bLineInFly  && pCnt->IsInFly())) ) ) &&
             pCnt->getFrameArea().Top() <= rRect.Bottom() &&
             pCnt->getFrameArea().Bottom() >= rRect.Top() )
        {
            static_cast<const SwTextFrame*>(pCnt)->PaintExtraData( rRect );
        }
        if ( bLineInFly && pCnt->GetDrawObjs() )
            for (SwAnchoredObject* pAnchoredObj : *pCnt->GetDrawObjs())
            {
                if ( auto pFly = 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(), nullptr,
                                        *pPage, rRetouche, &aPageBackgrdColor,
                                        pPage->IsRightToLeft(),
                                        &aSwRedirector );
                pSh->Imp()->PaintLayer( rIDDMA.GetHeavenId(), nullptr,
                                        *pPage, rRetouche, &aPageBackgrdColor,
                                        pPage->IsRightToLeft(),
                                        &aSwRedirector );
            }
 
            SetRetouche();
 
            //Because we leave all paint areas, we need to refresh the
            //subsidiary lines.
            pPage->RefreshSubsidiary( rRetouche );
        }
    }
    if ( SwViewShell::IsLstEndAction() )
        ResetRetouche();
}
 
/**
 * Determine the background brush for the frame:
 * the background brush is taken from it-self or from its parent (anchor/upper).
 * Normally, the background brush is taken, which has no transparent color or
 * which has a background graphic. But there are some special cases:
 * (1) No background brush is taken from a page frame, if view option "IsPageBack"
 *     isn't set.
 * (2) Background brush from 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(), nullptr,
                          *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(), nullptr,
                          *pFlyPage, aOut, &aPageBackgrdColor,
                          pFlyPage->IsRightToLeft(),
                          &aSwRedirector );
        gProp.pSLines->PaintLines( pDev, gProp );
        gProp.pSLines.reset();
        gProp.pSFlyOnlyDraw = nullptr;
 
        gProp.pSFlyMetafileOut = nullptr;
        gProp.bSFlyMetafile = false;
        ::SetOutDevAndWin( pSh, pOld, pWin, nZoom );
 
        // #i92711# end Pre/PostPaint encapsulation when pOut is back and content is painted
        pSh->DLPostPaint2(true);
 
        aMet.Stop();
        aMet.Move( -pFly->getFrameArea().Left(), -pFly->getFrameArea().Top() );
        aRet = Graphic( aMet );
 
        if( bNoteURL )
        {
            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.GetMarkedObjBitmapEx(/*bNoVDevIfOneBmpMarked=*/false, nMaximumQuadraticPixels, rTargetDPI);
        aView.HideSdrPage();
    }
    return aRet;
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V530 The return value of function 'Intersection' is required to be utilized.

V530 The return value of function 'Intersection' is required to be utilized.

V530 The return value of function 'MirrorSelf' is required to be utilized.

V530 The return value of function 'MirrorSelf' is required to be utilized.

V530 The return value of function 'MirrorSelf' is required to be utilized.

V530 The return value of function 'Intersection' is required to be utilized.

V530 The return value of function 'Intersection_' is required to be utilized.

V530 The return value of function 'Intersection_' is required to be utilized.

V530 The return value of function 'Intersection_' is required to be utilized.

V530 The return value of function 'Intersection_' is required to be utilized.

V530 The return value of function 'Intersection_' is required to be utilized.

V530 The return value of function 'Intersection_' is required to be utilized.

V530 The return value of function 'Intersection' is required to be utilized.

V530 The return value of function 'Intersection_' is required to be utilized.

V530 The return value of function 'Intersection_' is required to be utilized.

V530 The return value of function 'setLength' is required to be utilized.

V530 The return value of function 'setLength' is required to be utilized.

V530 The return value of function 'setLength' is required to be utilized.

V530 The return value of function 'setLength' is required to be utilized.

V530 The return value of function 'MirrorSelf' is required to be utilized.

V530 The return value of function 'MirrorSelf' is required to be utilized.

V530 The return value of function 'Intersection' is required to be utilized.

V530 The return value of function 'Intersection_' is required to be utilized.

V530 The return value of function 'Intersection' is required to be utilized.

V530 The return value of function 'Intersection_' is required to be utilized.

V530 The return value of function 'Intersection_' is required to be utilized.

V530 The return value of function 'Intersection' is required to be utilized.

V595 The 'pBrush' pointer was utilized before it was verified against nullptr. Check lines: 2016, 2031.

V1004 The 'pObj' pointer was used unsafely after it was verified against nullptr. Check lines: 7201, 7214.

V1048 The 'bLowMode' variable was assigned the same value.