Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/LibreOffice/sw/source/core/layout/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 311 kB image not shown  

Quelle  paintfrm.cxx   Sprache: C

 
/* -*- 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 <sfx2/StylePreviewRenderer.hxx>
#include <editeng/brushitem.hxx>
#include <editeng/prntitem.hxx>
#include <editeng/boxitem.hxx>
#include <editeng/shaditem.hxx>
#include <svx/ctredlin.hxx>
#include <svx/framelink.hxx>
#include <svx/svdouno.hxx>
#include <drawdoc.hxx>
#include <tgrditem.hxx>
#include <calbck.hxx>
#include <fmtsrnd.hxx>
#include <fmtclds.hxx>
#include <fmturl.hxx>
#include <strings.hrc>
#include <swmodule.hxx>
#include <rootfrm.hxx>
#include <pagefrm.hxx>
#include <section.hxx>
#include <sectfrm.hxx>
#include <viewimp.hxx>
#include <dflyobj.hxx>
#include <flyfrm.hxx>
#include <frmatr.hxx>
#include <frmtool.hxx>
#include <viewopt.hxx>
#include <dview.hxx>
#include <dcontact.hxx>
#include <txtfrm.hxx>
#include <ftnfrm.hxx>
#include <tabfrm.hxx>
#include <rowfrm.hxx>
#include <cellfrm.hxx>
#include <notxtfrm.hxx>
#include <layact.hxx>
#include <pagedesc.hxx>
#include <ptqueue.hxx>
#include <noteurl.hxx>
#include "virtoutp.hxx"
#include <lineinfo.hxx>
#include <dbg_lay.hxx>
#include <docsh.hxx>
#include <svx/svdogrp.hxx>
#include <sortedobjs.hxx>
#include <EnhancedPDFExportHelper.hxx>
#include <bodyfrm.hxx>
#include <hffrm.hxx>
#include <colfrm.hxx>
#include <sw_primitivetypes2d.hxx>
#include <swfont.hxx>

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

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

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

#include <svtools/borderhelper.hxx>

#include <bitmaps.hlst>
#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
#include <drawinglayer/primitive2d/PolyPolygonStrokePrimitive2D.hxx>
#include <drawinglayer/primitive2d/discreteshadowprimitive2d.hxx>
#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
#include <drawinglayer/primitive2d/textprimitive2d.hxx>
#include <drawinglayer/primitive2d/textlayoutdevice.hxx>
#include <drawinglayer/processor2d/baseprocessor2d.hxx>
#include <drawinglayer/processor2d/processor2dtools.hxx>
#include <svx/unoapi.hxx>
#include <svx/svdpagv.hxx>
#include <svx/xfillit0.hxx>
#include <basegfx/matrix/b2dhommatrixtools.hxx>
#include <basegfx/color/bcolortools.hxx>
#include <basegfx/utils/b2dclipstate.hxx>
#include <sal/log.hxx>

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

#include <vcl/BitmapTools.hxx>
#include <comphelper/configuration.hxx>
#include <comphelper/lok.hxx>
#include <svtools/optionsdrawinglayer.hxx>
#include <vcl/GraphicLoader.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>

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

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

namespace {

struct SwPaintProperties;

//Class declaration; here because they are only used in this file
enum class SubColFlags {
    Page     = 0x01,    //Helplines of the page
    Tab      = 0x08,   //Helplines inside tables
    Fly      = 0x10,    //Helplines inside fly frames
    Sect     = 0x20,    //Helplines inside sections
};

}

namespace o3tl {
    template<> struct typed_flags<SubColFlags> : is_typed_flags<SubColFlags, 0x39> {};
}

namespace {

// Classes collecting the border lines and help lines
class SwLineRect : public SwRect
{
    Color m_aColor;
    SvxBorderLineStyle m_nStyle;
    const SwTabFrame* m_pTabFrame;
    SubColFlags m_nSubColor; //colorize subsidiary lines
    bool m_bPainted; //already painted?
    sal_uInt8 m_nLock; //To distinguish the line and the hell layer.
public:
    SwLineRect( const SwRect &rRect, const Color *pCol, const SvxBorderLineStyle nStyle,
                const SwTabFrame *pT , const SubColFlags nSCol );

    const Color& GetColor() const { return m_aColor; }
    SvxBorderLineStyle GetStyle() const { return m_nStyle; }
    const SwTabFrame* GetTab() const { return m_pTabFrame; }
    void SetPainted() { m_bPainted = true; }
    void Lock(bool bLock)
    {
        if (bLock)
            ++m_nLock;
        else if (m_nLock)
            --m_nLock;
    }
    bool IsPainted() const { return m_bPainted; }
    bool IsLocked() const { return m_nLock != 0; }
    SubColFlags GetSubColor() const { return m_nSubColor; }

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

}

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

namespace {

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

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

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

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

}

// Default zoom factor
const double aEdgeScale = 0.5;

//To optimize the expensive RetouchColor determination
Color aGlobalRetoucheColor;

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

namespace {

/**
 * Container for static properties
 */

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

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

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

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

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

    Color               aSGlobalRetoucheColor;

    // Current zoom factor
    double              aSScaleX;
    double              aSScaleY;

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

};

}

static SwPaintProperties gProp;

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

/**
 * Set borders alignment statics
 * Adjustment for 'small' twip-to-pixel relations:
 * For 'small' twip-to-pixel relations (less than 2:1)
 * values of <gProp.nSHalfPixelSzW> and <gProp.nSHalfPixelSzH> are set to ZERO
 */

void SwCalcPixStatics( vcl::RenderContext const *pOut )
{
    // determine 'small' twip-to-pixel relation
    bool bSmallTwipToPxRelW = false;
    bool bSmallTwipToPxRelH = false;
    {
        Size aCheckTwipToPxRelSz( pOut->PixelToLogic( Size( 100, 100 )) );
        if ( (aCheckTwipToPxRelSz.Width()/100.0) < 2.0 )
        {
            bSmallTwipToPxRelW = true;
        }
        if ( (aCheckTwipToPxRelSz.Height()/100.0) < 2.0 )
        {
            bSmallTwipToPxRelH = true;
        }
    }

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

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

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

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

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

namespace {

/**
 * To be able to save the statics so the paint is more or less reentrant
 */

class SwSavePaintStatics : public SwPaintProperties
{
public:
    SwSavePaintStatics();
    ~SwSavePaintStatics();
};

}

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

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

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

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

SwLineRect::SwLineRect(const SwRect& rRect, const Color* pCol, const SvxBorderLineStyle nStyl,
                       const SwTabFrame* pT, const SubColFlags nSCol)
    : SwRect(rRect)
    , m_nStyle(nStyl)
    , m_pTabFrame(pT)
    , m_nSubColor(nSCol)
    , m_bPainted(false)
    , m_nLock(0)
{
    if ( pCol != nullptr )
        m_aColor = *pCol;
}

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

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

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

    static const tools::Long nAdd = 20;

    std::vector<SwLineRect*> aCheck;

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

        aCheck.clear();

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

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

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

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

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

        bool bRemove = false;

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

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

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

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

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

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

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

        const bool bVerticalSubs = aSubsLineRect.Height() > aSubsLineRect.Width();
        SwRect aSubsRect( aSubsLineRect );
        if ( bVerticalSubs )
        {
            aSubsRect.AddLeft  ( - (properties.nSPixelSzW+properties.nSHalfPixelSzW) );
            aSubsRect.AddRight ( properties.nSPixelSzW+properties.nSHalfPixelSzW );
        }
        else
        {
            aSubsRect.AddTop   ( - (properties.nSPixelSzH+properties.nSHalfPixelSzH) );
            aSubsRect.AddBottom( properties.nSPixelSzH+properties.nSHalfPixelSzH );
        }
        for (const_iterator itK = rRects.m_aLineRects.begin(); itK != rRects.m_aLineRects.end();
             ++itK)
        {
            const SwLineRect &rLine = *itK;

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

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

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

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

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

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

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

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

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

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

    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::Tab: pCol = &pOpt->GetTableBoundariesColor(); break;
                case SubColFlags::Fly:
                case SubColFlags::Sect: pCol = &pOpt->GetSectionBoundColor(); break;
            }

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

            rLRect.SetPainted();
        }
    }

    pOut->SetDrawMode( nOldDrawMode );

    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 &&nbsp;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 &&nbsp;properties )
{
    if ( nHeight )
    {
        const tools::Long nH = nHeight % properties.nSPixelSzH;

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

/**
 * Calculate PrtArea plus surrounding plus shadow
 */

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

        SwRectFnSet fnRect(pFrame);

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

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

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

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

        if ( bShadow && rAttrs.GetShadow().GetLocation() != SvxShadowLocation::NONE )
        {
            const SvxShadowItem &rShadow = rAttrs.GetShadow();
            if ( bTop )
                fnRect.SubTop(rRect, rShadow.CalcShadowSpace(SvxShadowItemSide::TOP));
            fnRect.SubLeft(rRect, rShadow.CalcShadowSpace(SvxShadowItemSide::LEFT));
            if ( bBottom )
                fnRect.AddBottom(rRect, rShadow.CalcShadowSpace(SvxShadowItemSide::BOTTOM));
            fnRect.AddRight(rRect, rShadow.CalcShadowSpace(SvxShadowItemSide::RIGHT));
        }
    }

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

/**
 * Extend left/right border/shadow rectangle to bottom of previous frame/to
 * top of next frame, if border/shadow is joined with previous/next frame
 */

static void lcl_ExtendLeftAndRight( SwRect&                _rRect,
                                         const SwFrame&           _rFrame,
                                         const SwBorderAttrs&   _rAttrs,
                                         const SwRectFn&        _rRectFn )
{
    if ( _rAttrs.JoinedWithPrev( _rFrame ) )
    {
        const SwFrame* pPrevFrame = _rFrame.GetPrev();
        (_rRect.*_rRectFn->fnSetTop)( (pPrevFrame->*_rRectFn->fnGetPrtBottom)() );
    }
    if ( _rAttrs.JoinedWithNext( _rFrame ) )
    {
        const SwFrame* pNextFrame = _rFrame.GetNext();
        (_rRect.*_rRectFn->fnSetBottom)( (pNextFrame->*_rRectFn->fnGetPrtTop)() );
    }
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        //If the content of the Fly is transparent, we subtract it only if it's
        //contained in the hell layer.
        const IDocumentDrawModelAccess& rIDDMA = pFly->GetFormat()->getIDocumentDrawModelAccess();
        bool bHell = pSdrObj->GetLayer() == rIDDMA.GetHellId();
        if (bStopOnHell && bHell)
            continue;

        /// Change internal order of condition
        ///    first check "!bHell", then "..->Lower()" and "..->IsNoTextFrame()"
        ///    have not to be performed, if frame is in "Hell"
        const SwFrame* pLower = pFly->Lower();
        if (!bHell && pLower && pLower->IsNoTextFrame() &&
               (static_cast<SwNoTextFrame const*>(pLower)->IsTransparent() ||
                static_cast<SwNoTextFrame const*>(pLower)->HasAnimation() ||
                 pFly->GetFormat()->GetSurround().IsContour()
               )
             )
            continue;

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

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

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

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

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

/**
 * This is a local help method to draw a background for a graphic
 *
 * Under certain circumstances we have to draw a background for a graphic.
 * This method takes care of the conditions and draws the background with the
 * corresponding color.
 * Method introduced for bug fix #103876# in order to optimize drawing tiled
 * background graphics. Previously, this code was integrated in method
 * <lcl_DrawGraphic>.
 * Method implemented as an inline, checking the conditions and calling method
 * method <lcl_implDrawGraphicBackground(..)> for the intrinsic drawing.
 *
 * @param _rBackgrdBrush
 * background brush contain the color the background has to be drawn.
 *
 * @param _rOut
 * output device the background has to be drawn in.
 *
 * @param _rAlignedPaintRect
 * paint rectangle in the output device, which has to be drawn with the background.
 * rectangle have to be aligned by method ::SwAlignRect
 *
 * @param _rGraphicObj
 * graphic object, for which the background has to be drawn. Used for checking
 * the transparency of its bitmap, its type and if the graphic is drawn transparent
 *
 * @param _bNumberingGraphic
 * boolean indicating that graphic is used as a numbering.
 *
 * @param _bBackgrdAlreadyDrawn
 * boolean (optional; default: false) indicating, if the background is already drawn.
*/

static void lcl_DrawGraphicBackground( const SvxBrushItem& _rBackgrdBrush,
                                    OutputDevice& _rOut,
                                    const SwRect& _rAlignedPaintRect,
                                    const GraphicObject& _rGraphicObj,
                                    bool _bNumberingGraphic,
                                    SwPaintProperties const & properties,
                                    bool _bBackgrdAlreadyDrawn = false)
{
    // draw background with background color, if
    //     (1) graphic is not used as a numbering AND
    //     (2) background is not already drawn AND
    //     (3) intrinsic graphic is transparent OR intrinsic graphic doesn't exists
    if ( !_bNumberingGraphic &&
         !_bBackgrdAlreadyDrawn &&
         ( _rGraphicObj.IsTransparent() || _rGraphicObj.GetType() == GraphicType::NONE  )
       )
    {
        lcl_implDrawGraphicBackground( _rBackgrdBrush, _rOut, _rAlignedPaintRect, _rGraphicObj, properties );
    }
}

/**
 * NNOTE: the transparency of the background graphic is saved in
 * SvxBrushItem.GetGraphicObject(<shell>).GetAttr().Set/GetTransparency()
 * and is considered in the drawing of the graphic
 *
 * Thus, to provide transparent background graphic for text frames nothing
 * has to be coded
 *
 * Use align rectangle for drawing graphic Pixel-align coordinates for
 * drawing graphic
 * Outsource code for drawing background of the graphic
 * with a background color in method <lcl_DrawGraphicBackground>
 *
 * Also, change type of <bGrfNum> and <bClip> from <bool> to <bool>
 */

static void lcl_DrawGraphic( const SvxBrushItem& rBrush, vcl::RenderContext &rOutDev,
                      const SwViewShell &rSh, const SwRect &rGrf, const SwRect &rOut,
                      bool bGrfNum,
                      SwPaintProperties const & properties,
                      bool bBackgrdAlreadyDrawn )
                      // add parameter <bBackgrdAlreadyDrawn> to indicate
                      // that the background is already drawn.
{
    // Calculate align rectangle from parameter <rGrf> and use aligned
    // rectangle <aAlignedGrfRect> in the following code
    SwRect aAlignedGrfRect = rGrf;
    ::SwAlignRect( aAlignedGrfRect, &rSh, &rOutDev );

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

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

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

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

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

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

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

    if ( bNotInside )
        rOutDev.Pop();
}

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

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

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

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

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

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

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

--> maximum size reached

--> --------------------

Messung V0.5
C=90 H=96 G=93

¤ Dauer der Verarbeitung: 0.28 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.