sw/source/core/layout/paintfrm.cxx.orig | 7565 +++++++++++++++++++++++ sw/uiconfig/swriter/toolbar/graphicobjectbar.xml | 2 sw/uiconfig/swxform/toolbar/graphicobjectbar.xml | 4 3 files changed, 7568 insertions(+), 3 deletions(-)
New commits: commit e1742c565420bfb82b486e46fe0f55515ac2001b Author: Armin Le Grand <armin.le.gr...@cib.de> Date: Fri Sep 29 12:35:44 2017 +0200 RotGrfFlyFrame: Corrected position for CropHandles Position was taken from OuterBound FlyFrame, even in current master which is wrong. There can be a distance defined between InnerBound and OuterBound that has to be taken into account Change-Id: Id88f99c0b218bd26fa1daa5e8215eced00c0baa6 diff --git a/sw/source/core/layout/paintfrm.cxx.orig b/sw/source/core/layout/paintfrm.cxx.orig new file mode 100644 index 000000000000..351ac1a8ba35 --- /dev/null +++ b/sw/source/core/layout/paintfrm.cxx.orig @@ -0,0 +1,7565 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <vcl/lazydelete.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/progress.hxx> +#include <editeng/brushitem.hxx> +#include <editeng/prntitem.hxx> +#include <editeng/boxitem.hxx> +#include <editeng/shaditem.hxx> +#include <svx/framelink.hxx> +#include <drawdoc.hxx> +#include <tgrditem.hxx> +#include <calbck.hxx> +#include <fmtsrnd.hxx> +#include <fmtclds.hxx> +#include <strings.hrc> +#include <swmodule.hxx> +#include <rootfrm.hxx> +#include <pagefrm.hxx> +#include <section.hxx> +#include <sectfrm.hxx> +#include <viewimp.hxx> +#include <dflyobj.hxx> +#include <flyfrm.hxx> +#include <viewopt.hxx> +#include <dview.hxx> +#include <dcontact.hxx> +#include <txtfrm.hxx> +#include <ftnfrm.hxx> +#include <tabfrm.hxx> +#include <rowfrm.hxx> +#include <cellfrm.hxx> +#include <notxtfrm.hxx> +#include <layact.hxx> +#include <pagedesc.hxx> +#include <ptqueue.hxx> +#include <noteurl.hxx> +#include <virtoutp.hxx> +#include <lineinfo.hxx> +#include <dbg_lay.hxx> +#include <docsh.hxx> +#include <svx/svdogrp.hxx> +#include <sortedobjs.hxx> +#include <EnhancedPDFExportHelper.hxx> +#include <bodyfrm.hxx> +#include <hffrm.hxx> +#include <colfrm.hxx> +#include <svx/sdr/contact/viewobjectcontactredirector.hxx> +#include <svx/sdr/contact/viewobjectcontact.hxx> +#include <svx/sdr/contact/viewcontact.hxx> +#include <DocumentSettingManager.hxx> +#include <IDocumentDeviceAccess.hxx> + +#include <ndole.hxx> +#include <PostItMgr.hxx> +#include <FrameControlsManager.hxx> +#include <vcl/settings.hxx> + +#include <svx/sdr/attribute/sdrallfillattributeshelper.hxx> +#include <drawinglayer/processor2d/processor2dtools.hxx> + +#include <svtools/borderhelper.hxx> + +#include "bitmaps.hlst" +#include <drawinglayer/primitive2d/polygonprimitive2d.hxx> +#include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx> +#include <drawinglayer/primitive2d/borderlineprimitive2d.hxx> +#include <drawinglayer/primitive2d/discreteshadowprimitive2d.hxx> +#include <drawinglayer/primitive2d/maskprimitive2d.hxx> +#include <drawinglayer/primitive2d/textprimitive2d.hxx> +#include <drawinglayer/primitive2d/textlayoutdevice.hxx> +#include <drawinglayer/processor2d/processorfromoutputdevice.hxx> +#include <svx/unoapi.hxx> +#include <svx/framelinkarray.hxx> +#include <comphelper/sequence.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> +#include <basegfx/color/bcolortools.hxx> + +#include <memory> +#include <vector> +#include <algorithm> +#include <wrtsh.hxx> +#include <edtwin.hxx> +#include <view.hxx> +#include <paintfrm.hxx> +#include <o3tl/typed_flags_set.hxx> + +#include <vcl/BitmapTools.hxx> + +#define COL_NOTES_SIDEPANE RGB_COLORDATA(230,230,230) +#define COL_NOTES_SIDEPANE_BORDER RGB_COLORDATA(200,200,200) +#define COL_NOTES_SIDEPANE_SCROLLAREA RGB_COLORDATA(230,230,220) + +using namespace ::editeng; +using namespace ::com::sun::star; +using ::drawinglayer::primitive2d::BorderLinePrimitive2D; +using ::drawinglayer::primitive2d::BorderLine; +using std::pair; +using std::make_pair; + +struct SwPaintProperties; + +//other subsidiary lines enabled? +#define IS_SUBS (!gProp.pSGlobalShell->GetViewOptions()->IsPagePreview() && \ + !gProp.pSGlobalShell->GetViewOptions()->IsReadonly() && \ + !gProp.pSGlobalShell->GetViewOptions()->IsFormView() &&\ + !gProp.pSGlobalShell->GetViewOptions()->IsWhitespaceHidden() &&\ + SwViewOption::IsDocBoundaries()) +//subsidiary lines for sections +#define IS_SUBS_SECTION (!gProp.pSGlobalShell->GetViewOptions()->IsPagePreview() && \ + !gProp.pSGlobalShell->GetViewOptions()->IsReadonly()&&\ + !gProp.pSGlobalShell->GetViewOptions()->IsFormView() &&\ + SwViewOption::IsSectionBoundaries()) +#define IS_SUBS_FLYS (!gProp.pSGlobalShell->GetViewOptions()->IsPagePreview() && \ + !gProp.pSGlobalShell->GetViewOptions()->IsReadonly()&&\ + !gProp.pSGlobalShell->GetViewOptions()->IsFormView() &&\ + SwViewOption::IsObjectBoundaries()) + +//Class declaration; here because they are only used in this file +enum class SubColFlags { + Page = 0x01, //Helplines of the page + Tab = 0x08, //Helplines inside tables + Fly = 0x10, //Helplines inside fly frames + Sect = 0x20, //Helplines inside sections +}; +namespace o3tl { + template<> struct typed_flags<SubColFlags> : is_typed_flags<SubColFlags, 0x39> {}; +} + +// Classes collecting the border lines and help lines +class SwLineRect : public SwRect +{ + Color aColor; + SvxBorderLineStyle nStyle; + const SwTabFrame *pTab; + SubColFlags nSubColor; //colorize subsidiary lines + bool bPainted; //already painted? + sal_uInt8 nLock; //To distinguish the line and the hell layer. +public: + SwLineRect( const SwRect &rRect, const Color *pCol, const SvxBorderLineStyle nStyle, + const SwTabFrame *pT , const SubColFlags nSCol ); + + const Color& GetColor() const { return aColor;} + SvxBorderLineStyle GetStyle() const { return nStyle; } + const SwTabFrame *GetTab() const { return pTab; } + void SetPainted() { bPainted = true; } + void Lock( bool bLock ) { if ( bLock ) + ++nLock; + else if ( nLock ) + --nLock; + } + bool IsPainted() const { return bPainted; } + bool IsLocked() const { return nLock != 0; } + SubColFlags GetSubColor() const { return nSubColor;} + + bool MakeUnion( const SwRect &rRect, SwPaintProperties const &properties ); +}; + +#ifdef IOS +static void dummy_function() +{ + pid_t pid = getpid(); + (void) pid; +} +#endif + +class SwLineRects +{ +public: + std::vector< SwLineRect > aLineRects; + typedef std::vector< SwLineRect >::const_iterator const_iterator; + typedef std::vector< SwLineRect >::iterator iterator; + typedef std::vector< SwLineRect >::reverse_iterator reverse_iterator; + typedef std::vector< SwLineRect >::size_type size_type; + size_t nLastCount; //avoid unnecessary cycles in PaintLines + SwLineRects() : nLastCount( 0 ) + { +#ifdef IOS + // Work around what is either a compiler bug in Xcode 5.1.1, + // or some unknown problem in this file. If I ifdef out this + // call, I get a crash in SwSubsRects::PaintSubsidiary: the + // address of the rLi reference variable is claimed to be + // 0x4000000! + dummy_function(); +#endif + } + void AddLineRect( const SwRect& rRect, const Color *pColor, const SvxBorderLineStyle nStyle, + const SwTabFrame *pTab, const SubColFlags nSCol, SwPaintProperties const &properties ); + void ConnectEdges( OutputDevice const *pOut, SwPaintProperties const &properties ); + void PaintLines ( OutputDevice *pOut, SwPaintProperties const &properties ); + void LockLines( bool bLock ); + + //Limit lines to 100 + bool isFull() const { return aLineRects.size()>100; } +}; + +class SwSubsRects : public SwLineRects +{ + void RemoveSuperfluousSubsidiaryLines( const SwLineRects &rRects, SwPaintProperties const &properties ); +public: + void PaintSubsidiary( OutputDevice *pOut, const SwLineRects *pRects, SwPaintProperties const &properties ); +}; + +class BorderLines +{ + drawinglayer::primitive2d::Primitive2DContainer m_Lines; +public: + void AddBorderLine(const drawinglayer::primitive2d::Primitive2DReference& rLine); + drawinglayer::primitive2d::Primitive2DContainer GetBorderLines_Clear() + { + drawinglayer::primitive2d::Primitive2DContainer lines; + lines.swap(m_Lines); + return lines; + } +}; + +// Default zoom factor +const static double aMinDistScale = 0.73; +const static double aEdgeScale = 0.5; + +//To optimize the expensive RetouchColor determination +Color aGlobalRetoucheColor; + +namespace sw +{ +Color* GetActiveRetoucheColor() +{ + return &aGlobalRetoucheColor; +} +} + +/** + * Container for static properties + */ +struct SwPaintProperties { + // Only repaint the Fly content as well as the background of the Fly content if + // a metafile is taken of the Fly. + bool bSFlyMetafile; + VclPtr<OutputDevice> pSFlyMetafileOut; + SwViewShell *pSGlobalShell; + + // Retouch for transparent Flys is done by the background of the Flys. + // The Fly itself should certainly not be spared out. See PaintBackground 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. + BorderLines *pBLines; + SwLineRects *pSLines; + SwSubsRects *pSSubsLines; + + // global variable for sub-lines of body, header, footer, section and footnote frames. + SwSubsRects *pSSpecSubsLines; + SfxProgress *pSProgress; + + // Sizes of a pixel and the corresponding halves. Will be reset when + // entering SwRootFrame::Paint + long nSPixelSzW; + long nSPixelSzH; + long nSHalfPixelSzW; + long nSHalfPixelSzH; + long nSMinDistPixelW; + long nSMinDistPixelH; + + Color aSGlobalRetoucheColor; + + // Current zoom factor + double aSScaleX; + double aSScaleY; + + SwPaintProperties() + : bSFlyMetafile(false) + , pSFlyMetafileOut(nullptr) + , pSGlobalShell(nullptr) + , pSRetoucheFly(nullptr) + , pSRetoucheFly2(nullptr) + , pSFlyOnlyDraw(nullptr) + , pBLines(nullptr) + , pSLines(nullptr) + , pSSubsLines(nullptr) + , pSSpecSubsLines(nullptr) + , pSProgress(nullptr) + , nSPixelSzW(0) + , nSPixelSzH(0) + , nSHalfPixelSzW(0) + , nSHalfPixelSzH(0) + , nSMinDistPixelW(0) + , nSMinDistPixelH(0) + , aSScaleX(1) + , aSScaleY(1) + { + } + +}; + +static SwPaintProperties gProp; + +namespace { + +bool isTableBoundariesEnabled() +{ + if (!gProp.pSGlobalShell->GetViewOptions()->IsTable()) + return false; + + if (gProp.pSGlobalShell->GetViewOptions()->IsPagePreview()) + return false; + + if (gProp.pSGlobalShell->GetViewOptions()->IsReadonly()) + return false; + + if (gProp.pSGlobalShell->GetViewOptions()->IsFormView()) + return false; + + return SwViewOption::IsTableBoundaries(); +} + +} + +/** + * Set borders alignment statics + * Adjustment for 'small' twip-to-pixel relations: + * For 'small' twip-to-pixel relations (less then 2:1) + * values of <gProp.nSHalfPixelSzW> and <gProp.nSHalfPixelSzH> are set to ZERO + */ +void SwCalcPixStatics( vcl::RenderContext const *pOut ) +{ + // determine 'small' twip-to-pixel relation + bool bSmallTwipToPxRelW = false; + bool bSmallTwipToPxRelH = false; + { + Size aCheckTwipToPxRelSz( pOut->PixelToLogic( Size( 100, 100 )) ); + if ( (aCheckTwipToPxRelSz.Width()/100.0) < 2.0 ) + { + bSmallTwipToPxRelW = true; + } + if ( (aCheckTwipToPxRelSz.Height()/100.0) < 2.0 ) + { + bSmallTwipToPxRelH = true; + } + } + + Size aSz( pOut->PixelToLogic( Size( 1,1 )) ); + + gProp.nSPixelSzW = aSz.Width(); + if( !gProp.nSPixelSzW ) + gProp.nSPixelSzW = 1; + gProp.nSPixelSzH = aSz.Height(); + if( !gProp.nSPixelSzH ) + gProp.nSPixelSzH = 1; + + // consider 'small' twip-to-pixel relations + if ( !bSmallTwipToPxRelW ) + { + gProp.nSHalfPixelSzW = gProp.nSPixelSzW / 2 + 1; + } + else + { + gProp.nSHalfPixelSzW = 0; + } + // consider 'small' twip-to-pixel relations + if ( !bSmallTwipToPxRelH ) + { + gProp.nSHalfPixelSzH = gProp.nSPixelSzH / 2 + 1; + } + else + { + gProp.nSHalfPixelSzH = 0; + } + + gProp.nSMinDistPixelW = gProp.nSPixelSzW * 2 + 1; + gProp.nSMinDistPixelH = gProp.nSPixelSzH * 2 + 1; + + const MapMode &rMap = pOut->GetMapMode(); + gProp.aSScaleX = double(rMap.GetScaleX()); + gProp.aSScaleY = double(rMap.GetScaleY()); +} + +/** + * To be able to save the statics so the paint is more or less reentrant + */ +class SwSavePaintStatics : public SwPaintProperties +{ +public: + SwSavePaintStatics(); + ~SwSavePaintStatics(); +}; + +SwSavePaintStatics::SwSavePaintStatics() +{ + // Saving globales + bSFlyMetafile = gProp.bSFlyMetafile; + pSGlobalShell = gProp.pSGlobalShell; + pSFlyMetafileOut = gProp.pSFlyMetafileOut; + pSRetoucheFly = gProp.pSRetoucheFly; + pSRetoucheFly2 = gProp.pSRetoucheFly2; + pSFlyOnlyDraw = gProp.pSFlyOnlyDraw; + pBLines = gProp.pBLines; + pSLines = gProp.pSLines; + pSSubsLines = gProp.pSSubsLines; + pSSpecSubsLines = 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.pBLines = nullptr; + gProp.pSLines = nullptr; + gProp.pSSubsLines = nullptr; + gProp.pSSpecSubsLines = nullptr; + 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 = pBLines; + gProp.pSLines = pSLines; + gProp.pSSubsLines = pSSubsLines; + gProp.pSSpecSubsLines = 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::AddBorderLine(const drawinglayer::primitive2d::Primitive2DReference& rLine) +{ + for (drawinglayer::primitive2d::Primitive2DContainer::reverse_iterator it = m_Lines.rbegin(); it != m_Lines.rend(); ++it) + { + const drawinglayer::primitive2d::Primitive2DReference aMerged(drawinglayer::primitive2d::tryMergeBorderLinePrimitive2D(*it, rLine)); + + if (aMerged.is()) + { + *it = aMerged; // replace existing line with merged // lcl_TryMergeBorderLine + return; + } + } + + m_Lines.append(rLine); +} + +SwLineRect::SwLineRect( const SwRect &rRect, const Color *pCol, const SvxBorderLineStyle nStyl, + const SwTabFrame *pT, const SubColFlags nSCol ) : + SwRect( rRect ), + nStyle( nStyl ), + pTab( pT ), + nSubColor( nSCol ), + bPainted( false ), + nLock( 0 ) +{ + if ( pCol != nullptr ) + aColor = *pCol; +} + +bool SwLineRect::MakeUnion( const SwRect &rRect, SwPaintProperties const & properties) +{ + // It has already been tested outside, whether the rectangles have + // the same orientation (horizontal or vertical), color, etc. + if ( Height() > Width() ) //Vertical line + { + if ( Left() == rRect.Left() && Width() == rRect.Width() ) + { + // Merge when there is no gap between the lines + const long nAdd = properties.nSPixelSzW + properties.nSHalfPixelSzW; + if ( Bottom() + nAdd >= rRect.Top() && + Top() - nAdd <= rRect.Bottom() ) + { + Bottom( std::max( Bottom(), rRect.Bottom() ) ); + Top ( std::min( Top(), rRect.Top() ) ); + return true; + } + } + } + else + { + if ( Top() == rRect.Top() && Height() == rRect.Height() ) + { + // Merge when there is no gap between the lines + const long nAdd = properties.nSPixelSzW + properties.nSHalfPixelSzW; + if ( Right() + nAdd >= rRect.Left() && + Left() - nAdd <= rRect.Right() ) + { + Right( std::max( Right(), rRect.Right() ) ); + Left ( std::min( Left(), rRect.Left() ) ); + return true; + } + } + } + return false; +} + +void SwLineRects::AddLineRect( const SwRect &rRect, const Color *pCol, const SvxBorderLineStyle nStyle, + const SwTabFrame *pTab, const SubColFlags nSCol, SwPaintProperties const & properties ) +{ + // Loop backwards because lines which can be combined, can usually be painted + // in the same context + for (reverse_iterator it = aLineRects.rbegin(); it != aLineRects.rend(); + ++it) + { + SwLineRect &rLRect = (*it); + // Test for the orientation, color, table + if ( rLRect.GetTab() == pTab && + !rLRect.IsPainted() && rLRect.GetSubColor() == nSCol && + (rLRect.Height() > rLRect.Width()) == (rRect.Height() > rRect.Width()) && + (pCol && rLRect.GetColor() == *pCol) ) + { + if ( rLRect.MakeUnion( rRect, properties ) ) + return; + } + } + aLineRects.emplace_back( rRect, pCol, nStyle, pTab, nSCol ); +} + +void SwLineRects::ConnectEdges( OutputDevice const *pOut, SwPaintProperties const & properties ) +{ + if ( pOut->GetOutDevType() != OUTDEV_PRINTER ) + { + // I'm not doing anything for a too small zoom + if ( properties.aSScaleX < aEdgeScale || properties.aSScaleY < aEdgeScale ) + return; + } + + static const long nAdd = 20; + + std::vector<SwLineRect*> aCheck; + + for (size_t i = 0; i < aLineRects.size(); ++i) + { + SwLineRect &rL1 = aLineRects[i]; + if ( !rL1.GetTab() || rL1.IsPainted() || rL1.IsLocked() ) + continue; + + aCheck.clear(); + + const bool bVert = rL1.Height() > rL1.Width(); + long nL1a, nL1b, nL1c, nL1d; + + if ( bVert ) + { + nL1a = rL1.Top(); nL1b = rL1.Left(); + nL1c = rL1.Right(); nL1d = rL1.Bottom(); + } + else + { + nL1a = rL1.Left(); nL1b = rL1.Top(); + nL1c = rL1.Bottom(); nL1d = rL1.Right(); + } + + // Collect all lines to possibly link with i1 + for (iterator it2 = aLineRects.begin(); it2 != aLineRects.end(); ++it2) + { + SwLineRect &rL2 = (*it2); + if ( rL2.GetTab() != rL1.GetTab() || + rL2.IsPainted() || + rL2.IsLocked() || + (bVert == (rL2.Height() > rL2.Width())) ) + continue; + + long nL2a, nL2b, nL2c, nL2d; + if ( bVert ) + { + nL2a = rL2.Top(); nL2b = rL2.Left(); + nL2c = rL2.Right(); nL2d = rL2.Bottom(); + } + else + { + nL2a = rL2.Left(); nL2b = rL2.Top(); + nL2c = rL2.Bottom(); nL2d = rL2.Right(); + } + + if ( (nL1a - nAdd < nL2d && nL1d + nAdd > nL2a) && + ((nL1b > nL2b && nL1c < nL2c) || + (nL1c >= nL2c && nL1b - nAdd < nL2c) || + (nL1b <= nL2b && nL1c + nAdd > nL2b)) ) + { + aCheck.push_back( &rL2 ); + } + } + if ( aCheck.size() < 2 ) + continue; + + bool bRemove = false; + + // For each line test all following ones. + for ( size_t k = 0; !bRemove && k < aCheck.size(); ++k ) + { + SwLineRect &rR1 = *aCheck[k]; + + for ( size_t k2 = k+1; !bRemove && k2 < aCheck.size(); ++k2 ) + { + SwLineRect &rR2 = *aCheck[k2]; + if ( bVert ) + { + SwLineRect *pLA = nullptr; + SwLineRect *pLB = nullptr; + if ( rR1.Top() < rR2.Top() ) + { + pLA = &rR1; pLB = &rR2; + } + else if ( rR1.Top() > rR2.Top() ) + { + pLA = &rR2; pLB = &rR1; + } + // are k1 and k2 describing a double line? + if ( pLA && pLA->Bottom() + 60 > pLB->Top() ) + { + if ( rL1.Top() < pLA->Top() ) + { + if ( rL1.Bottom() == pLA->Bottom() ) + continue; //Small mistake (where?) + + SwRect aIns( rL1 ); + aIns.Bottom( pLA->Bottom() ); + if ( !rL1.IsInside( aIns ) ) + continue; + aLineRects.emplace_back( aIns, &rL1.GetColor(), + SvxBorderLineStyle::SOLID, + rL1.GetTab(), SubColFlags::Tab ); + if ( isFull() ) + { + --i; + k = aCheck.size(); + break; + } + } + + if ( rL1.Bottom() > pLB->Bottom() ) + rL1.Top( pLB->Top() ); // extend i1 on the top + else + bRemove = true; //stopping, remove i1 + } + } + else + { + SwLineRect *pLA = nullptr; + SwLineRect *pLB = nullptr; + if ( rR1.Left() < rR2.Left() ) + { + pLA = &rR1; pLB = &rR2; + } + else if ( rR1.Left() > rR2.Left() ) + { + pLA = &rR2; pLB = &rR1; + } + // Is it double line? + if ( pLA && pLA->Right() + 60 > pLB->Left() ) + { + if ( rL1.Left() < pLA->Left() ) + { + if ( rL1.Right() == pLA->Right() ) + continue; //small error + + SwRect aIns( rL1 ); + aIns.Right( pLA->Right() ); + if ( !rL1.IsInside( aIns ) ) + continue; + aLineRects.emplace_back( aIns, &rL1.GetColor(), + SvxBorderLineStyle::SOLID, + rL1.GetTab(), SubColFlags::Tab ); + if ( isFull() ) + { + --i; + k = aCheck.size(); + break; + } + } + if ( rL1.Right() > pLB->Right() ) + rL1.Left( pLB->Left() ); + else + bRemove = true; + } + } + } + } + if ( bRemove ) + { + aLineRects.erase(aLineRects.begin() + i); + --i; + } + } +} + +void SwSubsRects::RemoveSuperfluousSubsidiaryLines( const SwLineRects &rRects, SwPaintProperties const & properties ) +{ + // All help lines that are covered by any border will be removed or split + for (size_t i = 0; i < aLineRects.size(); ++i) + { + // get a copy instead of a reference, because an <insert> may destroy + // the object due to a necessary array resize. + const SwLineRect aSubsLineRect = SwLineRect(aLineRects[i]); + + // add condition <aSubsLineRect.IsLocked()> in order to consider only + // border lines, which are *not* locked. + if ( aSubsLineRect.IsPainted() || + aSubsLineRect.IsLocked() ) + continue; + + const bool bVerticalSubs = aSubsLineRect.Height() > aSubsLineRect.Width(); + SwRect aSubsRect( aSubsLineRect ); + if ( bVerticalSubs ) + { + aSubsRect.Left ( aSubsRect.Left() - (properties.nSPixelSzW+properties.nSHalfPixelSzW) ); + aSubsRect.Right ( aSubsRect.Right() + (properties.nSPixelSzW+properties.nSHalfPixelSzW) ); + } + else + { + aSubsRect.Top ( aSubsRect.Top() - (properties.nSPixelSzH+properties.nSHalfPixelSzH) ); + aSubsRect.Bottom( aSubsRect.Bottom() + (properties.nSPixelSzH+properties.nSHalfPixelSzH) ); + } + for (const_iterator itK = rRects.aLineRects.begin(); itK != rRects.aLineRects.end(); ++itK) + { + const SwLineRect &rLine = *itK; + + // do *not* consider painted or locked border lines. + // #i1837# - locked border lines have to be considered. + if ( rLine.IsLocked () ) + continue; + + if ( !bVerticalSubs == ( rLine.Height() > rLine.Width() ) ) //same direction? + continue; + + if ( aSubsRect.IsOver( rLine ) ) + { + if ( bVerticalSubs ) // Vertical? + { + if ( aSubsRect.Left() <= rLine.Right() && + aSubsRect.Right() >= rLine.Left() ) + { + long nTmp = rLine.Top()-(properties.nSPixelSzH+1); + if ( aSubsLineRect.Top() < nTmp ) + { + SwRect aNewSubsRect( aSubsLineRect ); + aNewSubsRect.Bottom( nTmp ); + aLineRects.emplace_back( aNewSubsRect, nullptr, aSubsLineRect.GetStyle(), nullptr, + aSubsLineRect.GetSubColor() ); + } + nTmp = rLine.Bottom()+properties.nSPixelSzH+1; + if ( aSubsLineRect.Bottom() > nTmp ) + { + SwRect aNewSubsRect( aSubsLineRect ); + aNewSubsRect.Top( nTmp ); + aLineRects.emplace_back( aNewSubsRect, nullptr, aSubsLineRect.GetStyle(), nullptr, + aSubsLineRect.GetSubColor() ); + } + aLineRects.erase(aLineRects.begin() + i); + --i; + break; + } + } + else // Horizontal + { + if ( aSubsRect.Top() <= rLine.Bottom() && + aSubsRect.Bottom() >= rLine.Top() ) + { + long nTmp = rLine.Left()-(properties.nSPixelSzW+1); + if ( aSubsLineRect.Left() < nTmp ) + { + SwRect aNewSubsRect( aSubsLineRect ); + aNewSubsRect.Right( nTmp ); + aLineRects.emplace_back( aNewSubsRect, nullptr, aSubsLineRect.GetStyle(), nullptr, + aSubsLineRect.GetSubColor() ); + } + nTmp = rLine.Right()+properties.nSPixelSzW+1; + if ( aSubsLineRect.Right() > nTmp ) + { + SwRect aNewSubsRect( aSubsLineRect ); + aNewSubsRect.Left( nTmp ); + aLineRects.emplace_back( aNewSubsRect, nullptr, aSubsLineRect.GetStyle(), nullptr, + aSubsLineRect.GetSubColor() ); + } + aLineRects.erase(aLineRects.begin() + i); + --i; + break; + } + } + } + } + } +} + +void SwLineRects::LockLines( bool bLock ) +{ + for (iterator it = aLineRects.begin(); it != aLineRects.end(); ++it) + (*it).Lock( bLock ); +} + +static void lcl_DrawDashedRect( OutputDevice * pOut, SwLineRect const & rLRect ) +{ + long startX = rLRect.Left( ), endX; + long startY = rLRect.Top( ), endY; + + // Discriminate vertically stretched rect from horizontally stretched + // and restrict minimum nHalfLWidth to 1 + long nHalfLWidth = std::max( static_cast<long>(std::min( rLRect.Width( ), rLRect.Height( ) ) / 2), 1L ); + + if ( rLRect.Height( ) > rLRect.Width( ) ) + { + startX += nHalfLWidth; + endX = startX; + endY = startY + rLRect.Height( ); + } + else + { + startY += nHalfLWidth; + endY = startY; + endX = startX + rLRect.Width( ); + } + + svtools::DrawLine( *pOut, Point( startX, startY ), Point( endX, endY ), + sal_uInt32( nHalfLWidth * 2 ), rLRect.GetStyle( ) ); +} + +void SwLineRects::PaintLines( OutputDevice *pOut, SwPaintProperties const &properties ) +{ + // Paint the borders. Sadly two passes are needed. + // Once for the inside and once for the outside edges of tables + if ( aLineRects.size() != nLastCount ) + { + // #i16816# tagged pdf support + SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, nullptr, *pOut ); + + pOut->Push( PushFlags::FILLCOLOR|PushFlags::LINECOLOR ); + pOut->SetFillColor(); + pOut->SetLineColor(); + ConnectEdges( pOut, properties ); + const Color *pLast = nullptr; + + bool bPaint2nd = false; + size_t nMinCount = aLineRects.size(); + + for ( size_t i = 0; i < aLineRects.size(); ++i ) + { + SwLineRect &rLRect = aLineRects[i]; + + if ( rLRect.IsPainted() ) + continue; + + if ( rLRect.IsLocked() ) + { + nMinCount = std::min( nMinCount, i ); + continue; + } + + // Paint it now or in the second pass? + bool bPaint = true; + if ( rLRect.GetTab() ) + { + if ( rLRect.Height() > rLRect.Width() ) + { + // Vertical edge, overlapping with the table edge? + SwTwips nLLeft = rLRect.Left() - 30, + nLRight = rLRect.Right() + 30, + nTLeft = rLRect.GetTab()->Frame().Left() + rLRect.GetTab()->Prt().Left(), + nTRight = rLRect.GetTab()->Frame().Left() + rLRect.GetTab()->Prt().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()->Frame().Top() + rLRect.GetTab()->Prt().Top(), + nTBottom = rLRect.GetTab()->Frame().Top() + rLRect.GetTab()->Prt().Bottom(); + if ( (nTTop >= nLTop && nTTop <= nLBottom) || + (nTBottom >= nLTop && nTBottom <= nLBottom) ) + bPaint = false; + } + } + if ( bPaint ) + { + if ( !pLast || *pLast != rLRect.GetColor() ) + { + pLast = &rLRect.GetColor(); + + DrawModeFlags nOldDrawMode = pOut->GetDrawMode(); + if( properties.pSGlobalShell->GetWin() && + Application::GetSettings().GetStyleSettings().GetHighContrastMode() ) + pOut->SetDrawMode( DrawModeFlags::Default ); + + pOut->SetLineColor( *pLast ); + pOut->SetFillColor( *pLast ); + pOut->SetDrawMode( nOldDrawMode ); + } + + if( !rLRect.IsEmpty() ) + lcl_DrawDashedRect( pOut, rLRect ); + rLRect.SetPainted(); + } + else + bPaint2nd = true; + } + if ( bPaint2nd ) + { + for ( size_t i = 0; i < aLineRects.size(); ++i ) + { + SwLineRect &rLRect = aLineRects[i]; + if ( rLRect.IsPainted() ) + continue; + + if ( rLRect.IsLocked() ) + { + nMinCount = std::min( nMinCount, i ); + continue; + } + + if ( !pLast || *pLast != rLRect.GetColor() ) + { + pLast = &rLRect.GetColor(); + + DrawModeFlags nOldDrawMode = pOut->GetDrawMode(); + if( properties.pSGlobalShell->GetWin() && + Application::GetSettings().GetStyleSettings().GetHighContrastMode() ) + { + pOut->SetDrawMode( DrawModeFlags::Default ); + } + + pOut->SetFillColor( *pLast ); + pOut->SetDrawMode( nOldDrawMode ); + } + if( !rLRect.IsEmpty() ) + lcl_DrawDashedRect( pOut, rLRect ); + rLRect.SetPainted(); + } + } + nLastCount = nMinCount; + pOut->Pop(); + } +} + +void SwSubsRects::PaintSubsidiary( OutputDevice *pOut, + const SwLineRects *pRects, + SwPaintProperties const & properties ) +{ + if ( !aLineRects.empty() ) + { + // #i16816# tagged pdf support + SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, nullptr, *pOut ); + + // Remove all help line that are almost covered (tables) + for (size_type i = 0; i != aLineRects.size(); ++i) + { + SwLineRect &rLi = aLineRects[i]; + const bool bVerticalSubs = rLi.Height() > rLi.Width(); + + for (size_type k = i + 1; k != aLineRects.size(); ++k) + { + SwLineRect &rLk = aLineRects[k]; + if ( rLi.SSize() == rLk.SSize() ) + { + if ( bVerticalSubs == ( rLk.Height() > rLk.Width() ) ) + { + if ( bVerticalSubs ) + { + long nLi = rLi.Right(); + long nLk = rLk.Right(); + if ( rLi.Top() == rLk.Top() && + ((nLi < rLk.Left() && nLi+21 > rLk.Left()) || + (nLk < rLi.Left() && nLk+21 > rLi.Left()))) + { + aLineRects.erase(aLineRects.begin() + k); + // don't continue with inner loop any more: + // the array may shrink! + --i; + break; + } + } + else + { + long nLi = rLi.Bottom(); + long nLk = rLk.Bottom(); + if ( rLi.Left() == rLk.Left() && + ((nLi < rLk.Top() && nLi+21 > rLk.Top()) || + (nLk < rLi.Top() && nLk+21 > rLi.Top()))) + { + aLineRects.erase(aLineRects.begin() + k); + // don't continue with inner loop any more: + // the array may shrink! + --i; + break; + } + } + } + } + } + } + + if ( pRects && (!pRects->aLineRects.empty()) ) + RemoveSuperfluousSubsidiaryLines( *pRects, properties ); + + if ( !aLineRects.empty() ) + { + pOut->Push( PushFlags::FILLCOLOR|PushFlags::LINECOLOR ); + pOut->SetLineColor(); + + // Reset draw mode in high contrast mode in order to get fill color + // set at output device. Recover draw mode after draw of lines. + // Necessary for the subsidiary lines painted by the fly frames. + DrawModeFlags nOldDrawMode = pOut->GetDrawMode(); + if( gProp.pSGlobalShell->GetWin() && + Application::GetSettings().GetStyleSettings().GetHighContrastMode() ) + { + pOut->SetDrawMode( DrawModeFlags::Default ); + } + + for (SwSubsRects::iterator it = aLineRects.begin(); it != aLineRects.end(); + ++it) + { + SwLineRect &rLRect = (*it); + // Add condition <!rLRect.IsLocked()> to prevent paint of locked subsidiary lines. + if ( !rLRect.IsPainted() && + !rLRect.IsLocked() ) + { + const Color *pCol = nullptr; + switch ( rLRect.GetSubColor() ) + { + case SubColFlags::Page: pCol = &SwViewOption::GetDocBoundariesColor(); break; + case SubColFlags::Fly: pCol = &SwViewOption::GetObjectBoundariesColor(); break; + case SubColFlags::Tab: pCol = &SwViewOption::GetTableBoundariesColor(); break; + case SubColFlags::Sect: pCol = &SwViewOption::GetSectionBoundColor(); break; + } + + if (pCol && pOut->GetFillColor() != *pCol) + pOut->SetFillColor( *pCol ); + pOut->DrawRect( rLRect.SVRect() ); + + rLRect.SetPainted(); + } + } + + pOut->SetDrawMode( nOldDrawMode ); + + pOut->Pop(); + } + } +} + +// Various functions that are use in this file. + +/** + * Function <SwAlignRect(..)> is also used outside this file + * + * Correction: adjust rectangle on pixel level in order to make sure, + * that the border "leaves its original pixel", if it has to + * No prior adjustments for odd relation between pixel and twip + */ +void SwAlignRect( SwRect &rRect, const SwViewShell *pSh, const vcl::RenderContext* pRenderContext ) +{ + if( !rRect.HasArea() ) + return; + + // Make sure that view shell (parameter <pSh>) exists, if the output device + // is taken from this view shell --> no output device, no alignment + // Output device taken from view shell <pSh>, if <gProp.bSFlyMetafile> not set + if ( !gProp.bSFlyMetafile && !pSh ) + { + return; + } + + const vcl::RenderContext *pOut = gProp.bSFlyMetafile ? + gProp.pSFlyMetafileOut.get() : pRenderContext; + + // Hold original rectangle in pixel + const tools::Rectangle aOrgPxRect = pOut->LogicToPixel( rRect.SVRect() ); + // Determine pixel-center rectangle in twip + const SwRect aPxCenterRect( pOut->PixelToLogic( aOrgPxRect ) ); + + // Perform adjustments on pixel level. + SwRect aAlignedPxRect( aOrgPxRect ); + if ( rRect.Top() > aPxCenterRect.Top() ) + { + // 'leave pixel overlapping on top' + aAlignedPxRect.Top( aAlignedPxRect.Top() + 1 ); + } + + if ( rRect.Bottom() < aPxCenterRect.Bottom() ) + { + // 'leave pixel overlapping on bottom' + aAlignedPxRect.Bottom( aAlignedPxRect.Bottom() - 1 ); + } + + if ( rRect.Left() > aPxCenterRect.Left() ) + { + // 'leave pixel overlapping on left' + aAlignedPxRect.Left( aAlignedPxRect.Left() + 1 ); + } + + if ( rRect.Right() < aPxCenterRect.Right() ) + { + // 'leave pixel overlapping on right' + aAlignedPxRect.Right( aAlignedPxRect.Right() - 1 ); + } + + // Consider negative width/height check, if aligned SwRect has negative width/height. + // If Yes, adjust it to width/height = 0 twip. + // NOTE: A SwRect with negative width/height can occur, if the width/height + // of the given SwRect in twip was less than a pixel in twip and that + // the alignment calculates that the aligned SwRect should not contain + // the pixels the width/height is on. + if ( aAlignedPxRect.Width() < 0 ) + { + aAlignedPxRect.Width(0); + } + if ( aAlignedPxRect.Height() < 0 ) + { + aAlignedPxRect.Height(0); + } + // Consider zero width/height for converting a rectangle from + // pixel to logic it needs a width/height. Thus, set width/height + // to one, if it's zero and correct this on the twip level after the conversion. + bool bZeroWidth = false; + if ( aAlignedPxRect.Width() == 0 ) + { + aAlignedPxRect.Width(1); + bZeroWidth = true; + } + bool bZeroHeight = false; + if ( aAlignedPxRect.Height() == 0 ) + { + aAlignedPxRect.Height(1); + bZeroHeight = true; + } + + rRect = pOut->PixelToLogic( aAlignedPxRect.SVRect() ); + + // Consider zero width/height and adjust calculated aligned twip rectangle. + // Reset width/height to zero; previous negative width/height haven't to be considered. + if ( bZeroWidth ) + { + rRect.Width(0); + } + if ( bZeroHeight ) + { + rRect.Height(0); + } +} + +/** + * Helper for twip adjustments on pixel base + * + * This method compares the x- or y-pixel position of two twip-points. + * If the x-/y-pixel positions are the same, the x-/y-pixel position of + * the second twip point is adjusted by a given amount of pixels +*/ +static void lcl_CompPxPosAndAdjustPos( const vcl::RenderContext& _rOut, + const Point& _rRefPt, + Point& _rCompPt, + const bool _bChkXPos, + const sal_Int8 _nPxAdjustment ) +{ + const Point aRefPxPt = _rOut.LogicToPixel( _rRefPt ); + Point aCompPxPt = _rOut.LogicToPixel( _rCompPt ); + + if ( _bChkXPos ) + { + if ( aCompPxPt.X() == aRefPxPt.X() ) + { + aCompPxPt.X() += _nPxAdjustment ; + const Point aAdjustedCompPt = _rOut.PixelToLogic( aCompPxPt ); + _rCompPt.X() = aAdjustedCompPt.X(); + } + } + else + { + if ( aCompPxPt.Y() == aRefPxPt.Y() ) + { + aCompPxPt.Y() += _nPxAdjustment ; + const Point aAdjustedCompPt = _rOut.PixelToLogic( aCompPxPt ); + _rCompPt.Y() = aAdjustedCompPt.Y(); + } + } +} + +/** + * Method to pixel-align rectangle for drawing graphic object + * + * Because we are drawing graphics from the left-top-corner in conjunction + * with size coordinates, these coordinates have to be calculated at a pixel + * level. + * Thus, we convert the rectangle to pixel and then convert to left-top-corner + * and then get size of pixel rectangle back to logic. + * This calculation is necessary, because there's a different between + * the conversion from logic to pixel of a normal rectangle with its left-top- + * and right-bottom-corner and the same conversion of the same rectangle + * with left-top-corner and size. + * + * NOTE: Call this method before each <GraphicObject.Draw(...)> +*/ +void SwAlignGrfRect( SwRect *pGrfRect, const vcl::RenderContext &rOut ) +{ + tools::Rectangle aPxRect = rOut.LogicToPixel( pGrfRect->SVRect() ); + pGrfRect->Pos( rOut.PixelToLogic( aPxRect.TopLeft() ) ); + pGrfRect->SSize( rOut.PixelToLogic( aPxRect.GetSize() ) ); +} + +static long lcl_AlignWidth( const long nWidth, SwPaintProperties const & properties ) +{ + if ( nWidth ) + { + const long nW = nWidth % properties.nSPixelSzW; + + if ( !nW || nW > properties.nSHalfPixelSzW ) + return std::max(1L, nWidth - properties.nSHalfPixelSzW); + } + return nWidth; +} + +static long lcl_AlignHeight( const long nHeight, SwPaintProperties const & properties ) +{ + if ( nHeight ) + { + const long nH = nHeight % properties.nSPixelSzH; + + if ( !nH || nH > properties.nSHalfPixelSzH ) + return std::max(1L, nHeight - properties.nSHalfPixelSzH); + } + return nHeight; +} + +static long lcl_MinHeightDist( const long nDist, SwPaintProperties const & properties ) +{ + if ( properties.aSScaleX < aMinDistScale || properties.aSScaleY < aMinDistScale ) + return nDist; + return ::lcl_AlignHeight( std::max( nDist, properties.nSMinDistPixelH ), properties); +} + +/** + * 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->Prt(); + rRect.Pos() += pFrame->Frame().Pos(); + } + else if ( pFrame->IsCellFrame() ) + rRect = pFrame->Frame(); + else + { + rRect = pFrame->Prt(); + rRect.Pos() += pFrame->Frame().Pos(); + + if ( rAttrs.IsLine() || rAttrs.IsBorderDist() || + (bShadow && rAttrs.GetShadow().GetLocation() != SvxShadowLocation::NONE) ) + { + SwRectFn fnRect = pFrame->IsVertical() ? ( pFrame->IsVertLR() ? fnRectVertL2R : fnRectVert ) : fnRectHori; + + const SvxBoxItem &rBox = rAttrs.GetBox(); + const bool bTop = 0 != (pFrame->*fnRect->fnGetTopMargin)(); + if ( bTop ) + { + SwTwips nDiff = rBox.GetTop() ? + rBox.CalcLineSpace( SvxBoxItemLine::TOP ) : + ( rAttrs.IsBorderDist() ? + // Increase of distance by one twip is incorrect. + rBox.GetDistance( SvxBoxItemLine::TOP ) : 0 ); + if( nDiff ) + (rRect.*fnRect->fnSubTop)( nDiff ); + } + + const bool bBottom = 0 != (pFrame->*fnRect->fnGetBottomMargin)(); + if ( bBottom ) + { + SwTwips nDiff = 0; + // #i29550# + if ( pFrame->IsTabFrame() && + static_cast<const SwTabFrame*>(pFrame)->IsCollapsingBorders() ) + { + // For collapsing borders, we have to add the height of + // the height of the last line + nDiff = static_cast<const SwTabFrame*>(pFrame)->GetBottomLineSize(); + } + else + { + nDiff = rBox.GetBottom() ? + rBox.CalcLineSpace( SvxBoxItemLine::BOTTOM ) : + ( rAttrs.IsBorderDist() ? + // Increase of distance by one twip is incorrect. + rBox.GetDistance( SvxBoxItemLine::BOTTOM ) : 0 ); + } + if( nDiff ) + (rRect.*fnRect->fnAddBottom)( nDiff ); + } + + if ( rBox.GetLeft() ) + (rRect.*fnRect->fnSubLeft)( rBox.CalcLineSpace( SvxBoxItemLine::LEFT ) ); + else if ( rAttrs.IsBorderDist() ) + // Increase of distance by one twip is incorrect. + (rRect.*fnRect->fnSubLeft)( rBox.GetDistance( SvxBoxItemLine::LEFT ) ); + + if ( rBox.GetRight() ) + (rRect.*fnRect->fnAddRight)( rBox.CalcLineSpace( SvxBoxItemLine::RIGHT ) ); + else if ( rAttrs.IsBorderDist() ) + // Increase of distance by one twip is incorrect. + (rRect.*fnRect->fnAddRight)( rBox.GetDistance( SvxBoxItemLine::RIGHT ) ); + + if ( bShadow && rAttrs.GetShadow().GetLocation() != SvxShadowLocation::NONE ) + { + const SvxShadowItem &rShadow = rAttrs.GetShadow(); + if ( bTop ) + (rRect.*fnRect->fnSubTop)(rShadow.CalcShadowSpace(SvxShadowItemSide::TOP)); + (rRect.*fnRect->fnSubLeft)(rShadow.CalcShadowSpace(SvxShadowItemSide::LEFT)); + if ( bBottom ) + (rRect.*fnRect->fnAddBottom) + (rShadow.CalcShadowSpace( SvxShadowItemSide::BOTTOM )); + (rRect.*fnRect->fnAddRight)(rShadow.CalcShadowSpace(SvxShadowItemSide::RIGHT)); + } + } + } + + ::SwAlignRect( rRect, properties.pSGlobalShell, properties.pSGlobalShell ? properties.pSGlobalShell->GetOut() : nullptr ); +} + +/** + * Extend left/right border/shadow rectangle to bottom of previous frame/to + * top of next frame, if border/shadow is joined with previous/next frame + */ +static void lcl_ExtendLeftAndRight( SwRect& _rRect, + const SwFrame& _rFrame, + const SwBorderAttrs& _rAttrs, + const SwRectFn& _rRectFn ) +{ + if ( _rAttrs.JoinedWithPrev( _rFrame ) ) + { + const SwFrame* pPrevFrame = _rFrame.GetPrev(); + (_rRect.*_rRectFn->fnSetTop)( (pPrevFrame->*_rRectFn->fnGetPrtBottom)() ); + } + if ( _rAttrs.JoinedWithNext( _rFrame ) ) + { + const SwFrame* pNextFrame = _rFrame.GetNext(); + (_rRect.*_rRectFn->fnSetBottom)( (pNextFrame->*_rRectFn->fnGetPrtTop)() ); + } +} + +/// Returns a range suitable for subtraction when lcl_SubtractFlys() is used. +/// Otherwise DrawFillAttributes() expands the clip path itself. +static basegfx::B2DRange lcl_ShrinkFly(const SwRect& rRect) +{ + static MapMode aMapMode(MapUnit::MapTwip); + static const Size aSingleUnit = Application::GetDefaultDevice()->PixelToLogic(Size(1, 1), aMapMode); + + double x1 = rRect.Left() + aSingleUnit.getWidth(); + double y1 = rRect.Top() + aSingleUnit.getHeight(); + double x2 = rRect.Right() - aSingleUnit.getWidth(); + double y2 = rRect.Bottom() - aSingleUnit.getHeight(); + + return basegfx::B2DRange(x1, y1, x2, y2); +} + +static void lcl_SubtractFlys( const SwFrame *pFrame, const SwPageFrame *pPage, + const SwRect &rRect, SwRegionRects &rRegion, basegfx::tools::B2DClipState& rClipState, SwPaintProperties const & rProperties) +{ + const SwSortedObjs& rObjs = *pPage->GetSortedObjs(); + const SwFlyFrame* pSelfFly = pFrame->IsInFly() ? pFrame->FindFlyFrame() : gProp.pSRetoucheFly2; + if (!gProp.pSRetoucheFly) + gProp.pSRetoucheFly = gProp.pSRetoucheFly2; + + for (size_t j = 0; (j < rObjs.size()) && !rRegion.empty(); ++j) + { + const SwAnchoredObject* pAnchoredObj = rObjs[j]; + const SdrObject* pSdrObj = pAnchoredObj->GetDrawObj(); + + // Do not consider invisible objects + if (!pPage->GetFormat()->GetDoc()->getIDocumentDrawModelAccess().IsVisibleLayerId(pSdrObj->GetLayer())) + continue; + + if (dynamic_cast< const SwFlyFrame *>( pAnchoredObj ) == nullptr) + continue; + + const SwFlyFrame *pFly = static_cast<const SwFlyFrame*>(pAnchoredObj); + + if (pSelfFly == pFly || gProp.pSRetoucheFly == pFly || !rRect.IsOver(pFly->Frame())) + 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->Prt() ); + aRect += pFly->Frame().Pos(); + rRegion -= aRect; + rClipState.subtractRange(lcl_ShrinkFly(aRect)); + } + } + if (gProp.pSRetoucheFly == gProp.pSRetoucheFly2) + gProp.pSRetoucheFly = nullptr; +} + +static void lcl_implDrawGraphicBackgrd( const SvxBrushItem& _rBackgrdBrush, + vcl::RenderContext* _pOut, + const SwRect& _rAlignedPaintRect, + const GraphicObject& _rGraphicObj, + SwPaintProperties const & properties) +{ + /// determine color of background + /// If color of background brush is not "no fill"/"auto fill" or + /// <SwPaintProperties.bSFlyMetafile> is set, use color of background brush, otherwise + /// use global retouche color. + const Color aColor( ( (_rBackgrdBrush.GetColor() != COL_TRANSPARENT) || properties.bSFlyMetafile ) + ? _rBackgrdBrush.GetColor() + : aGlobalRetoucheColor ); + + /// determine, if background color have to be drawn transparent + /// and calculate transparency percent value + sal_Int8 nTransparencyPercent = 0; + bool bDrawTransparent = false; + if ( aColor.GetTransparency() != 0 ) + /// background color is transparent --> draw transparent. + { + bDrawTransparent = true; + nTransparencyPercent = (aColor.GetTransparency()*100 + 0x7F)/0xFF; + } + else if ( (_rGraphicObj.GetAttr().GetTransparency() != 0) && + (_rBackgrdBrush.GetColor() == COL_TRANSPARENT) ) + /// graphic is drawn transparent and background color is + /// "no fill"/"auto fill" --> draw transparent + { + bDrawTransparent = true; + nTransparencyPercent = (_rGraphicObj.GetAttr().GetTransparency()*100 + 0x7F)/0xFF; + } + + if ( bDrawTransparent ) + { + /// draw background transparent + if( _pOut->GetFillColor() != aColor.GetRGBColor() ) + _pOut->SetFillColor( aColor.GetRGBColor() ); + tools::PolyPolygon aPoly( _rAlignedPaintRect.SVRect() ); + _pOut->DrawTransparent( aPoly, nTransparencyPercent ); + } + else + { + /// draw background opaque + if ( _pOut->GetFillColor() != aColor ) + _pOut->SetFillColor( aColor ); + _pOut->DrawRect( _rAlignedPaintRect.SVRect() ); + } +} + +/** + * This is a local help method to draw a background for a graphic + * + * Under certain circumstances we have to draw a background for a graphic. + * This method takes care of the conditions and draws the background with the + * corresponding color. + * Method introduced for bug fix #103876# in order to optimize drawing tiled + * background graphics. Previously, this code was integrated in method + * <lcl_DrawGraphic>. + * Method implemented as a inline, checking the conditions and calling method + * method <lcl_implDrawGraphicBackgrd(..)> for the intrinsic drawing. + * + * @param _rBackgrdBrush + * background brush contain the color the background has to be drawn. + * + * @param _pOut + * output device the background has to be drawn in. + * + * @param _rAlignedPaintRect + * paint rectangle in the output device, which has to be drawn with the background. + * rectangle have to be aligned by method ::SwAlignRect + * + * @param _rGraphicObj + * graphic object, for which the background has to be drawn. Used for checking + * the transparency of its bitmap, its type and if the graphic is drawn transparent + * + * @param _bNumberingGraphic + * boolean indicating that graphic is used as a numbering. + * + * @param _bBackgrdAlreadyDrawn + * boolean (optional; default: false) indicating, if the background is already drawn. +*/ +static inline void lcl_DrawGraphicBackgrd( const SvxBrushItem& _rBackgrdBrush, + OutputDevice* _pOut, + const SwRect& _rAlignedPaintRect, + const GraphicObject& _rGraphicObj, + bool _bNumberingGraphic, + SwPaintProperties const & properties, + bool _bBackgrdAlreadyDrawn = false) +{ + // draw background with background color, if + // (1) graphic is not used as a numbering AND + // (2) background is not already drawn AND + // (3) intrinsic graphic is transparent OR intrinsic graphic doesn't exists + if ( !_bNumberingGraphic && + !_bBackgrdAlreadyDrawn && + ( _rGraphicObj.IsTransparent() || _rGraphicObj.GetType() == GraphicType::NONE ) + ) + { + lcl_implDrawGraphicBackgrd( _rBackgrdBrush, _pOut, _rAlignedPaintRect, _rGraphicObj, properties ); + } +} + +/** + * NNOTE: the transparency of the background graphic is saved in + * SvxBrushItem.GetGraphicObject(<shell>).GetAttr().Set/GetTransparency() + * and is considered in the drawing of the graphic + * + * Thus, to provide transparent background graphic for text frames nothing + * has to be coded + * + * Use align rectangle for drawing graphic Pixel-align coordinates for + * drawing graphic + * Outsource code for drawing background of the graphic + * with a background color in method <lcl_DrawGraphicBackgrd> + * + * Also, change type of <bGrfNum> and <bClip> from <bool> to <bool> + */ +static void lcl_DrawGraphic( const SvxBrushItem& rBrush, vcl::RenderContext *pOut, + SwViewShell &rSh, const SwRect &rGrf, const SwRect &rOut, + bool bClip, bool bGrfNum, + SwPaintProperties const & properties, + bool bBackgrdAlreadyDrawn ) + // add parameter <bBackgrdAlreadyDrawn> to indicate + // that the background is already drawn. +{ + // Calculate align rectangle from parameter <rGrf> and use aligned + // rectangle <aAlignedGrfRect> in the following code + SwRect aAlignedGrfRect = rGrf; + ::SwAlignRect( aAlignedGrfRect, &rSh, pOut ); + + // Change type from <bool> to <bool>. + const bool bNotInside = bClip && !rOut.IsInside( aAlignedGrfRect ); + if ( bNotInside ) + { + pOut->Push( PushFlags::CLIPREGION ); + pOut->IntersectClipRegion( rOut.SVRect() ); + } + + GraphicObject *pGrf = const_cast<GraphicObject*>(rBrush.GetGraphicObject()); + + // Outsource drawing of background with a background color + ::lcl_DrawGraphicBackgrd( rBrush, pOut, aAlignedGrfRect, *pGrf, bGrfNum, properties, bBackgrdAlreadyDrawn ); + + // Because for drawing a graphic left-top-corner and size coordinates are + // used, these coordinates have to be determined on pixel level. + ::SwAlignGrfRect( &aAlignedGrfRect, *pOut ); + + paintGraphicUsingPrimitivesHelper(*pOut, + *pGrf, pGrf->GetAttr(), aAlignedGrfRect); + + if ( bNotInside ) + pOut->Pop(); +} + +bool DrawFillAttributes( + const drawinglayer::attribute::SdrAllFillAttributesHelperPtr& rFillAttributes, + const SwRect& rOriginalLayoutRect, + const SwRegionRects& rPaintRegion, + const basegfx::tools::B2DClipState& rClipState, + vcl::RenderContext& rOut) +{ + if(rFillAttributes.get() && rFillAttributes->isUsed()) + { + basegfx::B2DRange aPaintRange( + rPaintRegion.GetOrigin().Left(), + rPaintRegion.GetOrigin().Top(), + rPaintRegion.GetOrigin().Right(), + rPaintRegion.GetOrigin().Bottom()); + + if (!aPaintRange.isEmpty() && + !rPaintRegion.empty() && + !basegfx::fTools::equalZero(aPaintRange.getWidth()) && + !basegfx::fTools::equalZero(aPaintRange.getHeight())) + { + const SvtOptionsDrawinglayer aSvtOptionsDrawinglayer; + + // need to expand for correct AAed and non-AAed visualization as primitive. + // This must probably be removed again when we will be able to get all Writer visualization + // as primitives and Writer prepares all it's stuff in high precision coordinates (also + // needs to avoid moving boundaries around to better show overlapping stuff...) + if(aSvtOptionsDrawinglayer.IsAntiAliasing()) + { + // if AAed in principle expand by 0.5 in all directions. Since painting edges of + // AAed regions does not add to no transparence (0.5 opacity covered by 0.5 opacity + // is not full opacity but 0.75 opacity) we need some overlap here to avoid paint + // artifacts. Checked experimentally - a little bit more in Y is needed, probably + // due to still existing integer alignment and crunching in writer. + static double fExpandX = 0.55; + static double fExpandY = 0.70; + const basegfx::B2DVector aSingleUnit(rOut.GetInverseViewTransformation() * basegfx::B2DVector(fExpandX, fExpandY)); + + aPaintRange.expand(aPaintRange.getMinimum() - aSingleUnit); + aPaintRange.expand(aPaintRange.getMaximum() + aSingleUnit); + } + else + { + // if not AAed expand by one unit to bottom right due to the missing unit + // from SwRect/Rectangle integer handling + const basegfx::B2DVector aSingleUnit(rOut.GetInverseViewTransformation() * basegfx::B2DVector(1.0, 1.0)); + + aPaintRange.expand(aPaintRange.getMaximum() + aSingleUnit); + } + + const basegfx::B2DRange aDefineRange( + rOriginalLayoutRect.Left(), + rOriginalLayoutRect.Top(), + rOriginalLayoutRect.Right(), + rOriginalLayoutRect.Bottom()); + + const drawinglayer::primitive2d::Primitive2DContainer& rSequence = rFillAttributes->getPrimitive2DSequence( + aPaintRange, + aDefineRange); + + if(rSequence.size()) + { + drawinglayer::primitive2d::Primitive2DContainer const* + pPrimitives(&rSequence); + drawinglayer::primitive2d::Primitive2DContainer primitives; + // tdf#86578 the awful lcl_SubtractFlys hack + if (rPaintRegion.size() > 1 || rPaintRegion[0] != rPaintRegion.GetOrigin()) + { + basegfx::B2DPolyPolygon const maskRegion(rClipState.getClipPoly()); + primitives.resize(1); + primitives[0] = new drawinglayer::primitive2d::MaskPrimitive2D( + maskRegion, rSequence); + pPrimitives = &primitives; + } + assert(pPrimitives && pPrimitives->size()); + + const drawinglayer::geometry::ViewInformation2D aViewInformation2D( + basegfx::B2DHomMatrix(), + rOut.GetViewTransformation(), + aPaintRange, + nullptr, + 0.0, + uno::Sequence< beans::PropertyValue >()); + drawinglayer::processor2d::BaseProcessor2D* pProcessor = drawinglayer::processor2d::createProcessor2DFromOutputDevice( + rOut, + aViewInformation2D); + + if(pProcessor) + { + pProcessor->process(*pPrimitives); + + delete pProcessor; + + return true; + } + } + } + } + + return false; +} + +void DrawGraphic( + const SvxBrushItem *pBrush, + vcl::RenderContext *pOutDev, + const SwRect &rOrg, + const SwRect &rOut, + const sal_uInt8 nGrfNum, + const bool bConsiderBackgroundTransparency ) + // Add 6th parameter to indicate that method should + // consider background transparency, saved in the color of the brush item +{ + SwViewShell &rSh = *gProp.pSGlobalShell; + bool bReplaceGrfNum = GRFNUM_REPLACE == nGrfNum; + bool bGrfNum = GRFNUM_NO != nGrfNum; + Size aGrfSize; + SvxGraphicPosition ePos = GPOS_NONE; + if( pBrush && !bReplaceGrfNum ) + { + if( rSh.GetViewOptions()->IsGraphic() ) + { + OUString referer; + SfxObjectShell * sh = rSh.GetDoc()->GetPersist(); + if (sh != nullptr && sh->HasName()) { + referer = sh->GetMedium()->GetName(); + } + const Graphic* pGrf = pBrush->GetGraphic(referer); + if( pGrf && GraphicType::NONE != pGrf->GetType() ) + { + ePos = pBrush->GetGraphicPos(); + if( pGrf->IsSupportedGraphic() ) + // don't the use the specific output device! Bug 94802 + aGrfSize = ::GetGraphicSizeTwip( *pGrf, nullptr ); + } + } + else + bReplaceGrfNum = bGrfNum; + } + + SwRect aGrf; + aGrf.SSize( aGrfSize ); + bool bDraw = true; + bool bRetouche = true; + switch ( ePos ) + { + case GPOS_LT: + aGrf.Pos() = rOrg.Pos(); + break; + + case GPOS_MT: + aGrf.Pos().Y() = rOrg.Top(); + aGrf.Pos().X() = rOrg.Left() + rOrg.Width()/2 - aGrfSize.Width()/2; + break; + + case GPOS_RT: + aGrf.Pos().Y() = rOrg.Top(); + aGrf.Pos().X() = rOrg.Right() - aGrfSize.Width(); + break; + + case GPOS_LM: + aGrf.Pos().Y() = rOrg.Top() + rOrg.Height()/2 - aGrfSize.Height()/2; + aGrf.Pos().X() = rOrg.Left(); + break; + + case GPOS_MM: + aGrf.Pos().Y() = rOrg.Top() + rOrg.Height()/2 - aGrfSize.Height()/2; + aGrf.Pos().X() = rOrg.Left() + rOrg.Width()/2 - aGrfSize.Width()/2; + break; + + case GPOS_RM: + aGrf.Pos().Y() = rOrg.Top() + rOrg.Height()/2 - aGrfSize.Height()/2; + aGrf.Pos().X() = rOrg.Right() - aGrfSize.Width(); + break; + + case GPOS_LB: + aGrf.Pos().Y() = rOrg.Bottom() - aGrfSize.Height(); + aGrf.Pos().X() = rOrg.Left(); + break; + + case GPOS_MB: + aGrf.Pos().Y() = rOrg.Bottom() - aGrfSize.Height(); + aGrf.Pos().X() = rOrg.Left() + rOrg.Width()/2 - aGrfSize.Width()/2; + break; + + case GPOS_RB: + aGrf.Pos().Y() = rOrg.Bottom() - aGrfSize.Height(); + aGrf.Pos().X() = rOrg.Right() - aGrfSize.Width(); + break; + + case GPOS_AREA: + aGrf = rOrg; + // Despite the fact that the background graphic has to fill the complete + // area, we already checked, whether the graphic will completely fill out + // the region the <rOut> that is to be painted. Thus, nothing has to be + // touched again. + // E.g. this is the case for a Fly Frame without a background + // brush positioned on the border of the page which inherited the background + // brush from the page. + bRetouche = !rOut.IsInside( aGrf ); + break; + + case GPOS_TILED: + { + // draw background of tiled graphic before drawing tiled graphic in loop + // determine graphic object + GraphicObject* pGraphicObj = const_cast< GraphicObject* >(pBrush->GetGraphicObject()); + // calculate aligned paint rectangle + SwRect aAlignedPaintRect = rOut; + ::SwAlignRect( aAlignedPaintRect, &rSh, pOutDev ); + // draw background color for aligned paint rectangle + lcl_DrawGraphicBackgrd( *pBrush, pOutDev, aAlignedPaintRect, *pGraphicObj, bGrfNum, gProp ); + + // set left-top-corner of background graphic to left-top-corner of the + // area, from which the background brush is determined. + aGrf.Pos() = rOrg.Pos(); + // setup clipping at output device + pOutDev->Push( PushFlags::CLIPREGION ); + pOutDev->IntersectClipRegion( rOut.SVRect() ); + // use new method <GraphicObject::DrawTiled(::)> + { + // calculate paint offset + Point aPaintOffset( aAlignedPaintRect.Pos() - aGrf.Pos() ); + // draw background graphic tiled for aligned paint rectangle + // #i42643# + // For PDF export, every draw operation for bitmaps takes a + // noticeable amount of place (~50 characters). Thus, optimize + // between tile bitmap size and number of drawing operations here. + + // A_out + // n_chars = k1 * ---------- + k2 * A_bitmap + // A_bitmap + + // minimum n_chars is obtained for (derive for A_bitmap, + // set to 0, take positive solution): + // k1 + // A_bitmap = Sqrt( ---- A_out ) + // k2 + + // where k1 is the number of chars per draw operation, and + // k2 is the number of chars per bitmap pixel. + // This is approximately 50 and 7 for current PDF writer, respectively. + + const double k1( 50 ); + const double k2( 7 ); + const Size aSize( aAlignedPaintRect.SSize() ); + const double Abitmap( k1/k2 * static_cast<double>(aSize.Width())*aSize.Height() ); + + pGraphicObj->DrawTiled( pOutDev, + aAlignedPaintRect.SVRect(), + aGrf.SSize(), + Size( aPaintOffset.X(), aPaintOffset.Y() ), + GraphicManagerDrawFlags::STANDARD, + std::max( 128, static_cast<int>( sqrt(sqrt( Abitmap)) + .5 ) ) ); + } + // reset clipping at output device + pOutDev->Pop(); + // set <bDraw> and <bRetouche> to false, indicating that background + // graphic and background are already drawn. + bDraw = bRetouche = false; + } + break; + + case GPOS_NONE: + bDraw = false; + break; + + default: OSL_ENSURE( !pOutDev, "new Graphic position?" ); + } + + /// init variable <bGrfBackgrdAlreadDrawn> to indicate, if background of + /// graphic is already drawn or not. + bool bGrfBackgrdAlreadyDrawn = false; + if ( bRetouche ) + { + pOutDev->Push( PushFlags::FILLCOLOR|PushFlags::LINECOLOR ); + pOutDev->SetLineColor(); + + // check, if a existing background graphic (not filling the complete + // background) is transparent drawn and the background color is + // "no fill" respectively "auto fill", if background transparency + // has to be considered. + // If YES, memorize transparency of background graphic. + // check also, if background graphic bitmap is transparent. + bool bTransparentGrfWithNoFillBackgrd = false; + sal_Int32 nGrfTransparency = 0; + bool bGrfIsTransparent = false; + if ( (ePos != GPOS_NONE) && + (ePos != GPOS_TILED) && (ePos != GPOS_AREA) + ) + { + GraphicObject *pGrf = const_cast<GraphicObject*>(pBrush->GetGraphicObject()); + if ( bConsiderBackgroundTransparency ) + { + GraphicAttr aGrfAttr = pGrf->GetAttr(); + if ( (aGrfAttr.GetTransparency() != 0) && + (pBrush->GetColor() == COL_TRANSPARENT) + ) + { + bTransparentGrfWithNoFillBackgrd = true; + nGrfTransparency = aGrfAttr.GetTransparency(); + } + } + if ( pGrf->IsTransparent() ) + { + bGrfIsTransparent = true; + } + } + + // to get color of brush, check background color against COL_TRANSPARENT ("no fill"/"auto fill") + // instead of checking, if transparency is not set. + const Color aColor( pBrush && + ( !(pBrush->GetColor() == COL_TRANSPARENT) || + gProp.bSFlyMetafile ) + ? pBrush->GetColor() + : aGlobalRetoucheColor ); + + // determine, if background region have to be + // drawn transparent. + // background region has to be drawn transparent, if + // background transparency have to be considered + // AND + // ( background color is transparent OR + // background graphic is transparent and background color is "no fill" + // ) + + enum DrawStyle { + Default, + Transparent, + } eDrawStyle = Default; + + if (bConsiderBackgroundTransparency && + ( ( aColor.GetTransparency() != 0) || + bTransparentGrfWithNoFillBackgrd ) ) + { + eDrawStyle = Transparent; + } + + // #i75614# reset draw mode in high contrast mode in order to get fill color set + const DrawModeFlags nOldDrawMode = pOutDev->GetDrawMode(); + if ( gProp.pSGlobalShell->GetWin() && + Application::GetSettings().GetStyleSettings().GetHighContrastMode() ) + { + pOutDev->SetDrawMode( DrawModeFlags::Default ); + } + + // OD 06.08.2002 #99657# - if background region has to be drawn + // transparent, set only the RGB values of the background color as + // the fill color for the output device. + switch (eDrawStyle) + { + case Transparent: + { + if( pOutDev->GetFillColor() != aColor.GetRGBColor() ) + pOutDev->SetFillColor( aColor.GetRGBColor() ); + break; + } + default: + { + if( pOutDev->GetFillColor() != aColor ) + pOutDev->SetFillColor( aColor ); + break; + } + } + + // #i75614# + // restore draw mode + pOutDev->SetDrawMode( nOldDrawMode ); + + // OD 02.09.2002 #99657# + switch (eDrawStyle) + { + case Transparent: + { + // background region have to be drawn transparent. + // Thus, create a poly-polygon from the region and draw it with + // the corresponding transparency percent. + tools::PolyPolygon aDrawPoly( rOut.SVRect() ); + if ( aGrf.HasArea() ) + { + if ( !bGrfIsTransparent ) + { + // subtract area of background graphic from draw area + // OD 08.10.2002 #103898# - consider only that part of the + // graphic area that is overlapping with draw area. + SwRect aTmpGrf = aGrf; + aTmpGrf.Intersection( rOut ); + if ( aTmpGrf.HasArea() ) + { + tools::Polygon aGrfPoly( aTmpGrf.SVRect() ); + aDrawPoly.Insert( aGrfPoly ); + } + } + else + bGrfBackgrdAlreadyDrawn = true; + } + // calculate transparency percent: + // ( <transparency value[0x01..0xFF]>*100 + 0x7F ) / 0xFF + // If there is a background graphic with a background color "no fill"/"auto fill", + // the transparency value is taken from the background graphic, + // otherwise take the transparency value from the color. + sal_Int8 nTransparencyPercent = static_cast<sal_Int8>( + (( bTransparentGrfWithNoFillBackgrd ? nGrfTransparency : aColor.GetTransparency() + )*100 + 0x7F)/0xFF); + // draw poly-polygon transparent + pOutDev->DrawTransparent( aDrawPoly, nTransparencyPercent ); + + break; + } + case Default: + default: + { + SwRegionRects aRegion( rOut, 4 ); + if ( !bGrfIsTransparent ) + aRegion -= aGrf; + else + bGrfBackgrdAlreadyDrawn = true; + // loop rectangles of background region, which has to be drawn + for( size_t i = 0; i < aRegion.size(); ++i ) + { + pOutDev->DrawRect( aRegion[i].SVRect() ); + } + } + } + pOutDev ->Pop(); + } + + if( bDraw && aGrf.IsOver( rOut ) ) + // OD 02.09.2002 #99657# + // add parameter <bGrfBackgrdAlreadyDrawn> + lcl_DrawGraphic( *pBrush, pOutDev, rSh, aGrf, rOut, true, bGrfNum, gProp, + bGrfBackgrdAlreadyDrawn ); + + if( bReplaceGrfNum ) + { + const BitmapEx& rBmp = rSh.GetReplacementBitmap(false); + vcl::Font aTmp( pOutDev->GetFont() ); + Graphic::DrawEx( pOutDev, aEmptyOUStr, aTmp, rBmp, rOrg.Pos(), rOrg.SSize() ); + } +} + +/** + * Local helper for SwRootFrame::Paint(..) - 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.Left() -= (aTwipToPxSize.Width()/2 + 1); + aSizedRect.Right() += (aTwipToPxSize.Width()/2 + 1); + aSizedRect.Top() -= (aTwipToPxSize.Height()/2 + 1); + aSizedRect.Bottom() += (aTwipToPxSize.Height()/2 + 1); + + // adjust left() + while ( (aOut.LogicToPixel(aSizedRect)).Left() < aOrgPxRect.Left() ) + { + ++aSizedRect.Left(); + } + // adjust right() + while ( (aOut.LogicToPixel(aSizedRect)).Right() > aOrgPxRect.Right() ) + { + --aSizedRect.Right(); + } + // adjust top() + while ( (aOut.LogicToPixel(aSizedRect)).Top() < aOrgPxRect.Top() ) + { + ++aSizedRect.Top(); + } + // adjust bottom() + while ( (aOut.LogicToPixel(aSizedRect)).Bottom() > aOrgPxRect.Bottom() ) + { + --aSizedRect.Bottom(); + } + + 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.Left(); + aTestNewPxRect = aOut.LogicToPixel( aSizedRect ); + OSL_ENSURE( aTestOrgPxRect.Left() >= (aTestNewPxRect.Left()+1), + "Error in lcl_AlignRectToPixelSize(..): Left() not correct adjusted"); + ++aSizedRect.Left(); + // check Right() + ++aSizedRect.Right(); + aTestNewPxRect = aOut.LogicToPixel( aSizedRect ); + OSL_ENSURE( aTestOrgPxRect.Right() <= (aTestNewPxRect.Right()-1), + "Error in lcl_AlignRectToPixelSize(..): Right() not correct adjusted"); + --aSizedRect.Right(); + // check Top() + --aSizedRect.Top(); + aTestNewPxRect = aOut.LogicToPixel( aSizedRect ); + OSL_ENSURE( aTestOrgPxRect.Top() >= (aTestNewPxRect.Top()+1), + "Error in lcl_AlignRectToPixelSize(..): Top() not correct adjusted"); + ++aSizedRect.Top(); + // check Bottom() + ++aSizedRect.Bottom(); + aTestNewPxRect = aOut.LogicToPixel( aSizedRect ); + OSL_ENSURE( aTestOrgPxRect.Bottom() <= (aTestNewPxRect.Bottom()-1), + "Error in lcl_AlignRectToPixelSize(..): Bottom() not correct adjusted"); + --aSizedRect.Bottom(); +#endif +} + +// FUNCTIONS USED FOR COLLAPSING TABLE BORDER LINES START + +struct SwLineEntry +{ + SwTwips mnKey; + SwTwips mnStartPos; + SwTwips mnEndPos; + + svx::frame::Style maAttribute; + + enum OverlapType { NO_OVERLAP, OVERLAP1, OVERLAP2, OVERLAP3 }; + +public: + SwLineEntry( SwTwips nKey, + SwTwips nStartPos, + SwTwips nEndPos, + const svx::frame::Style& rAttribute ); + + OverlapType Overlaps( const SwLineEntry& rComp ) const; +}; + +SwLineEntry::SwLineEntry( SwTwips nKey, + SwTwips nStartPos, + SwTwips nEndPos, + const svx::frame::Style& rAttribute ) + : mnKey( nKey ), + mnStartPos( nStartPos ), + mnEndPos( nEndPos ), + maAttribute( rAttribute ) +{ +} + +/* + + 1. ---------- rOld + ---------- rNew + + 2. ---------- rOld + ------------- rNew + + 3. ------- rOld + ------------- rNew + + 4. ------------- rOld + ---------- rNew + + 5. ---------- rOld + ---- rNew + + 6. ---------- rOld + ---------- rNew + + 7. ------------- rOld + ---------- rNew + + 8. ---------- rOld + ------------- rNew + + 9. ---------- rOld + ---------- rNew +*/ + +SwLineEntry::OverlapType SwLineEntry::Overlaps( const SwLineEntry& rNew ) const +{ + SwLineEntry::OverlapType eRet = OVERLAP3; + + if ( mnStartPos >= rNew.mnEndPos || mnEndPos <= rNew.mnStartPos ) + eRet = NO_OVERLAP; + + // 1, 2, 3 + else if ( mnEndPos < rNew.mnEndPos ) + eRet = OVERLAP1; + + // 4, 5, 6, 7 + else if ( mnStartPos <= rNew.mnStartPos && mnEndPos >= rNew.mnEndPos ) + eRet = OVERLAP2; + + // 8, 9 + return eRet; +} + +struct lt_SwLineEntry +{ + bool operator()( const SwLineEntry& e1, const SwLineEntry& e2 ) const + { + return e1.mnStartPos < e2.mnStartPos; + } +}; + +typedef std::set< SwLineEntry, lt_SwLineEntry > SwLineEntrySet; +typedef std::map< SwTwips, SwLineEntrySet > SwLineEntryMap; + +class SwTabFramePainter +{ + SwLineEntryMap maVertLines; + SwLineEntryMap maHoriLines; + const SwTabFrame& mrTabFrame; + + void Insert( SwLineEntry&, bool bHori ); + void Insert( const SwFrame& rFrame, const SvxBoxItem& rBoxItem ); + void HandleFrame( const SwLayoutFrame& rFrame ); + void FindStylesForLine( const Point&, + const Point&, + svx::frame::Style*, + bool bHori ) const; + +public: + explicit SwTabFramePainter( const SwTabFrame& rTabFrame ); + + void PaintLines( OutputDevice& rDev, const SwRect& rRect ) const; +}; + +SwTabFramePainter::SwTabFramePainter( const SwTabFrame& rTabFrame ) + : mrTabFrame( rTabFrame ) +{ + HandleFrame( rTabFrame ); +} + +void SwTabFramePainter::HandleFrame( const SwLayoutFrame& rLayoutFrame ) +{ + // Add border lines of cell frames. Skip covered cells. Skip cells + // in special row span row, which do not have a negative row span: + if ( rLayoutFrame.IsCellFrame() && !rLayoutFrame.IsCoveredCell() ) + { + const SwCellFrame* pThisCell = static_cast<const SwCellFrame*>(&rLayoutFrame); + const SwRowFrame* pRowFrame = static_cast<const SwRowFrame*>(pThisCell->GetUpper()); + const long nRowSpan = pThisCell->GetTabBox()->getRowSpan(); + if ( !pRowFrame->IsRowSpanLine() || nRowSpan > 1 || nRowSpan < -1 ) + { + SwBorderAttrAccess aAccess( SwFrame::GetCache(), &rLayoutFrame ); + const SwBorderAttrs& rAttrs = *aAccess.Get(); + const SvxBoxItem& rBox = rAttrs.GetBox(); + Insert( rLayoutFrame, rBox ); + } + } + + // Recurse into lower layout frames, but do not recurse into lower tabframes. + const SwFrame* pLower = rLayoutFrame.Lower(); + while ( pLower ) + { + const SwLayoutFrame* pLowerLayFrame = dynamic_cast<const SwLayoutFrame*>(pLower); + if ( pLowerLayFrame && !pLowerLayFrame->IsTabFrame() ) + HandleFrame( *pLowerLayFrame ); + + pLower = pLower->GetNext(); + } +} + +void SwTabFramePainter::PaintLines(OutputDevice& rDev, const SwRect& rRect) const +{ + // #i16816# tagged pdf support + SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, nullptr, rDev ); + + SwLineEntryMap::const_iterator aIter = maHoriLines.begin(); + bool bHori = true; + + // color for subsidiary lines: + const Color& rCol( SwViewOption::GetTableBoundariesColor() ); + + // high contrast mode: + // overrides the color of non-subsidiary lines. + const Color* pHCColor = nullptr; + DrawModeFlags nOldDrawMode = rDev.GetDrawMode(); + if( gProp.pSGlobalShell->GetWin() && + Application::GetSettings().GetStyleSettings().GetHighContrastMode() ) + { + pHCColor = &SwViewOption::GetFontColor(); + rDev.SetDrawMode( DrawModeFlags::Default ); + } + + const SwFrame* pUpper = mrTabFrame.GetUpper(); + SwRect aUpper( pUpper->Prt() ); + aUpper.Pos() += pUpper->Frame().Pos(); + SwRect aUpperAligned( aUpper ); + ::SwAlignRect( aUpperAligned, gProp.pSGlobalShell, &rDev ); + drawinglayer::primitive2d::Primitive2DContainer aHorizontalSequence; + drawinglayer::primitive2d::Primitive2DContainer aVerticalSequence; + + while ( true ) + { + if ( bHori && aIter == maHoriLines.end() ) + { + aIter = maVertLines.begin(); + bHori = false; + } + + if ( !bHori && aIter == maVertLines.end() ) + break; + + const SwLineEntrySet& rEntrySet = (*aIter).second; + for (SwLineEntrySet::const_iterator aSetIter = rEntrySet.begin(); + aSetIter != rEntrySet.end(); ++aSetIter) + { + const SwLineEntry& rEntry = *aSetIter; + const svx::frame::Style& rEntryStyle( (*aSetIter).maAttribute ); + + Point aStart, aEnd; + if ( bHori ) + { + aStart.X() = rEntry.mnStartPos; + aStart.Y() = rEntry.mnKey; + aEnd.X() = rEntry.mnEndPos; + aEnd.Y() = rEntry.mnKey; + } + else + { + aStart.X() = rEntry.mnKey; + aStart.Y() = rEntry.mnStartPos; + aEnd.X() = rEntry.mnKey; + aEnd.Y() = rEntry.mnEndPos; + } + + svx::frame::Style aStyles[ 7 ]; + aStyles[ 0 ] = rEntryStyle; + FindStylesForLine( aStart, aEnd, aStyles, bHori ); + SwRect aRepaintRect( aStart, aEnd ); + + // the repaint rectangle has to be moved a bit for the centered lines: + SwTwips nRepaintRectSize = !rEntryStyle.GetWidth() ? 1 : rEntryStyle.GetWidth(); + if ( bHori ) + { + aRepaintRect.Height( 2 * nRepaintRectSize ); + aRepaintRect.Pos().Y() -= nRepaintRectSize; + } + else + { + aRepaintRect.Width( 2 * nRepaintRectSize ); + aRepaintRect.Pos().X() -= nRepaintRectSize; + } + + if (!rRect.IsOver(aRepaintRect)) + { + continue; + } + + // subsidiary lines + const Color* pTmpColor = nullptr; + if (0 == aStyles[ 0 ].GetWidth()) + { + if (isTableBoundariesEnabled() && gProp.pSGlobalShell->GetWin()) + aStyles[ 0 ].Set( rCol, rCol, rCol, false, 1, 0, 0 ); + else + aStyles[0].SetType(SvxBorderLineStyle::NONE); + } + else + pTmpColor = pHCColor; + + // The (twip) positions will be adjusted to meet these requirements: + // 1. The y coordinates are located in the middle of the pixel grid + // 2. The x coordinated are located at the beginning of the pixel grid + // This is done, because the horizontal lines are painted "at + // beginning", whereas the vertical lines are painted "centered". + // By making the line sizes a multiple of one pixel size, we can + // assure that all lines having the same twip size have the same + // pixel size, independent of their position on the screen. + Point aPaintStart = rDev.PixelToLogic( rDev.LogicToPixel(aStart) ); + Point aPaintEnd = rDev.PixelToLogic( rDev.LogicToPixel(aEnd) ); + + if (gProp.pSGlobalShell->GetWin()) + { + // The table borders do not use SwAlignRect, but all the other frames do. + // Therefore we tweak the outer borders a bit to achieve that the outer + // borders match the subsidiary lines of the upper: + if (aStart.X() == aUpper.Left()) + aPaintStart.X() = aUpperAligned.Left(); + else if (aStart.X() == aUpper.Rigth_()) + aPaintStart.X() = aUpperAligned.Rigth_(); + if (aStart.Y() == aUpper.Top()) + aPaintStart.Y() = aUpperAligned.Top(); + else if (aStart.Y() == aUpper.Bottom_()) + aPaintStart.Y() = aUpperAligned.Bottom_(); + + if (aEnd.X() == aUpper.Left()) + aPaintEnd.X() = aUpperAligned.Left(); + else if (aEnd.X() == aUpper.Rigth_()) + aPaintEnd.X() = aUpperAligned.Rigth_(); + if (aEnd.Y() == aUpper.Top()) + aPaintEnd.Y() = aUpperAligned.Top(); + else if (aEnd.Y() == aUpper.Bottom_()) ... etc. - the rest is truncated _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits