Add a comment to clarify what kind of inputs the class handles
[LibreOffice.git] / sw / source / core / layout / paintfrm.cxx
bloba0b91c84300a7b461104fcc3f671fc3b8162923e
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <utility>
21 #include <vcl/canvastools.hxx>
22 #include <tools/lazydelete.hxx>
23 #include <sfx2/docfile.hxx>
24 #include <sfx2/printer.hxx>
25 #include <sfx2/progress.hxx>
26 #include <sfx2/StylePreviewRenderer.hxx>
27 #include <editeng/brushitem.hxx>
28 #include <editeng/prntitem.hxx>
29 #include <editeng/boxitem.hxx>
30 #include <editeng/shaditem.hxx>
31 #include <svx/ctredlin.hxx>
32 #include <svx/framelink.hxx>
33 #include <svx/svdouno.hxx>
34 #include <drawdoc.hxx>
35 #include <tgrditem.hxx>
36 #include <calbck.hxx>
37 #include <fmtsrnd.hxx>
38 #include <fmtclds.hxx>
39 #include <fmturl.hxx>
40 #include <strings.hrc>
41 #include <swmodule.hxx>
42 #include <rootfrm.hxx>
43 #include <pagefrm.hxx>
44 #include <section.hxx>
45 #include <sectfrm.hxx>
46 #include <viewimp.hxx>
47 #include <dflyobj.hxx>
48 #include <flyfrm.hxx>
49 #include <frmatr.hxx>
50 #include <frmtool.hxx>
51 #include <viewopt.hxx>
52 #include <dview.hxx>
53 #include <dcontact.hxx>
54 #include <txtfrm.hxx>
55 #include <ftnfrm.hxx>
56 #include <tabfrm.hxx>
57 #include <rowfrm.hxx>
58 #include <cellfrm.hxx>
59 #include <notxtfrm.hxx>
60 #include <layact.hxx>
61 #include <pagedesc.hxx>
62 #include <ptqueue.hxx>
63 #include <noteurl.hxx>
64 #include "virtoutp.hxx"
65 #include <lineinfo.hxx>
66 #include <dbg_lay.hxx>
67 #include <docsh.hxx>
68 #include <svx/svdogrp.hxx>
69 #include <sortedobjs.hxx>
70 #include <EnhancedPDFExportHelper.hxx>
71 #include <bodyfrm.hxx>
72 #include <hffrm.hxx>
73 #include <colfrm.hxx>
74 #include <sw_primitivetypes2d.hxx>
75 #include <swfont.hxx>
77 #include <svx/sdr/primitive2d/sdrframeborderprimitive2d.hxx>
78 #include <svx/sdr/contact/viewobjectcontact.hxx>
79 #include <svx/sdr/contact/viewcontact.hxx>
80 #include <DocumentSettingManager.hxx>
81 #include <IDocumentDeviceAccess.hxx>
82 #include <IDocumentDrawModelAccess.hxx>
84 #include <ndole.hxx>
85 #include <PostItMgr.hxx>
86 #include <FrameControlsManager.hxx>
87 #include <vcl/settings.hxx>
89 #include <svx/sdr/attribute/sdrallfillattributeshelper.hxx>
91 #include <svtools/borderhelper.hxx>
93 #include <bitmaps.hlst>
94 #include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
95 #include <drawinglayer/primitive2d/PolyPolygonStrokePrimitive2D.hxx>
96 #include <drawinglayer/primitive2d/discreteshadowprimitive2d.hxx>
97 #include <drawinglayer/primitive2d/maskprimitive2d.hxx>
98 #include <drawinglayer/primitive2d/textprimitive2d.hxx>
99 #include <drawinglayer/primitive2d/textlayoutdevice.hxx>
100 #include <drawinglayer/processor2d/baseprocessor2d.hxx>
101 #include <drawinglayer/processor2d/processor2dtools.hxx>
102 #include <svx/unoapi.hxx>
103 #include <svx/svdpagv.hxx>
104 #include <svx/xfillit0.hxx>
105 #include <basegfx/matrix/b2dhommatrixtools.hxx>
106 #include <basegfx/color/bcolortools.hxx>
107 #include <basegfx/utils/b2dclipstate.hxx>
108 #include <sal/log.hxx>
110 #include <memory>
111 #include <vector>
112 #include <algorithm>
113 #include <wrtsh.hxx>
114 #include <edtwin.hxx>
115 #include <view.hxx>
116 #include <paintfrm.hxx>
117 #include <textboxhelper.hxx>
118 #include <o3tl/typed_flags_set.hxx>
120 #include <vcl/BitmapTools.hxx>
121 #include <comphelper/configuration.hxx>
122 #include <comphelper/lok.hxx>
123 #include <svtools/optionsdrawinglayer.hxx>
124 #include <vcl/GraphicLoader.hxx>
125 #include <basegfx/polygon/b2dpolygontools.hxx>
127 #include <svl/style.hxx>
128 #include <ndtxt.hxx>
129 #include <unotools/configmgr.hxx>
130 #include <vcl/hatch.hxx>
132 using namespace ::editeng;
133 using namespace ::com::sun::star;
135 namespace {
137 struct SwPaintProperties;
139 //Class declaration; here because they are only used in this file
140 enum class SubColFlags {
141 Page = 0x01, //Helplines of the page
142 Tab = 0x08, //Helplines inside tables
143 Fly = 0x10, //Helplines inside fly frames
144 Sect = 0x20, //Helplines inside sections
149 namespace o3tl {
150 template<> struct typed_flags<SubColFlags> : is_typed_flags<SubColFlags, 0x39> {};
153 namespace {
155 // Classes collecting the border lines and help lines
156 class SwLineRect : public SwRect
158 Color m_aColor;
159 SvxBorderLineStyle m_nStyle;
160 const SwTabFrame* m_pTabFrame;
161 SubColFlags m_nSubColor; //colorize subsidiary lines
162 bool m_bPainted; //already painted?
163 sal_uInt8 m_nLock; //To distinguish the line and the hell layer.
164 public:
165 SwLineRect( const SwRect &rRect, const Color *pCol, const SvxBorderLineStyle nStyle,
166 const SwTabFrame *pT , const SubColFlags nSCol );
168 const Color& GetColor() const { return m_aColor; }
169 SvxBorderLineStyle GetStyle() const { return m_nStyle; }
170 const SwTabFrame* GetTab() const { return m_pTabFrame; }
171 void SetPainted() { m_bPainted = true; }
172 void Lock(bool bLock)
174 if (bLock)
175 ++m_nLock;
176 else if (m_nLock)
177 --m_nLock;
179 bool IsPainted() const { return m_bPainted; }
180 bool IsLocked() const { return m_nLock != 0; }
181 SubColFlags GetSubColor() const { return m_nSubColor; }
183 bool MakeUnion(const SwRect& rRect, SwPaintProperties const& properties);
188 #ifdef IOS
189 static void dummy_function()
191 pid_t pid = getpid();
192 (void) pid;
194 #endif
196 namespace {
198 class SwLineRects
200 public:
201 std::vector<SwLineRect> m_aLineRects;
202 typedef std::vector< SwLineRect >::const_iterator const_iterator;
203 typedef std::vector< SwLineRect >::iterator iterator;
204 typedef std::vector< SwLineRect >::reverse_iterator reverse_iterator;
205 typedef std::vector< SwLineRect >::size_type size_type;
206 size_t m_nLastCount; //avoid unnecessary cycles in PaintLines
207 SwLineRects()
208 : m_nLastCount(0)
210 #ifdef IOS
211 // Work around what is either a compiler bug in Xcode 5.1.1,
212 // or some unknown problem in this file. If I ifdef out this
213 // call, I get a crash in SwSubsRects::PaintSubsidiary: the
214 // address of the rLi reference variable is claimed to be
215 // 0x4000000!
216 dummy_function();
217 #endif
219 void AddLineRect( const SwRect& rRect, const Color *pColor, const SvxBorderLineStyle nStyle,
220 const SwTabFrame *pTab, const SubColFlags nSCol, SwPaintProperties const &properties );
221 void ConnectEdges( OutputDevice const *pOut, SwPaintProperties const &properties );
222 void PaintLines ( OutputDevice *pOut, SwPaintProperties const &properties );
223 void LockLines( bool bLock );
225 //Limit lines to 100
226 bool isFull() const { return m_aLineRects.size() > 100; }
229 class SwSubsRects : public SwLineRects
231 void RemoveSuperfluousSubsidiaryLines( const SwLineRects &rRects, SwPaintProperties const &properties );
232 public:
233 void PaintSubsidiary( OutputDevice *pOut, const SwLineRects *pRects, SwPaintProperties const &properties );
236 class BorderLines
238 drawinglayer::primitive2d::Primitive2DContainer m_Lines;
239 public:
240 void AddBorderLines(drawinglayer::primitive2d::Primitive2DContainer&& rContainer);
241 drawinglayer::primitive2d::Primitive2DContainer GetBorderLines_Clear()
243 drawinglayer::primitive2d::Primitive2DContainer lines;
244 lines.swap(m_Lines);
245 return lines;
251 // Default zoom factor
252 const double aEdgeScale = 0.5;
254 //To optimize the expensive RetouchColor determination
255 Color aGlobalRetoucheColor;
257 namespace sw
259 Color* GetActiveRetoucheColor()
261 return &aGlobalRetoucheColor;
265 namespace {
268 * Container for static properties
270 struct SwPaintProperties {
271 // Only repaint the Fly content as well as the background of the Fly content if
272 // a metafile is taken of the Fly.
273 bool bSFlyMetafile;
274 VclPtr<OutputDevice> pSFlyMetafileOut;
275 SwViewShell *pSGlobalShell;
277 // Retouch for transparent Flys is done by the background of the Flys.
278 // The Fly itself should certainly not be spared out. See PaintSwFrameBackground and
279 // lcl_SubtractFlys()
280 SwFlyFrame *pSRetoucheFly;
281 SwFlyFrame *pSRetoucheFly2;
282 SwFlyFrame *pSFlyOnlyDraw;
284 // The borders will be collected in pSLines during the Paint and later
285 // possibly merge them.
286 // The help lines will be collected and merged in gProp.pSSubsLines. These will
287 // be compared with pSLines before the work in order to avoid help lines
288 // to hide borders.
289 std::unique_ptr<BorderLines> pBLines;
290 std::unique_ptr<SwLineRects> pSLines;
291 std::unique_ptr<SwSubsRects> pSSubsLines;
293 // global variable for sub-lines of body, header, footer, section and footnote frames.
294 std::unique_ptr<SwSubsRects> pSSpecSubsLines;
295 SfxProgress *pSProgress;
297 // Sizes of a pixel and the corresponding halves. Will be reset when
298 // entering SwRootFrame::PaintSwFrame
299 tools::Long nSPixelSzW;
300 tools::Long nSPixelSzH;
301 tools::Long nSHalfPixelSzW;
302 tools::Long nSHalfPixelSzH;
303 tools::Long nSMinDistPixelW;
304 tools::Long nSMinDistPixelH;
306 Color aSGlobalRetoucheColor;
308 // Current zoom factor
309 double aSScaleX;
310 double aSScaleY;
312 SwPaintProperties()
313 : bSFlyMetafile(false)
314 , pSFlyMetafileOut(nullptr)
315 , pSGlobalShell(nullptr)
316 , pSRetoucheFly(nullptr)
317 , pSRetoucheFly2(nullptr)
318 , pSFlyOnlyDraw(nullptr)
319 , pSProgress(nullptr)
320 , nSPixelSzW(0)
321 , nSPixelSzH(0)
322 , nSHalfPixelSzW(0)
323 , nSHalfPixelSzH(0)
324 , nSMinDistPixelW(0)
325 , nSMinDistPixelH(0)
326 , aSScaleX(1)
327 , aSScaleY(1)
335 static SwPaintProperties gProp;
337 static bool isSubsidiaryLinesEnabled()
339 return !gProp.pSGlobalShell->GetViewOptions()->IsPagePreview() &&
340 !gProp.pSGlobalShell->GetViewOptions()->IsReadonly() &&
341 !gProp.pSGlobalShell->GetViewOptions()->IsFormView() &&
342 gProp.pSGlobalShell->GetViewOptions()->IsShowBoundaries();
346 * Set borders alignment statics
347 * Adjustment for 'small' twip-to-pixel relations:
348 * For 'small' twip-to-pixel relations (less than 2:1)
349 * values of <gProp.nSHalfPixelSzW> and <gProp.nSHalfPixelSzH> are set to ZERO
351 void SwCalcPixStatics( vcl::RenderContext const *pOut )
353 // determine 'small' twip-to-pixel relation
354 bool bSmallTwipToPxRelW = false;
355 bool bSmallTwipToPxRelH = false;
357 Size aCheckTwipToPxRelSz( pOut->PixelToLogic( Size( 100, 100 )) );
358 if ( (aCheckTwipToPxRelSz.Width()/100.0) < 2.0 )
360 bSmallTwipToPxRelW = true;
362 if ( (aCheckTwipToPxRelSz.Height()/100.0) < 2.0 )
364 bSmallTwipToPxRelH = true;
368 Size aSz( pOut->PixelToLogic( Size( 1,1 )) );
370 gProp.nSPixelSzW = aSz.Width();
371 if( !gProp.nSPixelSzW )
372 gProp.nSPixelSzW = 1;
373 gProp.nSPixelSzH = aSz.Height();
374 if( !gProp.nSPixelSzH )
375 gProp.nSPixelSzH = 1;
377 // consider 'small' twip-to-pixel relations
378 if ( !bSmallTwipToPxRelW )
380 gProp.nSHalfPixelSzW = gProp.nSPixelSzW / 2 + 1;
382 else
384 gProp.nSHalfPixelSzW = 0;
386 // consider 'small' twip-to-pixel relations
387 if ( !bSmallTwipToPxRelH )
389 gProp.nSHalfPixelSzH = gProp.nSPixelSzH / 2 + 1;
391 else
393 gProp.nSHalfPixelSzH = 0;
396 gProp.nSMinDistPixelW = gProp.nSPixelSzW * 2 + 1;
397 gProp.nSMinDistPixelH = gProp.nSPixelSzH * 2 + 1;
399 const MapMode &rMap = pOut->GetMapMode();
400 gProp.aSScaleX = double(rMap.GetScaleX());
401 gProp.aSScaleY = double(rMap.GetScaleY());
404 namespace {
407 * To be able to save the statics so the paint is more or less reentrant
409 class SwSavePaintStatics : public SwPaintProperties
411 public:
412 SwSavePaintStatics();
413 ~SwSavePaintStatics();
418 SwSavePaintStatics::SwSavePaintStatics()
420 // Saving globales
421 bSFlyMetafile = gProp.bSFlyMetafile;
422 pSGlobalShell = gProp.pSGlobalShell;
423 pSFlyMetafileOut = gProp.pSFlyMetafileOut;
424 pSRetoucheFly = gProp.pSRetoucheFly;
425 pSRetoucheFly2 = gProp.pSRetoucheFly2;
426 pSFlyOnlyDraw = gProp.pSFlyOnlyDraw;
427 pBLines = std::move(gProp.pBLines);
428 pSLines = std::move(gProp.pSLines);
429 pSSubsLines = std::move(gProp.pSSubsLines);
430 pSSpecSubsLines = std::move(gProp.pSSpecSubsLines);
431 pSProgress = gProp.pSProgress;
432 nSPixelSzW = gProp.nSPixelSzW;
433 nSPixelSzH = gProp.nSPixelSzH;
434 nSHalfPixelSzW = gProp.nSHalfPixelSzW;
435 nSHalfPixelSzH = gProp.nSHalfPixelSzH;
436 nSMinDistPixelW = gProp.nSMinDistPixelW;
437 nSMinDistPixelH = gProp.nSMinDistPixelH ;
438 aSGlobalRetoucheColor = aGlobalRetoucheColor;
439 aSScaleX = gProp.aSScaleX;
440 aSScaleY = gProp.aSScaleY;
442 // Restoring globales to default
443 gProp.bSFlyMetafile = false;
444 gProp.pSFlyMetafileOut = nullptr;
445 gProp.pSRetoucheFly = nullptr;
446 gProp.pSRetoucheFly2 = nullptr;
447 gProp.nSPixelSzW = gProp.nSPixelSzH =
448 gProp.nSHalfPixelSzW = gProp.nSHalfPixelSzH =
449 gProp.nSMinDistPixelW = gProp.nSMinDistPixelH = 0;
450 gProp.aSScaleX = gProp.aSScaleY = 1.0;
451 gProp.pSProgress = nullptr;
454 SwSavePaintStatics::~SwSavePaintStatics()
456 // Restoring globales to saved one
457 gProp.pSGlobalShell = pSGlobalShell;
458 gProp.bSFlyMetafile = bSFlyMetafile;
459 gProp.pSFlyMetafileOut = pSFlyMetafileOut;
460 gProp.pSRetoucheFly = pSRetoucheFly;
461 gProp.pSRetoucheFly2 = pSRetoucheFly2;
462 gProp.pSFlyOnlyDraw = pSFlyOnlyDraw;
463 gProp.pBLines = std::move(pBLines);
464 gProp.pSLines = std::move(pSLines);
465 gProp.pSSubsLines = std::move(pSSubsLines);
466 gProp.pSSpecSubsLines = std::move(pSSpecSubsLines);
467 gProp.pSProgress = pSProgress;
468 gProp.nSPixelSzW = nSPixelSzW;
469 gProp.nSPixelSzH = nSPixelSzH;
470 gProp.nSHalfPixelSzW = nSHalfPixelSzW;
471 gProp.nSHalfPixelSzH = nSHalfPixelSzH;
472 gProp.nSMinDistPixelW = nSMinDistPixelW;
473 gProp.nSMinDistPixelH = nSMinDistPixelH;
474 aGlobalRetoucheColor = aSGlobalRetoucheColor;
475 gProp.aSScaleX = aSScaleX;
476 gProp.aSScaleY = aSScaleY;
479 void BorderLines::AddBorderLines(drawinglayer::primitive2d::Primitive2DContainer&& rContainer)
481 if(!rContainer.empty())
483 m_Lines.append(std::move(rContainer));
487 SwLineRect::SwLineRect(const SwRect& rRect, const Color* pCol, const SvxBorderLineStyle nStyl,
488 const SwTabFrame* pT, const SubColFlags nSCol)
489 : SwRect(rRect)
490 , m_nStyle(nStyl)
491 , m_pTabFrame(pT)
492 , m_nSubColor(nSCol)
493 , m_bPainted(false)
494 , m_nLock(0)
496 if ( pCol != nullptr )
497 m_aColor = *pCol;
500 bool SwLineRect::MakeUnion( const SwRect &rRect, SwPaintProperties const & properties)
502 // It has already been tested outside, whether the rectangles have
503 // the same orientation (horizontal or vertical), color, etc.
504 if ( Height() > Width() ) //Vertical line
506 if ( Left() == rRect.Left() && Width() == rRect.Width() )
508 // Merge when there is no gap between the lines
509 const tools::Long nAdd = properties.nSPixelSzW + properties.nSHalfPixelSzW;
510 if ( Bottom() + nAdd >= rRect.Top() &&
511 Top() - nAdd <= rRect.Bottom() )
513 Bottom( std::max( Bottom(), rRect.Bottom() ) );
514 Top ( std::min( Top(), rRect.Top() ) );
515 return true;
519 else
521 if ( Top() == rRect.Top() && Height() == rRect.Height() )
523 // Merge when there is no gap between the lines
524 const tools::Long nAdd = properties.nSPixelSzW + properties.nSHalfPixelSzW;
525 if ( Right() + nAdd >= rRect.Left() &&
526 Left() - nAdd <= rRect.Right() )
528 Right( std::max( Right(), rRect.Right() ) );
529 Left ( std::min( Left(), rRect.Left() ) );
530 return true;
534 return false;
537 void SwLineRects::AddLineRect( const SwRect &rRect, const Color *pCol, const SvxBorderLineStyle nStyle,
538 const SwTabFrame *pTab, const SubColFlags nSCol, SwPaintProperties const & properties )
540 // Loop backwards because lines which can be combined, can usually be painted
541 // in the same context
542 for (reverse_iterator it = m_aLineRects.rbegin(); it != m_aLineRects.rend(); ++it)
544 SwLineRect &rLRect = *it;
545 // Test for the orientation, color, table
546 if ( rLRect.GetTab() == pTab &&
547 !rLRect.IsPainted() && rLRect.GetSubColor() == nSCol &&
548 (rLRect.Height() > rLRect.Width()) == (rRect.Height() > rRect.Width()) &&
549 (pCol && rLRect.GetColor() == *pCol) )
551 if ( rLRect.MakeUnion( rRect, properties ) )
552 return;
555 m_aLineRects.emplace_back(rRect, pCol, nStyle, pTab, nSCol);
558 void SwLineRects::ConnectEdges( OutputDevice const *pOut, SwPaintProperties const & properties )
560 if ( pOut->GetOutDevType() != OUTDEV_PRINTER )
562 // I'm not doing anything for a too small zoom
563 if ( properties.aSScaleX < aEdgeScale || properties.aSScaleY < aEdgeScale )
564 return;
567 static const tools::Long nAdd = 20;
569 std::vector<SwLineRect*> aCheck;
571 for (size_t i = 0; i < m_aLineRects.size(); ++i)
573 SwLineRect& rL1 = m_aLineRects[i];
574 if ( !rL1.GetTab() || rL1.IsPainted() || rL1.IsLocked() )
575 continue;
577 aCheck.clear();
579 const bool bVert = rL1.Height() > rL1.Width();
580 tools::Long nL1a, nL1b, nL1c, nL1d;
582 if ( bVert )
584 nL1a = rL1.Top(); nL1b = rL1.Left();
585 nL1c = rL1.Right(); nL1d = rL1.Bottom();
587 else
589 nL1a = rL1.Left(); nL1b = rL1.Top();
590 nL1c = rL1.Bottom(); nL1d = rL1.Right();
593 // Collect all lines to possibly link with i1
594 for (iterator it2 = m_aLineRects.begin(); it2 != m_aLineRects.end(); ++it2)
596 SwLineRect &rL2 = *it2;
597 if ( rL2.GetTab() != rL1.GetTab() ||
598 rL2.IsPainted() ||
599 rL2.IsLocked() ||
600 (bVert == (rL2.Height() > rL2.Width())) )
601 continue;
603 tools::Long nL2a, nL2b, nL2c, nL2d;
604 if ( bVert )
606 nL2a = rL2.Top(); nL2b = rL2.Left();
607 nL2c = rL2.Right(); nL2d = rL2.Bottom();
609 else
611 nL2a = rL2.Left(); nL2b = rL2.Top();
612 nL2c = rL2.Bottom(); nL2d = rL2.Right();
615 if ( (nL1a - nAdd < nL2d && nL1d + nAdd > nL2a) &&
616 ((nL1b > nL2b && nL1c < nL2c) ||
617 (nL1c >= nL2c && nL1b - nAdd < nL2c) ||
618 (nL1b <= nL2b && nL1c + nAdd > nL2b)) )
620 aCheck.push_back( &rL2 );
623 if ( aCheck.size() < 2 )
624 continue;
626 bool bRemove = false;
628 // For each line test all following ones.
629 for ( size_t k = 0; !bRemove && k < aCheck.size(); ++k )
631 SwLineRect &rR1 = *aCheck[k];
633 for ( size_t k2 = k+1; !bRemove && k2 < aCheck.size(); ++k2 )
635 SwLineRect &rR2 = *aCheck[k2];
636 if ( bVert )
638 SwLineRect *pLA = nullptr;
639 SwLineRect *pLB = nullptr;
640 if ( rR1.Top() < rR2.Top() )
642 pLA = &rR1; pLB = &rR2;
644 else if ( rR1.Top() > rR2.Top() )
646 pLA = &rR2; pLB = &rR1;
648 // are k1 and k2 describing a double line?
649 if ( pLA && pLA->Bottom() + 60 > pLB->Top() )
651 if ( rL1.Top() < pLA->Top() )
653 if ( rL1.Bottom() == pLA->Bottom() )
654 continue; //Small mistake (where?)
656 SwRect aIns( rL1 );
657 aIns.Bottom( pLA->Bottom() );
658 if ( !rL1.Contains( aIns ) )
659 continue;
660 m_aLineRects.emplace_back(aIns, &rL1.GetColor(),
661 SvxBorderLineStyle::SOLID, rL1.GetTab(),
662 SubColFlags::Tab);
663 if ( isFull() )
665 --i;
666 k = aCheck.size();
667 break;
671 if ( rL1.Bottom() > pLB->Bottom() )
672 rL1.Top( pLB->Top() ); // extend i1 on the top
673 else
674 bRemove = true; //stopping, remove i1
677 else
679 SwLineRect *pLA = nullptr;
680 SwLineRect *pLB = nullptr;
681 if ( rR1.Left() < rR2.Left() )
683 pLA = &rR1; pLB = &rR2;
685 else if ( rR1.Left() > rR2.Left() )
687 pLA = &rR2; pLB = &rR1;
689 // Is it double line?
690 if ( pLA && pLA->Right() + 60 > pLB->Left() )
692 if ( rL1.Left() < pLA->Left() )
694 if ( rL1.Right() == pLA->Right() )
695 continue; //small error
697 SwRect aIns( rL1 );
698 aIns.Right( pLA->Right() );
699 if ( !rL1.Contains( aIns ) )
700 continue;
701 m_aLineRects.emplace_back(aIns, &rL1.GetColor(),
702 SvxBorderLineStyle::SOLID, rL1.GetTab(),
703 SubColFlags::Tab);
704 if ( isFull() )
706 assert(i > 0);
707 --i;
708 k = aCheck.size();
709 break;
712 if ( rL1.Right() > pLB->Right() )
713 rL1.Left( pLB->Left() );
714 else
715 bRemove = true;
720 if ( bRemove )
722 m_aLineRects.erase(m_aLineRects.begin() + i);
723 --i;
728 void SwSubsRects::RemoveSuperfluousSubsidiaryLines( const SwLineRects &rRects, SwPaintProperties const & properties )
730 // All help lines that are covered by any border will be removed or split
731 for (size_t i = 0; i < m_aLineRects.size(); ++i)
733 // get a copy instead of a reference, because an <insert> may destroy
734 // the object due to a necessary array resize.
735 const SwLineRect aSubsLineRect(m_aLineRects[i]);
737 // add condition <aSubsLineRect.IsLocked()> in order to consider only
738 // border lines, which are *not* locked.
739 if ( aSubsLineRect.IsPainted() ||
740 aSubsLineRect.IsLocked() )
741 continue;
743 const bool bVerticalSubs = aSubsLineRect.Height() > aSubsLineRect.Width();
744 SwRect aSubsRect( aSubsLineRect );
745 if ( bVerticalSubs )
747 aSubsRect.AddLeft ( - (properties.nSPixelSzW+properties.nSHalfPixelSzW) );
748 aSubsRect.AddRight ( properties.nSPixelSzW+properties.nSHalfPixelSzW );
750 else
752 aSubsRect.AddTop ( - (properties.nSPixelSzH+properties.nSHalfPixelSzH) );
753 aSubsRect.AddBottom( properties.nSPixelSzH+properties.nSHalfPixelSzH );
755 for (const_iterator itK = rRects.m_aLineRects.begin(); itK != rRects.m_aLineRects.end();
756 ++itK)
758 const SwLineRect &rLine = *itK;
760 // do *not* consider painted or locked border lines.
761 // #i1837# - locked border lines have to be considered.
762 if ( rLine.IsLocked () )
763 continue;
765 if ( !bVerticalSubs == ( rLine.Height() > rLine.Width() ) ) //same direction?
766 continue;
768 if ( aSubsRect.Overlaps( rLine ) )
770 if ( bVerticalSubs ) // Vertical?
772 if ( aSubsRect.Left() <= rLine.Right() &&
773 aSubsRect.Right() >= rLine.Left() )
775 tools::Long nTmp = rLine.Top()-(properties.nSPixelSzH+1);
776 if ( aSubsLineRect.Top() < nTmp )
778 SwRect aNewSubsRect( aSubsLineRect );
779 aNewSubsRect.Bottom( nTmp );
780 m_aLineRects.emplace_back(aNewSubsRect, nullptr,
781 aSubsLineRect.GetStyle(), nullptr,
782 aSubsLineRect.GetSubColor());
784 nTmp = rLine.Bottom()+properties.nSPixelSzH+1;
785 if ( aSubsLineRect.Bottom() > nTmp )
787 SwRect aNewSubsRect( aSubsLineRect );
788 aNewSubsRect.Top( nTmp );
789 m_aLineRects.emplace_back(aNewSubsRect, nullptr,
790 aSubsLineRect.GetStyle(), nullptr,
791 aSubsLineRect.GetSubColor());
793 m_aLineRects.erase(m_aLineRects.begin() + i);
794 --i;
795 break;
798 else // Horizontal
800 if ( aSubsRect.Top() <= rLine.Bottom() &&
801 aSubsRect.Bottom() >= rLine.Top() )
803 tools::Long nTmp = rLine.Left()-(properties.nSPixelSzW+1);
804 if ( aSubsLineRect.Left() < nTmp )
806 SwRect aNewSubsRect( aSubsLineRect );
807 aNewSubsRect.Right( nTmp );
808 m_aLineRects.emplace_back(aNewSubsRect, nullptr,
809 aSubsLineRect.GetStyle(), nullptr,
810 aSubsLineRect.GetSubColor());
812 nTmp = rLine.Right()+properties.nSPixelSzW+1;
813 if ( aSubsLineRect.Right() > nTmp )
815 SwRect aNewSubsRect( aSubsLineRect );
816 aNewSubsRect.Left( nTmp );
817 m_aLineRects.emplace_back(aNewSubsRect, nullptr,
818 aSubsLineRect.GetStyle(), nullptr,
819 aSubsLineRect.GetSubColor());
821 m_aLineRects.erase(m_aLineRects.begin() + i);
822 --i;
823 break;
831 void SwLineRects::LockLines( bool bLock )
833 for (SwLineRect& rLRect : m_aLineRects)
834 rLRect.Lock(bLock);
837 static void lcl_DrawDashedRect( OutputDevice * pOut, SwLineRect const & rLRect )
839 tools::Long startX = rLRect.Left( ), endX;
840 tools::Long startY = rLRect.Top( ), endY;
842 // Discriminate vertically stretched rect from horizontally stretched
843 // and restrict minimum nHalfLWidth to 1
844 tools::Long nHalfLWidth = std::max( std::min( rLRect.Width( ), rLRect.Height( ) ) / 2, tools::Long(1) );
846 if ( rLRect.Height( ) > rLRect.Width( ) )
848 startX += nHalfLWidth;
849 endX = startX;
850 endY = startY + rLRect.Height( );
852 else
854 startY += nHalfLWidth;
855 endY = startY;
856 endX = startX + rLRect.Width( );
859 svtools::DrawLine( *pOut, Point( startX, startY ), Point( endX, endY ),
860 sal_uInt32( nHalfLWidth * 2 ), rLRect.GetStyle( ) );
863 void SwLineRects::PaintLines( OutputDevice *pOut, SwPaintProperties const &properties )
865 // Paint the borders. Sadly two passes are needed.
866 // Once for the inside and once for the outside edges of tables
867 if (m_aLineRects.size() == m_nLastCount)
868 return;
870 // #i16816# tagged pdf support
871 SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, nullptr, *pOut );
873 pOut->Push( vcl::PushFlags::FILLCOLOR|vcl::PushFlags::LINECOLOR );
874 pOut->SetFillColor();
875 pOut->SetLineColor();
876 ConnectEdges( pOut, properties );
877 const Color *pLast = nullptr;
879 bool bPaint2nd = false;
880 size_t nMinCount = m_aLineRects.size();
882 for (size_t i = 0; i < m_aLineRects.size(); ++i)
884 SwLineRect& rLRect = m_aLineRects[i];
886 if ( rLRect.IsPainted() )
887 continue;
889 if ( rLRect.IsLocked() )
891 nMinCount = std::min( nMinCount, i );
892 continue;
895 // Paint it now or in the second pass?
896 bool bPaint = true;
897 if ( rLRect.GetTab() )
899 if ( rLRect.Height() > rLRect.Width() )
901 // Vertical edge, overlapping with the table edge?
902 SwTwips nLLeft = rLRect.Left() - 30,
903 nLRight = rLRect.Right() + 30,
904 nTLeft = rLRect.GetTab()->getFrameArea().Left() + rLRect.GetTab()->getFramePrintArea().Left(),
905 nTRight = rLRect.GetTab()->getFrameArea().Left() + rLRect.GetTab()->getFramePrintArea().Right();
906 if ( (nTLeft >= nLLeft && nTLeft <= nLRight) ||
907 (nTRight>= nLLeft && nTRight<= nLRight) )
908 bPaint = false;
910 else
912 // Horizontal edge, overlapping with the table edge?
913 SwTwips nLTop = rLRect.Top() - 30,
914 nLBottom = rLRect.Bottom() + 30,
915 nTTop = rLRect.GetTab()->getFrameArea().Top() + rLRect.GetTab()->getFramePrintArea().Top(),
916 nTBottom = rLRect.GetTab()->getFrameArea().Top() + rLRect.GetTab()->getFramePrintArea().Bottom();
917 if ( (nTTop >= nLTop && nTTop <= nLBottom) ||
918 (nTBottom >= nLTop && nTBottom <= nLBottom) )
919 bPaint = false;
922 if ( bPaint )
924 if ( !pLast || *pLast != rLRect.GetColor() )
926 pLast = &rLRect.GetColor();
928 DrawModeFlags nOldDrawMode = pOut->GetDrawMode();
929 if( properties.pSGlobalShell->GetWin() &&
930 Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
931 pOut->SetDrawMode( DrawModeFlags::Default );
933 pOut->SetLineColor( *pLast );
934 pOut->SetFillColor( *pLast );
935 pOut->SetDrawMode( nOldDrawMode );
938 if( !rLRect.IsEmpty() )
939 lcl_DrawDashedRect( pOut, rLRect );
940 rLRect.SetPainted();
942 else
943 bPaint2nd = true;
945 if ( bPaint2nd )
947 for (size_t i = 0; i < m_aLineRects.size(); ++i)
949 SwLineRect& rLRect = m_aLineRects[i];
950 if ( rLRect.IsPainted() )
951 continue;
953 if ( rLRect.IsLocked() )
955 nMinCount = std::min( nMinCount, i );
956 continue;
959 if ( !pLast || *pLast != rLRect.GetColor() )
961 pLast = &rLRect.GetColor();
963 DrawModeFlags nOldDrawMode = pOut->GetDrawMode();
964 if( properties.pSGlobalShell->GetWin() &&
965 Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
967 pOut->SetDrawMode( DrawModeFlags::Default );
970 pOut->SetFillColor( *pLast );
971 pOut->SetDrawMode( nOldDrawMode );
973 if( !rLRect.IsEmpty() )
974 lcl_DrawDashedRect( pOut, rLRect );
975 rLRect.SetPainted();
978 m_nLastCount = nMinCount;
979 pOut->Pop();
983 void SwSubsRects::PaintSubsidiary( OutputDevice *pOut,
984 const SwLineRects *pRects,
985 SwPaintProperties const & properties )
987 if (m_aLineRects.empty())
988 return;
990 // #i16816# tagged pdf support
991 SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, nullptr, *pOut );
993 // Remove all help line that are almost covered (tables)
994 for (sal_Int32 i = 0; i != static_cast<sal_Int32>(m_aLineRects.size()); ++i)
996 SwLineRect& rLi = m_aLineRects[i];
997 const bool bVerticalSubs = rLi.Height() > rLi.Width();
999 for (size_type k = i + 1; k != m_aLineRects.size(); ++k)
1001 SwLineRect& rLk = m_aLineRects[k];
1002 if ( rLi.SSize() == rLk.SSize() )
1004 if ( bVerticalSubs == ( rLk.Height() > rLk.Width() ) )
1006 if ( bVerticalSubs )
1008 tools::Long nLi = rLi.Right();
1009 tools::Long nLk = rLk.Right();
1010 if ( rLi.Top() == rLk.Top() &&
1011 ((nLi < rLk.Left() && nLi+21 > rLk.Left()) ||
1012 (nLk < rLi.Left() && nLk+21 > rLi.Left())))
1014 m_aLineRects.erase(m_aLineRects.begin() + i);
1015 // don't continue with inner loop any more:
1016 // the array may shrink!
1017 --i;
1018 break;
1021 else
1023 tools::Long nLi = rLi.Bottom();
1024 tools::Long nLk = rLk.Bottom();
1025 if ( rLi.Left() == rLk.Left() &&
1026 ((nLi < rLk.Top() && nLi+21 > rLk.Top()) ||
1027 (nLk < rLi.Top() && nLk+21 > rLi.Top())))
1029 m_aLineRects.erase(m_aLineRects.begin() + i);
1030 // don't continue with inner loop any more:
1031 // the array may shrink!
1032 --i;
1033 break;
1041 if (pRects && (!pRects->m_aLineRects.empty()))
1042 RemoveSuperfluousSubsidiaryLines( *pRects, properties );
1044 if (m_aLineRects.empty())
1045 return;
1047 pOut->Push( vcl::PushFlags::FILLCOLOR|vcl::PushFlags::LINECOLOR );
1048 pOut->SetLineColor();
1050 // Reset draw mode in high contrast mode in order to get fill color
1051 // set at output device. Recover draw mode after draw of lines.
1052 // Necessary for the subsidiary lines painted by the fly frames.
1053 DrawModeFlags nOldDrawMode = pOut->GetDrawMode();
1054 if( gProp.pSGlobalShell->GetWin() &&
1055 Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
1057 pOut->SetDrawMode( DrawModeFlags::Default );
1060 for (SwLineRect& rLRect : m_aLineRects)
1062 // Add condition <!rLRect.IsLocked()> to prevent paint of locked subsidiary lines.
1063 if ( !rLRect.IsPainted() &&
1064 !rLRect.IsLocked() )
1066 const Color *pCol = nullptr;
1067 SwViewShell *pShell = properties.pSGlobalShell;
1068 const SwViewOption *pOpt = pShell->GetViewOptions();
1069 switch ( rLRect.GetSubColor() )
1071 case SubColFlags::Page: pCol = &pOpt->GetDocBoundariesColor(); break;
1072 case SubColFlags::Tab: pCol = &pOpt->GetTableBoundariesColor(); break;
1073 case SubColFlags::Fly:
1074 case SubColFlags::Sect: pCol = &pOpt->GetSectionBoundColor(); break;
1077 if (pCol && pOut->GetFillColor() != *pCol)
1078 pOut->SetFillColor( *pCol );
1079 pOut->DrawRect( rLRect.SVRect() );
1081 rLRect.SetPainted();
1085 pOut->SetDrawMode( nOldDrawMode );
1087 pOut->Pop();
1090 // Various functions that are use in this file.
1093 * Function <SwAlignRect(..)> is also used outside this file
1095 * Correction: adjust rectangle on pixel level in order to make sure,
1096 * that the border "leaves its original pixel", if it has to
1097 * No prior adjustments for odd relation between pixel and twip
1099 void SwAlignRect( SwRect &rRect, const SwViewShell *pSh, const vcl::RenderContext* pRenderContext )
1101 if( !rRect.HasArea() )
1102 return;
1104 // Make sure that view shell (parameter <pSh>) exists, if the output device
1105 // is taken from this view shell --> no output device, no alignment
1106 // Output device taken from view shell <pSh>, if <gProp.bSFlyMetafile> not set
1107 if ( !gProp.bSFlyMetafile && !pSh )
1109 return;
1112 const vcl::RenderContext *pOut = gProp.bSFlyMetafile ?
1113 gProp.pSFlyMetafileOut.get() : pRenderContext;
1115 // Hold original rectangle in pixel
1116 const tools::Rectangle aOrgPxRect = pOut->LogicToPixel( rRect.SVRect() );
1117 // Determine pixel-center rectangle in twip
1118 const SwRect aPxCenterRect( pOut->PixelToLogic( aOrgPxRect ) );
1120 // Perform adjustments on pixel level.
1121 SwRect aAlignedPxRect( aOrgPxRect );
1122 if ( rRect.Top() > aPxCenterRect.Top() )
1124 // 'leave pixel overlapping on top'
1125 aAlignedPxRect.AddTop( 1 );
1128 if ( rRect.Bottom() < aPxCenterRect.Bottom() )
1130 // 'leave pixel overlapping on bottom'
1131 aAlignedPxRect.AddBottom( - 1 );
1134 if ( rRect.Left() > aPxCenterRect.Left() )
1136 // 'leave pixel overlapping on left'
1137 aAlignedPxRect.AddLeft( 1 );
1140 if ( rRect.Right() < aPxCenterRect.Right() )
1142 // 'leave pixel overlapping on right'
1143 aAlignedPxRect.AddRight( - 1 );
1146 // Consider negative width/height check, if aligned SwRect has negative width/height.
1147 // If Yes, adjust it to width/height = 0 twip.
1148 // NOTE: A SwRect with negative width/height can occur, if the width/height
1149 // of the given SwRect in twip was less than a pixel in twip and that
1150 // the alignment calculates that the aligned SwRect should not contain
1151 // the pixels the width/height is on.
1152 if ( aAlignedPxRect.Width() < 0 )
1154 aAlignedPxRect.Width(0);
1156 if ( aAlignedPxRect.Height() < 0 )
1158 aAlignedPxRect.Height(0);
1160 // Consider zero width/height for converting a rectangle from
1161 // pixel to logic it needs a width/height. Thus, set width/height
1162 // to one, if it's zero and correct this on the twip level after the conversion.
1163 bool bZeroWidth = false;
1164 if ( aAlignedPxRect.Width() == 0 )
1166 aAlignedPxRect.Width(1);
1167 bZeroWidth = true;
1169 bool bZeroHeight = false;
1170 if ( aAlignedPxRect.Height() == 0 )
1172 aAlignedPxRect.Height(1);
1173 bZeroHeight = true;
1176 rRect = SwRect(pOut->PixelToLogic( aAlignedPxRect.SVRect() ));
1178 // Consider zero width/height and adjust calculated aligned twip rectangle.
1179 // Reset width/height to zero; previous negative width/height haven't to be considered.
1180 if ( bZeroWidth )
1182 rRect.Width(0);
1184 if ( bZeroHeight )
1186 rRect.Height(0);
1191 * Method to pixel-align rectangle for drawing graphic object
1193 * Because we are drawing graphics from the left-top-corner in conjunction
1194 * with size coordinates, these coordinates have to be calculated at a pixel
1195 * level.
1196 * Thus, we convert the rectangle to pixel and then convert to left-top-corner
1197 * and then get size of pixel rectangle back to logic.
1198 * This calculation is necessary, because there's a different between
1199 * the conversion from logic to pixel of a normal rectangle with its left-top-
1200 * and right-bottom-corner and the same conversion of the same rectangle
1201 * with left-top-corner and size.
1203 * NOTE: Call this method before each <GraphicObject.Draw(...)>
1205 void SwAlignGrfRect( SwRect *pGrfRect, const vcl::RenderContext &rOut )
1207 tools::Rectangle aPxRect = rOut.LogicToPixel( pGrfRect->SVRect() );
1208 pGrfRect->Pos( rOut.PixelToLogic( aPxRect.TopLeft() ) );
1209 pGrfRect->SSize( rOut.PixelToLogic( aPxRect.GetSize() ) );
1212 static tools::Long lcl_AlignWidth( const tools::Long nWidth, SwPaintProperties const & properties )
1214 if ( nWidth )
1216 const tools::Long nW = nWidth % properties.nSPixelSzW;
1218 if ( !nW || nW > properties.nSHalfPixelSzW )
1219 return std::max(tools::Long(1), nWidth - properties.nSHalfPixelSzW);
1221 return nWidth;
1224 static tools::Long lcl_AlignHeight( const tools::Long nHeight, SwPaintProperties const & properties )
1226 if ( nHeight )
1228 const tools::Long nH = nHeight % properties.nSPixelSzH;
1230 if ( !nH || nH > properties.nSHalfPixelSzH )
1231 return std::max(tools::Long(1), nHeight - properties.nSHalfPixelSzH);
1233 return nHeight;
1237 * Calculate PrtArea plus surrounding plus shadow
1239 static void lcl_CalcBorderRect( SwRect &rRect, const SwFrame *pFrame,
1240 const SwBorderAttrs &rAttrs,
1241 const bool bShadow,
1242 SwPaintProperties const & properties)
1244 // Special handling for cell frames.
1245 // The printing area of a cell frame is completely enclosed in the frame area
1246 // and a cell frame has no shadow. Thus, for cell frames the calculated
1247 // area equals the frame area.
1248 // Notes: Borders of cell frames in R2L text direction will switch its side
1249 // - left border is painted on the right; right border on the left.
1250 // See <lcl_PaintLeftLine> and <lcl_PaintRightLine>.
1251 if( pFrame->IsSctFrame() )
1253 rRect = pFrame->getFramePrintArea();
1254 rRect.Pos() += pFrame->getFrameArea().Pos();
1256 else if ( pFrame->IsCellFrame() )
1257 rRect = pFrame->getFrameArea();
1258 else
1260 rRect = pFrame->getFramePrintArea();
1261 rRect.Pos() += pFrame->getFrameArea().Pos();
1263 SwRectFnSet fnRect(pFrame);
1265 const SvxBoxItem &rBox = rAttrs.GetBox();
1266 const bool bTop = 0 != fnRect.GetTopMargin(*pFrame);
1267 if ( bTop || rBox.GetTop() )
1269 SwTwips nDiff = rBox.GetTop() ?
1270 rBox.CalcLineSpace( SvxBoxItemLine::TOP, /*bEvenIfNoLine=*/false, /*bAllowNegative=*/true ) :
1271 rBox.GetDistance( SvxBoxItemLine::TOP );
1272 if( nDiff )
1273 fnRect.SubTop(rRect, nDiff);
1276 const bool bBottom = 0 != fnRect.GetBottomMargin(*pFrame);
1277 if ( bBottom )
1279 SwTwips nDiff = 0;
1280 // #i29550#
1281 if ( pFrame->IsTabFrame() &&
1282 static_cast<const SwTabFrame*>(pFrame)->IsCollapsingBorders() )
1284 // For collapsing borders, we have to add the height of
1285 // the height of the last line
1286 nDiff = static_cast<const SwTabFrame*>(pFrame)->GetBottomLineSize();
1288 else
1290 nDiff = rBox.GetBottom() ?
1291 rBox.CalcLineSpace( SvxBoxItemLine::BOTTOM ) :
1292 rBox.GetDistance( SvxBoxItemLine::BOTTOM );
1294 if( nDiff )
1295 fnRect.AddBottom(rRect, nDiff);
1298 if ( rBox.GetLeft() )
1299 fnRect.SubLeft(rRect, rBox.CalcLineSpace(SvxBoxItemLine::LEFT));
1300 else
1301 fnRect.SubLeft(rRect, rBox.GetDistance(SvxBoxItemLine::LEFT));
1303 if ( rBox.GetRight() )
1304 fnRect.AddRight(rRect, rBox.CalcLineSpace(SvxBoxItemLine::RIGHT));
1305 else
1306 fnRect.AddRight(rRect, rBox.GetDistance(SvxBoxItemLine::RIGHT));
1308 if ( bShadow && rAttrs.GetShadow().GetLocation() != SvxShadowLocation::NONE )
1310 const SvxShadowItem &rShadow = rAttrs.GetShadow();
1311 if ( bTop )
1312 fnRect.SubTop(rRect, rShadow.CalcShadowSpace(SvxShadowItemSide::TOP));
1313 fnRect.SubLeft(rRect, rShadow.CalcShadowSpace(SvxShadowItemSide::LEFT));
1314 if ( bBottom )
1315 fnRect.AddBottom(rRect, rShadow.CalcShadowSpace(SvxShadowItemSide::BOTTOM));
1316 fnRect.AddRight(rRect, rShadow.CalcShadowSpace(SvxShadowItemSide::RIGHT));
1320 ::SwAlignRect( rRect, properties.pSGlobalShell, properties.pSGlobalShell ? properties.pSGlobalShell->GetOut() : nullptr );
1324 * Extend left/right border/shadow rectangle to bottom of previous frame/to
1325 * top of next frame, if border/shadow is joined with previous/next frame
1327 static void lcl_ExtendLeftAndRight( SwRect& _rRect,
1328 const SwFrame& _rFrame,
1329 const SwBorderAttrs& _rAttrs,
1330 const SwRectFn& _rRectFn )
1332 if ( _rAttrs.JoinedWithPrev( _rFrame ) )
1334 const SwFrame* pPrevFrame = _rFrame.GetPrev();
1335 (_rRect.*_rRectFn->fnSetTop)( (pPrevFrame->*_rRectFn->fnGetPrtBottom)() );
1337 if ( _rAttrs.JoinedWithNext( _rFrame ) )
1339 const SwFrame* pNextFrame = _rFrame.GetNext();
1340 (_rRect.*_rRectFn->fnSetBottom)( (pNextFrame->*_rRectFn->fnGetPrtTop)() );
1344 /// Returns a range suitable for subtraction when lcl_SubtractFlys() is used.
1345 /// Otherwise DrawFillAttributes() expands the clip path itself.
1346 static basegfx::B2DRange lcl_ShrinkFly(const SwRect& rRect)
1348 static MapMode aMapMode(MapUnit::MapTwip);
1349 static const Size aSingleUnit = Application::GetDefaultDevice()->PixelToLogic(Size(1, 1), aMapMode);
1351 double x1 = rRect.Left() + aSingleUnit.getWidth();
1352 double y1 = rRect.Top() + aSingleUnit.getHeight();
1353 double x2 = rRect.Right() - aSingleUnit.getWidth();
1354 double y2 = rRect.Bottom() - aSingleUnit.getHeight();
1356 return basegfx::B2DRange(x1, y1, x2, y2);
1359 static void lcl_SubtractFlys( const SwFrame *pFrame, const SwPageFrame *pPage,
1360 const SwRect &rRect, SwRegionRects &rRegion, basegfx::utils::B2DClipState& rClipState, SwPaintProperties const & rProperties)
1362 const SwSortedObjs& rObjs = *pPage->GetSortedObjs();
1363 const SwFlyFrame* pSelfFly = pFrame->IsInFly() ? pFrame->FindFlyFrame() : gProp.pSRetoucheFly2;
1364 if (!gProp.pSRetoucheFly)
1365 gProp.pSRetoucheFly = gProp.pSRetoucheFly2;
1367 for (size_t j = 0; (j < rObjs.size()) && !rRegion.empty(); ++j)
1369 const SwAnchoredObject* pAnchoredObj = rObjs[j];
1370 const SdrObject* pSdrObj = pAnchoredObj->GetDrawObj();
1372 // Do not consider invisible objects
1373 if (!pPage->GetFormat()->GetDoc()->getIDocumentDrawModelAccess().IsVisibleLayerId(pSdrObj->GetLayer()))
1374 continue;
1376 const SwFlyFrame *pFly = pAnchoredObj->DynCastFlyFrame();
1377 if (!pFly)
1378 continue;
1380 if (pSelfFly == pFly || gProp.pSRetoucheFly == pFly || !rRect.Overlaps(pFly->getFrameArea()))
1381 continue;
1383 if (!pFly->GetFormat()->GetPrint().GetValue() &&
1384 (OUTDEV_PRINTER == gProp.pSGlobalShell->GetOut()->GetOutDevType() ||
1385 gProp.pSGlobalShell->IsPreview()))
1386 continue;
1388 const bool bLowerOfSelf = pSelfFly && pFly->IsLowerOf( pSelfFly );
1390 //For character bound Flys only examine those Flys in which it is not
1391 //anchored itself.
1392 //Why only for character bound ones you may ask? It never makes sense to
1393 //subtract frames in which it is anchored itself right?
1394 if (pSelfFly && pSelfFly->IsLowerOf(pFly))
1395 continue;
1397 //Any why does it not apply for the RetoucheFly too?
1398 if (gProp.pSRetoucheFly && gProp.pSRetoucheFly->IsLowerOf(pFly))
1399 continue;
1401 #if OSL_DEBUG_LEVEL > 0
1402 //Flys who are anchored inside their own one, must have a bigger OrdNum
1403 //or be character bound.
1404 if (pSelfFly && bLowerOfSelf)
1406 OSL_ENSURE( pFly->IsFlyInContentFrame() ||
1407 pSdrObj->GetOrdNumDirect() > pSelfFly->GetVirtDrawObj()->GetOrdNumDirect(),
1408 "Fly with wrong z-Order" );
1410 #endif
1412 bool bStopOnHell = true;
1413 if (pSelfFly)
1415 const SdrObject *pTmp = pSelfFly->GetVirtDrawObj();
1416 if (pSdrObj->GetLayer() == pTmp->GetLayer())
1418 if (pSdrObj->GetOrdNumDirect() < pTmp->GetOrdNumDirect())
1419 //In the same layer we only observe those that are above.
1420 continue;
1422 else
1424 if (!bLowerOfSelf && !pFly->GetFormat()->GetOpaque().GetValue())
1425 //From other layers we are only interested in non
1426 //transparent ones or those that are internal
1427 continue;
1428 bStopOnHell = false;
1431 if (gProp.pSRetoucheFly)
1433 const SdrObject *pTmp = gProp.pSRetoucheFly->GetVirtDrawObj();
1434 if ( pSdrObj->GetLayer() == pTmp->GetLayer() )
1436 if ( pSdrObj->GetOrdNumDirect() < pTmp->GetOrdNumDirect() )
1437 //In the same layer we only observe those that are above.
1438 continue;
1440 else
1442 if (!pFly->IsLowerOf( gProp.pSRetoucheFly ) && !pFly->GetFormat()->GetOpaque().GetValue())
1443 //From other layers we are only interested in non
1444 //transparent ones or those that are internal
1445 continue;
1446 bStopOnHell = false;
1450 //If the content of the Fly is transparent, we subtract it only if it's
1451 //contained in the hell layer.
1452 const IDocumentDrawModelAccess& rIDDMA = pFly->GetFormat()->getIDocumentDrawModelAccess();
1453 bool bHell = pSdrObj->GetLayer() == rIDDMA.GetHellId();
1454 if (bStopOnHell && bHell)
1455 continue;
1457 /// Change internal order of condition
1458 /// first check "!bHell", then "..->Lower()" and "..->IsNoTextFrame()"
1459 /// have not to be performed, if frame is in "Hell"
1460 const SwFrame* pLower = pFly->Lower();
1461 if (!bHell && pLower && pLower->IsNoTextFrame() &&
1462 (static_cast<SwNoTextFrame const*>(pLower)->IsTransparent() ||
1463 static_cast<SwNoTextFrame const*>(pLower)->HasAnimation() ||
1464 pFly->GetFormat()->GetSurround().IsContour()
1467 continue;
1469 // Own if-statements for transparent background/shadow of fly frames
1470 // in order to handle special conditions.
1471 if (pFly->IsBackgroundTransparent())
1473 // Background <pFly> is transparent drawn. Thus normally, its region
1474 // have not to be subtracted from given region.
1475 // But, if method is called for a fly frame and
1476 // <pFly> is a direct lower of this fly frame and
1477 // <pFly> inherites its transparent background brush from its parent,
1478 // then <pFly> frame area have to be subtracted from given region.
1479 // NOTE: Because in Status Quo transparent backgrounds can only be
1480 // assigned to fly frames, the handle of this special case
1481 // avoids drawing of transparent areas more than once, if
1482 // a fly frame inherites a transparent background from its
1483 // parent fly frame.
1484 if (pFrame->IsFlyFrame() &&
1485 (pFly->GetAnchorFrame()->FindFlyFrame() == pFrame) &&
1486 pFly->GetFormat()->IsBackgroundBrushInherited()
1489 SwRect aRect;
1490 SwBorderAttrAccess aAccess( SwFrame::GetCache(), static_cast<SwFrame const *>(pFly) );
1491 const SwBorderAttrs &rAttrs = *aAccess.Get();
1492 ::lcl_CalcBorderRect( aRect, pFly, rAttrs, true, rProperties );
1493 rRegion -= aRect;
1494 rClipState.subtractRange(lcl_ShrinkFly(aRect));
1495 continue;
1497 else
1499 continue;
1503 if (bHell && pFly->GetAnchorFrame()->IsInFly())
1505 //So the border won't get dismantled by the background of the other
1506 //Fly.
1507 SwRect aRect;
1508 SwBorderAttrAccess aAccess( SwFrame::GetCache(), static_cast<SwFrame const *>(pFly) );
1509 const SwBorderAttrs &rAttrs = *aAccess.Get();
1510 ::lcl_CalcBorderRect( aRect, pFly, rAttrs, true, rProperties );
1511 rRegion -= aRect;
1512 rClipState.subtractRange(lcl_ShrinkFly(aRect));
1514 else
1516 SwRect aRect( pFly->getFramePrintArea() );
1517 aRect += pFly->getFrameArea().Pos();
1518 rRegion -= aRect;
1519 rClipState.subtractRange(lcl_ShrinkFly(aRect));
1522 if (gProp.pSRetoucheFly == gProp.pSRetoucheFly2)
1523 gProp.pSRetoucheFly = nullptr;
1526 static void lcl_implDrawGraphicBackground(const SvxBrushItem& _rBackgrdBrush,
1527 vcl::RenderContext& _rOut,
1528 const SwRect& _rAlignedPaintRect,
1529 const GraphicObject& _rGraphicObj,
1530 SwPaintProperties const & properties)
1532 /// determine color of background
1533 /// If color of background brush is not "no fill"/"auto fill" or
1534 /// <SwPaintProperties.bSFlyMetafile> is set, use color of background brush, otherwise
1535 /// use global retouche color.
1536 const Color aColor( ( (_rBackgrdBrush.GetColor() != COL_TRANSPARENT) || properties.bSFlyMetafile )
1537 ? _rBackgrdBrush.GetColor()
1538 : aGlobalRetoucheColor );
1540 /// determine, if background color have to be drawn transparent
1541 /// and calculate transparency percent value
1542 sal_Int8 nTransparencyPercent = 0;
1543 bool bDrawTransparent = false;
1544 if ( aColor.IsTransparent() )
1545 /// background color is transparent --> draw transparent.
1547 bDrawTransparent = true;
1548 nTransparencyPercent = ((255 - aColor.GetAlpha())*100 + 0x7F)/0xFF;
1550 else if ( (_rGraphicObj.GetAttr().IsTransparent()) &&
1551 (_rBackgrdBrush.GetColor() == COL_TRANSPARENT) )
1552 /// graphic is drawn transparent and background color is
1553 /// "no fill"/"auto fill" --> draw transparent
1555 bDrawTransparent = true;
1556 nTransparencyPercent = 100 - (_rGraphicObj.GetAttr().GetAlpha() * 100 + 127) / 255;
1559 if ( bDrawTransparent )
1561 /// draw background transparent
1562 if( _rOut.GetFillColor() != aColor.GetRGBColor() )
1563 _rOut.SetFillColor( aColor.GetRGBColor() );
1564 tools::PolyPolygon aPoly( _rAlignedPaintRect.SVRect() );
1565 _rOut.DrawTransparent( aPoly, nTransparencyPercent );
1567 else
1569 /// draw background opaque
1570 if ( _rOut.GetFillColor() != aColor )
1571 _rOut.SetFillColor( aColor );
1572 _rOut.DrawRect( _rAlignedPaintRect.SVRect() );
1577 * This is a local help method to draw a background for a graphic
1579 * Under certain circumstances we have to draw a background for a graphic.
1580 * This method takes care of the conditions and draws the background with the
1581 * corresponding color.
1582 * Method introduced for bug fix #103876# in order to optimize drawing tiled
1583 * background graphics. Previously, this code was integrated in method
1584 * <lcl_DrawGraphic>.
1585 * Method implemented as an inline, checking the conditions and calling method
1586 * method <lcl_implDrawGraphicBackground(..)> for the intrinsic drawing.
1588 * @param _rBackgrdBrush
1589 * background brush contain the color the background has to be drawn.
1591 * @param _rOut
1592 * output device the background has to be drawn in.
1594 * @param _rAlignedPaintRect
1595 * paint rectangle in the output device, which has to be drawn with the background.
1596 * rectangle have to be aligned by method ::SwAlignRect
1598 * @param _rGraphicObj
1599 * graphic object, for which the background has to be drawn. Used for checking
1600 * the transparency of its bitmap, its type and if the graphic is drawn transparent
1602 * @param _bNumberingGraphic
1603 * boolean indicating that graphic is used as a numbering.
1605 * @param _bBackgrdAlreadyDrawn
1606 * boolean (optional; default: false) indicating, if the background is already drawn.
1608 static void lcl_DrawGraphicBackground( const SvxBrushItem& _rBackgrdBrush,
1609 OutputDevice& _rOut,
1610 const SwRect& _rAlignedPaintRect,
1611 const GraphicObject& _rGraphicObj,
1612 bool _bNumberingGraphic,
1613 SwPaintProperties const & properties,
1614 bool _bBackgrdAlreadyDrawn = false)
1616 // draw background with background color, if
1617 // (1) graphic is not used as a numbering AND
1618 // (2) background is not already drawn AND
1619 // (3) intrinsic graphic is transparent OR intrinsic graphic doesn't exists
1620 if ( !_bNumberingGraphic &&
1621 !_bBackgrdAlreadyDrawn &&
1622 ( _rGraphicObj.IsTransparent() || _rGraphicObj.GetType() == GraphicType::NONE )
1625 lcl_implDrawGraphicBackground( _rBackgrdBrush, _rOut, _rAlignedPaintRect, _rGraphicObj, properties );
1630 * NNOTE: the transparency of the background graphic is saved in
1631 * SvxBrushItem.GetGraphicObject(<shell>).GetAttr().Set/GetTransparency()
1632 * and is considered in the drawing of the graphic
1634 * Thus, to provide transparent background graphic for text frames nothing
1635 * has to be coded
1637 * Use align rectangle for drawing graphic Pixel-align coordinates for
1638 * drawing graphic
1639 * Outsource code for drawing background of the graphic
1640 * with a background color in method <lcl_DrawGraphicBackground>
1642 * Also, change type of <bGrfNum> and <bClip> from <bool> to <bool>
1644 static void lcl_DrawGraphic( const SvxBrushItem& rBrush, vcl::RenderContext &rOutDev,
1645 const SwViewShell &rSh, const SwRect &rGrf, const SwRect &rOut,
1646 bool bGrfNum,
1647 SwPaintProperties const & properties,
1648 bool bBackgrdAlreadyDrawn )
1649 // add parameter <bBackgrdAlreadyDrawn> to indicate
1650 // that the background is already drawn.
1652 // Calculate align rectangle from parameter <rGrf> and use aligned
1653 // rectangle <aAlignedGrfRect> in the following code
1654 SwRect aAlignedGrfRect = rGrf;
1655 ::SwAlignRect( aAlignedGrfRect, &rSh, &rOutDev );
1657 // Change type from <bool> to <bool>.
1658 const bool bNotInside = !rOut.Contains( aAlignedGrfRect );
1659 if ( bNotInside )
1661 rOutDev.Push( vcl::PushFlags::CLIPREGION );
1662 rOutDev.IntersectClipRegion( rOut.SVRect() );
1665 GraphicObject *pGrf = const_cast<GraphicObject*>(rBrush.GetGraphicObject());
1667 OUString aOriginURL = pGrf->GetGraphic().getOriginURL();
1668 if (pGrf->GetGraphic().GetType() == GraphicType::Default && !aOriginURL.isEmpty())
1670 Graphic aGraphic = vcl::graphic::loadFromURL(aOriginURL);
1671 pGrf->SetGraphic(aGraphic);
1674 // Outsource drawing of background with a background color
1675 ::lcl_DrawGraphicBackground( rBrush, rOutDev, aAlignedGrfRect, *pGrf, bGrfNum, properties, bBackgrdAlreadyDrawn );
1677 // Because for drawing a graphic left-top-corner and size coordinates are
1678 // used, these coordinates have to be determined on pixel level.
1679 ::SwAlignGrfRect( &aAlignedGrfRect, rOutDev );
1681 const basegfx::B2DHomMatrix aGraphicTransform(
1682 basegfx::utils::createScaleTranslateB2DHomMatrix(
1683 aAlignedGrfRect.Width(), aAlignedGrfRect.Height(),
1684 aAlignedGrfRect.Left(), aAlignedGrfRect.Top()));
1686 paintGraphicUsingPrimitivesHelper(
1687 rOutDev,
1688 *pGrf,
1689 pGrf->GetAttr(),
1690 aGraphicTransform,
1691 OUString(),
1692 OUString(),
1693 OUString());
1695 if ( bNotInside )
1696 rOutDev.Pop();
1699 bool DrawFillAttributes(
1700 const drawinglayer::attribute::SdrAllFillAttributesHelperPtr& rFillAttributes,
1701 const SwRect& rOriginalLayoutRect,
1702 const SwRegionRects& rPaintRegion,
1703 const basegfx::utils::B2DClipState& rClipState,
1704 vcl::RenderContext& rOut)
1706 if(rFillAttributes && rFillAttributes->isUsed())
1708 basegfx::B2DRange aPaintRange(
1709 rPaintRegion.GetOrigin().Left(),
1710 rPaintRegion.GetOrigin().Top(),
1711 rPaintRegion.GetOrigin().Right(),
1712 rPaintRegion.GetOrigin().Bottom());
1714 if (!aPaintRange.isEmpty() &&
1715 !rPaintRegion.empty() &&
1716 !basegfx::fTools::equalZero(aPaintRange.getWidth()) &&
1717 !basegfx::fTools::equalZero(aPaintRange.getHeight()))
1719 // need to expand for correct AAed and non-AAed visualization as primitive.
1720 // This must probably be removed again when we will be able to get all Writer visualization
1721 // as primitives and Writer prepares all it's stuff in high precision coordinates (also
1722 // needs to avoid moving boundaries around to better show overlapping stuff...)
1723 if (comphelper::IsFuzzing() || SvtOptionsDrawinglayer::IsAntiAliasing())
1725 // if AAed in principle expand by 0.5 in all directions. Since painting edges of
1726 // AAed regions does not add to no transparence (0.5 opacity covered by 0.5 opacity
1727 // is not full opacity but 0.75 opacity) we need some overlap here to avoid paint
1728 // artifacts. Checked experimentally - a little bit more in Y is needed, probably
1729 // due to still existing integer alignment and crunching in writer.
1730 static const double fExpandX = 0.55;
1731 static const double fExpandY = 0.70;
1732 const basegfx::B2DVector aSingleUnit(rOut.GetInverseViewTransformation() * basegfx::B2DVector(fExpandX, fExpandY));
1734 aPaintRange.expand(aPaintRange.getMinimum() - aSingleUnit);
1735 aPaintRange.expand(aPaintRange.getMaximum() + aSingleUnit);
1737 else
1739 // if not AAed expand by one unit to bottom right due to the missing unit
1740 // from SwRect/Rectangle integer handling
1741 const basegfx::B2DVector aSingleUnit(rOut.GetInverseViewTransformation() * basegfx::B2DVector(1.0, 1.0));
1743 aPaintRange.expand(aPaintRange.getMaximum() + aSingleUnit);
1746 const basegfx::B2DRange aDefineRange(
1747 rOriginalLayoutRect.Left(),
1748 rOriginalLayoutRect.Top(),
1749 rOriginalLayoutRect.Right(),
1750 rOriginalLayoutRect.Bottom());
1752 const drawinglayer::primitive2d::Primitive2DContainer& rSequence = rFillAttributes->getPrimitive2DSequence(
1753 aPaintRange,
1754 aDefineRange);
1756 if(rSequence.size())
1758 drawinglayer::primitive2d::Primitive2DContainer const*
1759 pPrimitives(&rSequence);
1760 drawinglayer::primitive2d::Primitive2DContainer primitives;
1761 // tdf#86578 the awful lcl_SubtractFlys hack
1762 if (rPaintRegion.size() > 1 || rPaintRegion[0] != rPaintRegion.GetOrigin())
1764 basegfx::B2DPolyPolygon const& maskRegion(rClipState.getClipPoly());
1765 primitives.resize(1);
1766 primitives[0] = new drawinglayer::primitive2d::MaskPrimitive2D(
1767 maskRegion, drawinglayer::primitive2d::Primitive2DContainer(rSequence));
1768 pPrimitives = &primitives;
1770 assert(pPrimitives && pPrimitives->size());
1772 drawinglayer::geometry::ViewInformation2D aViewInformation2D;
1773 aViewInformation2D.setViewTransformation(rOut.GetViewTransformation());
1774 aViewInformation2D.setViewport(aPaintRange);
1776 std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor(drawinglayer::processor2d::createProcessor2DFromOutputDevice(
1777 rOut,
1778 aViewInformation2D) );
1779 pProcessor->process(*pPrimitives);
1780 return true;
1785 return false;
1788 void DrawGraphic(
1789 const SvxBrushItem *pBrush,
1790 vcl::RenderContext &rOutDev,
1791 const SwRect &rOrg,
1792 const SwRect &rOut,
1793 const sal_uInt8 nGrfNum,
1794 const bool bConsiderBackgroundTransparency )
1795 // Add 6th parameter to indicate that method should
1796 // consider background transparency, saved in the color of the brush item
1798 SwViewShell &rSh = *gProp.pSGlobalShell;
1799 bool bReplaceGrfNum = GRFNUM_REPLACE == nGrfNum;
1800 bool bGrfNum = GRFNUM_NO != nGrfNum;
1801 Size aGrfSize;
1802 SvxGraphicPosition ePos = GPOS_NONE;
1803 if( pBrush && !bReplaceGrfNum )
1805 if( rSh.GetViewOptions()->IsGraphic() )
1807 OUString referer;
1808 SfxObjectShell * sh = rSh.GetDoc()->GetPersist();
1809 if (sh != nullptr && sh->HasName()) {
1810 referer = sh->GetMedium()->GetName();
1812 const Graphic* pGrf = pBrush->GetGraphic(referer);
1813 if( pGrf && GraphicType::NONE != pGrf->GetType() )
1815 ePos = pBrush->GetGraphicPos();
1816 if( pGrf->IsSupportedGraphic() )
1817 // don't the use the specific output device! Bug 94802
1818 aGrfSize = ::GetGraphicSizeTwip( *pGrf, nullptr );
1821 else
1822 bReplaceGrfNum = bGrfNum;
1825 SwRect aGrf;
1826 aGrf.SSize( aGrfSize );
1827 bool bDraw = true;
1828 bool bRetouche = true;
1829 switch ( ePos )
1831 case GPOS_LT:
1832 aGrf.Pos() = rOrg.Pos();
1833 break;
1835 case GPOS_MT:
1836 aGrf.Pos().setY( rOrg.Top() );
1837 aGrf.Pos().setX( rOrg.Left() + rOrg.Width()/2 - aGrfSize.Width()/2 );
1838 break;
1840 case GPOS_RT:
1841 aGrf.Pos().setY( rOrg.Top() );
1842 aGrf.Pos().setX( rOrg.Right() - aGrfSize.Width() );
1843 break;
1845 case GPOS_LM:
1846 aGrf.Pos().setY( rOrg.Top() + rOrg.Height()/2 - aGrfSize.Height()/2 );
1847 aGrf.Pos().setX( rOrg.Left() );
1848 break;
1850 case GPOS_MM:
1851 aGrf.Pos().setY( rOrg.Top() + rOrg.Height()/2 - aGrfSize.Height()/2 );
1852 aGrf.Pos().setX( rOrg.Left() + rOrg.Width()/2 - aGrfSize.Width()/2 );
1853 break;
1855 case GPOS_RM:
1856 aGrf.Pos().setY( rOrg.Top() + rOrg.Height()/2 - aGrfSize.Height()/2 );
1857 aGrf.Pos().setX( rOrg.Right() - aGrfSize.Width() );
1858 break;
1860 case GPOS_LB:
1861 aGrf.Pos().setY( rOrg.Bottom() - aGrfSize.Height() );
1862 aGrf.Pos().setX( rOrg.Left() );
1863 break;
1865 case GPOS_MB:
1866 aGrf.Pos().setY( rOrg.Bottom() - aGrfSize.Height() );
1867 aGrf.Pos().setX( rOrg.Left() + rOrg.Width()/2 - aGrfSize.Width()/2 );
1868 break;
1870 case GPOS_RB:
1871 aGrf.Pos().setY( rOrg.Bottom() - aGrfSize.Height() );
1872 aGrf.Pos().setX( rOrg.Right() - aGrfSize.Width() );
1873 break;
1875 case GPOS_AREA:
1876 aGrf = rOrg;
1877 // Despite the fact that the background graphic has to fill the complete
1878 // area, we already checked, whether the graphic will completely fill out
1879 // the region the <rOut> that is to be painted. Thus, nothing has to be
1880 // touched again.
1881 // E.g. this is the case for a Fly Frame without a background
1882 // brush positioned on the border of the page which inherited the background
1883 // brush from the page.
1884 bRetouche = !rOut.Contains( aGrf );
1885 break;
1887 case GPOS_TILED:
1889 // draw background of tiled graphic before drawing tiled graphic in loop
1890 // determine graphic object
1891 GraphicObject* pGraphicObj = const_cast< GraphicObject* >(pBrush->GetGraphicObject());
1892 // calculate aligned paint rectangle
1893 SwRect aAlignedPaintRect = rOut;
1894 ::SwAlignRect( aAlignedPaintRect, &rSh, &rOutDev );
1895 // draw background color for aligned paint rectangle
1896 lcl_DrawGraphicBackground( *pBrush, rOutDev, aAlignedPaintRect, *pGraphicObj, bGrfNum, gProp );
1898 // set left-top-corner of background graphic to left-top-corner of the
1899 // area, from which the background brush is determined.
1900 aGrf.Pos() = rOrg.Pos();
1901 // setup clipping at output device
1902 rOutDev.Push( vcl::PushFlags::CLIPREGION );
1903 rOutDev.IntersectClipRegion( rOut.SVRect() );
1904 // use new method <GraphicObject::DrawTiled(::)>
1906 // calculate paint offset
1907 Point aPaintOffset( aAlignedPaintRect.Pos() - aGrf.Pos() );
1908 // draw background graphic tiled for aligned paint rectangle
1909 // #i42643#
1910 // For PDF export, every draw operation for bitmaps takes a
1911 // noticeable amount of place (~50 characters). Thus, optimize
1912 // between tile bitmap size and number of drawing operations here.
1914 // A_out
1915 // n_chars = k1 * ---------- + k2 * A_bitmap
1916 // A_bitmap
1918 // minimum n_chars is obtained for (derive for A_bitmap,
1919 // set to 0, take positive solution):
1920 // k1
1921 // A_bitmap = Sqrt( ---- A_out )
1922 // k2
1924 // where k1 is the number of chars per draw operation, and
1925 // k2 is the number of chars per bitmap pixel.
1926 // This is approximately 50 and 7 for current PDF writer, respectively.
1928 const double k1( 50 );
1929 const double k2( 7 );
1930 const Size aSize( aAlignedPaintRect.SSize() );
1931 const double Abitmap( k1/k2 * static_cast<double>(aSize.Width())*aSize.Height() );
1933 pGraphicObj->DrawTiled( rOutDev,
1934 aAlignedPaintRect.SVRect(),
1935 aGrf.SSize(),
1936 Size( aPaintOffset.X(), aPaintOffset.Y() ),
1937 std::max( 128, static_cast<int>( sqrt(sqrt( Abitmap)) + .5 ) ) );
1939 // reset clipping at output device
1940 rOutDev.Pop();
1941 // set <bDraw> and <bRetouche> to false, indicating that background
1942 // graphic and background are already drawn.
1943 bDraw = bRetouche = false;
1945 break;
1947 case GPOS_NONE:
1948 bDraw = false;
1949 break;
1951 default: OSL_ENSURE( false, "new Graphic position?" );
1954 /// init variable <bGrfBackgrdAlreadDrawn> to indicate, if background of
1955 /// graphic is already drawn or not.
1956 bool bGrfBackgrdAlreadyDrawn = false;
1957 if ( bRetouche )
1959 rOutDev.Push( vcl::PushFlags::FILLCOLOR|vcl::PushFlags::LINECOLOR );
1960 rOutDev.SetLineColor();
1962 // check, if an existing background graphic (not filling the complete
1963 // background) is transparent drawn and the background color is
1964 // "no fill" respectively "auto fill", if background transparency
1965 // has to be considered.
1966 // If YES, memorize transparency of background graphic.
1967 // check also, if background graphic bitmap is transparent.
1968 bool bTransparentGrfWithNoFillBackgrd = false;
1969 sal_Int32 nGrfTransparency = 0;
1970 bool bGrfIsTransparent = false;
1971 if ( (ePos != GPOS_NONE) &&
1972 (ePos != GPOS_TILED) && (ePos != GPOS_AREA)
1975 GraphicObject *pGrf = const_cast<GraphicObject*>(pBrush->GetGraphicObject());
1976 if ( bConsiderBackgroundTransparency )
1978 GraphicAttr aGrfAttr = pGrf->GetAttr();
1979 if ( (aGrfAttr.IsTransparent()) &&
1980 (pBrush->GetColor() == COL_TRANSPARENT)
1983 bTransparentGrfWithNoFillBackgrd = true;
1984 nGrfTransparency = 255 - aGrfAttr.GetAlpha();
1987 if ( pGrf->IsTransparent() )
1989 bGrfIsTransparent = true;
1993 // to get color of brush, check background color against COL_TRANSPARENT ("no fill"/"auto fill")
1994 // instead of checking, if transparency is not set.
1995 const Color aColor( pBrush &&
1996 ( (pBrush->GetColor() != COL_TRANSPARENT) ||
1997 gProp.bSFlyMetafile )
1998 ? pBrush->GetColor()
1999 : aGlobalRetoucheColor );
2001 // determine, if background region have to be
2002 // drawn transparent.
2003 // background region has to be drawn transparent, if
2004 // background transparency have to be considered
2005 // AND
2006 // ( background color is transparent OR
2007 // background graphic is transparent and background color is "no fill"
2008 // )
2010 enum DrawStyle {
2011 Default,
2012 Transparent,
2013 } eDrawStyle = Default;
2015 if (bConsiderBackgroundTransparency &&
2016 ( ( aColor.IsTransparent()) ||
2017 bTransparentGrfWithNoFillBackgrd ) )
2019 eDrawStyle = Transparent;
2022 // #i75614# reset draw mode in high contrast mode in order to get fill color set
2023 const DrawModeFlags nOldDrawMode = rOutDev.GetDrawMode();
2024 if ( gProp.pSGlobalShell->GetWin() &&
2025 Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
2027 rOutDev.SetDrawMode( DrawModeFlags::Default );
2030 // If background region has to be drawn transparent, set only the RGB values of the background color as
2031 // the fill color for the output device.
2032 switch (eDrawStyle)
2034 case Transparent:
2036 if( rOutDev.GetFillColor() != aColor.GetRGBColor() )
2037 rOutDev.SetFillColor( aColor.GetRGBColor() );
2038 break;
2040 default:
2042 if( rOutDev.GetFillColor() != aColor )
2043 rOutDev.SetFillColor( aColor );
2044 break;
2048 // #i75614#
2049 // restore draw mode
2050 rOutDev.SetDrawMode( nOldDrawMode );
2052 switch (eDrawStyle)
2054 case Transparent:
2056 // background region have to be drawn transparent.
2057 // Thus, create a poly-polygon from the region and draw it with
2058 // the corresponding transparency percent.
2059 tools::PolyPolygon aDrawPoly( rOut.SVRect() );
2060 if ( aGrf.HasArea() )
2062 if ( !bGrfIsTransparent )
2064 // subtract area of background graphic from draw area
2065 // Consider only that part of the graphic area that is overlapping with draw area.
2066 SwRect aTmpGrf = aGrf;
2067 aTmpGrf.Intersection( rOut );
2068 if ( aTmpGrf.HasArea() )
2070 tools::Polygon aGrfPoly( aTmpGrf.SVRect() );
2071 aDrawPoly.Insert( aGrfPoly );
2074 else
2075 bGrfBackgrdAlreadyDrawn = true;
2077 // calculate transparency percent:
2078 // ( <transparency value[0x01..0xFF]>*100 + 0x7F ) / 0xFF
2079 // If there is a background graphic with a background color "no fill"/"auto fill",
2080 // the transparency value is taken from the background graphic,
2081 // otherwise take the transparency value from the color.
2082 sal_Int8 nTransparencyPercent = static_cast<sal_Int8>(
2083 (( bTransparentGrfWithNoFillBackgrd ? nGrfTransparency : (255 - aColor.GetAlpha())
2084 )*100 + 0x7F)/0xFF);
2085 // draw poly-polygon transparent
2086 rOutDev.DrawTransparent( aDrawPoly, nTransparencyPercent );
2088 break;
2090 case Default:
2091 default:
2093 SwRegionRects aRegion( rOut, 4 );
2094 if ( !bGrfIsTransparent )
2095 aRegion -= aGrf;
2096 else
2097 bGrfBackgrdAlreadyDrawn = true;
2098 // loop rectangles of background region, which has to be drawn
2099 for( size_t i = 0; i < aRegion.size(); ++i )
2101 rOutDev.DrawRect( aRegion[i].SVRect() );
2105 rOutDev.Pop();
2108 if( bDraw && aGrf.Overlaps( rOut ) )
2109 lcl_DrawGraphic( *pBrush, rOutDev, rSh, aGrf, rOut, bGrfNum, gProp,
2110 bGrfBackgrdAlreadyDrawn );
2112 if( bReplaceGrfNum )
2114 const BitmapEx& rBmp = rSh.GetReplacementBitmap(false);
2115 vcl::Font aTmp( rOutDev.GetFont() );
2116 Graphic::DrawEx(rOutDev, OUString(), aTmp, rBmp, rOrg.Pos(), rOrg.SSize());
2121 * Local helper for SwRootFrame::PaintSwFrame(..) - Adjust given rectangle to pixel size
2123 * By OD at 27.09.2002 for #103636#
2124 * In order to avoid paint errors caused by multiple alignments (e.g. ::SwAlignRect(..))
2125 * and other changes to the to be painted rectangle, this method is called for the
2126 * rectangle to be painted in order to adjust it to the pixel it is overlapping
2128 static void lcl_AdjustRectToPixelSize( SwRect& io_aSwRect, const vcl::RenderContext &aOut )
2130 // local constant object of class <Size> to determine number of Twips
2131 // representing a pixel.
2132 const Size aTwipToPxSize( aOut.PixelToLogic( Size( 1,1 )) );
2134 // local object of class <Rectangle> in Twip coordinates
2135 // calculated from given rectangle aligned to pixel centers.
2136 const tools::Rectangle aPxCenterRect = aOut.PixelToLogic(
2137 aOut.LogicToPixel( io_aSwRect.SVRect() ) );
2139 // local constant object of class <Rectangle> representing given rectangle
2140 // in pixel.
2141 const tools::Rectangle aOrgPxRect = aOut.LogicToPixel( io_aSwRect.SVRect() );
2143 // calculate adjusted rectangle from pixel centered rectangle.
2144 // Due to rounding differences <aPxCenterRect> doesn't exactly represents
2145 // the Twip-centers. Thus, adjust borders by half of pixel width/height plus 1.
2146 // Afterwards, adjust calculated Twip-positions of the all borders.
2147 tools::Rectangle aSizedRect = aPxCenterRect;
2148 aSizedRect.AdjustLeft( -(aTwipToPxSize.Width()/2 + 1) );
2149 aSizedRect.AdjustRight( aTwipToPxSize.Width()/2 + 1 );
2150 aSizedRect.AdjustTop( -(aTwipToPxSize.Height()/2 + 1) );
2151 aSizedRect.AdjustBottom(aTwipToPxSize.Height()/2 + 1);
2153 // adjust left()
2154 while ( aOut.LogicToPixel(aSizedRect).Left() < aOrgPxRect.Left() )
2156 aSizedRect.AdjustLeft( 1 );
2158 // adjust right()
2159 while ( aOut.LogicToPixel(aSizedRect).Right() > aOrgPxRect.Right() )
2161 aSizedRect.AdjustRight( -1 );
2163 // adjust top()
2164 while ( aOut.LogicToPixel(aSizedRect).Top() < aOrgPxRect.Top() )
2166 aSizedRect.AdjustTop( 1 );
2168 // adjust bottom()
2169 while ( aOut.LogicToPixel(aSizedRect).Bottom() > aOrgPxRect.Bottom() )
2171 aSizedRect.AdjustBottom( -1 );
2174 io_aSwRect = SwRect( aSizedRect );
2176 #if OSL_DEBUG_LEVEL > 0
2177 tools::Rectangle aTestOrgPxRect = aOut.LogicToPixel( io_aSwRect.SVRect() );
2178 tools::Rectangle aTestNewPxRect = aOut.LogicToPixel( aSizedRect );
2179 OSL_ENSURE( aTestOrgPxRect == aTestNewPxRect,
2180 "Error in lcl_AlignRectToPixelSize(..): Adjusted rectangle has incorrect position or size");
2181 // check Left()
2182 aSizedRect.AdjustLeft( -1 );
2183 aTestNewPxRect = aOut.LogicToPixel( aSizedRect );
2184 OSL_ENSURE( aTestOrgPxRect.Left() >= (aTestNewPxRect.Left()+1),
2185 "Error in lcl_AlignRectToPixelSize(..): Left() not correct adjusted");
2186 aSizedRect.AdjustLeft( 1 );
2187 // check Right()
2188 aSizedRect.AdjustRight( 1 );
2189 aTestNewPxRect = aOut.LogicToPixel( aSizedRect );
2190 OSL_ENSURE( aTestOrgPxRect.Right() <= (aTestNewPxRect.Right()-1),
2191 "Error in lcl_AlignRectToPixelSize(..): Right() not correct adjusted");
2192 aSizedRect.AdjustRight( -1 );
2193 // check Top()
2194 aSizedRect.AdjustTop( -1 );
2195 aTestNewPxRect = aOut.LogicToPixel( aSizedRect );
2196 OSL_ENSURE( aTestOrgPxRect.Top() >= (aTestNewPxRect.Top()+1),
2197 "Error in lcl_AlignRectToPixelSize(..): Top() not correct adjusted");
2198 aSizedRect.AdjustTop( 1 );
2199 // check Bottom()
2200 aSizedRect.AdjustBottom( 1 );
2201 aTestNewPxRect = aOut.LogicToPixel( aSizedRect );
2202 OSL_ENSURE( aTestOrgPxRect.Bottom() <= (aTestNewPxRect.Bottom()-1),
2203 "Error in lcl_AlignRectToPixelSize(..): Bottom() not correct adjusted");
2204 aSizedRect.AdjustBottom( -1 );
2205 #endif
2208 // FUNCTIONS USED FOR COLLAPSING TABLE BORDER LINES START
2210 namespace {
2212 struct SwLineEntry
2214 SwTwips mnKey;
2215 SwTwips mnStartPos;
2216 SwTwips mnEndPos;
2217 SwTwips mnLimitedEndPos;
2218 bool mbOuter;
2220 svx::frame::Style maAttribute;
2222 enum OverlapType { NO_OVERLAP, OVERLAP1, OVERLAP2, OVERLAP3 };
2224 enum class VerticalType { LEFT, RIGHT };
2226 public:
2227 SwLineEntry( SwTwips nKey,
2228 SwTwips nStartPos,
2229 SwTwips nEndPos,
2230 bool bOuter,
2231 const svx::frame::Style& rAttribute );
2233 OverlapType Overlaps( const SwLineEntry& rComp ) const;
2236 * Assuming that this entry is for a Word-style covering cell and the border matching eType is
2237 * set, limit the end position of this border in case covered cells have no borders set.
2239 void LimitVerticalEndPos(const SwFrame& rFrame, VerticalType eType);
2244 SwLineEntry::SwLineEntry( SwTwips nKey,
2245 SwTwips nStartPos,
2246 SwTwips nEndPos,
2247 bool bOuter,
2248 const svx::frame::Style& rAttribute )
2249 : mnKey( nKey ),
2250 mnStartPos( nStartPos ),
2251 mnEndPos( nEndPos ),
2252 mnLimitedEndPos(0),
2253 mbOuter(bOuter),
2254 maAttribute( rAttribute )
2260 1. ---------- rOld
2261 ---------- rNew
2263 2. ---------- rOld
2264 ------------- rNew
2266 3. ------- rOld
2267 ------------- rNew
2269 4. ------------- rOld
2270 ---------- rNew
2272 5. ---------- rOld
2273 ---- rNew
2275 6. ---------- rOld
2276 ---------- rNew
2278 7. ------------- rOld
2279 ---------- rNew
2281 8. ---------- rOld
2282 ------------- rNew
2284 9. ---------- rOld
2285 ---------- rNew
2288 SwLineEntry::OverlapType SwLineEntry::Overlaps( const SwLineEntry& rNew ) const
2290 SwLineEntry::OverlapType eRet = OVERLAP3;
2292 if ( mnStartPos >= rNew.mnEndPos || mnEndPos <= rNew.mnStartPos )
2293 eRet = NO_OVERLAP;
2295 // 1, 2, 3
2296 else if ( mnEndPos < rNew.mnEndPos )
2297 eRet = OVERLAP1;
2299 // 4, 5, 6, 7
2300 else if (mnStartPos <= rNew.mnStartPos)
2301 eRet = OVERLAP2;
2303 // 8, 9
2304 return eRet;
2307 void SwLineEntry::LimitVerticalEndPos(const SwFrame& rFrame, VerticalType eType)
2309 if (!rFrame.IsCellFrame())
2311 return;
2314 const auto& rCellFrame = static_cast<const SwCellFrame&>(rFrame);
2315 std::vector<const SwCellFrame*> aCoveredCells = rCellFrame.GetCoveredCells();
2316 // Iterate in reverse order, so we can stop at the first cell that has a border. This can
2317 // determine what is the minimal end position that is safe to use as a limit.
2318 for (auto it = aCoveredCells.rbegin(); it != aCoveredCells.rend(); ++it)
2320 const SwCellFrame* pCoveredCell = *it;
2321 SwBorderAttrAccess aAccess( SwFrame::GetCache(), pCoveredCell );
2322 const SwBorderAttrs& rAttrs = *aAccess.Get();
2323 const SvxBoxItem& rBox = rAttrs.GetBox();
2324 if (eType == VerticalType::LEFT && rBox.GetLeft())
2326 break;
2329 if (eType == VerticalType::RIGHT && rBox.GetRight())
2331 break;
2334 mnLimitedEndPos = pCoveredCell->getFrameArea().Top();
2338 namespace {
2340 struct lt_SwLineEntry
2342 bool operator()( const SwLineEntry& e1, const SwLineEntry& e2 ) const
2344 return e1.mnStartPos < e2.mnStartPos;
2350 typedef std::set< SwLineEntry, lt_SwLineEntry > SwLineEntrySet;
2351 typedef std::map< SwTwips, SwLineEntrySet > SwLineEntryMap;
2353 namespace {
2355 class SwTabFramePainter
2357 SwLineEntryMap maVertLines;
2358 SwLineEntryMap maHoriLines;
2359 const SwTabFrame& mrTabFrame;
2361 void Insert( SwLineEntry&, bool bHori );
2362 void Insert(const SwFrame& rFrame, const SvxBoxItem& rBoxItem, const SwRect &rPaintArea);
2364 /// Inserts top border at the top of a follow table.
2365 void InsertFollowTopBorder(const SwFrame& rFrame, const SvxBoxItem& rBoxItem,
2366 bool bWordTableCell, SwTwips nTop, SwTwips nLeft, SwTwips nRight,
2367 bool bTopIsOuter);
2368 void InsertMasterBottomBorder(const SwFrame& rFrame, const SvxBoxItem& rBoxItem,
2369 bool bWordTableCell, SwTwips nBottom, SwTwips nLeft, SwTwips nRight,
2370 bool bBottomIsOuter);
2372 void HandleFrame(const SwLayoutFrame& rFrame, const SwRect& rPaintArea);
2373 void FindStylesForLine( Point&,
2374 Point&,
2375 svx::frame::Style*,
2376 bool bHori,
2377 bool bOuter ) const;
2379 public:
2380 explicit SwTabFramePainter( const SwTabFrame& rTabFrame );
2382 void PaintLines( OutputDevice& rDev, const SwRect& rRect ) const;
2387 SwTabFramePainter::SwTabFramePainter( const SwTabFrame& rTabFrame )
2388 : mrTabFrame( rTabFrame )
2390 SwRect aPaintArea = rTabFrame.GetUpper()->GetPaintArea();
2391 HandleFrame(rTabFrame, aPaintArea);
2394 void SwTabFramePainter::HandleFrame(const SwLayoutFrame& rLayoutFrame, const SwRect& rPaintArea)
2396 // Add border lines of cell frames. Skip covered cells. Skip cells
2397 // in special row span row, which do not have a negative row span:
2398 if ( rLayoutFrame.IsCellFrame() && !rLayoutFrame.IsCoveredCell() )
2400 const SwCellFrame* pThisCell = static_cast<const SwCellFrame*>(&rLayoutFrame);
2401 const SwRowFrame* pRowFrame = static_cast<const SwRowFrame*>(pThisCell->GetUpper());
2402 const tools::Long nRowSpan = pThisCell->GetTabBox()->getRowSpan();
2403 if ( !pRowFrame->IsRowSpanLine() || nRowSpan > 1 || nRowSpan < -1 )
2405 SwBorderAttrAccess aAccess( SwFrame::GetCache(), &rLayoutFrame );
2406 const SwBorderAttrs& rAttrs = *aAccess.Get();
2407 const SvxBoxItem& rBox = rAttrs.GetBox();
2408 Insert(rLayoutFrame, rBox, rPaintArea);
2412 // Recurse into lower layout frames, but do not recurse into lower tabframes.
2413 const SwFrame* pLower = rLayoutFrame.Lower();
2414 while ( pLower )
2416 if (pLower->IsLayoutFrame() && !pLower->IsTabFrame())
2418 const SwLayoutFrame* pLowerLayFrame = static_cast<const SwLayoutFrame*>(pLower);
2419 HandleFrame(*pLowerLayFrame, rPaintArea);
2421 pLower = pLower->GetNext();
2425 void SwTabFramePainter::PaintLines(OutputDevice& rDev, const SwRect& rRect) const
2427 // #i16816# tagged pdf support
2428 SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, nullptr, rDev );
2430 SwLineEntryMap::const_iterator aIter = maHoriLines.begin();
2431 bool bHori = true;
2433 // color for subsidiary lines:
2434 const Color& rCol( gProp.pSGlobalShell->GetViewOptions()->GetTableBoundariesColor() );
2436 // high contrast mode:
2437 // overrides the color of non-subsidiary lines.
2438 const Color* pHCColor = nullptr;
2439 DrawModeFlags nOldDrawMode = rDev.GetDrawMode();
2440 if( gProp.pSGlobalShell->GetWin() &&
2441 Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
2443 pHCColor = &gProp.pSGlobalShell->GetViewOptions()->GetFontColor();
2444 rDev.SetDrawMode( DrawModeFlags::Default );
2447 const SwFrame* pUpper = mrTabFrame.GetUpper();
2448 SwRect aUpper( pUpper->getFramePrintArea() );
2449 aUpper.Pos() += pUpper->getFrameArea().Pos();
2450 SwRect aUpperAligned( aUpper );
2451 ::SwAlignRect( aUpperAligned, gProp.pSGlobalShell, &rDev );
2453 // prepare SdrFrameBorderDataVector
2454 drawinglayer::primitive2d::SdrFrameBorderDataVector aData;
2456 while ( true )
2458 if ( bHori && aIter == maHoriLines.end() )
2460 aIter = maVertLines.begin();
2461 bHori = false;
2464 if ( !bHori && aIter == maVertLines.end() )
2465 break;
2467 const SwLineEntrySet& rEntrySet = (*aIter).second;
2468 for (const SwLineEntry& rEntry : rEntrySet)
2470 const svx::frame::Style& rEntryStyle( rEntry.maAttribute );
2472 Point aStart, aEnd;
2473 if ( bHori )
2475 aStart.setX( rEntry.mnStartPos );
2476 aStart.setY( rEntry.mnKey );
2477 aEnd.setX( rEntry.mnEndPos );
2478 aEnd.setY( rEntry.mnKey );
2480 else
2482 aStart.setX( rEntry.mnKey );
2483 aStart.setY( rEntry.mnStartPos );
2484 aEnd.setX( rEntry.mnKey );
2485 aEnd.setY( rEntry.mnEndPos );
2488 svx::frame::Style aStyles[ 7 ];
2489 aStyles[ 0 ] = rEntryStyle;
2490 FindStylesForLine(aStart, aEnd, aStyles, bHori, rEntry.mbOuter);
2492 if (!bHori && rEntry.mnLimitedEndPos)
2494 aEnd.setY(rEntry.mnLimitedEndPos);
2497 SwRect aRepaintRect( aStart, aEnd );
2499 // the repaint rectangle has to be moved a bit for the centered lines:
2500 SwTwips nRepaintRectSize = !rEntryStyle.GetWidth() ? 1 : rEntryStyle.GetWidth();
2501 if ( bHori )
2503 aRepaintRect.Height( 2 * nRepaintRectSize );
2504 aRepaintRect.Pos().AdjustY( -nRepaintRectSize );
2506 // To decide on visibility it is also necessary to expand the RepaintRect
2507 // to left/right according existing BorderLine overlap matchings, else there
2508 // will be repaint errors when scrolling in e.t TripleLine BorderLines.
2509 // aStyles[1] == aLFromT, aStyles[3] == aLFromB, aStyles[4] == aRFromT, aStyles[6] == aRFromB
2510 if(aStyles[1].IsUsed() || aStyles[3].IsUsed() || aStyles[4].IsUsed() || aStyles[6].IsUsed())
2512 const double fLineWidthMaxLeft(std::max(aStyles[1].GetWidth(), aStyles[3].GetWidth()));
2513 const double fLineWidthMaxRight(std::max(aStyles[4].GetWidth(), aStyles[6].GetWidth()));
2514 aRepaintRect.Width(aRepaintRect.Width() + (fLineWidthMaxLeft + fLineWidthMaxRight));
2515 aRepaintRect.Pos().AdjustX( -fLineWidthMaxLeft );
2518 else
2520 aRepaintRect.Width( 2 * nRepaintRectSize );
2521 aRepaintRect.Pos().AdjustX( -nRepaintRectSize );
2523 // Accordingly to horizontal case, but for top/bottom
2524 // aStyles[3] == aTFromR, aStyles[1] == aTFromL, aStyles[6] == aBFromR, aStyles[4] == aBFromL
2525 if(aStyles[3].IsUsed() || aStyles[1].IsUsed() || aStyles[6].IsUsed() || aStyles[4].IsUsed())
2527 const double fLineWidthMaxTop(std::max(aStyles[3].GetWidth(), aStyles[1].GetWidth()));
2528 const double fLineWidthMaxBottom(std::max(aStyles[6].GetWidth(), aStyles[4].GetWidth()));
2529 aRepaintRect.Height(aRepaintRect.Height() + (fLineWidthMaxTop + fLineWidthMaxBottom));
2530 aRepaintRect.Pos().AdjustY( -fLineWidthMaxTop );
2534 if (!rRect.Overlaps(aRepaintRect))
2536 continue;
2539 // subsidiary lines
2540 const Color* pTmpColor = nullptr;
2541 if (0 == aStyles[ 0 ].GetWidth())
2543 if (isSubsidiaryLinesEnabled() &&
2544 gProp.pSGlobalShell->GetViewOptions()->IsTableBoundaries() &&
2545 gProp.pSGlobalShell->GetWin())
2546 aStyles[ 0 ].Set( rCol, rCol, rCol, false, 1, 0, 0 );
2547 else
2548 aStyles[0].SetType(SvxBorderLineStyle::NONE);
2550 else
2551 pTmpColor = pHCColor;
2553 // The (twip) positions will be adjusted to meet these requirements:
2554 // 1. The y coordinates are located in the middle of the pixel grid
2555 // 2. The x coordinated are located at the beginning of the pixel grid
2556 // This is done, because the horizontal lines are painted "at
2557 // beginning", whereas the vertical lines are painted "centered".
2558 // By making the line sizes a multiple of one pixel size, we can
2559 // assure that all lines having the same twip size have the same
2560 // pixel size, independent of their position on the screen.
2561 Point aPaintStart = rDev.PixelToLogic( rDev.LogicToPixel(aStart) );
2562 Point aPaintEnd = rDev.PixelToLogic( rDev.LogicToPixel(aEnd) );
2564 if (gProp.pSGlobalShell->GetWin())
2566 // The table borders do not use SwAlignRect, but all the other frames do.
2567 // Therefore we tweak the outer borders a bit to achieve that the outer
2568 // borders match the subsidiary lines of the upper:
2569 if (aStart.X() == aUpper.Left())
2570 aPaintStart.setX( aUpperAligned.Left() );
2571 else if (aStart.X() == aUpper.Right_())
2572 aPaintStart.setX( aUpperAligned.Right_() );
2573 if (aStart.Y() == aUpper.Top())
2574 aPaintStart.setY( aUpperAligned.Top() );
2575 else if (aStart.Y() == aUpper.Bottom_())
2576 aPaintStart.setY( aUpperAligned.Bottom_() );
2578 if (aEnd.X() == aUpper.Left())
2579 aPaintEnd.setX( aUpperAligned.Left() );
2580 else if (aEnd.X() == aUpper.Right_())
2581 aPaintEnd.setX( aUpperAligned.Right_() );
2582 if (aEnd.Y() == aUpper.Top())
2583 aPaintEnd.setY( aUpperAligned.Top() );
2584 else if (aEnd.Y() == aUpper.Bottom_())
2585 aPaintEnd.setY( aUpperAligned.Bottom_() );
2588 if(aStyles[0].IsUsed())
2590 if (bHori)
2592 const basegfx::B2DPoint aOrigin(aPaintStart.X(), aPaintStart.Y());
2593 const basegfx::B2DVector aX(basegfx::B2DPoint(aPaintEnd.X(), aPaintEnd.Y()) - aOrigin);
2595 if(!aX.equalZero())
2597 const basegfx::B2DVector aY(basegfx::getNormalizedPerpendicular(aX));
2598 aData.emplace_back(
2599 aOrigin,
2601 aStyles[0],
2602 pTmpColor);
2603 drawinglayer::primitive2d::SdrFrameBorderData& rInstance(aData.back());
2605 rInstance.addSdrConnectStyleData(true, aStyles[1], -aY, true); // aLFromT
2606 rInstance.addSdrConnectStyleData(true, aStyles[2], -aX, true); // aLFromL
2607 rInstance.addSdrConnectStyleData(true, aStyles[3], aY, false); // aLFromB
2609 rInstance.addSdrConnectStyleData(false, aStyles[4], -aY, true); // aRFromT
2610 rInstance.addSdrConnectStyleData(false, aStyles[5], aX, false); // aRFromR
2611 rInstance.addSdrConnectStyleData(false, aStyles[6], aY, false); // aRFromB
2614 else // vertical
2616 const basegfx::B2DPoint aOrigin(aPaintStart.X(), aPaintStart.Y());
2617 const basegfx::B2DVector aX(basegfx::B2DPoint(aPaintEnd.X(), aPaintEnd.Y()) - aOrigin);
2619 if(!aX.equalZero())
2621 const basegfx::B2DVector aY(basegfx::getNormalizedPerpendicular(aX));
2622 aData.emplace_back(
2623 aOrigin,
2625 aStyles[0],
2626 pTmpColor);
2627 drawinglayer::primitive2d::SdrFrameBorderData& rInstance(aData.back());
2629 rInstance.addSdrConnectStyleData(true, aStyles[3], -aY, false); // aTFromR
2630 rInstance.addSdrConnectStyleData(true, aStyles[2], -aX, true); // aTFromT
2631 rInstance.addSdrConnectStyleData(true, aStyles[1], aY, true); // aTFromL
2633 rInstance.addSdrConnectStyleData(false, aStyles[6], -aY, false); // aBFromR
2634 rInstance.addSdrConnectStyleData(false, aStyles[5], aX, false); // aBFromB
2635 rInstance.addSdrConnectStyleData(false, aStyles[4], aY, true); // aBFromL
2640 ++aIter;
2643 // create instance of SdrFrameBorderPrimitive2D if
2644 // SdrFrameBorderDataVector is used
2645 if(!aData.empty())
2647 drawinglayer::primitive2d::Primitive2DContainer aSequence;
2648 aSequence.append(
2649 new drawinglayer::primitive2d::SdrFrameBorderPrimitive2D(
2650 std::move(aData),
2651 true)); // force visualization to minimal one discrete unit (pixel)
2652 // paint
2653 mrTabFrame.ProcessPrimitives(aSequence);
2656 // restore output device:
2657 rDev.SetDrawMode( nOldDrawMode );
2661 * Finds the lines that join the line defined by (StartPoint, EndPoint) in either
2662 * StartPoint or Endpoint. The styles of these lines are required for DR's magic
2663 * line painting functions
2665 void SwTabFramePainter::FindStylesForLine( Point& rStartPoint,
2666 Point& rEndPoint,
2667 svx::frame::Style* pStyles,
2668 bool bHori, bool bOuter ) const
2670 // For example, aLFromB means: this vertical line intersects my horizontal line at its left end,
2671 // from bottom.
2672 // pStyles[ 1 ] = bHori ? aLFromT : TFromL
2673 // pStyles[ 2 ] = bHori ? aLFromL : TFromT,
2674 // pStyles[ 3 ] = bHori ? aLFromB : TFromR,
2675 // pStyles[ 4 ] = bHori ? aRFromT : BFromL,
2676 // pStyles[ 5 ] = bHori ? aRFromR : BFromB,
2677 // pStyles[ 6 ] = bHori ? aRFromB : BFromR,
2679 bool bWordTableCell = false;
2680 SwViewShell* pShell = mrTabFrame.getRootFrame()->GetCurrShell();
2681 if (pShell)
2683 const IDocumentSettingAccess& rIDSA = pShell->GetDoc()->getIDocumentSettingAccess();
2684 bWordTableCell = rIDSA.get(DocumentSettingId::TABLE_ROW_KEEP);
2687 SwLineEntryMap::const_iterator aMapIter = maVertLines.find( rStartPoint.X() );
2688 assert(aMapIter != maVertLines.end() && "FindStylesForLine: Error");
2689 const SwLineEntrySet& rVertSet = (*aMapIter).second;
2691 for ( const SwLineEntry& rEntry : rVertSet )
2693 if ( bHori )
2695 if ( rStartPoint.Y() == rEntry.mnStartPos )
2696 pStyles[ 3 ] = rEntry.maAttribute;
2697 else if ( rStartPoint.Y() == rEntry.mnEndPos )
2698 pStyles[ 1 ] = rEntry.maAttribute;
2700 if (bWordTableCell && rStartPoint.X() == rEntry.mnKey && !bOuter && rEntry.mbOuter)
2702 rStartPoint.AdjustX(rEntry.maAttribute.GetWidth());
2705 else
2707 if ( rStartPoint.Y() == rEntry.mnEndPos )
2708 pStyles[ 2 ] = rEntry.maAttribute;
2709 else if ( rEndPoint.Y() == rEntry.mnStartPos )
2710 pStyles[ 5 ] = rEntry.maAttribute;
2714 aMapIter = maHoriLines.find( rStartPoint.Y() );
2715 assert(aMapIter != maHoriLines.end() && "FindStylesForLine: Error");
2716 const SwLineEntrySet& rHoriSet = aMapIter->second;
2718 for ( const SwLineEntry& rEntry : rHoriSet )
2720 if ( bHori )
2722 if ( rStartPoint.X() == rEntry.mnEndPos )
2723 pStyles[ 2 ] = rEntry.maAttribute;
2724 else if ( rEndPoint.X() == rEntry.mnStartPos )
2725 pStyles[ 5 ] = rEntry.maAttribute;
2727 else
2729 if ( rStartPoint.X() == rEntry.mnEndPos )
2730 pStyles[ 1 ] = rEntry.maAttribute;
2731 else if ( rStartPoint.X() == rEntry.mnStartPos )
2732 pStyles[ 3 ] = rEntry.maAttribute;
2734 if (bWordTableCell && rStartPoint.Y() == rEntry.mnKey && !bOuter && rEntry.mbOuter)
2736 rStartPoint.AdjustY(rEntry.maAttribute.GetWidth());
2741 if ( bHori )
2743 aMapIter = maVertLines.find( rEndPoint.X() );
2744 assert(aMapIter != maVertLines.end() && "FindStylesForLine: Error");
2745 const SwLineEntrySet& rVertSet2 = (*aMapIter).second;
2747 for ( const SwLineEntry& rEntry : rVertSet2 )
2749 if ( rEndPoint.Y() == rEntry.mnStartPos )
2750 pStyles[ 6 ] = rEntry.maAttribute;
2751 else if ( rEndPoint.Y() == rEntry.mnEndPos )
2752 pStyles[ 4 ] = rEntry.maAttribute;
2754 if (bWordTableCell && rEndPoint.X() == rEntry.mnKey && !bOuter && rEntry.mbOuter)
2756 rEndPoint.AdjustX(-rEntry.maAttribute.GetWidth());
2760 else
2762 aMapIter = maHoriLines.find( rEndPoint.Y() );
2763 assert(aMapIter != maHoriLines.end() && "FindStylesForLine: Error");
2764 const SwLineEntrySet& rHoriSet2 = (*aMapIter).second;
2766 for ( const SwLineEntry& rEntry : rHoriSet2 )
2768 if ( rEndPoint.X() == rEntry.mnEndPos )
2769 pStyles[ 4 ] = rEntry.maAttribute;
2770 else if ( rEndPoint.X() == rEntry.mnStartPos )
2771 pStyles[ 6 ] = rEntry.maAttribute;
2773 if (bWordTableCell && rEndPoint.Y() == rEntry.mnKey && !bOuter && rEntry.mbOuter)
2775 rEndPoint.AdjustY(-rEntry.maAttribute.GetWidth());
2782 * Special case: #i9860#
2783 * first line in follow table without repeated headlines
2784 * Special case: tdf#150308
2785 * first visible line of a table with preceding hidden deleted rows
2787 static bool lcl_IsFirstRowInFollowTableWithoutRepeatedHeadlines(
2788 SwTabFrame const& rTabFrame, SwFrame const& rFrame, SvxBoxItem const& rBoxItem)
2790 SwRowFrame const*const pThisRowFrame =
2791 dynamic_cast<const SwRowFrame*>(rFrame.GetUpper());
2792 return (pThisRowFrame
2793 && (pThisRowFrame->GetUpper() == &rTabFrame)
2794 && ( rTabFrame.IsFollow()
2795 // tdf#150308 first table row isn't equal to the table row of the first
2796 // row frame of the first table frame: there are invisible deleted rows
2797 // in Hide Changes mode before the first visible table row
2798 || rTabFrame.GetTable()->GetTabLines().front() != pThisRowFrame->GetTabLine() )
2799 && !rTabFrame.GetTable()->GetRowsToRepeat()
2800 && ( !pThisRowFrame->GetPrev()
2801 || static_cast<const SwRowFrame*>(pThisRowFrame->GetPrev())
2802 ->IsRowSpanLine())
2803 && !rBoxItem.GetTop()
2804 && rBoxItem.GetBottom());
2808 * Special case:
2809 * last visible cell of a table row followed with a hidden deleted cell,
2810 * and the right border of the visible cell was painted by its left border
2812 static bool lcl_IsLastVisibleCellBeforeHiddenCellAtTheEndOfRow(
2813 SwFrame const& rFrame, SvxBoxItem const& rBoxItem)
2815 SwRowFrame const*const pThisRowFrame =
2816 dynamic_cast<const SwRowFrame*>(rFrame.GetUpper());
2817 const SwCellFrame* pThisCell = static_cast<const SwCellFrame*>(&rFrame);
2819 return pThisRowFrame
2820 // last visible cell
2821 && !rFrame.GetNext()
2822 // it has only left border
2823 && !rBoxItem.GetRight()
2824 && rBoxItem.GetLeft()
2825 // last visible table cell isn't equal to the last cell:
2826 // there are invisible deleted cells in Hide Changes mode
2827 && pThisRowFrame->GetTabLine()->GetTabBoxes().back() != pThisCell->GetTabBox();
2830 void SwTabFramePainter::InsertFollowTopBorder(const SwFrame& rFrame, const SvxBoxItem& rBoxItem,
2831 bool bWordTableCell, SwTwips nTop, SwTwips nLeft,
2832 SwTwips nRight, bool bTopIsOuter)
2834 // Figure out which cell to copy.
2835 int nCol = 0;
2836 const SwFrame* pCell = &rFrame;
2837 while (pCell)
2839 if (!pCell->GetPrev())
2841 break;
2844 ++nCol;
2845 pCell = pCell->GetPrev();
2848 auto pThisRow = dynamic_cast<const SwRowFrame*>(rFrame.GetUpper());
2849 if (!pThisRow || pThisRow->GetUpper() != &mrTabFrame)
2851 return;
2854 if (!mrTabFrame.IsFollow() || mrTabFrame.GetTable()->GetRowsToRepeat())
2856 return;
2859 if (pThisRow->GetPrev() || rBoxItem.GetTop() || rBoxItem.GetBottom())
2861 return;
2864 // This is then a first row in a follow table, without repeated headlines.
2865 auto pLastRow = dynamic_cast<const SwRowFrame*>(mrTabFrame.GetLastLower());
2866 if (!pLastRow || pLastRow == pThisRow)
2868 return;
2871 const SwFrame* pLastCell = pLastRow->GetLower();
2872 if (!pLastCell)
2874 return;
2877 for (int i = 0; i < nCol; ++i)
2879 if (!pLastCell->GetNext())
2881 // Reference row has merged cells, work with the last possible one.
2882 break;
2885 pLastCell = pLastCell->GetNext();
2888 SwBorderAttrAccess aAccess(SwFrame::GetCache(), pLastCell);
2889 const SwBorderAttrs& rAttrs = *aAccess.Get();
2890 const SvxBoxItem& rLastBoxItem = rAttrs.GetBox();
2891 if (!rLastBoxItem.GetBottom())
2893 return;
2896 // The matching (same column) row in the last row has a bottom border for us.
2897 svx::frame::Style aFollowB(rLastBoxItem.GetBottom(), 1.0);
2898 aFollowB.SetWordTableCell(bWordTableCell);
2899 SwLineEntry aFollowTop(nTop, nLeft, nRight, bTopIsOuter, aFollowB);
2900 aFollowB.SetRefMode(svx::frame::RefMode::Begin);
2901 Insert(aFollowTop, true);
2904 void SwTabFramePainter::InsertMasterBottomBorder(const SwFrame& rFrame, const SvxBoxItem& rBoxItem,
2905 bool bWordTableCell, SwTwips nBottom, SwTwips nLeft,
2906 SwTwips nRight, bool bBottomIsOuter)
2908 // Figure out which cell to copy.
2909 int nCol = 0;
2910 const SwFrame* pCell = &rFrame;
2911 while (pCell)
2913 if (!pCell->GetPrev())
2915 break;
2918 ++nCol;
2919 pCell = pCell->GetPrev();
2922 auto pThisRow = dynamic_cast<const SwRowFrame*>(rFrame.GetUpper());
2923 if (!pThisRow || pThisRow->GetUpper() != &mrTabFrame)
2925 return;
2928 if (mrTabFrame.IsFollow() || !mrTabFrame.GetFollow())
2930 return;
2933 // This is a master table that is split.
2934 if (pThisRow->GetNext() || rBoxItem.GetTop() || rBoxItem.GetBottom())
2936 return;
2939 // This is then a last row in a master table.
2940 auto pFirstRow = dynamic_cast<const SwRowFrame*>(mrTabFrame.GetLower());
2941 if (!pFirstRow || pFirstRow == pThisRow)
2943 return;
2946 const SwFrame* pFirstCell = pFirstRow->GetLower();
2947 if (!pFirstCell)
2949 return;
2952 for (int i = 0; i < nCol; ++i)
2954 if (!pFirstCell->GetNext())
2956 // Reference row has merged cells, work with the last possible one.
2957 break;
2960 pFirstCell = pFirstCell->GetNext();
2963 SwBorderAttrAccess aAccess(SwFrame::GetCache(), pFirstCell);
2964 const SwBorderAttrs& rAttrs = *aAccess.Get();
2965 const SvxBoxItem& rFirstBoxItem = rAttrs.GetBox();
2966 if (!rFirstBoxItem.GetTop())
2968 return;
2971 // The matching (same column) row in the first row has a top border for us.
2972 svx::frame::Style aMasterT(rFirstBoxItem.GetTop(), 1.0);
2973 aMasterT.SetWordTableCell(bWordTableCell);
2974 SwLineEntry aMasterBottom(nBottom, nLeft, nRight, bBottomIsOuter, aMasterT);
2975 aMasterT.SetRefMode(svx::frame::RefMode::Begin);
2976 Insert(aMasterBottom, true);
2979 void SwTabFramePainter::Insert(const SwFrame& rFrame, const SvxBoxItem& rBoxItem, const SwRect& rPaintArea)
2981 // build 4 line entries for the 4 borders:
2982 SwRect aBorderRect = rFrame.getFrameArea();
2984 aBorderRect.Intersection(rPaintArea);
2986 bool const bBottomAsTop(lcl_IsFirstRowInFollowTableWithoutRepeatedHeadlines(
2987 mrTabFrame, rFrame, rBoxItem));
2988 bool const bLeftAsRight(lcl_IsLastVisibleCellBeforeHiddenCellAtTheEndOfRow(
2989 rFrame, rBoxItem));
2991 bool const bVert = mrTabFrame.IsVertical();
2992 bool const bR2L = mrTabFrame.IsRightToLeft();
2994 bool bWordTableCell = false;
2995 SwViewShell* pShell = rFrame.getRootFrame()->GetCurrShell();
2996 if (pShell)
2998 const IDocumentSettingAccess& rIDSA = pShell->GetDoc()->getIDocumentSettingAccess();
2999 bWordTableCell = rIDSA.get(DocumentSettingId::TABLE_ROW_KEEP);
3002 // no scaling needed, it's all in the primitives and the target device
3003 svx::frame::Style aL(rBoxItem.GetLeft(), 1.0);
3004 aL.SetWordTableCell(bWordTableCell);
3005 svx::frame::Style aR(rBoxItem.GetRight(), 1.0);
3006 aR.SetWordTableCell(bWordTableCell);
3007 svx::frame::Style aT(rBoxItem.GetTop(), 1.0);
3008 aT.SetWordTableCell(bWordTableCell);
3009 svx::frame::Style aB(rBoxItem.GetBottom(), 1.0);
3010 aB.SetWordTableCell(bWordTableCell);
3012 // First cell in a row.
3013 bool bLeftIsOuter = rFrame.IsCellFrame() && rFrame.GetUpper()->GetLower() == &rFrame;
3014 // Last cell in a row.
3015 bool bRightIsOuter = rFrame.IsCellFrame() && rFrame.GetNext() == nullptr;
3016 // First row in a table.
3017 bool bTopIsOuter = rFrame.IsCellFrame() && rFrame.GetUpper()->GetUpper()->GetLower() == rFrame.GetUpper();
3018 // Last row in a table.
3019 bool bBottomIsOuter = rFrame.IsCellFrame() && rFrame.GetUpper()->GetNext() == nullptr;
3021 aR.MirrorSelf();
3022 if (!bWordTableCell || !bBottomIsOuter)
3024 // Outer horizontal lines are never mirrored in Word.
3025 aB.MirrorSelf();
3028 const SwTwips nLeft = aBorderRect.Left_();
3029 const SwTwips nRight = aBorderRect.Right_();
3030 const SwTwips nTop = aBorderRect.Top_();
3031 const SwTwips nBottom = aBorderRect.Bottom_();
3033 aL.SetRefMode( svx::frame::RefMode::Centered );
3034 aR.SetRefMode( svx::frame::RefMode::Centered );
3035 aT.SetRefMode( !bVert ? svx::frame::RefMode::Begin : svx::frame::RefMode::End );
3036 aB.SetRefMode( !bVert ? svx::frame::RefMode::Begin : svx::frame::RefMode::End );
3038 if (bWordTableCell && bLeftIsOuter)
3040 // Outer vertical lines are always mirrored in Word.
3041 aL.MirrorSelf();
3044 SwLineEntry aLeft (nLeft, nTop, nBottom, bLeftIsOuter,
3045 bVert ? aB : (bR2L ? aR : aL));
3046 if (bWordTableCell && rBoxItem.GetLeft())
3048 aLeft.LimitVerticalEndPos(rFrame, SwLineEntry::VerticalType::LEFT);
3051 SwLineEntry aRight (nRight, nTop, nBottom, bRightIsOuter,
3052 bVert ? (bBottomAsTop ? aB : aT) : ((bR2L || bLeftAsRight) ? aL : aR));
3053 if (bWordTableCell && rBoxItem.GetRight())
3055 aRight.LimitVerticalEndPos(rFrame, SwLineEntry::VerticalType::RIGHT);
3058 SwLineEntry aTop (nTop, nLeft, nRight, bTopIsOuter,
3059 bVert ? aL : (bBottomAsTop ? aB : aT));
3061 SwLineEntry aBottom(nBottom, nLeft, nRight, bBottomIsOuter,
3062 bVert ? aR : aB);
3064 Insert( aLeft, false );
3065 Insert( aRight, false );
3066 Insert( aTop, true );
3067 Insert( aBottom, true );
3069 InsertMasterBottomBorder(rFrame, rBoxItem, bWordTableCell, nBottom, nLeft, nRight,
3070 bBottomIsOuter);
3071 InsertFollowTopBorder(rFrame, rBoxItem, bWordTableCell, nTop, nLeft, nRight, bTopIsOuter);
3074 void SwTabFramePainter::Insert( SwLineEntry& rNew, bool bHori )
3076 // get all lines from structure, that have key entry of pLE
3077 SwLineEntryMap* pLine2 = bHori ? &maHoriLines : &maVertLines;
3078 const SwTwips nKey = rNew.mnKey;
3079 SwLineEntryMap::iterator aMapIter = pLine2->find( nKey );
3081 SwLineEntrySet* pLineSet = aMapIter != pLine2->end() ? &((*aMapIter).second) : nullptr;
3082 if ( !pLineSet )
3084 (*pLine2)[nKey] = SwLineEntrySet();
3085 pLineSet = &(*pLine2)[ nKey ];
3087 SwLineEntrySet::iterator aIter = pLineSet->begin();
3089 bool bWordTableCell = false;
3090 SwViewShell* pShell = mrTabFrame.getRootFrame()->GetCurrShell();
3091 if (pShell)
3093 const IDocumentSettingAccess& rIDSA = pShell->GetDoc()->getIDocumentSettingAccess();
3094 bWordTableCell = rIDSA.get(DocumentSettingId::TABLE_ROW_KEEP);
3096 bool bR2L = mrTabFrame.IsRightToLeft();
3097 while ( aIter != pLineSet->end() && rNew.mnStartPos < rNew.mnEndPos )
3099 const SwLineEntry& rOld = *aIter;
3101 // The bWordTableCell code only works for LTR at the moment, avoid it for RTL.
3102 if (rOld.mnLimitedEndPos || (bWordTableCell && (rOld.mbOuter != rNew.mbOuter) && !bR2L))
3104 // Don't merge with this line entry as it ends sooner than mnEndPos.
3105 ++aIter;
3106 continue;
3109 const SwLineEntry::OverlapType nOverlapType = rOld.Overlaps( rNew );
3111 const svx::frame::Style& rOldAttr = rOld.maAttribute;
3112 const svx::frame::Style& rNewAttr = rNew.maAttribute;
3113 const svx::frame::Style& rCmpAttr = std::max(rNewAttr, rOldAttr);
3115 if ( SwLineEntry::OVERLAP1 == nOverlapType )
3117 OSL_ENSURE( rNew.mnStartPos >= rOld.mnStartPos, "Overlap type 3? How this?" );
3119 // new left segment
3120 const SwLineEntry aLeft(nKey, rOld.mnStartPos, rNew.mnStartPos, rOld.mbOuter, rOldAttr);
3122 // new middle segment
3123 const SwLineEntry aMiddle(nKey, rNew.mnStartPos, rOld.mnEndPos, rOld.mbOuter, rCmpAttr);
3125 // new right segment
3126 rNew.mnStartPos = rOld.mnEndPos;
3128 // update current lines set
3129 pLineSet->erase( aIter );
3130 if ( aLeft.mnStartPos < aLeft.mnEndPos ) pLineSet->insert( aLeft );
3131 if ( aMiddle.mnStartPos < aMiddle.mnEndPos ) pLineSet->insert( aMiddle );
3133 aIter = pLineSet->begin();
3135 continue; // start over
3137 else if ( SwLineEntry::OVERLAP2 == nOverlapType )
3139 // new left segment
3140 const SwLineEntry aLeft(nKey, rOld.mnStartPos, rNew.mnStartPos, rOld.mbOuter, rOldAttr);
3142 // new middle segment
3143 const SwLineEntry aMiddle(nKey, rNew.mnStartPos, rNew.mnEndPos, rOld.mbOuter, rCmpAttr);
3145 // new right segment
3146 const SwLineEntry aRight(nKey, rNew.mnEndPos, rOld.mnEndPos, rOld.mbOuter, rOldAttr);
3148 // update current lines set
3149 pLineSet->erase( aIter );
3150 if ( aLeft.mnStartPos < aLeft.mnEndPos ) pLineSet->insert( aLeft );
3151 if ( aMiddle.mnStartPos < aMiddle.mnEndPos ) pLineSet->insert( aMiddle );
3152 if ( aRight.mnStartPos < aRight.mnEndPos ) pLineSet->insert( aRight );
3154 rNew.mnStartPos = rNew.mnEndPos; // rNew should not be inserted!
3156 break; // we are finished
3158 else if ( SwLineEntry::OVERLAP3 == nOverlapType )
3160 // new left segment
3161 const SwLineEntry aLeft(nKey, rNew.mnStartPos, rOld.mnStartPos, rOld.mbOuter, rNewAttr);
3163 // new middle segment
3164 const SwLineEntry aMiddle(nKey, rOld.mnStartPos, rNew.mnEndPos, rOld.mbOuter, rCmpAttr);
3166 // new right segment
3167 const SwLineEntry aRight(nKey, rNew.mnEndPos, rOld.mnEndPos, rOld.mbOuter, rOldAttr);
3169 // update current lines set
3170 pLineSet->erase( aIter );
3171 if ( aLeft.mnStartPos < aLeft.mnEndPos ) pLineSet->insert( aLeft );
3172 if ( aMiddle.mnStartPos < aMiddle.mnEndPos ) pLineSet->insert( aMiddle );
3173 if ( aRight.mnStartPos < aRight.mnEndPos ) pLineSet->insert( aRight );
3175 rNew.mnStartPos = rNew.mnEndPos; // rNew should not be inserted!
3177 break; // we are finished
3180 ++aIter;
3183 if ( rNew.mnStartPos < rNew.mnEndPos ) // insert rest
3184 pLineSet->insert( rNew );
3189 * Paint once for every visible page which is touched by Rect
3191 * 1. Paint borders and backgrounds
3192 * 2. Paint the draw layer (frames and drawing objects) that is
3193 * below the document (hell)
3194 * 3. Paint the document content (text)
3195 * 4. Paint the draw layer that is above the document
3197 void SwRootFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const& rRect, PaintFrameMode) const
3199 OSL_ENSURE( Lower() && Lower()->IsPageFrame(), "Lower of root is no page." );
3201 PROTOCOL( this, PROT::FileInit, DbgAction::NONE, nullptr)
3203 bool bResetRootPaint = false;
3204 SwViewShell *pSh = mpCurrShell;
3206 if ( pSh->GetWin() )
3208 if ( pSh->GetOut() == pSh->GetWin()->GetOutDev() && !pSh->GetWin()->IsVisible() )
3210 return;
3212 if (SwRootFrame::s_isInPaint)
3214 SwPaintQueue::Add( pSh, rRect );
3215 return;
3218 else
3219 SwRootFrame::s_isInPaint = bResetRootPaint = true;
3221 const IDocumentSettingAccess& rIDSA = pSh->GetDoc()->getIDocumentSettingAccess();
3222 bool isPaintHellOverHF = rIDSA.get(DocumentSettingId::PAINT_HELL_OVER_HEADER_FOOTER) && pSh->Imp()->HasDrawView();
3223 std::unique_ptr<SwSavePaintStatics> pStatics;
3224 if ( gProp.pSGlobalShell )
3225 pStatics.reset(new SwSavePaintStatics());
3226 gProp.pSGlobalShell = pSh;
3228 if( !pSh->GetWin() )
3229 gProp.pSProgress = SfxProgress::GetActiveProgress( static_cast<SfxObjectShell*>(pSh->GetDoc()->GetDocShell()) );
3231 ::SwCalcPixStatics( pSh->GetOut() );
3232 aGlobalRetoucheColor = pSh->Imp()->GetRetoucheColor();
3234 // Copy rRect; for one, rRect could become dangling during the below action, and for another it
3235 // needs to be copied to aRect anyway as that is modified further down below:
3236 SwRect aRect( rRect );
3238 //Trigger an action to clear things up if needed.
3239 //Using this trick we can ensure that all values are valid in all paints -
3240 //no problems, no special case(s).
3241 // #i92745#
3242 // Extend check on certain states of the 'current' <SwViewShell> instance to
3243 // all existing <SwViewShell> instances.
3244 bool bPerformLayoutAction( true );
3246 for(SwViewShell& rTmpViewShell : pSh->GetRingContainer())
3248 if ( rTmpViewShell.IsInEndAction() ||
3249 rTmpViewShell.IsPaintInProgress() ||
3250 ( rTmpViewShell.Imp()->IsAction() &&
3251 rTmpViewShell.Imp()->GetLayAction().IsActionInProgress() ) )
3253 bPerformLayoutAction = false;
3256 if(!bPerformLayoutAction)
3257 break;
3260 if ( bPerformLayoutAction )
3262 const_cast<SwRootFrame*>(this)->ResetTurbo();
3263 SwLayAction aAction( const_cast<SwRootFrame*>(this), pSh->Imp() );
3264 aAction.SetPaint( false );
3265 aAction.SetComplete( false );
3266 aAction.SetReschedule( gProp.pSProgress != nullptr );
3267 aAction.Action(&rRenderContext);
3268 ResetTurboFlag();
3269 if ( !pSh->ActionPend() )
3270 pSh->Imp()->DeletePaintRegion();
3273 aRect.Intersection( pSh->VisArea() );
3275 const bool bExtraData = ::IsExtraData( GetFormat()->GetDoc() );
3277 gProp.pSLines.reset(new SwLineRects); // Container for borders.
3279 // #104289#. During painting, something (OLE) can
3280 // load the linguistic, which in turn can cause a reformat
3281 // of the document. Dangerous! We better set this flag to
3282 // avoid the reformat.
3283 const bool bOldAction = IsCallbackActionEnabled();
3284 const_cast<SwRootFrame*>(this)->SetCallbackActionEnabled( false );
3286 const SwPageFrame *pPage = pSh->Imp()->GetFirstVisPage(&rRenderContext);
3288 // #126222. The positions of headers and footers of the previous
3289 // pages have to be updated, else these headers and footers could
3290 // get visible at a wrong position.
3291 const SwPageFrame *pPageDeco = static_cast<const SwPageFrame*>(pPage->GetPrev());
3292 while (pPageDeco)
3294 pPageDeco->PaintDecorators();
3295 OSL_ENSURE(!pPageDeco->GetPrev() || pPageDeco->GetPrev()->IsPageFrame(),
3296 "Neighbour of page is not a page.");
3297 pPageDeco = static_cast<const SwPageFrame*>(pPageDeco->GetPrev());
3300 const bool bBookMode = gProp.pSGlobalShell->GetViewOptions()->IsViewLayoutBookMode();
3301 if ( bBookMode && pPage->GetPrev() && static_cast<const SwPageFrame*>(pPage->GetPrev())->IsEmptyPage() )
3302 pPage = static_cast<const SwPageFrame*>(pPage->GetPrev());
3304 // #i68597#
3305 const bool bGridPainting(pSh->GetWin() && pSh->Imp()->HasDrawView() && pSh->Imp()->GetDrawView()->IsGridVisible());
3307 // Hide all page break controls before showing them again
3308 SwWrtShell* pWrtSh = dynamic_cast< SwWrtShell* >( gProp.pSGlobalShell );
3309 if ( pWrtSh )
3311 SwEditWin& rEditWin = pWrtSh->GetView().GetEditWin();
3312 SwFrameControlsManager& rMngr = rEditWin.GetFrameControlsManager();
3313 const SwPageFrame* pHiddenPage = pPage;
3314 while ( pHiddenPage->GetPrev() != nullptr )
3316 pHiddenPage = static_cast< const SwPageFrame* >( pHiddenPage->GetPrev() );
3317 SwFrameControlPtr pControl = rMngr.GetControl( FrameControlType::PageBreak, pHiddenPage );
3318 if ( pControl )
3319 pControl->ShowAll( false );
3323 // #i76669#
3324 SwViewObjectContactRedirector aSwRedirector( *pSh );
3326 while ( pPage )
3328 const bool bPaintRightShadow = pPage->IsRightShadowNeeded();
3329 const bool bPaintLeftShadow = pPage->IsLeftShadowNeeded();
3330 const bool bRightSidebar = pPage->SidebarPosition() == sw::sidebarwindows::SidebarPosition::RIGHT;
3332 if ( !pPage->IsEmptyPage() )
3334 SwRect aPaintRect;
3335 SwPageFrame::GetBorderAndShadowBoundRect( pPage->getFrameArea(), pSh, &rRenderContext, aPaintRect,
3336 bPaintLeftShadow, bPaintRightShadow, bRightSidebar );
3338 if ( aRect.Overlaps( aPaintRect ) )
3340 if ( pSh->GetWin() )
3342 gProp.pSSubsLines.reset(new SwSubsRects);
3343 gProp.pSSpecSubsLines.reset(new SwSubsRects);
3345 gProp.pBLines.reset(new BorderLines);
3347 aPaintRect.Intersection_( aRect );
3349 if ( bExtraData &&
3350 pSh->GetWin() && pSh->IsInEndAction() )
3352 // enlarge paint rectangle to complete page width, subtract
3353 // current paint area and invalidate the resulting region.
3354 SwRectFnSet aRectFnSet(pPage);
3355 SwRect aPageRectTemp( aPaintRect );
3356 aRectFnSet.SetLeftAndWidth( aPageRectTemp,
3357 aRectFnSet.GetLeft(pPage->getFrameArea()),
3358 aRectFnSet.GetWidth(pPage->getFrameArea()) );
3359 aPageRectTemp.Intersection_( pSh->VisArea() );
3360 vcl::Region aPageRectRegion( aPageRectTemp.SVRect() );
3361 aPageRectRegion.Exclude( aPaintRect.SVRect() );
3362 pSh->GetWin()->Invalidate( aPageRectRegion, InvalidateFlags::Children );
3365 // #i80793#
3366 // enlarge paint rectangle for objects overlapping the same pixel
3367 // in all cases and before the DrawingLayer overlay is initialized.
3368 lcl_AdjustRectToPixelSize( aPaintRect, *(pSh->GetOut()) );
3370 // #i68597#
3371 // moved paint pre-process for DrawingLayer overlay here since the above
3372 // code dependent from bExtraData may expand the PaintRect
3374 // #i75172# if called from SwViewShell::ImplEndAction it should no longer
3375 // really be used but handled by SwViewShell::ImplEndAction already
3376 const vcl::Region aDLRegion(aPaintRect.SVRect());
3377 pSh->DLPrePaint2(aDLRegion);
3380 if(OUTDEV_WINDOW == gProp.pSGlobalShell->GetOut()->GetOutDevType())
3382 // changed method SwLayVout::Enter(..)
3383 // 2nd parameter is no longer <const> and will be set to the
3384 // rectangle the virtual output device is calculated from <aPaintRect>,
3385 // if the virtual output is used.
3386 s_pVout->Enter(pSh, aPaintRect, !s_isNoVirDev);
3388 // Adjust paint rectangle to pixel size
3389 // Thus, all objects overlapping on pixel level with the unadjusted
3390 // paint rectangle will be considered in the paint.
3391 lcl_AdjustRectToPixelSize( aPaintRect, *(pSh->GetOut()) );
3394 // maybe this can be put in the above scope. Since we are not sure, just leave it ATM
3395 s_pVout->SetOrgRect( aPaintRect );
3397 // determine background color of page for <PaintLayer> method
3398 // calls, paint <hell> or <heaven>
3399 const Color aPageBackgrdColor(pPage->GetDrawBackgroundColor());
3401 pPage->PaintBaBo( aPaintRect, pPage );
3403 if ( pSh->Imp()->HasDrawView() )
3405 gProp.pSLines->LockLines( true );
3406 const IDocumentDrawModelAccess& rIDDMA = pSh->getIDocumentDrawModelAccess();
3407 pSh->Imp()->PaintLayer( rIDDMA.GetHellId(),
3408 /*pPrintData*/nullptr,
3409 *pPage, pPage->getFrameArea(),
3410 &aPageBackgrdColor,
3411 pPage->IsRightToLeft(),
3412 &aSwRedirector );
3413 gProp.pSLines->PaintLines( pSh->GetOut(), gProp );
3414 gProp.pSLines->LockLines( false );
3417 if (isPaintHellOverHF)
3419 if ( pSh->GetDoc()->GetDocumentSettingManager().get( DocumentSettingId::BACKGROUND_PARA_OVER_DRAWINGS ) )
3420 pPage->PaintBaBo( aPaintRect, pPage, /*bOnlyTextBackground=*/true, PAINT_HEADER_FOOTER );
3421 pPage->PaintSwFrame(rRenderContext, aPaintRect, PAINT_HEADER_FOOTER);
3422 gProp.pSLines->LockLines( true );
3423 const IDocumentDrawModelAccess& rIDDMA = pSh->getIDocumentDrawModelAccess();
3424 pSh->Imp()->PaintLayer( rIDDMA.GetHeaderFooterHellId(),
3425 /*pPrintData*/nullptr,
3426 *pPage, pPage->getFrameArea(),
3427 &aPageBackgrdColor,
3428 pPage->IsRightToLeft(),
3429 &aSwRedirector );
3430 gProp.pSLines->PaintLines( pSh->GetOut(), gProp );
3431 gProp.pSLines->LockLines( false );
3434 if ( pSh->GetDoc()->GetDocumentSettingManager().get( DocumentSettingId::BACKGROUND_PARA_OVER_DRAWINGS ) )
3435 pPage->PaintBaBo( aPaintRect, pPage, /*bOnlyTextBackground=*/true, isPaintHellOverHF ? PAINT_NON_HEADER_FOOTER : PAINT_ALL );
3436 if( pSh->GetWin() )
3438 // collect sub-lines
3439 pPage->RefreshSubsidiary( aPaintRect );
3440 // paint special sub-lines
3441 gProp.pSSpecSubsLines->PaintSubsidiary( pSh->GetOut(), nullptr, gProp );
3443 pPage->PaintSwFrame( rRenderContext, aPaintRect, isPaintHellOverHF ? PAINT_NON_HEADER_FOOTER : PAINT_ALL);
3445 // no paint of page border and shadow, if writer is in place mode.
3446 SwDocShell* pShell = pSh->GetDoc()->GetDocShell();
3447 if( pSh->GetWin() && pShell && !pShell->IsInPlaceActive() )
3449 SwPageFrame::PaintBorderAndShadow( pPage->getFrameArea(), pSh, bPaintLeftShadow, bPaintRightShadow, bRightSidebar );
3450 SwPageFrame::PaintNotesSidebar( pPage->getFrameArea(), pSh, pPage->GetPhyPageNum(), bRightSidebar);
3453 gProp.pSLines->PaintLines( pSh->GetOut(), gProp );
3454 if ( pSh->GetWin() )
3456 gProp.pSSubsLines->PaintSubsidiary( pSh->GetOut(), gProp.pSLines.get(), gProp );
3457 gProp.pSSubsLines.reset();
3458 gProp.pSSpecSubsLines.reset();
3460 // fdo#42750: delay painting these until after subsidiary lines
3461 // fdo#45562: delay painting these until after hell layer
3462 // fdo#47717: but do it before heaven layer
3464 SwTaggedPDFHelper tag(nullptr, nullptr, nullptr, rRenderContext);
3465 ProcessPrimitives(gProp.pBLines->GetBorderLines_Clear());
3468 if ( pSh->Imp()->HasDrawView() )
3470 pSh->Imp()->PaintLayer( pSh->GetDoc()->getIDocumentDrawModelAccess().GetHeavenId(),
3471 /*pPrintData*/nullptr,
3472 *pPage, pPage->getFrameArea(),
3473 &aPageBackgrdColor,
3474 pPage->IsRightToLeft(),
3475 &aSwRedirector );
3478 if ( bExtraData )
3479 pPage->RefreshExtraData( aPaintRect );
3481 gProp.pBLines.reset();
3482 s_pVout->Leave();
3484 // #i68597#
3485 // needed to move grid painting inside Begin/EndDrawLayer bounds and to change
3486 // output rect for it accordingly
3487 if(bGridPainting)
3489 SdrPaintView* pPaintView = pSh->Imp()->GetDrawView();
3490 SdrPageView* pPageView = pPaintView->GetSdrPageView();
3491 pPageView->DrawPageViewGrid(*pSh->GetOut(), aPaintRect.SVRect(), pSh->GetViewOptions()->GetTextGridColor() );
3494 // #i68597#
3495 // moved paint post-process for DrawingLayer overlay here, see above
3497 pSh->DLPostPaint2(true);
3501 pPage->PaintDecorators( );
3502 pPage->PaintBreak();
3504 else if ( bBookMode && pSh->GetWin() && !pSh->GetDoc()->GetDocShell()->IsInPlaceActive() )
3506 // paint empty page
3507 SwRect aPaintRect;
3508 SwRect aEmptyPageRect( pPage->getFrameArea() );
3510 // code from vprint.cxx
3511 const SwPageFrame& rFormatPage = pPage->GetFormatPage();
3512 aEmptyPageRect.SSize( rFormatPage.getFrameArea().SSize() );
3514 SwPageFrame::GetBorderAndShadowBoundRect( aEmptyPageRect, pSh, &rRenderContext, aPaintRect,
3515 bPaintLeftShadow, bPaintRightShadow, bRightSidebar );
3516 aPaintRect.Intersection_( aRect );
3518 if ( aRect.Overlaps( aEmptyPageRect ) )
3520 // #i75172# if called from SwViewShell::ImplEndAction it should no longer
3521 // really be used but handled by SwViewShell::ImplEndAction already
3523 const vcl::Region aDLRegion(aPaintRect.SVRect());
3524 pSh->DLPrePaint2(aDLRegion);
3527 if( pSh->GetOut()->GetFillColor() != aGlobalRetoucheColor )
3528 pSh->GetOut()->SetFillColor( aGlobalRetoucheColor );
3529 // No line color
3530 pSh->GetOut()->SetLineColor();
3531 // Use aligned page rectangle
3533 SwRect aTmpPageRect( aEmptyPageRect );
3534 ::SwAlignRect( aTmpPageRect, pSh, &rRenderContext );
3535 aEmptyPageRect = aTmpPageRect;
3538 pSh->GetOut()->DrawRect( aEmptyPageRect.SVRect() );
3540 // paint empty page text
3541 const vcl::Font& rEmptyPageFont = SwPageFrame::GetEmptyPageFont();
3542 const vcl::Font aOldFont( pSh->GetOut()->GetFont() );
3544 pSh->GetOut()->SetFont( rEmptyPageFont );
3545 pSh->GetOut()->DrawText( aEmptyPageRect.SVRect(), SwResId( STR_EMPTYPAGE ),
3546 DrawTextFlags::VCenter |
3547 DrawTextFlags::Center |
3548 DrawTextFlags::Clip );
3550 pSh->GetOut()->SetFont( aOldFont );
3551 // paint shadow and border for empty page
3552 SwPageFrame::PaintBorderAndShadow( aEmptyPageRect, pSh, bPaintLeftShadow, bPaintRightShadow, bRightSidebar );
3553 SwPageFrame::PaintNotesSidebar( aEmptyPageRect, pSh, pPage->GetPhyPageNum(), bRightSidebar);
3556 pSh->DLPostPaint2(true);
3561 OSL_ENSURE( !pPage->GetNext() || pPage->GetNext()->IsPageFrame(),
3562 "Neighbour of page is not a page." );
3563 pPage = static_cast<const SwPageFrame*>(pPage->GetNext());
3566 gProp.pSLines.reset();
3568 if ( bResetRootPaint )
3569 SwRootFrame::s_isInPaint = false;
3570 if ( pStatics )
3571 pStatics.reset();
3572 else
3574 gProp.pSProgress = nullptr;
3575 gProp.pSGlobalShell = nullptr;
3578 const_cast<SwRootFrame*>(this)->SetCallbackActionEnabled( bOldAction );
3581 static void lcl_EmergencyFormatFootnoteCont( SwFootnoteContFrame *pCont )
3583 vcl::RenderContext* pRenderContext = pCont->getRootFrame()->GetCurrShell()->GetOut();
3585 //It's possible that the Cont will get destroyed.
3586 SwContentFrame *pCnt = pCont->ContainsContent();
3587 while ( pCnt && pCnt->IsInFootnote() )
3589 pCnt->Calc(pRenderContext);
3590 pCnt = pCnt->GetNextContentFrame();
3594 namespace {
3596 class SwShortCut
3598 SwRectDist m_fnCheck;
3599 tools::Long m_nLimit;
3601 public:
3602 SwShortCut( const SwFrame& rFrame, const SwRect& rRect );
3603 bool Stop(const SwRect& rRect) const { return (rRect.*m_fnCheck)(m_nLimit) > 0; }
3608 SwShortCut::SwShortCut( const SwFrame& rFrame, const SwRect& rRect )
3610 bool bVert = rFrame.IsVertical();
3611 bool bR2L = rFrame.IsRightToLeft();
3612 if( rFrame.IsNeighbourFrame() && bVert == bR2L )
3614 if( bVert )
3616 m_fnCheck = &SwRect::GetBottomDistance;
3617 m_nLimit = rRect.Top();
3619 else
3621 m_fnCheck = &SwRect::GetLeftDistance;
3622 m_nLimit = rRect.Left() + rRect.Width();
3625 else if( bVert == rFrame.IsNeighbourFrame() )
3627 m_fnCheck = &SwRect::GetTopDistance;
3628 m_nLimit = rRect.Top() + rRect.Height();
3630 else
3632 if ( rFrame.IsVertLR() )
3634 m_fnCheck = &SwRect::GetLeftDistance;
3635 m_nLimit = rRect.Right();
3637 else
3639 m_fnCheck = &SwRect::GetRightDistance;
3640 m_nLimit = rRect.Left();
3645 void SwLayoutFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const& rRect, PaintFrameMode ePaintFrameMode) const
3647 if (!getFramePrintArea().HasArea() && !IsRowFrame())
3648 { // tdf#163032 row frame may contain rowspan>1 cell that must be painted
3649 return; // do not paint hidden frame
3652 // #i16816# tagged pdf support
3653 Frame_Info aFrameInfo(*this, false);
3654 SwTaggedPDFHelper aTaggedPDFHelper( nullptr,
3655 PAINT_HEADER_FOOTER == ePaintFrameMode ? nullptr : &aFrameInfo,
3656 nullptr, rRenderContext );
3657 ::std::optional<SwTaggedPDFHelper> oTaggedLink;
3658 if (IsFlyFrame())
3660 // tdf#154939 Link nested inside Figure
3661 auto const pItem(GetFormat()->GetAttrSet().GetItemIfSet(RES_URL));
3662 if (pItem && !pItem->GetURL().isEmpty())
3664 Frame_Info linkInfo(*this, true);
3665 oTaggedLink.emplace(nullptr, &linkInfo, nullptr, rRenderContext);
3669 const SwFrame *pFrame = Lower();
3670 if ( !pFrame )
3671 return;
3673 pFrame = SkipFrame(pFrame, ePaintFrameMode);
3674 if (!pFrame)
3675 return;
3677 SwFrameDeleteGuard g(const_cast<SwLayoutFrame*>(this)); // lock because Calc() and recursion
3678 SwShortCut aShortCut( *pFrame, rRect );
3679 bool bCnt = pFrame->IsContentFrame();
3680 if ( bCnt )
3681 pFrame->Calc(&rRenderContext);
3683 if ( pFrame->IsFootnoteContFrame() )
3685 ::lcl_EmergencyFormatFootnoteCont( const_cast<SwFootnoteContFrame*>(static_cast<const SwFootnoteContFrame*>(pFrame)) );
3686 pFrame = Lower();
3689 const SwPageFrame *pPage = nullptr;
3690 bool bWin = gProp.pSGlobalShell->GetWin() != nullptr;
3691 if (comphelper::LibreOfficeKit::isTiledPainting())
3692 // Tiled rendering is similar to printing in this case: painting transparently multiple
3693 // times will result in darker colors: avoid that.
3694 bWin = false;
3696 while ( IsAnLower( pFrame ) )
3698 assert(pFrame);
3699 SwRect aPaintRect( pFrame->GetPaintArea() );
3700 if( aShortCut.Stop( aPaintRect ) )
3701 break;
3702 if ( bCnt && gProp.pSProgress )
3703 SfxProgress::Reschedule();
3705 //We need to retouch if a frame explicitly requests it.
3706 //First do the retouch, because this could flatten the borders.
3707 if ( pFrame->IsRetouche() )
3709 if ( pFrame->IsRetoucheFrame() && bWin && !pFrame->GetNext() )
3711 if ( !pPage )
3712 pPage = FindPageFrame();
3713 pFrame->Retouch( pPage, rRect );
3715 pFrame->ResetRetouche();
3718 if ( rRect.Overlaps( aPaintRect ) )
3720 if ( bCnt && pFrame->IsCompletePaint() &&
3721 !(comphelper::LibreOfficeKit::isActive() && comphelper::LibreOfficeKit::isTiledPainting()) &&
3722 !rRect.Contains( aPaintRect ) && Application::AnyInput( VclInputFlags::KEYBOARD ) )
3724 //fix(8104): It may happen, that the processing wasn't complete
3725 //but some parts of the paragraph were still repainted.
3726 //This could lead to the situation, that other parts of the
3727 //paragraph won't be repainted at all. The only solution seems
3728 //to be an invalidation of the window.
3729 //To not make it too severe the rectangle is limited by
3730 //painting the desired part and only invalidating the
3731 //remaining paragraph parts.
3732 if ( aPaintRect.Left() == rRect.Left() &&
3733 aPaintRect.Right() == rRect.Right() )
3735 aPaintRect.Bottom( rRect.Top() - 1 );
3736 if ( aPaintRect.Height() > 0 )
3737 gProp.pSGlobalShell->InvalidateWindows(aPaintRect);
3738 aPaintRect.Top( rRect.Bottom() + 1 );
3739 aPaintRect.Bottom( pFrame->getFrameArea().Bottom() );
3740 if ( aPaintRect.Height() > 0 )
3741 gProp.pSGlobalShell->InvalidateWindows(aPaintRect);
3742 aPaintRect.Top( pFrame->getFrameArea().Top() );
3743 aPaintRect.Bottom( pFrame->getFrameArea().Bottom() );
3745 else
3747 gProp.pSGlobalShell->InvalidateWindows( aPaintRect );
3748 pFrame = pFrame->GetNext();
3749 if ( pFrame )
3751 bCnt = pFrame->IsContentFrame();
3752 if ( bCnt )
3753 pFrame->Calc(&rRenderContext);
3755 continue;
3758 pFrame->ResetCompletePaint();
3759 aPaintRect.Intersection_( rRect );
3761 pFrame->PaintSwFrame( rRenderContext, aPaintRect );
3763 if ( Lower() && Lower()->IsColumnFrame() )
3765 //Paint the column separator line if needed. The page is
3766 //responsible for the page frame - not the upper.
3767 const SwFrameFormat *pFormat = GetUpper() && GetUpper()->IsPageFrame()
3768 ? GetUpper()->GetFormat()
3769 : GetFormat();
3770 const SwFormatCol &rCol = pFormat->GetCol();
3771 if ( rCol.GetLineAdj() != COLADJ_NONE )
3773 if ( !pPage )
3774 pPage = pFrame->FindPageFrame();
3776 PaintColLines( aPaintRect, rCol, pPage );
3780 if ( !bCnt && pFrame->GetNext() && pFrame->GetNext()->IsFootnoteContFrame() )
3781 ::lcl_EmergencyFormatFootnoteCont( const_cast<SwFootnoteContFrame*>(static_cast<const SwFootnoteContFrame*>(pFrame->GetNext())) );
3783 pFrame = pFrame->GetNext();
3784 pFrame = SkipFrame(pFrame, ePaintFrameMode);
3786 if ( pFrame )
3788 bCnt = pFrame->IsContentFrame();
3789 if ( bCnt )
3790 pFrame->Calc(&rRenderContext);
3795 static drawinglayer::primitive2d::Primitive2DContainer lcl_CreateDashedIndicatorPrimitive(
3796 const basegfx::B2DPoint& rStart, const basegfx::B2DPoint& rEnd,
3797 basegfx::BColor aColor )
3799 drawinglayer::primitive2d::Primitive2DContainer aSeq( 1 );
3801 std::vector< double > aStrokePattern;
3802 basegfx::B2DPolygon aLinePolygon;
3803 aLinePolygon.append(rStart);
3804 aLinePolygon.append(rEnd);
3806 const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
3807 if ( rSettings.GetHighContrastMode( ) )
3809 // Only a solid line in high contrast mode
3810 aColor = rSettings.GetDialogTextColor().getBColor();
3812 else
3814 // Get a color for the contrast
3815 basegfx::BColor aHslLine = basegfx::utils::rgb2hsl( aColor );
3816 double nLuminance = aHslLine.getZ() * 2.5;
3817 if ( nLuminance == 0 )
3818 nLuminance = 0.5;
3819 else if ( nLuminance >= 1.0 )
3820 nLuminance = aHslLine.getZ() * 0.4;
3821 aHslLine.setZ( nLuminance );
3822 const basegfx::BColor aOtherColor = basegfx::utils::hsl2rgb( aHslLine );
3824 // Compute the plain line
3825 aSeq[0] =
3826 new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(
3827 aLinePolygon, aOtherColor );
3829 // Dashed line in twips
3830 aStrokePattern.push_back( 40 );
3831 aStrokePattern.push_back( 40 );
3833 aSeq.resize( 2 );
3836 // Compute the dashed line primitive
3837 aSeq[ aSeq.size( ) - 1 ] =
3838 new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D (
3839 basegfx::B2DPolyPolygon( aLinePolygon ),
3840 drawinglayer::attribute::LineAttribute( aColor ),
3841 drawinglayer::attribute::StrokeAttribute( std::move(aStrokePattern) ) );
3844 return aSeq;
3847 void SwPageFrame::PaintBreak( ) const
3849 if ( gProp.pSGlobalShell->GetOut()->GetOutDevType() == OUTDEV_PRINTER ||
3850 gProp.pSGlobalShell->GetViewOptions()->IsPDFExport() ||
3851 gProp.pSGlobalShell->GetViewOptions()->IsReadonly() ||
3852 gProp.pSGlobalShell->IsPreview() )
3853 return;
3855 const SwFrame* pBodyFrame = Lower();
3856 while ( pBodyFrame && !pBodyFrame->IsBodyFrame() )
3857 pBodyFrame = pBodyFrame->GetNext();
3859 if ( pBodyFrame )
3861 const SwLayoutFrame* pLayBody = static_cast< const SwLayoutFrame* >( pBodyFrame );
3862 const SwFlowFrame *pFlowFrame = pLayBody->ContainsContent();
3864 // Test if the first node is a table
3865 const SwFrame* pFirstFrame = pLayBody->Lower();
3866 if ( pFirstFrame && pFirstFrame->IsTabFrame() )
3867 pFlowFrame = static_cast< const SwTabFrame* >( pFirstFrame );
3869 SwWrtShell* pWrtSh = dynamic_cast< SwWrtShell* >( gProp.pSGlobalShell );
3870 if ( pWrtSh )
3872 SwEditWin& rEditWin = pWrtSh->GetView().GetEditWin();
3873 SwFrameControlsManager& rMngr = rEditWin.GetFrameControlsManager();
3875 if ( pFlowFrame && pFlowFrame->IsPageBreak( true ) )
3876 rMngr.SetPageBreakControl( this );
3877 else
3878 rMngr.RemoveControlsByType( FrameControlType::PageBreak, this );
3881 SwLayoutFrame::PaintBreak( );
3884 void SwColumnFrame::PaintBreak( ) const
3886 if ( gProp.pSGlobalShell->GetOut()->GetOutDevType() == OUTDEV_PRINTER ||
3887 gProp.pSGlobalShell->GetViewOptions()->IsPDFExport() ||
3888 gProp.pSGlobalShell->GetViewOptions()->IsReadonly() ||
3889 gProp.pSGlobalShell->IsPreview() )
3890 return;
3892 const SwFrame* pBodyFrame = Lower();
3893 while ( pBodyFrame && !pBodyFrame->IsBodyFrame() )
3894 pBodyFrame = pBodyFrame->GetNext();
3896 if ( !pBodyFrame )
3897 return;
3899 const SwContentFrame *pCnt = static_cast< const SwLayoutFrame* >( pBodyFrame )->ContainsContent();
3900 if ( !(pCnt && pCnt->IsColBreak( true )) )
3901 return;
3903 // Paint the break only if:
3904 // * Not in header footer edition, to avoid conflicts with the
3905 // header/footer marker
3906 // * Non-printing characters are shown, as this is more consistent
3907 // with other formatting marks
3908 if ( !(!gProp.pSGlobalShell->IsShowHeaderFooterSeparator( FrameControlType::Header ) &&
3909 !gProp.pSGlobalShell->IsShowHeaderFooterSeparator( FrameControlType::Footer ) &&
3910 gProp.pSGlobalShell->GetViewOptions()->IsLineBreak()) )
3911 return;
3913 SwRect aRect( pCnt->getFramePrintArea() );
3914 aRect.Pos() += pCnt->getFrameArea().Pos();
3916 // Draw the line
3917 basegfx::B2DPoint aStart( double( aRect.Left() ), aRect.Top() );
3918 basegfx::B2DPoint aEnd( double( aRect.Right() ), aRect.Top() );
3919 double nWidth = aRect.Width();
3920 if ( IsVertical( ) )
3922 aStart = basegfx::B2DPoint( double( aRect.Right() ), double( aRect.Top() ) );
3923 aEnd = basegfx::B2DPoint( double( aRect.Right() ), double( aRect.Bottom() ) );
3924 nWidth = aRect.Height();
3927 basegfx::BColor aLineColor = gProp.pSGlobalShell->GetViewOptions()->GetPageBreakColor().getBColor();
3929 drawinglayer::primitive2d::Primitive2DContainer aSeq =
3930 lcl_CreateDashedIndicatorPrimitive( aStart, aEnd, aLineColor );
3932 // Add the text above
3933 OUString aBreakText = SwResId(STR_COLUMN_BREAK);
3935 basegfx::B2DVector aFontSize;
3936 OutputDevice* pOut = gProp.pSGlobalShell->GetOut();
3937 vcl::Font aFont = pOut->GetSettings().GetStyleSettings().GetToolFont();
3938 aFont.SetFontHeight( 8 * 20 );
3939 pOut->SetFont( aFont );
3940 drawinglayer::attribute::FontAttribute aFontAttr = drawinglayer::primitive2d::getFontAttributeFromVclFont(
3941 aFontSize, aFont, IsRightToLeft(), false );
3943 tools::Rectangle aTextRect;
3944 pOut->GetTextBoundRect( aTextRect, aBreakText );
3945 tools::Long nTextOff = ( nWidth - aTextRect.GetWidth() ) / 2;
3947 basegfx::B2DHomMatrix aTextMatrix( basegfx::utils::createScaleTranslateB2DHomMatrix(
3948 aFontSize.getX(), aFontSize.getY(),
3949 aRect.Left() + nTextOff, aRect.Top() ) );
3950 if ( IsVertical() )
3952 aTextMatrix = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix (
3953 aFontSize.getX(), aFontSize.getY(), 0.0, M_PI_2,
3954 aRect.Right(), aRect.Top() + nTextOff );
3957 aSeq.push_back(
3958 new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
3959 aTextMatrix,
3960 aBreakText, 0, aBreakText.getLength(),
3961 std::vector< double >(),
3963 std::move(aFontAttr),
3964 lang::Locale(),
3965 aLineColor ) );
3967 ProcessPrimitives( aSeq );
3970 void SwLayoutFrame::PaintBreak( ) const
3972 const SwFrame* pFrame = Lower();
3973 while ( pFrame )
3975 if ( pFrame->IsLayoutFrame() )
3976 static_cast< const SwLayoutFrame*>( pFrame )->PaintBreak( );
3977 pFrame = pFrame->GetNext();
3981 void SwPageFrame::PaintDecorators( ) const
3983 SwWrtShell* pWrtSh = dynamic_cast< SwWrtShell* >( gProp.pSGlobalShell );
3984 if ( !pWrtSh )
3985 return;
3987 SwEditWin& rEditWin = pWrtSh->GetView().GetEditWin();
3989 const SwLayoutFrame* pBody = FindBodyCont();
3990 if ( !pBody )
3991 return;
3993 SwRect aBodyRect( pBody->getFrameArea() );
3995 if ( !(gProp.pSGlobalShell->GetOut()->GetOutDevType() != OUTDEV_PRINTER &&
3996 !gProp.pSGlobalShell->GetViewOptions()->IsPDFExport() &&
3997 !gProp.pSGlobalShell->IsPreview() &&
3998 !gProp.pSGlobalShell->GetViewOptions()->IsReadonly() &&
3999 !gProp.pSGlobalShell->GetViewOptions()->getBrowseMode() &&
4000 ( gProp.pSGlobalShell->IsShowHeaderFooterSeparator( FrameControlType::Header ) ||
4001 gProp.pSGlobalShell->IsShowHeaderFooterSeparator( FrameControlType::Footer ) )) )
4002 return;
4004 bool bRtl = AllSettings::GetLayoutRTL();
4005 const SwRect& rVisArea = gProp.pSGlobalShell->VisArea();
4006 tools::Long nXOff = std::min( aBodyRect.Right(), rVisArea.Right() );
4007 if ( bRtl )
4008 nXOff = std::max( aBodyRect.Left(), rVisArea.Left() );
4010 // Header
4011 if ( gProp.pSGlobalShell->IsShowHeaderFooterSeparator( FrameControlType::Header ) )
4013 const SwFrame* pHeaderFrame = Lower();
4014 if ( pHeaderFrame && !pHeaderFrame->IsHeaderFrame() )
4015 pHeaderFrame = nullptr;
4017 tools::Long nHeaderYOff = aBodyRect.Top();
4018 Point nOutputOff = rEditWin.LogicToPixel( Point( nXOff, nHeaderYOff ) );
4019 rEditWin.GetFrameControlsManager().SetHeaderFooterControl( this, FrameControlType::Header, nOutputOff );
4022 // Footer
4023 if ( !gProp.pSGlobalShell->IsShowHeaderFooterSeparator( FrameControlType::Footer ) )
4024 return;
4026 const SwFrame* pFootnoteContFrame = Lower();
4027 while ( pFootnoteContFrame )
4029 if ( pFootnoteContFrame->IsFootnoteContFrame() )
4030 aBodyRect.AddBottom( pFootnoteContFrame->getFrameArea().Bottom() - aBodyRect.Bottom() );
4031 pFootnoteContFrame = pFootnoteContFrame->GetNext();
4034 tools::Long nFooterYOff = aBodyRect.Bottom();
4035 Point nOutputOff = rEditWin.LogicToPixel( Point( nXOff, nFooterYOff ) );
4036 rEditWin.GetFrameControlsManager().SetHeaderFooterControl( this, FrameControlType::Footer, nOutputOff );
4040 * For feature #99657#
4042 * OD 12.08.2002
4043 * determines, if background of fly frame has to be drawn transparent
4044 * declaration found in /core/inc/flyfrm.cxx
4046 * OD 08.10.2002 #103898# - If the background of the fly frame itself is not
4047 * transparent and the background is inherited from its parent/grandparent,
4048 * the background brush, used for drawing, has to be investigated for transparency.
4050 * @return true, if background is transparent drawn
4052 bool SwFlyFrame::IsBackgroundTransparent() const
4054 bool bBackgroundTransparent = GetFormat()->IsBackgroundTransparent();
4055 if ( !bBackgroundTransparent &&
4056 GetFormat()->IsBackgroundBrushInherited() )
4058 const SvxBrushItem* pBackgroundBrush = nullptr;
4059 std::optional<Color> xSectionTOXColor;
4060 SwRect aDummyRect;
4061 drawinglayer::attribute::SdrAllFillAttributesHelperPtr aFillAttributes;
4063 if ( GetBackgroundBrush( aFillAttributes, pBackgroundBrush, xSectionTOXColor, aDummyRect, false, /*bConsiderTextBox=*/false) )
4065 if ( xSectionTOXColor &&
4066 (xSectionTOXColor->IsTransparent()) &&
4067 (xSectionTOXColor != COL_TRANSPARENT) )
4069 bBackgroundTransparent = true;
4071 else if(aFillAttributes && aFillAttributes->isUsed())
4073 bBackgroundTransparent = aFillAttributes->isTransparent();
4075 else if ( pBackgroundBrush )
4077 if ( (pBackgroundBrush->GetColor().IsTransparent()) &&
4078 (pBackgroundBrush->GetColor() != COL_TRANSPARENT) )
4080 bBackgroundTransparent = true;
4082 else
4084 const GraphicObject *pTmpGrf =
4085 pBackgroundBrush->GetGraphicObject();
4086 if ( pTmpGrf &&
4087 (pTmpGrf->GetAttr().IsTransparent())
4090 bBackgroundTransparent = true;
4097 return bBackgroundTransparent;
4100 static void lcl_PaintReplacement( const SwRect &rRect, const SwViewShell &rSh )
4102 const BitmapEx& rBmp = const_cast<SwViewShell&>(rSh).GetReplacementBitmap(false);
4103 vcl::Font aFont(rSh.GetOut()->GetFont() );
4104 Graphic::DrawEx(*rSh.GetOut(), OUString(), aFont, rBmp, rRect.Pos(), rRect.SSize());
4107 bool SwFlyFrame::IsPaint(SdrObject *pObj, const SwViewShell& rSh)
4109 SdrObjUserCall *pUserCall = GetUserCall(pObj);
4111 if ( nullptr == pUserCall )
4112 return true;
4114 if ( (!rSh.GetViewOptions()->IsDraw()
4115 && (dynamic_cast<SdrUnoObj*>(pObj) || dynamic_cast<SdrAttrObj*>(pObj) || dynamic_cast<SwFlyDrawObj*>(pObj)))
4116 || (!rSh.GetViewOptions()->IsGraphic() && dynamic_cast<SwVirtFlyDrawObj*>(pObj)) )
4118 SwRect rBoundRect = GetBoundRectOfAnchoredObj( pObj );
4119 lcl_PaintReplacement(rBoundRect, rSh);
4120 return false;
4122 assert(pObj);
4124 //Attribute dependent, don't paint for printer or Preview
4125 bool bPaint = gProp.pSFlyOnlyDraw ||
4126 static_cast<SwContact*>(pUserCall)->GetFormat()->GetPrint().GetValue();
4127 if ( !bPaint )
4128 bPaint = rSh.GetWin() && !rSh.IsPreview();
4130 if ( bPaint )
4132 //The paint may be prevented by the superior Flys.
4133 SwFrame *pAnch = nullptr;
4134 if ( dynamic_cast< const SwFlyDrawObj *>( pObj ) != nullptr ) // i#117962#
4136 bPaint = false;
4138 if ( auto pFlyDraw = dynamic_cast<SwVirtFlyDrawObj *>( pObj ) )
4140 SwFlyFrame *pFly = pFlyDraw->GetFlyFrame();
4141 if ( gProp.pSFlyOnlyDraw && gProp.pSFlyOnlyDraw == pFly )
4142 return true;
4144 //Try to avoid displaying the intermediate stage, Flys which don't
4145 //overlap with the page on which they are anchored won't be
4146 //painted.
4147 //HACK: exception: printing of frames in tables, those can overlap
4148 //a page once in a while when dealing with oversized tables (HTML).
4149 SwPageFrame *pPage = pFly->FindPageFrame();
4150 if ( pPage && pPage->getFrameArea().Overlaps( pFly->getFrameArea() ) )
4152 pAnch = pFly->AnchorFrame();
4156 else
4158 // Consider 'virtual' drawing objects
4159 SwDrawContact* pDrawContact = dynamic_cast<SwDrawContact*>(pUserCall);
4160 pAnch = pDrawContact ? pDrawContact->GetAnchorFrame(pObj) : nullptr;
4161 if ( pAnch )
4163 if ( !pAnch->isFrameAreaPositionValid() )
4164 pAnch = nullptr;
4165 else if ( rSh.GetOut() == rSh.getIDocumentDeviceAccess().getPrinter( false ))
4167 //HACK: we have to omit some of the objects for printing,
4168 //otherwise they would be printed twice.
4169 //The objects should get printed if the TableHack is active
4170 //right now. Afterwards they must not be printed if the
4171 //page over which they float position wise gets printed.
4172 const SwPageFrame *pPage = pAnch->FindPageFrame();
4173 if ( !pPage->getFrameArea().Overlaps( SwRect(pObj->GetCurrentBoundRect()) ) )
4174 pAnch = nullptr;
4177 else
4179 if ( dynamic_cast< const SdrObjGroup *>( pObj ) == nullptr )
4181 OSL_FAIL( "<SwFlyFrame::IsPaint(..)> - paint of drawing object without anchor frame!?" );
4185 if ( pAnch )
4187 if ( pAnch->IsInFly() )
4188 bPaint = SwFlyFrame::IsPaint(pAnch->FindFlyFrame()->GetVirtDrawObj(), rSh);
4189 else if ( gProp.pSFlyOnlyDraw )
4190 bPaint = false;
4192 else
4193 bPaint = false;
4195 return bPaint;
4198 void SwCellFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const& rRect, PaintFrameMode) const
4200 if ( GetLayoutRowSpan() >= 1 )
4201 SwLayoutFrame::PaintSwFrame( rRenderContext, rRect );
4204 namespace {
4206 struct BorderLinesGuard
4208 explicit BorderLinesGuard() : m_pBorderLines(std::move(gProp.pBLines))
4210 gProp.pBLines.reset(new BorderLines);
4212 ~BorderLinesGuard()
4214 gProp.pBLines = std::move(m_pBorderLines);
4216 private:
4217 std::unique_ptr<BorderLines> m_pBorderLines;
4222 // set strikethrough for deleted objects anchored to character
4223 void SwFrame::SetDrawObjsAsDeleted( bool bDeleted )
4225 if ( SwSortedObjs *pObjs = GetDrawObjs() )
4227 for (SwAnchoredObject* pAnchoredObj : *pObjs)
4229 if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
4231 pFly->SetDeleted(bDeleted);
4237 void SwFlyFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const& rRect, PaintFrameMode) const
4239 //optimize thumbnail generation and store procedure to improve odt saving performance, #i120030#
4240 SwViewShell *pShell = getRootFrame()->GetCurrShell();
4241 if (pShell && pShell->GetDoc() && pShell->GetDoc()->GetDocShell())
4243 bool bInGenerateThumbnail = pShell->GetDoc()->GetDocShell()->IsInGenerateAndStoreThumbnail();
4244 if (bInGenerateThumbnail)
4246 const SwRect& aVisRect = pShell->VisArea();
4247 if (!aVisRect.Overlaps(getFrameArea()))
4248 return;
4252 //because of the overlapping of frames and drawing objects the flys have to
4253 //paint their borders (and those of the internal ones) directly.
4254 //e.g. #33066#
4255 gProp.pSLines->LockLines(true);
4256 BorderLinesGuard blg; // this should not paint borders added from PaintBaBo
4258 SwRect aRect( rRect );
4259 aRect.Intersection_( getFrameArea() );
4261 rRenderContext.Push( vcl::PushFlags::CLIPREGION );
4262 rRenderContext.SetClipRegion();
4263 const SwPageFrame* pPage = FindPageFrame();
4265 const SwNoTextFrame *pNoText = Lower() && Lower()->IsNoTextFrame()
4266 ? static_cast<const SwNoTextFrame*>(Lower()) : nullptr;
4268 bool bIsChart = false; //#i102950# don't paint additional borders for charts
4269 //check whether we have a chart
4270 if(pNoText)
4272 const SwNoTextNode* pNoTNd = dynamic_cast<const SwNoTextNode*>(pNoText->GetNode());
4273 if( pNoTNd )
4275 SwOLENode* pOLENd = const_cast<SwOLENode*>(pNoTNd->GetOLENode());
4276 if( pOLENd && pOLENd->GetOLEObj().GetObject().IsChart() )
4277 bIsChart = true;
4282 bool bContour = GetFormat()->GetSurround().IsContour();
4283 tools::PolyPolygon aPoly;
4284 if ( bContour )
4286 // add 2nd parameter with value <true>
4287 // to indicate that method is called for paint in order to avoid
4288 // load of the intrinsic graphic.
4289 bContour = GetContour( aPoly, true );
4292 // #i47804# - distinguish complete background paint
4293 // and margin paint.
4294 // paint complete background for Writer text fly frames
4295 bool bPaintCompleteBack( !pNoText );
4296 // paint complete background for transparent graphic and contour,
4297 // if own background color exists.
4298 const bool bIsGraphicTransparent = pNoText && pNoText->IsTransparent();
4299 if ( !bPaintCompleteBack &&
4300 ( bIsGraphicTransparent|| bContour ) )
4302 const SwFlyFrameFormat* pSwFrameFormat = GetFormat();
4304 if (pSwFrameFormat && pSwFrameFormat->supportsFullDrawingLayerFillAttributeSet())
4306 // check for transparency
4307 const drawinglayer::attribute::SdrAllFillAttributesHelperPtr aFillAttributes(pSwFrameFormat->getSdrAllFillAttributesHelper());
4309 // check if the new fill attributes are used
4310 if(aFillAttributes && aFillAttributes->isUsed())
4312 bPaintCompleteBack = true;
4315 else
4317 std::unique_ptr<SvxBrushItem> aBack = GetFormat()->makeBackgroundBrushItem();
4318 // to determine, if background has to be painted, by checking, if
4319 // background color is not COL_TRANSPARENT ("no fill"/"auto fill")
4320 // or a background graphic exists.
4321 bPaintCompleteBack =
4322 aBack->GetColor() != COL_TRANSPARENT ||
4323 aBack->GetGraphicPos() != GPOS_NONE;
4326 // paint of margin needed.
4327 const bool bPaintMarginOnly( !bPaintCompleteBack &&
4328 getFramePrintArea().SSize() != getFrameArea().SSize() );
4330 // #i47804# - paint background of parent fly frame
4331 // for transparent graphics in layer Hell, if parent fly frame isn't
4332 // in layer Hell. It's only painted the intersection between the
4333 // parent fly frame area and the paint area <aRect>
4334 const IDocumentDrawModelAccess& rIDDMA = GetFormat()->getIDocumentDrawModelAccess();
4336 if (bIsGraphicTransparent &&
4337 GetFormat()->GetDoc()->getIDocumentSettingAccess().get(DocumentSettingId::SUBTRACT_FLYS) &&
4338 GetVirtDrawObj()->GetLayer() == rIDDMA.GetHellId() &&
4339 GetAnchorFrame()->FindFlyFrame() )
4341 const SwFlyFrame* pParentFlyFrame = GetAnchorFrame()->FindFlyFrame();
4342 if ( pParentFlyFrame->GetDrawObj()->GetLayer() !=
4343 rIDDMA.GetHellId() )
4345 SwFlyFrame* pOldRet = gProp.pSRetoucheFly2;
4346 gProp.pSRetoucheFly2 = const_cast<SwFlyFrame*>(this);
4348 SwBorderAttrAccess aAccess( SwFrame::GetCache(), pParentFlyFrame );
4349 const SwBorderAttrs &rAttrs = *aAccess.Get();
4350 SwRect aPaintRect( aRect );
4351 aPaintRect.Intersection_( pParentFlyFrame->getFrameArea() );
4352 pParentFlyFrame->PaintSwFrameBackground( aPaintRect, pPage, rAttrs );
4354 gProp.pSRetoucheFly2 = pOldRet;
4358 if ( bPaintCompleteBack || bPaintMarginOnly )
4360 //#24926# JP 01.02.96, PaintBaBo is here partially so PaintSwFrameShadowAndBorder
4361 //receives the original Rect but PaintSwFrameBackground only the limited
4362 //one.
4364 rRenderContext.Push( vcl::PushFlags::FILLCOLOR|vcl::PushFlags::LINECOLOR );
4365 rRenderContext.SetLineColor();
4367 pPage = FindPageFrame();
4369 SwBorderAttrAccess aAccess( SwFrame::GetCache(), static_cast<SwFrame const *>(this) );
4370 const SwBorderAttrs &rAttrs = *aAccess.Get();
4372 // paint background
4374 SwRegionRects aRegion( aRect );
4375 // #i80822#
4376 // suppress painting of background in printing area for
4377 // non-transparent graphics.
4378 if ( bPaintMarginOnly ||
4379 ( pNoText && !bIsGraphicTransparent ) )
4381 //What we actually want to paint is the small stripe between
4382 //PrtArea and outer border.
4383 SwRect aTmp( getFramePrintArea() ); aTmp += getFrameArea().Pos();
4384 aRegion -= aTmp;
4386 if ( bContour )
4388 rRenderContext.Push();
4389 // #i80822#
4390 // apply clip region under the same conditions, which are
4391 // used in <SwNoTextFrame::PaintSwFrame(..)> to set the clip region
4392 // for painting the graphic/OLE. Thus, the clip region is
4393 // also applied for the PDF export.
4394 SwViewShell *pSh = getRootFrame()->GetCurrShell();
4396 if ( !rRenderContext.GetConnectMetaFile() || !pSh || !pSh->GetWin() )
4398 rRenderContext.SetClipRegion(vcl::Region(aPoly));
4401 for ( size_t i = 0; i < aRegion.size(); ++i )
4403 PaintSwFrameBackground( aRegion[i], pPage, rAttrs, false, true );
4406 rRenderContext.Pop();
4408 else
4410 for ( size_t i = 0; i < aRegion.size(); ++i )
4412 PaintSwFrameBackground( aRegion[i], pPage, rAttrs, false, true );
4417 // paint border before painting background
4418 PaintSwFrameShadowAndBorder(rRect, pPage, rAttrs);
4420 rRenderContext.Pop();
4424 // fly frame will paint it's subsidiary lines and
4425 // the subsidiary lines of its lowers on its own, due to overlapping with
4426 // other fly frames or other objects.
4427 if( gProp.pSGlobalShell->GetWin()
4428 && !bIsChart ) //#i102950# don't paint additional borders for charts
4430 bool bSubsLineRectsCreated;
4431 if ( gProp.pSSubsLines )
4433 // Lock already existing subsidiary lines
4434 gProp.pSSubsLines->LockLines( true );
4435 bSubsLineRectsCreated = false;
4437 else
4439 // create new subsidiary lines
4440 gProp.pSSubsLines.reset(new SwSubsRects);
4441 bSubsLineRectsCreated = true;
4444 bool bSpecSubsLineRectsCreated;
4445 if ( gProp.pSSpecSubsLines )
4447 // Lock already existing special subsidiary lines
4448 gProp.pSSpecSubsLines->LockLines( true );
4449 bSpecSubsLineRectsCreated = false;
4451 else
4453 // create new special subsidiary lines
4454 gProp.pSSpecSubsLines.reset(new SwSubsRects);
4455 bSpecSubsLineRectsCreated = true;
4457 // Add subsidiary lines of fly frame and its lowers
4458 RefreshLaySubsidiary( pPage, aRect );
4459 // paint subsidiary lines of fly frame and its lowers
4460 gProp.pSSpecSubsLines->PaintSubsidiary( &rRenderContext, nullptr, gProp );
4461 gProp.pSSubsLines->PaintSubsidiary(&rRenderContext, gProp.pSLines.get(), gProp);
4462 if ( !bSubsLineRectsCreated )
4463 // unlock subsidiary lines
4464 gProp.pSSubsLines->LockLines( false );
4465 else
4467 // delete created subsidiary lines container
4468 gProp.pSSubsLines.reset();
4471 if ( !bSpecSubsLineRectsCreated )
4472 // unlock special subsidiary lines
4473 gProp.pSSpecSubsLines->LockLines( false );
4474 else
4476 // delete created special subsidiary lines container
4477 gProp.pSSpecSubsLines.reset();
4481 SwLayoutFrame::PaintSwFrame( rRenderContext, aRect );
4483 Validate();
4486 SwTaggedPDFHelper tag(nullptr, nullptr, nullptr, rRenderContext);
4487 // first paint lines added by fly frame paint
4488 // and then unlock other lines.
4489 gProp.pSLines->PaintLines( &rRenderContext, gProp );
4490 gProp.pSLines->LockLines( false );
4491 // have to paint frame borders added in heaven layer here...
4492 ProcessPrimitives(gProp.pBLines->GetBorderLines_Clear());
4495 PaintDecorators();
4497 // crossing out for tracked deletion
4498 if ( GetAuthor() != std::string::npos && IsDeleted() )
4500 tools::Long startX = aRect.Left( ), endX = aRect.Right();
4501 tools::Long startY = aRect.Top( ), endY = aRect.Bottom();
4502 rRenderContext.SetLineColor( SwPostItMgr::GetColorAnchor(GetAuthor()) );
4503 rRenderContext.DrawLine(Point(startX, startY), Point(endX, endY));
4504 rRenderContext.DrawLine(Point(startX, endY), Point(endX, startY));
4507 rRenderContext.Pop();
4509 if ( gProp.pSProgress && pNoText )
4510 SfxProgress::Reschedule();
4513 void SwFlyFrame::PaintDecorators() const
4515 // Show the un-float button
4516 SwWrtShell* pWrtSh = dynamic_cast< SwWrtShell* >( gProp.pSGlobalShell );
4517 if ( pWrtSh )
4519 UpdateUnfloatButton(pWrtSh, IsShowUnfloatButton(pWrtSh));
4523 SwView* SwTextFrame::GetView()
4525 SwWrtShell* pWrtSh = dynamic_cast<SwWrtShell*>(gProp.pSGlobalShell);
4527 if (!pWrtSh)
4528 return nullptr;
4530 return &pWrtSh->GetView();
4533 void SwTextFrame::PaintParagraphStylesHighlighting() const
4535 // Maybe avoid the dynamic_cast and just use GetActiveWrtShell()
4536 // NO! Multiple windows will not display the highlighting correctly if GetActiveWrtShell is used.
4537 SwWrtShell* pWrtSh = dynamic_cast<SwWrtShell*>(gProp.pSGlobalShell);
4539 if (!pWrtSh)
4540 return;
4542 if (!pWrtSh->GetView().IsSpotlightParaStyles())
4543 return;
4545 vcl::RenderContext* pRenderContext = pWrtSh->GetOut();
4546 if (!pRenderContext)
4547 return;
4549 const SwTextFormatColl* pColl = GetTextNodeFirst()->GetTextColl();
4550 OUString sStyleName = pColl->GetName();
4552 Color nStyleColor;
4553 int nStyleNumber(-1);
4555 bool bSpotlightStyle;
4556 if (comphelper::LibreOfficeKit::isActive())
4558 // For simplicity in kit mode, we render in the document "all styles"
4559 bSpotlightStyle = true;
4560 // Do this so these are stable across views regardless of an individual
4561 // user's selection mode in the style panel.
4562 nStyleNumber = pWrtSh->GetDoc()->GetTextFormatColls()->GetPos(pColl);
4563 nStyleColor = ColorHash(sStyleName);
4565 else
4567 StylesHighlighterColorMap& rParaStylesColorMap
4568 = pWrtSh->GetView().GetStylesHighlighterParaColorMap();
4569 bSpotlightStyle = rParaStylesColorMap.contains(sStyleName);
4570 if (bSpotlightStyle)
4572 nStyleNumber = rParaStylesColorMap[sStyleName].second;
4573 nStyleColor = rParaStylesColorMap[sStyleName].first;
4577 // draw styles highlighter
4578 if (bSpotlightStyle)
4580 SwRect aFrameAreaRect(getFrameArea());
4582 if (IsRightToLeft())
4584 aFrameAreaRect.AddRight(75);
4585 aFrameAreaRect.Left(aFrameAreaRect.Right() + 300);
4587 else
4589 aFrameAreaRect.AddLeft(-375);
4590 aFrameAreaRect.Right(aFrameAreaRect.Left() + 300);
4593 const tools::Rectangle aRect = aFrameAreaRect.SVRect();
4595 vcl::Font aFont(OutputDevice::GetDefaultFont(DefaultFontType::UI_SANS, GetAppLanguage(),
4596 GetDefaultFontFlags::OnlyOne, pRenderContext));
4597 aFont.SetFontSize(Size(0, 140 * pRenderContext->GetDPIScaleFactor()));
4598 aFont.SetUnderline(FontLineStyle::LINESTYLE_NONE);
4599 aFont.SetTransparent(false);
4600 aFont.SetWeight(WEIGHT_NORMAL);
4601 aFont.SetFamily(FontFamily::FAMILY_MODERN);
4602 aFont.SetColor(COL_BLACK);
4604 pRenderContext->Push(vcl::PushFlags::ALL);
4606 pRenderContext->SetFillColor(nStyleColor);
4607 pRenderContext->SetLineColor(nStyleColor);
4609 pRenderContext->DrawRect(aRect);
4611 // draw hatch pattern if paragraph has direct formatting
4612 if (SwDoc::HasParagraphDirectFormatting(SwPosition(*GetTextNodeForParaProps())))
4614 Color aHatchColor(nStyleColor);
4615 // make hatch line color 41% darker than the fill color
4616 aHatchColor.ApplyTintOrShade(-4100);
4617 Hatch aHatch(HatchStyle::Single, aHatchColor, 50, 450_deg10);
4618 pRenderContext->DrawHatch(tools::PolyPolygon(aRect), aHatch);
4621 pRenderContext->SetFont(aFont);
4622 pRenderContext->SetLayoutMode(vcl::text::ComplexTextLayoutFlags::Default);
4623 pRenderContext->SetTextFillColor(nStyleColor);
4624 pRenderContext->DrawText(aRect, OUString::number(nStyleNumber),
4625 DrawTextFlags::Center | DrawTextFlags::VCenter);
4627 pRenderContext->Pop();
4631 void SwTextFrame::PaintOutlineContentVisibilityButton() const
4633 SwWrtShell* pWrtSh = dynamic_cast<SwWrtShell*>(gProp.pSGlobalShell);
4634 if (pWrtSh && pWrtSh->GetViewOptions()->IsShowOutlineContentVisibilityButton())
4635 UpdateOutlineContentVisibilityButton(pWrtSh);
4638 void SwTabFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const& rRect, PaintFrameMode) const
4640 if (!getFramePrintArea().HasArea())
4642 return; // do not paint hidden frame
4644 const SwViewOption* pViewOption = gProp.pSGlobalShell->GetViewOptions();
4645 if (pViewOption->IsTable())
4647 // tdf#77388 first paint the cell content to avoid of removing own border
4648 SwLayoutFrame::PaintSwFrame( rRenderContext, rRect );
4650 // #i29550#
4651 if ( IsCollapsingBorders() )
4653 SwBorderAttrAccess aAccess( SwFrame::GetCache(), static_cast<SwFrame const *>(this) );
4654 const SwBorderAttrs &rAttrs = *aAccess.Get();
4656 // paint shadow
4657 if ( rAttrs.GetShadow().GetLocation() != SvxShadowLocation::NONE )
4659 SwRect aRect;
4660 ::lcl_CalcBorderRect( aRect, this, rAttrs, true, gProp );
4661 PaintShadow( rRect, aRect, rAttrs );
4664 SwTabFramePainter aHelper(*this);
4665 aHelper.PaintLines(rRenderContext, rRect);
4668 // #i6467# - no light grey rectangle for page preview
4669 else if ( gProp.pSGlobalShell->GetWin() && !gProp.pSGlobalShell->IsPreview() )
4671 // #i6467# - intersect output rectangle with table frame
4672 SwRect aTabRect( getFramePrintArea() );
4673 aTabRect.Pos() += getFrameArea().Pos();
4674 SwRect aTabOutRect( rRect );
4675 aTabOutRect.Intersection( aTabRect );
4676 SwViewOption::DrawRect( &rRenderContext, aTabOutRect, COL_LIGHTGRAY );
4678 const_cast<SwTabFrame*>(this)->ResetComplete();
4682 * Paint border shadow
4684 * @param[in] rRect aligned rect to clip the result
4685 * @param[in,out] rOutRect full painting area as input
4686 * painting area reduced by shadow space for border and background as output
4687 * @param[in] rShadow includes shadow attributes
4688 * @param[in] bDrawFullShadowRectangle paint full rect of shadow
4689 * @param[in] bTop paint top part of the shadow
4690 * @param[in] bBottom paint bottom part of the shadow
4691 * @param[in] bLeft paint left part of the shadow
4692 * @param[in] bRight paint right part of the shadow
4694 static void lcl_PaintShadow( const SwRect& rRect, SwRect& rOutRect,
4695 const SvxShadowItem& rShadow, const bool bDrawFullShadowRectangle,
4696 const bool bTop, const bool bBottom,
4697 const bool bLeft, const bool bRight,
4698 SwPaintProperties const & properties)
4700 const tools::Long nWidth = ::lcl_AlignWidth ( rShadow.GetWidth(), properties );
4701 const tools::Long nHeight = ::lcl_AlignHeight( rShadow.GetWidth(), properties );
4703 SwRects aRegion;
4704 SwRect aOut( rOutRect );
4706 switch ( rShadow.GetLocation() )
4708 case SvxShadowLocation::BottomRight:
4710 if ( bDrawFullShadowRectangle )
4712 // draw full shadow rectangle
4713 aOut.Top( rOutRect.Top() + nHeight );
4714 aOut.Left( rOutRect.Left() + nWidth );
4715 aRegion.push_back( aOut );
4717 else
4719 if( bBottom )
4721 aOut.Top( rOutRect.Bottom() - nHeight );
4722 if( bLeft )
4723 aOut.Left( rOutRect.Left() + nWidth );
4724 aRegion.push_back( aOut );
4726 if( bRight )
4728 aOut.Left( rOutRect.Right() - nWidth );
4729 if( bTop )
4730 aOut.Top( rOutRect.Top() + nHeight );
4731 else
4732 aOut.Top( rOutRect.Top() );
4733 if( bBottom )
4734 aOut.Bottom( rOutRect.Bottom() - nHeight );
4735 aRegion.push_back( aOut );
4739 if( bRight )
4740 rOutRect.AddRight(- nWidth );
4741 if( bBottom )
4742 rOutRect.AddBottom(- nHeight );
4744 break;
4745 case SvxShadowLocation::TopLeft:
4747 if ( bDrawFullShadowRectangle )
4749 // draw full shadow rectangle
4750 aOut.Bottom( rOutRect.Bottom() - nHeight );
4751 aOut.Right( rOutRect.Right() - nWidth );
4752 aRegion.push_back( aOut );
4754 else
4756 if( bTop )
4758 aOut.Bottom( rOutRect.Top() + nHeight );
4759 if( bRight )
4760 aOut.Right( rOutRect.Right() - nWidth );
4761 aRegion.push_back( aOut );
4763 if( bLeft )
4765 aOut.Right( rOutRect.Left() + nWidth );
4766 if( bBottom )
4767 aOut.Bottom( rOutRect.Bottom() - nHeight );
4768 else
4769 aOut.Bottom( rOutRect.Bottom() );
4770 if( bTop )
4771 aOut.Top( rOutRect.Top() + nHeight );
4772 aRegion.push_back( aOut );
4776 if( bLeft )
4777 rOutRect.AddLeft( nWidth );
4778 if( bTop )
4779 rOutRect.AddTop( nHeight );
4781 break;
4782 case SvxShadowLocation::TopRight:
4784 if ( bDrawFullShadowRectangle )
4786 // draw full shadow rectangle
4787 aOut.Bottom( rOutRect.Bottom() - nHeight);
4788 aOut.Left( rOutRect.Left() + nWidth );
4789 aRegion.push_back( aOut );
4791 else
4793 if( bTop )
4795 aOut.Bottom( rOutRect.Top() + nHeight );
4796 if( bLeft )
4797 aOut.Left( rOutRect.Left() + nWidth );
4798 aRegion.push_back( aOut );
4800 if( bRight )
4802 aOut.Left( rOutRect.Right() - nWidth );
4803 if( bBottom )
4804 aOut.Bottom( rOutRect.Bottom() - nHeight );
4805 else
4806 aOut.Bottom( rOutRect.Bottom() );
4807 if( bTop )
4808 aOut.Top( rOutRect.Top() + nHeight );
4809 aRegion.push_back( aOut );
4813 if( bRight )
4814 rOutRect.AddRight( - nWidth );
4815 if( bTop )
4816 rOutRect.AddTop( nHeight );
4818 break;
4819 case SvxShadowLocation::BottomLeft:
4821 if ( bDrawFullShadowRectangle )
4823 // draw full shadow rectangle
4824 aOut.Top( rOutRect.Top() + nHeight );
4825 aOut.Right( rOutRect.Right() - nWidth );
4826 aRegion.push_back( aOut );
4828 else
4830 if( bBottom )
4832 aOut.Top( rOutRect.Bottom()- nHeight );
4833 if( bRight )
4834 aOut.Right( rOutRect.Right() - nWidth );
4835 aRegion.push_back( aOut );
4837 if( bLeft )
4839 aOut.Right( rOutRect.Left() + nWidth );
4840 if( bTop )
4841 aOut.Top( rOutRect.Top() + nHeight );
4842 else
4843 aOut.Top( rOutRect.Top() );
4844 if( bBottom )
4845 aOut.Bottom( rOutRect.Bottom() - nHeight );
4846 aRegion.push_back( aOut );
4850 if( bLeft )
4851 rOutRect.AddLeft( nWidth );
4852 if( bBottom )
4853 rOutRect.AddBottom( - nHeight );
4855 break;
4856 default:
4857 assert(false);
4858 break;
4861 vcl::RenderContext *pOut = properties.pSGlobalShell->GetOut();
4863 DrawModeFlags nOldDrawMode = pOut->GetDrawMode();
4864 Color aShadowColor( rShadow.GetColor().GetRGBColor() );
4865 if( !aRegion.empty() && properties.pSGlobalShell->GetWin() &&
4866 Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
4868 // In high contrast mode, the output device has already set the
4869 // DrawModeFlags::SettingsFill flag. This causes the SetFillColor function
4870 // to ignore the setting of a new color. Therefore we have to reset
4871 // the drawing mode
4872 pOut->SetDrawMode( DrawModeFlags::Default );
4873 aShadowColor = properties.pSGlobalShell->GetViewOptions()->GetFontColor();
4876 if ( pOut->GetFillColor() != aShadowColor )
4877 pOut->SetFillColor( aShadowColor );
4879 pOut->SetLineColor();
4881 pOut->SetDrawMode( nOldDrawMode );
4883 for (const SwRect & rOut : aRegion)
4885 aOut = rOut;
4886 if ( rRect.Overlaps( aOut ) && aOut.Height() > 0 && aOut.Width() > 0 )
4888 aOut.Intersection_( rRect );
4889 pOut->DrawRect( aOut.SVRect() );
4895 * Paints a shadow if the format requests so.
4897 * The shadow is always painted on the outer edge of the OutRect.
4898 * If needed, the OutRect is shrunk so the painting of the border can be
4899 * done on it.
4901 * @note: draw full shadow rectangle for frames with transparent drawn backgrounds (OD 23.08.2002 #99657#)
4903 void SwFrame::PaintShadow( const SwRect& rRect, SwRect& rOutRect,
4904 const SwBorderAttrs &rAttrs ) const
4906 SvxShadowItem rShadow = rAttrs.GetShadow();
4908 const bool bCnt = IsContentFrame();
4909 const bool bTop = !bCnt || rAttrs.GetTopLine ( *(this) );
4910 const bool bBottom = !bCnt || rAttrs.GetBottomLine( *(this) );
4912 if( IsVertical() )
4914 switch( rShadow.GetLocation() )
4916 case SvxShadowLocation::BottomRight: rShadow.SetLocation(SvxShadowLocation::BottomLeft); break;
4917 case SvxShadowLocation::TopLeft: rShadow.SetLocation(SvxShadowLocation::TopRight); break;
4918 case SvxShadowLocation::TopRight: rShadow.SetLocation(SvxShadowLocation::BottomRight); break;
4919 case SvxShadowLocation::BottomLeft: rShadow.SetLocation(SvxShadowLocation::TopLeft); break;
4920 default: break;
4924 // determine, if full shadow rectangle have to be drawn or only two shadow rectangles beside the frame.
4925 // draw full shadow rectangle, if frame background is drawn transparent.
4926 // Status Quo:
4927 // SwLayoutFrame can have transparent drawn backgrounds. Thus,
4928 // "asked" their frame format.
4929 const bool bDrawFullShadowRectangle =
4930 ( IsLayoutFrame() &&
4931 static_cast<const SwLayoutFrame*>(this)->GetFormat()->IsBackgroundTransparent()
4934 SwRectFnSet aRectFnSet(this);
4935 ::lcl_ExtendLeftAndRight( rOutRect, *(this), rAttrs, aRectFnSet.FnRect() );
4937 lcl_PaintShadow(rRect, rOutRect, rShadow, bDrawFullShadowRectangle, bTop, bBottom, true, true, gProp);
4940 void SwFrame::PaintBorderLine( const SwRect& rRect,
4941 const SwRect& rOutRect,
4942 const SwPageFrame * pPage,
4943 const Color *pColor,
4944 const SvxBorderLineStyle nStyle ) const
4946 if ( !rOutRect.Overlaps( rRect ) )
4947 return;
4949 SwRect aOut( rOutRect );
4950 aOut.Intersection_( rRect );
4952 const SwTabFrame *pTab = IsCellFrame() ? FindTabFrame() : nullptr;
4953 SubColFlags nSubCol = ( IsCellFrame() || IsRowFrame() )
4954 ? SubColFlags::Tab
4955 : ( IsInSct()
4956 ? SubColFlags::Sect
4957 : ( IsInFly() ? SubColFlags::Fly : SubColFlags::Page ) );
4958 if( pColor && gProp.pSGlobalShell->GetWin() &&
4959 Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
4961 SwViewShell *pSh = getRootFrame()->GetCurrShell();
4962 const SwViewOption *pOpt = pSh->GetViewOptions();
4963 pColor = &pOpt->GetFontColor();
4966 if (pPage->GetSortedObjs() &&
4967 pPage->GetFormat()->GetDoc()->getIDocumentSettingAccess().get(DocumentSettingId::SUBTRACT_FLYS))
4969 SwRegionRects aRegion( aOut, 4 );
4970 basegfx::utils::B2DClipState aClipState;
4971 ::lcl_SubtractFlys( this, pPage, aOut, aRegion, aClipState, gProp );
4972 for ( size_t i = 0; i < aRegion.size(); ++i )
4973 gProp.pSLines->AddLineRect( aRegion[i], pColor, nStyle, pTab, nSubCol, gProp );
4975 else
4976 gProp.pSLines->AddLineRect( aOut, pColor, nStyle, pTab, nSubCol, gProp );
4979 namespace drawinglayer::primitive2d
4981 namespace {
4983 class SwBorderRectanglePrimitive2D : public BufferedDecompositionPrimitive2D
4985 private:
4986 /// the transformation defining the geometry of this BorderRectangle
4987 basegfx::B2DHomMatrix maB2DHomMatrix;
4989 /// the four styles to be used
4990 svx::frame::Style maStyleTop;
4991 svx::frame::Style maStyleRight;
4992 svx::frame::Style maStyleBottom;
4993 svx::frame::Style maStyleLeft;
4995 protected:
4996 /// local decomposition.
4997 virtual Primitive2DReference create2DDecomposition(
4998 const geometry::ViewInformation2D& rViewInformation) const override;
5000 public:
5001 /// constructor
5002 SwBorderRectanglePrimitive2D(
5003 basegfx::B2DHomMatrix aB2DHomMatrix,
5004 const svx::frame::Style& rStyleTop,
5005 const svx::frame::Style& rStyleRight,
5006 const svx::frame::Style& rStyleBottom,
5007 const svx::frame::Style& rStyleLeft);
5009 /// data read access
5010 const basegfx::B2DHomMatrix& getB2DHomMatrix() const { return maB2DHomMatrix; }
5011 const svx::frame::Style& getStyleTop() const { return maStyleTop; }
5012 const svx::frame::Style& getStyleRight() const { return maStyleRight; }
5013 const svx::frame::Style& getStyleBottom() const { return maStyleBottom; }
5014 const svx::frame::Style& getStyleLeft() const { return maStyleLeft; }
5016 /// compare operator
5017 virtual bool operator==(const BasePrimitive2D& rPrimitive) const override;
5019 /// get range
5020 virtual basegfx::B2DRange getB2DRange(const geometry::ViewInformation2D& rViewInformation) const override;
5022 /// provide unique ID
5023 virtual sal_uInt32 getPrimitive2DID() const override;
5028 Primitive2DReference SwBorderRectanglePrimitive2D::create2DDecomposition(
5029 const geometry::ViewInformation2D& /*rViewInformation*/) const
5031 basegfx::B2DPoint aTopLeft(getB2DHomMatrix() * basegfx::B2DPoint(0.0, 0.0));
5032 basegfx::B2DPoint aTopRight(getB2DHomMatrix() * basegfx::B2DPoint(1.0, 0.0));
5033 basegfx::B2DPoint aBottomLeft(getB2DHomMatrix() * basegfx::B2DPoint(0.0, 1.0));
5034 basegfx::B2DPoint aBottomRight(getB2DHomMatrix() * basegfx::B2DPoint(1.0, 1.0));
5036 // prepare SdrFrameBorderDataVector
5037 drawinglayer::primitive2d::SdrFrameBorderDataVector aData;
5039 if(getStyleTop().IsUsed())
5041 // move top left/right inwards half border width
5042 basegfx::B2DVector aDown(getB2DHomMatrix() * basegfx::B2DVector(0.0, 1.0));
5043 aDown.setLength(getStyleTop().GetWidth() * 0.5);
5044 aTopLeft += aDown;
5045 aTopRight += aDown;
5048 if(getStyleBottom().IsUsed())
5050 // move bottom left/right inwards half border width
5051 basegfx::B2DVector aUp(getB2DHomMatrix() * basegfx::B2DVector(0.0, -1.0));
5052 aUp.setLength(getStyleBottom().GetWidth() * 0.5);
5053 aBottomLeft += aUp;
5054 aBottomRight += aUp;
5057 if(getStyleLeft().IsUsed())
5059 // move left top/bottom inwards half border width
5060 basegfx::B2DVector aRight(getB2DHomMatrix() * basegfx::B2DVector(1.0, 0.0));
5061 aRight.setLength(getStyleLeft().GetWidth() * 0.5);
5062 aTopLeft += aRight;
5063 aBottomLeft += aRight;
5066 if(getStyleRight().IsUsed())
5068 // move right top/bottom inwards half border width
5069 basegfx::B2DVector aLeft(getB2DHomMatrix() * basegfx::B2DVector(-1.0, 0.0));
5070 aLeft.setLength(getStyleRight().GetWidth() * 0.5);
5071 aTopRight += aLeft;
5072 aBottomRight += aLeft;
5075 // go round-robin, from TopLeft to TopRight, down, left and back up. That
5076 // way, the borders will not need to be mirrored in any way
5077 if(getStyleTop().IsUsed())
5079 // create BorderPrimitive(s) for top border
5080 const basegfx::B2DVector aVector(aTopRight - aTopLeft);
5081 aData.emplace_back(
5082 aTopLeft,
5083 aVector,
5084 getStyleTop(),
5085 nullptr);
5086 drawinglayer::primitive2d::SdrFrameBorderData& rInstance(aData.back());
5088 if(getStyleLeft().IsUsed())
5090 rInstance.addSdrConnectStyleData(true, getStyleLeft(), basegfx::B2DVector(aBottomLeft - aTopLeft), false);
5093 if(getStyleRight().IsUsed())
5095 rInstance.addSdrConnectStyleData(false, getStyleRight(), basegfx::B2DVector(aBottomRight - aTopRight), false);
5099 if(getStyleRight().IsUsed())
5101 // create BorderPrimitive(s) for right border
5102 const basegfx::B2DVector aVector(aBottomRight - aTopRight);
5103 aData.emplace_back(
5104 aTopRight,
5105 aVector,
5106 getStyleRight(),
5107 nullptr);
5108 drawinglayer::primitive2d::SdrFrameBorderData& rInstance(aData.back());
5110 if(getStyleTop().IsUsed())
5112 rInstance.addSdrConnectStyleData(true, getStyleTop(), basegfx::B2DVector(aTopLeft - aTopRight), false);
5115 if(getStyleBottom().IsUsed())
5117 rInstance.addSdrConnectStyleData(false, getStyleBottom(), basegfx::B2DVector(aBottomLeft - aBottomRight), false);
5121 if(getStyleBottom().IsUsed())
5123 // create BorderPrimitive(s) for bottom border
5124 const basegfx::B2DVector aVector(aBottomLeft - aBottomRight);
5125 aData.emplace_back(
5126 aBottomRight,
5127 aVector,
5128 getStyleBottom(),
5129 nullptr);
5130 drawinglayer::primitive2d::SdrFrameBorderData& rInstance(aData.back());
5132 if(getStyleRight().IsUsed())
5134 rInstance.addSdrConnectStyleData(true, getStyleRight(), basegfx::B2DVector(aTopRight - aBottomRight), false);
5137 if(getStyleLeft().IsUsed())
5139 rInstance.addSdrConnectStyleData(false, getStyleLeft(), basegfx::B2DVector(aTopLeft - aBottomLeft), false);
5143 if(getStyleLeft().IsUsed())
5145 // create BorderPrimitive(s) for left border
5146 const basegfx::B2DVector aVector(aTopLeft - aBottomLeft);
5147 aData.emplace_back(
5148 aBottomLeft,
5149 aVector,
5150 getStyleLeft(),
5151 nullptr);
5152 drawinglayer::primitive2d::SdrFrameBorderData& rInstance(aData.back());
5154 if(getStyleBottom().IsUsed())
5156 rInstance.addSdrConnectStyleData(true, getStyleBottom(), basegfx::B2DVector(aBottomRight - aBottomLeft), false);
5159 if(getStyleTop().IsUsed())
5161 rInstance.addSdrConnectStyleData(false, getStyleTop(), basegfx::B2DVector(aTopRight - aTopLeft), false);
5165 // create instance of SdrFrameBorderPrimitive2D if
5166 // SdrFrameBorderDataVector is used
5167 if(aData.empty())
5168 return nullptr;
5170 return new drawinglayer::primitive2d::SdrFrameBorderPrimitive2D(
5171 std::move(aData),
5172 true); // force visualization to minimal one discrete unit (pixel)
5175 SwBorderRectanglePrimitive2D::SwBorderRectanglePrimitive2D(
5176 basegfx::B2DHomMatrix aB2DHomMatrix,
5177 const svx::frame::Style& rStyleTop,
5178 const svx::frame::Style& rStyleRight,
5179 const svx::frame::Style& rStyleBottom,
5180 const svx::frame::Style& rStyleLeft)
5181 : maB2DHomMatrix(std::move(aB2DHomMatrix)),
5182 maStyleTop(rStyleTop),
5183 maStyleRight(rStyleRight),
5184 maStyleBottom(rStyleBottom),
5185 maStyleLeft(rStyleLeft)
5189 bool SwBorderRectanglePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
5191 if(BasePrimitive2D::operator==(rPrimitive))
5193 const SwBorderRectanglePrimitive2D& rCompare = static_cast<const SwBorderRectanglePrimitive2D&>(rPrimitive);
5195 return (getB2DHomMatrix() == rCompare.getB2DHomMatrix() &&
5196 getStyleTop() == rCompare.getStyleTop() &&
5197 getStyleRight() == rCompare.getStyleRight() &&
5198 getStyleBottom() == rCompare.getStyleBottom() &&
5199 getStyleLeft() == rCompare.getStyleLeft());
5202 return false;
5205 basegfx::B2DRange SwBorderRectanglePrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
5207 basegfx::B2DRange aRetval(0.0, 0.0, 1.0, 1.0);
5209 aRetval.transform(getB2DHomMatrix());
5210 return aRetval;
5213 // provide unique ID
5214 sal_uInt32 SwBorderRectanglePrimitive2D::getPrimitive2DID() const
5216 return PRIMITIVE2D_ID_SWBORDERRECTANGLERIMITIVE;
5219 } // end of namespace drawinglayer::primitive2d
5221 namespace {
5223 editeng::SvxBorderLine const * get_ptr(std::optional<editeng::SvxBorderLine> const & opt) {
5224 return opt ? &*opt : nullptr;
5229 void PaintCharacterBorder(
5230 const SwFont& rFont,
5231 const SwRect& rPaintArea,
5232 const bool bVerticalLayout,
5233 const bool bVerticalLayoutLRBT,
5234 const bool bJoinWithPrev,
5235 const bool bJoinWithNext )
5237 SwRect aAlignedRect(rPaintArea);
5238 SwAlignRect(aAlignedRect, gProp.pSGlobalShell, gProp.pSGlobalShell->GetOut());
5240 bool bTop = true;
5241 bool bBottom = true;
5242 bool bLeft = true;
5243 bool bRight = true;
5245 switch (rFont.GetOrientation(bVerticalLayout, bVerticalLayoutLRBT).get())
5247 case 0 :
5248 bLeft = !bJoinWithPrev;
5249 bRight = !bJoinWithNext;
5250 break;
5251 case 900 :
5252 bBottom = !bJoinWithPrev;
5253 bTop = !bJoinWithNext;
5254 break;
5255 case 1800 :
5256 bRight = !bJoinWithPrev;
5257 bLeft = !bJoinWithNext;
5258 break;
5259 case 2700 :
5260 bTop = !bJoinWithPrev;
5261 bBottom = !bJoinWithNext;
5262 break;
5265 // Paint shadow (reduce painting rect)
5267 const SvxShadowItem aShadow(
5268 0, &rFont.GetShadowColor(), rFont.GetShadowWidth(),
5269 rFont.GetAbsShadowLocation(bVerticalLayout, bVerticalLayoutLRBT));
5271 if( aShadow.GetLocation() != SvxShadowLocation::NONE )
5273 lcl_PaintShadow( rPaintArea, aAlignedRect, aShadow,
5274 false, bTop, bBottom, bLeft, bRight, gProp);
5278 const basegfx::B2DHomMatrix aBorderTransform(
5279 basegfx::utils::createScaleTranslateB2DHomMatrix(
5280 aAlignedRect.Width(), aAlignedRect.Height(),
5281 aAlignedRect.Left(), aAlignedRect.Top()));
5282 const svx::frame::Style aStyleTop(
5283 bTop ? get_ptr(rFont.GetAbsTopBorder(bVerticalLayout, bVerticalLayoutLRBT)) : nullptr,
5284 1.0);
5285 const svx::frame::Style aStyleRight(
5286 bRight ? get_ptr(rFont.GetAbsRightBorder(bVerticalLayout, bVerticalLayoutLRBT)) : nullptr,
5287 1.0);
5288 const svx::frame::Style aStyleBottom(
5289 bBottom ? get_ptr(rFont.GetAbsBottomBorder(bVerticalLayout, bVerticalLayoutLRBT))
5290 : nullptr,
5291 1.0);
5292 const svx::frame::Style aStyleLeft(
5293 bLeft ? get_ptr(rFont.GetAbsLeftBorder(bVerticalLayout, bVerticalLayoutLRBT)) : nullptr,
5294 1.0);
5295 drawinglayer::primitive2d::Primitive2DContainer aBorderLineTarget;
5297 aBorderLineTarget.append(
5298 new drawinglayer::primitive2d::SwBorderRectanglePrimitive2D(
5299 aBorderTransform,
5300 aStyleTop,
5301 aStyleRight,
5302 aStyleBottom,
5303 aStyleLeft));
5304 gProp.pBLines->AddBorderLines(std::move(aBorderLineTarget));
5307 /// #i15844#
5308 static const SwFrame* lcl_HasNextCell( const SwFrame& rFrame )
5310 OSL_ENSURE( rFrame.IsCellFrame(),
5311 "lcl_HasNextCell( const SwFrame& rFrame ) should be called with SwCellFrame" );
5313 const SwFrame* pTmpFrame = &rFrame;
5316 if ( pTmpFrame->GetNext() )
5317 return pTmpFrame->GetNext();
5319 pTmpFrame = pTmpFrame->GetUpper()->GetUpper();
5321 while ( pTmpFrame->IsCellFrame() );
5323 return nullptr;
5327 * Determine cell frame, from which the border attributes
5328 * for paint of top/bottom border has to be used.
5330 * OD 21.02.2003 #b4779636#, #107692#
5332 * @param _pCellFrame
5333 * input parameter - constant pointer to cell frame for which the cell frame
5334 * for the border attributes has to be determined.
5336 * @param _rCellBorderAttrs
5337 * input parameter - constant reference to the border attributes of cell frame
5338 * <_pCellFrame>.
5340 * @param _bTop
5341 * input parameter - boolean, that controls, if cell frame for top border or
5342 * for bottom border has to be determined.
5344 * @return constant pointer to cell frame, for which the border attributes has
5345 * to be used
5347 static const SwFrame* lcl_GetCellFrameForBorderAttrs( const SwFrame* _pCellFrame,
5348 const SwBorderAttrs& _rCellBorderAttrs,
5349 const bool _bTop )
5351 OSL_ENSURE( _pCellFrame, "No cell frame available, dying soon" );
5353 // determine, if cell frame is at bottom/top border of a table frame and
5354 // the table frame has/is a follow.
5355 const SwFrame* pTmpFrame = _pCellFrame;
5356 bool bCellAtBorder = true;
5357 bool bCellAtLeftBorder = !_pCellFrame->GetPrev();
5358 bool bCellAtRightBorder = !_pCellFrame->GetNext();
5359 while( !pTmpFrame->IsRowFrame() || !pTmpFrame->GetUpper()->IsTabFrame() )
5361 pTmpFrame = pTmpFrame->GetUpper();
5362 if ( pTmpFrame->IsRowFrame() &&
5363 (_bTop ? pTmpFrame->GetPrev() : pTmpFrame->GetNext())
5366 bCellAtBorder = false;
5368 if ( pTmpFrame->IsCellFrame() )
5370 if ( pTmpFrame->GetPrev() )
5372 bCellAtLeftBorder = false;
5374 if ( pTmpFrame->GetNext() )
5376 bCellAtRightBorder = false;
5380 OSL_ENSURE( pTmpFrame && pTmpFrame->IsRowFrame(), "No RowFrame available" );
5382 const SwLayoutFrame* pParentRowFrame = static_cast<const SwLayoutFrame*>(pTmpFrame);
5383 const SwTabFrame* pParentTabFrame =
5384 static_cast<const SwTabFrame*>(pParentRowFrame->GetUpper());
5386 const bool bCellNeedsAttribute = bCellAtBorder &&
5387 ( _bTop ?
5388 // bCellInFirstRowWithMaster
5389 ( !pParentRowFrame->GetPrev() &&
5390 pParentTabFrame->IsFollow() &&
5391 0 == pParentTabFrame->GetTable()->GetRowsToRepeat() ) :
5392 // bCellInLastRowWithFollow
5393 ( !pParentRowFrame->GetNext() &&
5394 pParentTabFrame->GetFollow() )
5397 const SwFrame* pRet = _pCellFrame;
5398 if ( bCellNeedsAttribute )
5400 // determine, if cell frame has no borders inside the table.
5401 const SwFrame* pNextCell = nullptr;
5402 bool bNoBordersInside = false;
5404 if ( bCellAtLeftBorder && ( nullptr != ( pNextCell = lcl_HasNextCell( *_pCellFrame ) ) ) )
5406 SwBorderAttrAccess aAccess( SwFrame::GetCache(), pNextCell );
5407 const SwBorderAttrs &rBorderAttrs = *aAccess.Get();
5408 const SvxBoxItem& rBorderBox = rBorderAttrs.GetBox();
5409 bCellAtRightBorder = !lcl_HasNextCell( *pNextCell );
5410 bNoBordersInside =
5411 ( !rBorderBox.GetTop() || !pParentRowFrame->GetPrev() ) &&
5412 !rBorderBox.GetLeft() &&
5413 ( !rBorderBox.GetRight() || bCellAtRightBorder ) &&
5414 ( !rBorderBox.GetBottom() || !pParentRowFrame->GetNext() );
5416 else
5418 const SvxBoxItem& rBorderBox = _rCellBorderAttrs.GetBox();
5419 bNoBordersInside =
5420 ( !rBorderBox.GetTop() || !pParentRowFrame->GetPrev() ) &&
5421 ( !rBorderBox.GetLeft() || bCellAtLeftBorder ) &&
5422 ( !rBorderBox.GetRight() || bCellAtRightBorder ) &&
5423 ( !rBorderBox.GetBottom() || !pParentRowFrame->GetNext() );
5426 if ( bNoBordersInside )
5428 if ( _bTop && !_rCellBorderAttrs.GetBox().GetTop() )
5430 //-hack
5431 // Cell frame has no top border and no border inside the table, but
5432 // it is at the top border of a table frame, which is a follow.
5433 // Thus, use border attributes of cell frame in first row of complete table.
5434 // First, determine first table frame of complete table.
5435 SwTabFrame* pMasterTabFrame = pParentTabFrame->FindMaster( true );
5436 // determine first row of complete table.
5437 const SwFrame* pFirstRow = pMasterTabFrame->GetLower();
5438 // return first cell in first row
5439 SwFrame* pLowerCell = const_cast<SwFrame*>(pFirstRow->GetLower());
5440 while ( !pLowerCell->IsCellFrame() ||
5441 ( pLowerCell->GetLower() && pLowerCell->GetLower()->IsRowFrame() )
5444 pLowerCell = pLowerCell->GetLower();
5446 OSL_ENSURE( pLowerCell && pLowerCell->IsCellFrame(), "No CellFrame available" );
5447 pRet = pLowerCell;
5449 else if ( !_bTop && !_rCellBorderAttrs.GetBox().GetBottom() )
5451 //-hack
5452 // Cell frame has no bottom border and no border inside the table,
5453 // but it is at the bottom border of a table frame, which has a follow.
5454 // Thus, use border attributes of cell frame in last row of complete table.
5455 // First, determine last table frame of complete table.
5456 SwTabFrame* pLastTabFrame = const_cast<SwTabFrame*>(pParentTabFrame->GetFollow());
5457 while ( pLastTabFrame->GetFollow() )
5459 pLastTabFrame = pLastTabFrame->GetFollow();
5461 // determine last row of complete table.
5462 SwFrame* pLastRow = pLastTabFrame->GetLastLower();
5463 // return first bottom border cell in last row
5464 SwFrame* pLowerCell = pLastRow->GetLower();
5465 while ( !pLowerCell->IsCellFrame() ||
5466 ( pLowerCell->GetLower() && pLowerCell->GetLower()->IsRowFrame() )
5469 if ( pLowerCell->IsRowFrame() )
5471 while ( pLowerCell->GetNext() )
5473 pLowerCell = pLowerCell->GetNext();
5476 pLowerCell = pLowerCell->GetLower();
5478 OSL_ENSURE( pLowerCell && pLowerCell->IsCellFrame(), "No CellFrame available" );
5479 pRet = pLowerCell;
5484 return pRet;
5487 std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> SwFrame::CreateProcessor2D( ) const
5489 basegfx::B2DRange aViewRange;
5491 SdrPage *pDrawPage = getRootFrame()->GetCurrShell()->Imp()->GetPageView()->GetPage();
5492 drawinglayer::geometry::ViewInformation2D aNewViewInfos;
5493 aNewViewInfos.setViewTransformation(getRootFrame()->GetCurrShell()->GetOut()->GetViewTransformation());
5494 aNewViewInfos.setViewport(aViewRange);
5495 aNewViewInfos.setVisualizedPage(GetXDrawPageForSdrPage( pDrawPage ));
5497 return drawinglayer::processor2d::createProcessor2DFromOutputDevice(
5498 *getRootFrame()->GetCurrShell()->GetOut(),
5499 aNewViewInfos );
5502 void SwFrame::ProcessPrimitives( const drawinglayer::primitive2d::Primitive2DContainer& rSequence ) const
5504 std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor2D = CreateProcessor2D();
5505 if ( pProcessor2D )
5507 pProcessor2D->process( rSequence );
5511 /// Paints shadows and borders
5512 void SwFrame::PaintSwFrameShadowAndBorder(
5513 const SwRect& rRect,
5514 const SwPageFrame* /*pPage*/,
5515 const SwBorderAttrs& rAttrs) const
5517 // There's nothing (Row,Body,Footnote,Root,Column,NoText) need to do here
5518 if (GetType() & (SwFrameType::NoTxt|SwFrameType::Row|SwFrameType::Body|SwFrameType::Ftn|SwFrameType::Column|SwFrameType::Root))
5519 return;
5521 if (IsCellFrame() && !gProp.pSGlobalShell->GetViewOptions()->IsTable())
5522 return;
5524 // #i29550#
5525 if ( IsTabFrame() || IsCellFrame() || IsRowFrame() )
5527 const SwTabFrame* pTabFrame = FindTabFrame();
5528 if ( pTabFrame->IsCollapsingBorders() )
5529 return;
5531 if ( pTabFrame->GetTable()->IsNewModel() && ( !IsCellFrame() || IsCoveredCell() ) )
5532 return;
5535 if (!getFramePrintArea().HasArea())
5536 { // hidden text frame, may be called by upper from PaintSwFrameBackground()
5537 return;
5540 const bool bLine = rAttrs.IsLine();
5541 const bool bShadow = rAttrs.GetShadow().GetLocation() != SvxShadowLocation::NONE;
5543 // - flag to control,
5544 //-hack has to be used.
5545 const bool bb4779636HackActive = true;
5547 const SwFrame* pCellFrameForBottomBorderAttrs = nullptr;
5548 const SwFrame* pCellFrameForTopBorderAttrs = nullptr;
5549 bool bFoundCellForTopOrBorderAttrs = false;
5550 if ( bb4779636HackActive && IsCellFrame() )
5552 pCellFrameForBottomBorderAttrs = lcl_GetCellFrameForBorderAttrs( this, rAttrs, false );
5553 if ( pCellFrameForBottomBorderAttrs != this )
5554 bFoundCellForTopOrBorderAttrs = true;
5555 pCellFrameForTopBorderAttrs = lcl_GetCellFrameForBorderAttrs( this, rAttrs, true );
5556 if ( pCellFrameForTopBorderAttrs != this )
5557 bFoundCellForTopOrBorderAttrs = true;
5560 // - add condition <bFoundCellForTopOrBorderAttrs>
5561 //-hack
5562 if ( !(bLine || bShadow || bFoundCellForTopOrBorderAttrs) )
5563 return;
5565 //If the rectangle is completely inside the PrtArea, no border needs to
5566 //be painted.
5567 //For the PrtArea the aligned value needs to be used, otherwise it could
5568 //happen, that some parts won't be processed.
5569 SwRect aRect( getFramePrintArea() );
5570 aRect += getFrameArea().Pos();
5571 ::SwAlignRect( aRect, gProp.pSGlobalShell, gProp.pSGlobalShell->GetOut() );
5572 // new local boolean variable in order to
5573 // suspend border paint under special cases - see below.
5574 // NOTE: This is a fix for the implementation of feature #99657#.
5575 bool bDrawOnlyShadowForTransparentFrame = false;
5576 if ( aRect.Contains( rRect ) )
5578 // paint shadow, if background is transparent.
5579 // Because of introduced transparent background for fly frame #99657#,
5580 // the shadow have to be drawn if the background is transparent,
5581 // in spite the fact that the paint rectangle <rRect> lies fully
5582 // in the printing area.
5583 // NOTE to chosen solution:
5584 // On transparent background, continue processing, but suspend
5585 // drawing of border by setting <bDrawOnlyShadowForTransparentFrame>
5586 // to true.
5587 if ( IsLayoutFrame() &&
5588 static_cast<const SwLayoutFrame*>(this)->GetFormat()->IsBackgroundTransparent() )
5590 bDrawOnlyShadowForTransparentFrame = true;
5592 else
5594 return;
5598 ::lcl_CalcBorderRect( aRect, this, rAttrs, true, gProp );
5599 rAttrs.SetGetCacheLine( true );
5601 if(bShadow)
5603 PaintShadow(rRect, aRect, rAttrs);
5606 // suspend drawing of border
5607 // add condition < NOT bDrawOnlyShadowForTransparentFrame > - see above
5608 // - add condition <bFoundCellForTopOrBorderAttrs>
5609 //-hack.
5610 if((bLine || bFoundCellForTopOrBorderAttrs) && !bDrawOnlyShadowForTransparentFrame)
5612 // define SvxBorderLine(s) to use
5613 const SvxBoxItem& rBox(rAttrs.GetBox());
5614 const SvxBorderLine* pLeftBorder(rBox.GetLeft());
5615 const SvxBorderLine* pRightBorder(rBox.GetRight());
5616 const SvxBorderLine* pTopBorder(rBox.GetTop());
5617 const SvxBorderLine* pBottomBorder(rBox.GetBottom());
5619 // if R2L, exchange Right/Left
5620 const bool bR2L(IsCellFrame() && IsRightToLeft());
5622 if(bR2L)
5624 std::swap(pLeftBorder, pRightBorder);
5627 // if ContentFrame and joined Prev/Next, reset top/bottom as needed
5628 if(IsContentFrame())
5630 const SwFrame* pDirRefFrame(IsCellFrame() ? FindTabFrame() : this);
5631 const SwRectFnSet aRectFnSet(pDirRefFrame);
5632 const SwRectFn _aRectFn(aRectFnSet.FnRect());
5634 if(rAttrs.JoinedWithPrev(*this))
5636 // tdf#115296 re-add adaptation of vert distance to close the evtl.
5637 // existing gap to previous frame
5638 const SwFrame* pPrevFrame(GetPrev());
5639 (aRect.*_aRectFn->fnSetTop)( (pPrevFrame->*_aRectFn->fnGetPrtBottom)() );
5641 // ...and disable top border paint/creation
5642 pTopBorder = nullptr;
5645 if(rAttrs.JoinedWithNext(*this))
5647 // tdf#115296 re-add adaptation of vert distance to close the evtl.
5648 // existing gap to next frame
5649 const SwFrame* pNextFrame(GetNext());
5650 (aRect.*_aRectFn->fnSetBottom)( (pNextFrame->*_aRectFn->fnGetPrtTop)() );
5652 // ...and disable bottom border paint/creation
5653 pBottomBorder = nullptr;
5657 // necessary to replace TopBorder?
5658 if((!IsContentFrame() || rAttrs.GetTopLine(*this)) && IsCellFrame() && pCellFrameForTopBorderAttrs != this)
5660 SwBorderAttrAccess aAccess(SwFrame::GetCache(), pCellFrameForTopBorderAttrs);
5661 pTopBorder = aAccess.Get()->GetBox().GetTop();
5664 // necessary to replace BottomBorder?
5665 if((!IsContentFrame() || rAttrs.GetBottomLine(*this)) && IsCellFrame() && pCellFrameForBottomBorderAttrs != this)
5667 SwBorderAttrAccess aAccess(SwFrame::GetCache(), pCellFrameForBottomBorderAttrs);
5668 pBottomBorder = aAccess.Get()->GetBox().GetBottom();
5671 bool bWordBorder = false;
5672 SwViewShell* pShell = getRootFrame()->GetCurrShell();
5673 if (pShell)
5675 const IDocumentSettingAccess& rIDSA = pShell->GetDoc()->getIDocumentSettingAccess();
5676 bWordBorder = rIDSA.get(DocumentSettingId::TABLE_ROW_KEEP);
5678 bool bInWordTableCell = IsContentFrame() && GetUpper()->IsCellFrame() && bWordBorder;
5679 if (bInWordTableCell)
5681 // Compat mode: don't paint bottom border if we know the bottom of the content was cut
5682 // off.
5683 auto pContentFrame = static_cast<const SwContentFrame*>(this);
5684 if (pContentFrame->IsUndersized())
5686 pBottomBorder = nullptr;
5690 if(nullptr != pLeftBorder || nullptr != pRightBorder || nullptr != pTopBorder || nullptr != pBottomBorder)
5692 // now we have all SvxBorderLine(s) sorted out, create geometry
5693 const basegfx::B2DHomMatrix aBorderTransform(
5694 basegfx::utils::createScaleTranslateB2DHomMatrix(
5695 aRect.Width(), aRect.Height(),
5696 aRect.Left(), aRect.Top()));
5697 const svx::frame::Style aStyleTop(pTopBorder, 1.0);
5698 svx::frame::Style aStyleRight(pRightBorder, 1.0);
5700 // Right/bottom page borders are always mirrored in Word.
5701 if (IsPageFrame() && bWordBorder)
5703 aStyleRight.MirrorSelf();
5706 svx::frame::Style aStyleBottom(pBottomBorder, 1.0);
5708 if (IsPageFrame() && bWordBorder)
5710 aStyleBottom.MirrorSelf();
5713 const svx::frame::Style aStyleLeft(pLeftBorder, 1.0);
5714 drawinglayer::primitive2d::Primitive2DContainer aBorderLineTarget;
5716 drawinglayer::primitive2d::Primitive2DReference aRetval(
5717 new drawinglayer::primitive2d::SwBorderRectanglePrimitive2D(
5718 aBorderTransform,
5719 aStyleTop,
5720 aStyleRight,
5721 aStyleBottom,
5722 aStyleLeft));
5724 if (bInWordTableCell)
5726 // Compat mode: cut off the borders which are outside of our own area.
5727 const SwRect& rClip = getFrameArea();
5728 basegfx::B2DRectangle aClip(rClip.Left(), rClip.Top(), rClip.Right(),
5729 rClip.Bottom());
5730 basegfx::B2DPolyPolygon aPolyPolygon(
5731 basegfx::utils::createPolygonFromRect(aClip));
5732 const drawinglayer::primitive2d::Primitive2DReference xClipped(
5733 new drawinglayer::primitive2d::MaskPrimitive2D(std::move(aPolyPolygon), { aRetval }));
5734 aRetval = xClipped;
5737 aBorderLineTarget.append(aRetval);
5738 gProp.pBLines->AddBorderLines(std::move(aBorderLineTarget));
5742 rAttrs.SetGetCacheLine( false );
5746 * Special implementation because of the footnote line
5748 * Currently only the top frame needs to be taken into account
5749 * Other lines and shadows are set aside
5751 void SwFootnoteContFrame::PaintSwFrameShadowAndBorder(
5752 const SwRect& rRect,
5753 const SwPageFrame* pPage,
5754 const SwBorderAttrs&) const
5756 //If the rectangle is completely inside the PrtArea, no border needs to
5757 //be painted.
5758 SwRect aRect( getFramePrintArea() );
5759 aRect.Pos() += getFrameArea().Pos();
5760 if ( !aRect.Contains( rRect ) )
5761 PaintLine( rRect, pPage );
5764 /// Paint footnote lines.
5765 void SwFootnoteContFrame::PaintLine( const SwRect& rRect,
5766 const SwPageFrame *pPage ) const
5768 //The length of the line is derived from the percentual indication on the
5769 //PageDesc. The position is also stated on the PageDesc.
5770 //The pen can directly be taken from the PageDesc.
5772 if ( !pPage )
5773 pPage = FindPageFrame();
5774 const SwPageFootnoteInfo &rInf = pPage->GetPageDesc()->GetFootnoteInfo();
5776 SwRectFnSet aRectFnSet(this);
5777 SwTwips nPrtWidth = aRectFnSet.GetWidth(getFramePrintArea());
5778 Fraction aFract( nPrtWidth, 1 );
5779 aFract *= rInf.GetWidth();
5780 SwTwips nWidth = static_cast<tools::Long>(aFract);
5782 SwTwips nX = aRectFnSet.GetPrtLeft(*this);
5783 switch ( rInf.GetAdj() )
5785 case css::text::HorizontalAdjust_CENTER:
5786 nX += nPrtWidth/2 - nWidth/2; break;
5787 case css::text::HorizontalAdjust_RIGHT:
5788 nX += nPrtWidth - nWidth; break;
5789 case css::text::HorizontalAdjust_LEFT:
5790 /* do nothing */; break;
5791 default:
5792 SAL_WARN("sw.core", "New adjustment for footnote lines?");
5793 assert(false);
5795 SwTwips nLineWidth = rInf.GetLineWidth();
5796 std::optional<SwRect> oLineRect;
5797 if (aRectFnSet.IsVert())
5799 oLineRect.emplace(Point(getFrameArea().Left()+getFrameArea().Width()-rInf.GetTopDist()-nLineWidth,
5800 nX), Size( nLineWidth, nWidth ) );
5802 else
5804 Point aPoint(nX, getFrameArea().Pos().Y() + rInf.GetTopDist());
5805 const IDocumentSettingAccess& rIDSA = GetFormat()->getIDocumentSettingAccess();
5806 if (rIDSA.get(DocumentSettingId::CONTINUOUS_ENDNOTES))
5808 // Word style: instead of fixed value, upper spacing is 60% of all space.
5809 auto nPrintAreaTop = static_cast<double>(getFramePrintArea().Top());
5810 aPoint.setY(getFrameArea().Pos().Y() + nPrintAreaTop * 0.6);
5812 const SwFootnoteFrame* pEndnoteFrame = FindEndNote();
5813 bool bEndnoteContinuation = pEndnoteFrame && pEndnoteFrame->GetMaster();
5814 if (bEndnoteContinuation)
5816 // Endnote continuation separator is print area wide.
5817 nWidth = nPrtWidth;
5819 else
5821 // Length is 2 inches, but don't paint outside the container frame.
5822 nWidth = o3tl::convert(2, o3tl::Length::in, o3tl::Length::twip);
5823 if (nWidth > nPrtWidth)
5825 nWidth = nPrtWidth;
5829 oLineRect.emplace(aPoint, Size(nWidth, rInf.GetLineWidth()));
5831 if ( oLineRect->HasArea() && rInf.GetLineStyle() != SvxBorderLineStyle::NONE)
5832 PaintBorderLine( rRect, *oLineRect , pPage, &rInf.GetLineColor(),
5833 rInf.GetLineStyle() );
5836 void SwFootnoteContFrame::dumpAsXml(xmlTextWriterPtr writer) const
5838 (void)xmlTextWriterStartElement(writer, reinterpret_cast<const xmlChar*>("ftncont"));
5839 dumpAsXmlAttributes(writer);
5841 (void)xmlTextWriterStartElement(writer, BAD_CAST("infos"));
5842 dumpInfosAsXml(writer);
5843 (void)xmlTextWriterEndElement(writer);
5844 dumpChildrenAsXml(writer);
5846 (void)xmlTextWriterEndElement(writer);
5849 /// Paints the separator line for inside columns
5850 void SwLayoutFrame::PaintColLines( const SwRect &rRect, const SwFormatCol &rFormatCol,
5851 const SwPageFrame *pPage ) const
5853 const SwFrame *pCol = Lower();
5854 if ( !pCol || !pCol->IsColumnFrame() )
5855 return;
5857 SwRectFnSet fnRect(pCol);
5859 SwRect aLineRect = getFramePrintArea();
5860 aLineRect += getFrameArea().Pos();
5862 SwTwips nTop = (fnRect.GetHeight(aLineRect)*rFormatCol.GetLineHeight())
5863 / 100 - fnRect.GetHeight(aLineRect);
5864 SwTwips nBottom = 0;
5866 switch ( rFormatCol.GetLineAdj() )
5868 case COLADJ_CENTER:
5869 nBottom = nTop / 2; nTop -= nBottom; break;
5870 case COLADJ_TOP:
5871 nBottom = nTop; nTop = 0; break;
5872 case COLADJ_BOTTOM:
5873 break;
5874 default:
5875 OSL_ENSURE( false, "New adjustment for column lines?" );
5878 if( nTop )
5879 fnRect.SubTop(aLineRect, nTop);
5880 if( nBottom )
5881 fnRect.AddBottom(aLineRect, nBottom);
5883 SwTwips nPenHalf = rFormatCol.GetLineWidth();
5884 fnRect.SetWidth(aLineRect, nPenHalf);
5885 nPenHalf /= 2;
5887 //We need to be a bit generous here, to not lose something.
5888 SwRect aRect( rRect );
5889 fnRect.SubLeft(aRect, nPenHalf + gProp.nSPixelSzW);
5890 fnRect.AddRight(aRect, nPenHalf + gProp.nSPixelSzW);
5891 while ( pCol->GetNext() )
5893 fnRect.SetPosX(aLineRect, (IsRightToLeft() ? fnRect.GetLeft(pCol->getFrameArea())
5894 : fnRect.GetRight(pCol->getFrameArea()))
5895 - nPenHalf);
5896 if ( aRect.Overlaps( aLineRect ) )
5897 PaintBorderLine( aRect, aLineRect , pPage, &rFormatCol.GetLineColor(),
5898 rFormatCol.GetLineStyle() );
5899 pCol = pCol->GetNext();
5903 void SwPageFrame::PaintGrid( OutputDevice const * pOut, SwRect const &rRect ) const
5905 if( !m_bHasGrid || gProp.pSRetoucheFly || gProp.pSRetoucheFly2 )
5906 return;
5907 SwTextGridItem const*const pGrid(GetGridItem(this));
5908 if( !(pGrid && ( OUTDEV_PRINTER != pOut->GetOutDevType() ?
5909 pGrid->GetDisplayGrid() : pGrid->GetPrintGrid() )) )
5910 return;
5912 const SwLayoutFrame* pBody = FindBodyCont();
5913 if( !pBody )
5914 return;
5916 SwRect aGrid( pBody->getFramePrintArea() );
5917 aGrid += pBody->getFrameArea().Pos();
5919 SwRect aInter( aGrid );
5920 aInter.Intersection( rRect );
5921 if( !aInter.HasArea() )
5922 return;
5924 bool bGrid = pGrid->GetRubyTextBelow();
5925 bool bCell = SwTextGrid::LinesAndChars == pGrid->GetGridType();
5926 tools::Long nGrid = pGrid->GetBaseHeight();
5927 const SwDoc* pDoc = GetFormat()->GetDoc();
5928 tools::Long nGridWidth = GetGridWidth(*pGrid, *pDoc);
5929 tools::Long nRuby = pGrid->GetRubyHeight();
5930 tools::Long nSum = nGrid + nRuby;
5931 const Color *pCol = &pGrid->GetColor();
5933 SwTwips nRight = aInter.Left() + aInter.Width();
5934 SwTwips nBottom = aInter.Top() + aInter.Height();
5935 if( IsVertical() )
5937 SwTwips nOrig = aGrid.Left() + aGrid.Width();
5938 SwTwips nY = nOrig + nSum *
5939 ( ( nOrig - aInter.Left() ) / nSum );
5940 SwRect aTmp( Point( nY, aInter.Top() ),
5941 Size( 1, aInter.Height() ) );
5942 SwTwips nX = aGrid.Top() + nGrid *
5943 ( ( aInter.Top() - aGrid.Top() )/ nGrid );
5944 if( nX < aInter.Top() )
5945 nX += nGrid;
5946 SwTwips nGridBottom = aGrid.Top() + aGrid.Height();
5947 bool bLeft = aGrid.Top() >= aInter.Top();
5948 bool bRight = nGridBottom <= nBottom;
5949 bool bBorder = bLeft || bRight;
5950 while( nY > nRight )
5952 aTmp.Pos().setX( nY );
5953 if( bGrid )
5955 nY -= nGrid;
5956 SwTwips nPosY = std::max( SwTwips(aInter.Left()), nY );
5957 SwTwips nHeight = std::min(nRight, SwTwips(aTmp.Pos().X()))-nPosY;
5958 if( nHeight > 0 )
5960 if( bCell )
5962 SwRect aVert( Point( nPosY, nX ),
5963 Size( nHeight, 1 ) );
5964 while( aVert.Top() <= nBottom )
5966 PaintBorderLine(rRect,aVert,this,pCol);
5967 aVert.Pos().AdjustY(nGrid );
5970 else if( bBorder )
5972 SwRect aVert( Point( nPosY, aGrid.Top() ),
5973 Size( nHeight, 1 ) );
5974 if( bLeft )
5975 PaintBorderLine(rRect,aVert,this,pCol);
5976 if( bRight )
5978 aVert.Pos().setY( nGridBottom );
5979 PaintBorderLine(rRect,aVert,this,pCol);
5984 else
5986 nY -= nRuby;
5987 if( bBorder )
5989 SwTwips nPos = std::max( SwTwips(aInter.Left()), nY );
5990 SwTwips nW = std::min(nRight, SwTwips(aTmp.Pos().X())) - nPos;
5991 SwRect aVert( Point( nPos, aGrid.Top() ),
5992 Size( nW, 1 ) );
5993 if( nW > 0 )
5995 if( bLeft )
5996 PaintBorderLine(rRect,aVert,this,pCol);
5997 if( bRight )
5999 aVert.Pos().setY( nGridBottom );
6000 PaintBorderLine(rRect,aVert,this,pCol);
6005 bGrid = !bGrid;
6007 while( nY >= aInter.Left() )
6009 aTmp.Pos().setX( nY );
6010 PaintBorderLine( rRect, aTmp, this, pCol);
6011 if( bGrid )
6013 nY -= nGrid;
6014 SwTwips nHeight = aTmp.Pos().X()
6015 - std::max(SwTwips(aInter.Left()), nY );
6016 if( nHeight > 0 )
6018 if( bCell )
6020 SwRect aVert( Point(aTmp.Pos().X()-nHeight,
6021 nX ), Size( nHeight, 1 ) );
6022 while( aVert.Top() <= nBottom )
6024 PaintBorderLine(rRect,aVert,this,pCol);
6025 aVert.Pos().AdjustY(nGrid );
6028 else if( bBorder )
6030 SwRect aVert( Point(aTmp.Pos().X()-nHeight,
6031 aGrid.Top() ), Size( nHeight, 1 ) );
6032 if( bLeft )
6033 PaintBorderLine(rRect,aVert,this,pCol);
6034 if( bRight )
6036 aVert.Pos().setY( nGridBottom );
6037 PaintBorderLine(rRect,aVert,this,pCol);
6042 else
6044 nY -= nRuby;
6045 if( bBorder )
6047 SwTwips nPos = std::max( SwTwips(aInter.Left()), nY );
6048 SwTwips nW = std::min(nRight, SwTwips(aTmp.Pos().X())) - nPos;
6049 SwRect aVert( Point( nPos, aGrid.Top() ),
6050 Size( nW, 1 ) );
6051 if( nW > 0 )
6053 if( bLeft )
6054 PaintBorderLine(rRect,aVert,this,pCol);
6055 if( bRight )
6057 aVert.Pos().setY( nGridBottom );
6058 PaintBorderLine(rRect,aVert,this,pCol);
6063 bGrid = !bGrid;
6066 else
6068 SwTwips nOrig = aGrid.Top();
6069 SwTwips nY = nOrig + nSum *( (aInter.Top()-nOrig)/nSum );
6070 SwRect aTmp( Point( aInter.Left(), nY ),
6071 Size( aInter.Width(), 1 ) );
6072 //for textgrid refactor
6073 SwTwips nX = aGrid.Left() + nGridWidth *
6074 ( ( aInter.Left() - aGrid.Left() )/ nGridWidth );
6075 if( nX < aInter.Left() )
6076 nX += nGridWidth;
6077 SwTwips nGridRight = aGrid.Left() + aGrid.Width();
6078 bool bLeft = aGrid.Left() >= aInter.Left();
6079 bool bRight = nGridRight <= nRight;
6080 bool bBorder = bLeft || bRight;
6081 while( nY < aInter.Top() )
6083 aTmp.Pos().setY(nY);
6084 if( bGrid )
6086 nY += nGrid;
6087 SwTwips nPosY = std::max( aInter.Top(), aTmp.Pos().getY() );
6088 SwTwips nHeight = std::min(nBottom, nY ) - nPosY;
6089 if( nHeight )
6091 if( bCell )
6093 SwRect aVert( Point( nX, nPosY ),
6094 Size( 1, nHeight ) );
6095 while( aVert.Left() <= nRight )
6097 PaintBorderLine(rRect,aVert,this,pCol);
6098 aVert.Pos().AdjustX(nGridWidth ); //for textgrid refactor
6101 else if ( bBorder )
6103 SwRect aVert( Point( aGrid.Left(), nPosY ),
6104 Size( 1, nHeight ) );
6105 if( bLeft )
6106 PaintBorderLine(rRect,aVert,this,pCol);
6107 if( bRight )
6109 aVert.Pos().setX( nGridRight );
6110 PaintBorderLine(rRect,aVert,this,pCol);
6115 else
6117 nY += nRuby;
6118 if( bBorder )
6120 SwTwips nPos = std::max(aInter.Top(),aTmp.Pos().getY());
6121 SwTwips nH = std::min( nBottom, nY ) - nPos;
6122 SwRect aVert( Point( aGrid.Left(), nPos ),
6123 Size( 1, nH ) );
6124 if( nH > 0 )
6126 if( bLeft )
6127 PaintBorderLine(rRect,aVert,this,pCol);
6128 if( bRight )
6130 aVert.Pos().setX(nGridRight);
6131 PaintBorderLine(rRect,aVert,this,pCol);
6136 bGrid = !bGrid;
6138 while( nY <= nBottom )
6140 aTmp.Pos().setY(nY);
6141 PaintBorderLine( rRect, aTmp, this, pCol);
6142 if( bGrid )
6144 nY += nGrid;
6145 SwTwips nHeight = std::min(nBottom, nY) - aTmp.Pos().getY();
6146 if( nHeight )
6148 if( bCell )
6150 SwRect aVert( Point( nX, aTmp.Pos().getY() ),
6151 Size( 1, nHeight ) );
6152 while( aVert.Left() <= nRight )
6154 PaintBorderLine( rRect, aVert, this, pCol);
6155 aVert.Pos().setX(aVert.Pos().getX() + nGridWidth); //for textgrid refactor
6158 else if( bBorder )
6160 SwRect aVert( Point( aGrid.Left(),
6161 aTmp.Pos().getY() ), Size( 1, nHeight ) );
6162 if( bLeft )
6163 PaintBorderLine(rRect,aVert,this,pCol);
6164 if( bRight )
6166 aVert.Pos().setX(nGridRight);
6167 PaintBorderLine(rRect,aVert,this,pCol);
6172 else
6174 nY += nRuby;
6175 if( bBorder )
6177 SwTwips nPos = std::max(aInter.Top(),aTmp.Pos().Y());
6178 SwTwips nH = std::min( nBottom, nY ) - nPos;
6179 SwRect aVert( Point( aGrid.Left(), nPos ),
6180 Size( 1, nH ) );
6181 if( nH > 0 )
6183 if( bLeft )
6184 PaintBorderLine(rRect,aVert,this,pCol);
6185 if( bRight )
6187 aVert.Pos().setX(nGridRight);
6188 PaintBorderLine(rRect,aVert,this,pCol);
6193 bGrid = !bGrid;
6199 * Paint margin area of a page
6201 * OD 20.11.2002 for #104598#:
6202 * implement paint of margin area; margin area will be painted for a
6203 * view shell with a window and if the document is not in online layout.
6205 * @param _rOutputRect
6206 * input parameter - constant instance reference of the rectangle, for
6207 * which an output has to be generated.
6209 * @param _pViewShell
6210 * input parameter - instance of the view shell, on which the output
6211 * has to be generated.
6213 void SwPageFrame::PaintMarginArea( const SwRect& _rOutputRect,
6214 SwViewShell const * _pViewShell ) const
6216 if ( !_pViewShell->GetWin() || _pViewShell->GetViewOptions()->getBrowseMode() )
6217 return;
6219 // Simplified paint with DrawingLayer FillStyle
6220 SwRect aPgRect = getFrameArea();
6221 aPgRect.Intersection_( _rOutputRect );
6223 if(!aPgRect.IsEmpty())
6225 OutputDevice *pOut = _pViewShell->GetOut();
6227 if(pOut->GetFillColor() != aGlobalRetoucheColor)
6229 pOut->SetFillColor(aGlobalRetoucheColor);
6232 pOut->DrawRect(aPgRect.SVRect());
6236 const sal_Int8 SwPageFrame::snShadowPxWidth = 9;
6238 bool SwPageFrame::IsRightShadowNeeded() const
6240 const SwViewShell *pSh = getRootFrame()->GetCurrShell();
6241 const bool bIsLTR = getRootFrame()->IsLeftToRightViewLayout();
6243 // We paint the right shadow if we're not in book mode
6244 // or if we've no sibling or are the last page of the "row"
6245 return !pSh || (!pSh->GetViewOptions()->IsViewLayoutBookMode()) || !GetNext()
6246 || (this == Lower()) || (bIsLTR && OnRightPage())
6247 || (!bIsLTR && !OnRightPage());
6251 bool SwPageFrame::IsLeftShadowNeeded() const
6253 const SwViewShell *pSh = getRootFrame()->GetCurrShell();
6254 const bool bIsLTR = getRootFrame()->IsLeftToRightViewLayout();
6256 // We paint the left shadow if we're not in book mode
6257 // or if we've no sibling or are the last page of the "row"
6258 return !pSh || (!pSh->GetViewOptions()->IsViewLayoutBookMode()) || !GetPrev()
6259 || (bIsLTR && !OnRightPage())
6260 || (!bIsLTR && OnRightPage());
6264 * Determine rectangle for bottom page shadow
6265 * for #i9719#
6267 /*static*/ void SwPageFrame::GetHorizontalShadowRect( const SwRect& _rPageRect,
6268 const SwViewShell* _pViewShell,
6269 OutputDevice const * pRenderContext,
6270 SwRect& _orHorizontalShadowRect,
6271 bool bPaintLeftShadow,
6272 bool bPaintRightShadow,
6273 bool bRightSidebar )
6275 const SwPostItMgr *pMgr = _pViewShell->GetPostItMgr();
6276 SwRect aAlignedPageRect( _rPageRect );
6277 ::SwAlignRect( aAlignedPageRect, _pViewShell, pRenderContext );
6278 SwRect aPagePxRect(pRenderContext->LogicToPixel( aAlignedPageRect.SVRect() ));
6280 tools::Long lShadowAdjustment = snShadowPxWidth - 1; // TODO: extract this
6282 _orHorizontalShadowRect.Chg(
6283 Point( aPagePxRect.Left() + (bPaintLeftShadow ? lShadowAdjustment : 0), 0 ),
6284 Size( aPagePxRect.Width() - ( (bPaintLeftShadow ? lShadowAdjustment : 0) + (bPaintRightShadow ? lShadowAdjustment : 0) ),
6285 snShadowPxWidth ) );
6287 if(pMgr && pMgr->ShowNotes() && pMgr->HasNotes())
6289 // Notes are displayed, we've to extend borders
6290 SwTwips aSidebarTotalWidth = pMgr->GetSidebarWidth(true) + pMgr->GetSidebarBorderWidth(true);
6291 if(bRightSidebar)
6292 _orHorizontalShadowRect.AddRight( aSidebarTotalWidth );
6293 else
6294 _orHorizontalShadowRect.AddLeft( - aSidebarTotalWidth );
6298 namespace {
6300 enum PaintArea {LEFT, RIGHT, TOP, BOTTOM};
6304 #define BORDER_TILE_SIZE 512
6306 /// Wrapper around pOut->DrawBitmapEx.
6307 static void lcl_paintBitmapExToRect(vcl::RenderContext *pOut, const Point& aPoint, const Size& aSize, const BitmapEx& rBitmapEx, PaintArea eArea)
6309 if(!comphelper::LibreOfficeKit::isActive())
6311 // The problem is that if we get called multiple times and the color is
6312 // partly transparent, then the result will get darker and darker. To avoid
6313 // this, always paint the background color before doing the real paint.
6314 tools::Rectangle aRect(aPoint, aSize);
6316 if (!aRect.IsEmpty())
6318 switch (eArea)
6320 case LEFT: aRect.SetLeft( aRect.Right() - 1 ); break;
6321 case RIGHT: aRect.SetRight( aRect.Left() + 1 ); break;
6322 case TOP: aRect.SetTop( aRect.Bottom() - 1 ); break;
6323 case BOTTOM: aRect.SetBottom( aRect.Top() + 1 ); break;
6327 pOut->SetFillColor(SwViewOption::GetCurrentViewOptions().GetAppBackgroundColor());
6328 pOut->SetLineColor();
6329 pOut->DrawRect(pOut->PixelToLogic(aRect));
6332 // Tiled render if necessary
6333 tools::Rectangle aComplete(aPoint, aSize);
6334 Size aTileSize(BORDER_TILE_SIZE, BORDER_TILE_SIZE);
6336 tools::Long iterX = eArea != RIGHT && eArea != LEFT ? BORDER_TILE_SIZE : 0;
6337 tools::Long iterY = eArea == RIGHT || eArea == LEFT ? BORDER_TILE_SIZE : 0;
6339 for (tools::Rectangle aTile(aPoint, aTileSize); true; aTile.Move(iterX, iterY))
6341 tools::Rectangle aRender = aComplete.GetIntersection(aTile);
6342 if (aRender.IsEmpty())
6343 break;
6344 pOut->DrawBitmapEx(pOut->PixelToLogic(aRender.TopLeft()),
6345 pOut->PixelToLogic(aRender.GetSize()),
6346 Point(0, 0), aRender.GetSize(),
6347 rBitmapEx);
6353 * Paint page border and shadow
6355 * for #i9719#
6356 * implement paint of page border and shadow
6358 /*static*/ void SwPageFrame::PaintBorderAndShadow( const SwRect& _rPageRect,
6359 const SwViewShell* _pViewShell,
6360 bool bPaintLeftShadow,
6361 bool bPaintRightShadow,
6362 bool bRightSidebar )
6364 // No shadow in prefs
6365 if (!_pViewShell->GetViewOptions()->IsShadow())
6366 return;
6368 // #i16816# tagged pdf support
6369 SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, nullptr, *_pViewShell->GetOut() );
6371 static tools::DeleteOnDeinit<drawinglayer::primitive2d::DiscreteShadow> shadowMaskObj(
6372 vcl::bitmap::loadFromName(BMP_PAGE_SHADOW_MASK,
6373 ImageLoadFlags::IgnoreDarkTheme | ImageLoadFlags::IgnoreScalingFactor));
6375 drawinglayer::primitive2d::DiscreteShadow& shadowMask = *shadowMaskObj.get();
6376 static tools::DeleteOnDeinit< BitmapEx > aPageTopRightShadowObj {};
6377 static tools::DeleteOnDeinit< BitmapEx > aPageBottomRightShadowObj {};
6378 static tools::DeleteOnDeinit< BitmapEx > aPageBottomLeftShadowObj {};
6379 static tools::DeleteOnDeinit< BitmapEx > aPageBottomShadowBaseObj {};
6380 static tools::DeleteOnDeinit< BitmapEx > aPageRightShadowBaseObj {};
6381 static tools::DeleteOnDeinit< BitmapEx > aPageTopShadowBaseObj {};
6382 static tools::DeleteOnDeinit< BitmapEx > aPageTopLeftShadowObj {};
6383 static tools::DeleteOnDeinit< BitmapEx > aPageLeftShadowBaseObj {};
6384 BitmapEx& aPageTopRightShadow = *aPageTopRightShadowObj.get();
6385 BitmapEx& aPageBottomRightShadow = *aPageBottomRightShadowObj.get();
6386 BitmapEx& aPageBottomLeftShadow = *aPageBottomLeftShadowObj.get();
6387 BitmapEx& aPageBottomShadow = *aPageBottomShadowBaseObj.get();
6388 BitmapEx& aPageRightShadow = *aPageRightShadowBaseObj.get();
6389 BitmapEx& aPageTopShadow = *aPageTopShadowBaseObj.get();
6390 BitmapEx& aPageTopLeftShadow = *aPageTopLeftShadowObj.get();
6391 BitmapEx& aPageLeftShadow = *aPageLeftShadowBaseObj.get();
6392 static Color aShadowColor( COL_AUTO );
6394 SwRect aAlignedPageRect( _rPageRect );
6395 ::SwAlignRect( aAlignedPageRect, _pViewShell, _pViewShell->GetOut() );
6396 SwRect aPagePxRect(_pViewShell->GetOut()->LogicToPixel( aAlignedPageRect.SVRect() ));
6398 if (aShadowColor != _pViewShell->GetViewOptions()->GetShadowColor())
6400 aShadowColor = _pViewShell->GetViewOptions()->GetShadowColor();
6402 AlphaMask aMask( shadowMask.getBottomRight().GetBitmap() );
6403 Bitmap aFilledSquare(aMask.GetSizePixel(), vcl::PixelFormat::N24_BPP);
6404 aFilledSquare.Erase( aShadowColor );
6405 aPageBottomRightShadow = BitmapEx( aFilledSquare, aMask );
6407 aMask = AlphaMask( shadowMask.getBottomLeft().GetBitmap() );
6408 aFilledSquare = Bitmap(aMask.GetSizePixel(), vcl::PixelFormat::N24_BPP);
6409 aFilledSquare.Erase( aShadowColor );
6410 aPageBottomLeftShadow = BitmapEx( aFilledSquare, aMask );
6412 aMask = AlphaMask( shadowMask.getBottom().GetBitmap() );
6413 aFilledSquare = Bitmap(aMask.GetSizePixel(), vcl::PixelFormat::N24_BPP);
6414 aFilledSquare.Erase( aShadowColor );
6415 aPageBottomShadow = BitmapEx( aFilledSquare, aMask );
6417 aMask = AlphaMask( shadowMask.getTop().GetBitmap() );
6418 aFilledSquare = Bitmap(aMask.GetSizePixel(), vcl::PixelFormat::N24_BPP);
6419 aFilledSquare.Erase( aShadowColor );
6420 aPageTopShadow = BitmapEx( aFilledSquare, aMask );
6422 aMask = AlphaMask( shadowMask.getTopRight().GetBitmap() );
6423 aFilledSquare = Bitmap(aMask.GetSizePixel(), vcl::PixelFormat::N24_BPP);
6424 aFilledSquare.Erase( aShadowColor );
6425 aPageTopRightShadow = BitmapEx( aFilledSquare, aMask );
6427 aMask = AlphaMask( shadowMask.getRight().GetBitmap() );
6428 aFilledSquare = Bitmap(aMask.GetSizePixel(), vcl::PixelFormat::N24_BPP);
6429 aFilledSquare.Erase( aShadowColor );
6430 aPageRightShadow = BitmapEx( aFilledSquare, aMask );
6432 aMask = AlphaMask( shadowMask.getTopLeft().GetBitmap() );
6433 aFilledSquare = Bitmap(aMask.GetSizePixel(), vcl::PixelFormat::N24_BPP);
6434 aFilledSquare.Erase( aShadowColor );
6435 aPageTopLeftShadow = BitmapEx( aFilledSquare, aMask );
6437 aMask = AlphaMask( shadowMask.getLeft().GetBitmap() );
6438 aFilledSquare = Bitmap(aMask.GetSizePixel(), vcl::PixelFormat::N24_BPP);
6439 aFilledSquare.Erase( aShadowColor );
6440 aPageLeftShadow = BitmapEx( aFilledSquare, aMask );
6443 SwRect aPaintRect;
6444 OutputDevice *pOut = _pViewShell->GetOut();
6446 SwPageFrame::GetHorizontalShadowRect( _rPageRect, _pViewShell, pOut, aPaintRect, bPaintLeftShadow, bPaintRightShadow, bRightSidebar );
6448 // Right shadow & corners
6449 if ( bPaintRightShadow )
6451 pOut->DrawBitmapEx( pOut->PixelToLogic( Point( aPaintRect.Right(), aPagePxRect.Bottom() + 1 - (aPageBottomRightShadow.GetSizePixel().Height() - snShadowPxWidth) ) ),
6452 aPageBottomRightShadow );
6453 pOut->DrawBitmapEx( pOut->PixelToLogic( Point( aPaintRect.Right(), aPagePxRect.Top() - snShadowPxWidth ) ),
6454 aPageTopRightShadow );
6456 if (aPagePxRect.Height() > 2 * snShadowPxWidth)
6458 const tools::Long nWidth = aPageRightShadow.GetSizePixel().Width();
6459 const tools::Long nHeight = aPagePxRect.Height() - 2 * (snShadowPxWidth - 1);
6460 if (aPageRightShadow.GetSizePixel().Height() < BORDER_TILE_SIZE)
6461 aPageRightShadow.Scale(Size(nWidth, BORDER_TILE_SIZE), BmpScaleFlag::Fast);
6463 lcl_paintBitmapExToRect(pOut,
6464 Point(aPaintRect.Right() + snShadowPxWidth, aPagePxRect.Top() + snShadowPxWidth - 1),
6465 Size(nWidth, nHeight),
6466 aPageRightShadow, RIGHT);
6470 // Left shadows and corners
6471 if(bPaintLeftShadow)
6473 const tools::Long lLeft = aPaintRect.Left() - aPageBottomLeftShadow.GetSizePixel().Width();
6474 pOut->DrawBitmapEx( pOut->PixelToLogic( Point( lLeft,
6475 aPagePxRect.Bottom() + 1 + snShadowPxWidth - aPageBottomLeftShadow.GetSizePixel().Height() ) ), aPageBottomLeftShadow );
6476 pOut->DrawBitmapEx( pOut->PixelToLogic( Point( lLeft, aPagePxRect.Top() - snShadowPxWidth ) ), aPageTopLeftShadow );
6477 if (aPagePxRect.Height() > 2 * snShadowPxWidth)
6479 const tools::Long nWidth = aPageLeftShadow.GetSizePixel().Width();
6480 const tools::Long nHeight = aPagePxRect.Height() - 2 * (snShadowPxWidth - 1);
6481 if (aPageLeftShadow.GetSizePixel().Height() < BORDER_TILE_SIZE)
6482 aPageLeftShadow.Scale(Size(nWidth, BORDER_TILE_SIZE), BmpScaleFlag::Fast);
6484 lcl_paintBitmapExToRect(pOut,
6485 Point(lLeft, aPagePxRect.Top() + snShadowPxWidth - 1),
6486 Size(nWidth, nHeight),
6487 aPageLeftShadow, LEFT);
6491 // Bottom shadow
6492 const tools::Long nBottomHeight = aPageBottomShadow.GetSizePixel().Height();
6493 if (aPageBottomShadow.GetSizePixel().Width() < BORDER_TILE_SIZE)
6494 aPageBottomShadow.Scale(Size(BORDER_TILE_SIZE, nBottomHeight), BmpScaleFlag::Fast);
6496 lcl_paintBitmapExToRect(pOut,
6497 Point(aPaintRect.Left(), aPagePxRect.Bottom() + 2),
6498 Size(aPaintRect.Width(), nBottomHeight),
6499 aPageBottomShadow, BOTTOM);
6501 // Top shadow
6502 const tools::Long nTopHeight = aPageTopShadow.GetSizePixel().Height();
6503 if (aPageTopShadow.GetSizePixel().Width() < BORDER_TILE_SIZE)
6504 aPageTopShadow.Scale(Size(BORDER_TILE_SIZE, nTopHeight), BmpScaleFlag::Fast);
6506 lcl_paintBitmapExToRect(pOut,
6507 Point(aPaintRect.Left(), aPagePxRect.Top() - snShadowPxWidth),
6508 Size(aPaintRect.Width(), nTopHeight),
6509 aPageTopShadow, TOP);
6513 * mod #i6193# paint sidebar for notes
6514 * IMPORTANT: if you change the rects here, also change SwPostItMgr::ScrollbarHit
6516 /*static*/void SwPageFrame::PaintNotesSidebar(const SwRect& _rPageRect, SwViewShell* _pViewShell, sal_uInt16 nPageNum, bool bRight)
6518 //TODO: cut out scrollbar area and arrows out of sidepane rect, otherwise it could flicker when pressing arrow buttons
6519 if (!_pViewShell )
6520 return;
6522 SwRect aPageRect( _rPageRect );
6523 SwAlignRect( aPageRect, _pViewShell, _pViewShell->GetOut() );
6525 const SwPostItMgr *pMgr = _pViewShell->GetPostItMgr();
6526 if (!(pMgr && pMgr->ShowNotes() && pMgr->HasNotes())) // do not show anything in print preview
6527 return;
6529 sal_Int32 nScrollerHeight = pMgr->GetSidebarScrollerHeight();
6530 const tools::Rectangle aVisRect = _pViewShell->VisArea().SVRect();
6531 //draw border and sidepane
6532 _pViewShell->GetOut()->SetLineColor();
6533 if (!bRight)
6535 _pViewShell->GetOut()->SetFillColor(_pViewShell->GetViewOptions()->GetDocBoundariesColor());
6536 _pViewShell->GetOut()->DrawRect(tools::Rectangle(Point(aPageRect.Left()-pMgr->GetSidebarBorderWidth(),aPageRect.Top()),Size(pMgr->GetSidebarBorderWidth(),aPageRect.Height()))) ;
6537 if (Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
6538 _pViewShell->GetOut()->SetFillColor(COL_BLACK);
6539 else
6540 _pViewShell->GetOut()->SetFillColor(_pViewShell->GetViewOptions()->GetSectionBoundColor());
6541 _pViewShell->GetOut()->DrawRect(tools::Rectangle(Point(aPageRect.Left()-pMgr->GetSidebarWidth()-pMgr->GetSidebarBorderWidth(),aPageRect.Top()),Size(pMgr->GetSidebarWidth(),aPageRect.Height()))) ;
6543 else
6545 _pViewShell->GetOut()->SetFillColor(_pViewShell->GetViewOptions()->GetDocBoundariesColor());
6546 SwRect aSidebarBorder(aPageRect.TopRight(),Size(pMgr->GetSidebarBorderWidth(),aPageRect.Height()));
6547 _pViewShell->GetOut()->DrawRect(aSidebarBorder.SVRect());
6548 if (Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
6549 _pViewShell->GetOut()->SetFillColor(COL_BLACK);
6550 else
6551 _pViewShell->GetOut()->SetFillColor(_pViewShell->GetViewOptions()->GetSectionBoundColor());
6552 SwRect aSidebar(Point(aPageRect.Right()+pMgr->GetSidebarBorderWidth(),aPageRect.Top()),Size(pMgr->GetSidebarWidth(),aPageRect.Height()));
6553 _pViewShell->GetOut()->DrawRect(aSidebar.SVRect());
6555 if (!pMgr->ShowScrollbar(nPageNum))
6556 return;
6558 // draw scrollbar area and arrows
6559 Point aPointBottom;
6560 Point aPointTop;
6561 aPointBottom = !bRight ? Point(aPageRect.Left() - pMgr->GetSidebarWidth() - pMgr->GetSidebarBorderWidth() + _pViewShell->GetOut()->PixelToLogic(Size(2,0)).Width(),aPageRect.Bottom()- _pViewShell->GetOut()->PixelToLogic(Size(0,2+pMgr->GetSidebarScrollerHeight())).Height()) :
6562 Point(aPageRect.Right() + pMgr->GetSidebarBorderWidth() + _pViewShell->GetOut()->PixelToLogic(Size(2,0)).Width(),aPageRect.Bottom()- _pViewShell->GetOut()->PixelToLogic(Size(0,2+pMgr->GetSidebarScrollerHeight())).Height());
6563 aPointTop = !bRight ? Point(aPageRect.Left() - pMgr->GetSidebarWidth() + _pViewShell->GetOut()->PixelToLogic(Size(2,0)).Width(),aPageRect.Top() + _pViewShell->GetOut()->PixelToLogic(Size(0,2)).Height()) :
6564 Point(aPageRect.Right() + pMgr->GetSidebarBorderWidth() + _pViewShell->GetOut()->PixelToLogic(Size(2,0)).Width(),aPageRect.Top() + _pViewShell->GetOut()->PixelToLogic(Size(0,2)).Height());
6565 Size aSize(pMgr->GetSidebarWidth() - _pViewShell->GetOut()->PixelToLogic(Size(4,0)).Width(), _pViewShell->GetOut()->PixelToLogic(Size(0,nScrollerHeight)).Height()) ;
6566 tools::Rectangle aRectBottom(aPointBottom,aSize);
6567 tools::Rectangle aRectTop(aPointTop,aSize);
6569 if (aRectBottom.Overlaps(aVisRect))
6572 if (Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
6574 _pViewShell->GetOut()->SetLineColor(COL_WHITE);
6575 _pViewShell->GetOut()->SetFillColor(COL_BLACK);
6577 else
6579 _pViewShell->GetOut()->SetLineColor(COL_BLACK);
6580 _pViewShell->GetOut()->SetFillColor(COL_LIGHTGRAY);
6582 _pViewShell->GetOut()->DrawRect(aRectBottom);
6583 _pViewShell->GetOut()->DrawLine(aPointBottom + Point(pMgr->GetSidebarWidth()/3,0), aPointBottom + Point(pMgr->GetSidebarWidth()/3 , _pViewShell->GetOut()->PixelToLogic(Size(0,nScrollerHeight)).Height()));
6585 _pViewShell->GetOut()->SetLineColor();
6586 Point aMiddleFirst(aPointBottom + Point(pMgr->GetSidebarWidth()/6,_pViewShell->GetOut()->PixelToLogic(Size(0,nScrollerHeight)).Height()/2));
6587 Point aMiddleSecond(aPointBottom + Point(pMgr->GetSidebarWidth()/3*2,_pViewShell->GetOut()->PixelToLogic(Size(0,nScrollerHeight)).Height()/2));
6588 PaintNotesSidebarArrows(aMiddleFirst,aMiddleSecond,_pViewShell,pMgr->GetArrowColor(KEY_PAGEUP,nPageNum), pMgr->GetArrowColor(KEY_PAGEDOWN,nPageNum));
6590 if (!aRectTop.Overlaps(aVisRect))
6591 return;
6593 if (Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
6595 _pViewShell->GetOut()->SetLineColor(COL_WHITE);
6596 _pViewShell->GetOut()->SetFillColor(COL_BLACK);
6598 else
6600 _pViewShell->GetOut()->SetLineColor(COL_BLACK);
6601 _pViewShell->GetOut()->SetFillColor(COL_LIGHTGRAY);
6603 _pViewShell->GetOut()->DrawRect(aRectTop);
6604 _pViewShell->GetOut()->DrawLine(aPointTop + Point(pMgr->GetSidebarWidth()/3*2,0), aPointTop + Point(pMgr->GetSidebarWidth()/3*2 , _pViewShell->GetOut()->PixelToLogic(Size(0,nScrollerHeight)).Height()));
6606 _pViewShell->GetOut()->SetLineColor();
6607 Point aMiddleFirst(aPointTop + Point(pMgr->GetSidebarWidth()/3,_pViewShell->GetOut()->PixelToLogic(Size(0,nScrollerHeight)).Height()/2));
6608 Point aMiddleSecond(aPointTop + Point(pMgr->GetSidebarWidth()/6*5,_pViewShell->GetOut()->PixelToLogic(Size(0,nScrollerHeight)).Height()/2));
6609 PaintNotesSidebarArrows(aMiddleFirst,aMiddleSecond,_pViewShell, pMgr->GetArrowColor(KEY_PAGEUP,nPageNum), pMgr->GetArrowColor(KEY_PAGEDOWN,nPageNum));
6612 /*static*/ void SwPageFrame::PaintNotesSidebarArrows(const Point &aMiddleFirst, const Point &aMiddleSecond, SwViewShell const * _pViewShell, const Color& rColorUp, const Color& rColorDown)
6614 tools::Polygon aTriangleUp(3);
6615 tools::Polygon aTriangleDown(3);
6617 aTriangleUp.SetPoint(aMiddleFirst + Point(0,_pViewShell->GetOut()->PixelToLogic(Size(0,-3)).Height()),0);
6618 aTriangleUp.SetPoint(aMiddleFirst + Point(_pViewShell->GetOut()->PixelToLogic(Size(-3,0)).Width(),_pViewShell->GetOut()->PixelToLogic(Size(0,3)).Height()),1);
6619 aTriangleUp.SetPoint(aMiddleFirst + Point(_pViewShell->GetOut()->PixelToLogic(Size(3,0)).Width(),_pViewShell->GetOut()->PixelToLogic(Size(0,3)).Height()),2);
6621 aTriangleDown.SetPoint(aMiddleSecond + Point(_pViewShell->GetOut()->PixelToLogic(Size(-3,0)).Width(),_pViewShell->GetOut()->PixelToLogic(Size(0,-3)).Height()),0);
6622 aTriangleDown.SetPoint(aMiddleSecond + Point(_pViewShell->GetOut()->PixelToLogic(Size(+3,0)).Width(),_pViewShell->GetOut()->PixelToLogic(Size(0,-3)).Height()),1);
6623 aTriangleDown.SetPoint(aMiddleSecond + Point(0,_pViewShell->GetOut()->PixelToLogic(Size(0,3)).Height()),2);
6625 _pViewShell->GetOut()->SetFillColor(rColorUp);
6626 _pViewShell->GetOut()->DrawPolygon(aTriangleUp);
6627 _pViewShell->GetOut()->SetFillColor(rColorDown);
6628 _pViewShell->GetOut()->DrawPolygon(aTriangleDown);
6632 * Get bound rectangle of border and shadow for repaints
6634 * for #i9719#
6636 /*static*/ void SwPageFrame::GetBorderAndShadowBoundRect( const SwRect& _rPageRect,
6637 const SwViewShell* _pViewShell,
6638 OutputDevice const * pRenderContext,
6639 SwRect& _orBorderAndShadowBoundRect,
6640 bool bLeftShadow,
6641 bool bRightShadow,
6642 bool bRightSidebar
6645 SwRect aAlignedPageRect( _rPageRect );
6646 ::SwAlignRect( aAlignedPageRect, _pViewShell, pRenderContext );
6647 SwRect aPagePxRect(pRenderContext->LogicToPixel( aAlignedPageRect.SVRect() ));
6648 aPagePxRect.AddBottom( snShadowPxWidth + 1 );
6649 aPagePxRect.AddTop( - snShadowPxWidth - 1 );
6651 SwRect aTmpRect;
6653 // Always ask for full shadow since we want a bounding rect
6654 // including at least the page frame
6655 SwPageFrame::GetHorizontalShadowRect( _rPageRect, _pViewShell, pRenderContext, aTmpRect, false, false, bRightSidebar );
6657 if(bLeftShadow) aPagePxRect.Left( aTmpRect.Left() - snShadowPxWidth - 1);
6658 if(bRightShadow) aPagePxRect.Right( aTmpRect.Right() + snShadowPxWidth + 1);
6660 _orBorderAndShadowBoundRect = SwRect(pRenderContext->PixelToLogic( aPagePxRect.SVRect() ));
6663 SwRect SwPageFrame::GetBoundRect(OutputDevice const * pOutputDevice) const
6665 const SwViewShell *pSh = getRootFrame()->GetCurrShell();
6666 SwRect aPageRect( getFrameArea() );
6667 SwRect aResult;
6669 if(!pSh) {
6670 return SwRect( Point(0, 0), Size(0, 0) );
6673 SwPageFrame::GetBorderAndShadowBoundRect( aPageRect, pSh, pOutputDevice, aResult,
6674 IsLeftShadowNeeded(), IsRightShadowNeeded(), SidebarPosition() == sw::sidebarwindows::SidebarPosition::RIGHT );
6675 return aResult;
6678 /*static*/ SwTwips SwPageFrame::GetSidebarBorderWidth( const SwViewShell* _pViewShell )
6680 const SwPostItMgr* pPostItMgr = _pViewShell ? _pViewShell->GetPostItMgr() : nullptr;
6681 const SwTwips nRet = pPostItMgr && pPostItMgr->HasNotes() && pPostItMgr->ShowNotes() ? pPostItMgr->GetSidebarWidth() + pPostItMgr->GetSidebarBorderWidth() : 0;
6682 return nRet;
6685 const SwFrame* SwFrame::SkipFrame(const SwFrame* pFrame, PaintFrameMode ePaintFrameMode )
6687 if (ePaintFrameMode != PAINT_ALL)
6689 if (ePaintFrameMode == PAINT_NON_HEADER_FOOTER)
6691 while (pFrame && (pFrame->IsHeaderFrame() || pFrame->IsFooterFrame()))
6692 pFrame = pFrame->GetNext();
6694 else
6696 while ( pFrame && !pFrame->IsHeaderFrame() && !pFrame->IsFooterFrame())
6697 pFrame = pFrame->GetNext();
6700 return pFrame;
6703 void SwFrame::PaintBaBo( const SwRect& rRect, const SwPageFrame *pPage,
6704 const bool bOnlyTextBackground, PaintFrameMode ePaintFrameMode ) const
6706 if ( !pPage )
6707 pPage = FindPageFrame();
6709 OutputDevice *pOut = gProp.pSGlobalShell->GetOut();
6711 // #i16816# tagged pdf support
6712 SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, nullptr, *pOut );
6714 pOut->Push( vcl::PushFlags::FILLCOLOR|vcl::PushFlags::LINECOLOR );
6715 pOut->SetLineColor();
6717 SwBorderAttrAccess aAccess( SwFrame::GetCache(), this );
6718 const SwBorderAttrs &rAttrs = *aAccess.Get();
6720 // take care of page margin area
6721 // Note: code move from <SwFrame::PaintSwFrameBackground(..)> to new method
6722 // <SwPageFrame::Paintmargin(..)>.
6723 if ( IsPageFrame() && !bOnlyTextBackground)
6725 static_cast<const SwPageFrame*>(this)->PaintMarginArea( rRect, gProp.pSGlobalShell );
6728 // paint background
6730 PaintSwFrameBackground( rRect, pPage, rAttrs, false, true/*bLowerBorder*/, bOnlyTextBackground, ePaintFrameMode );
6733 // paint border before painting background
6734 // paint grid for page frame and paint border
6735 if (!bOnlyTextBackground)
6737 SwRect aRect( rRect );
6739 if( IsPageFrame() )
6741 static_cast<const SwPageFrame*>(this)->PaintGrid( pOut, aRect );
6744 PaintSwFrameShadowAndBorder(aRect, pPage, rAttrs);
6747 pOut->Pop();
6750 static bool lcl_compareFillAttributes(const drawinglayer::attribute::SdrAllFillAttributesHelperPtr& pA, const drawinglayer::attribute::SdrAllFillAttributesHelperPtr& pB)
6752 if (pA == pB)
6753 return true;
6754 if (!pA || !pB)
6755 return false;
6756 return pA->getFillAttribute() == pB->getFillAttribute();
6759 /// Do not paint background for fly frames without a background brush by
6760 /// calling <PaintBaBo> at the page or at the fly frame its anchored
6761 void SwFrame::PaintSwFrameBackground( const SwRect &rRect, const SwPageFrame *pPage,
6762 const SwBorderAttrs & rAttrs,
6763 const bool bLowerMode,
6764 const bool bLowerBorder,
6765 const bool bOnlyTextBackground,
6766 PaintFrameMode ePaintFrameMode) const
6768 // #i1837# - no paint of table background, if corresponding option is *not* set.
6769 SwViewShell *pSh = gProp.pSGlobalShell;
6770 if( IsTabFrame() &&
6771 !pSh->GetViewOptions()->IsTable() )
6773 return;
6776 // nothing to do for covered table cells:
6777 if( IsCellFrame() && IsCoveredCell() )
6778 return;
6780 // #i16816# tagged pdf support
6781 SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, nullptr, *pSh->GetOut() );
6783 const SvxBrushItem* pItem;
6784 // temporary background brush for a fly frame without a background brush
6785 std::unique_ptr<SvxBrushItem> pTmpBackBrush;
6786 std::optional<Color> pCol;
6787 SwRect aOrigBackRect;
6788 const bool bPageFrame = IsPageFrame();
6789 bool bLowMode = true;
6790 drawinglayer::attribute::SdrAllFillAttributesHelperPtr aFillAttributes;
6792 bool bBack = GetBackgroundBrush( aFillAttributes, pItem, pCol, aOrigBackRect, bLowerMode, /*bConsiderTextBox=*/false );
6794 // show track changes of table row
6795 if( IsRowFrame() && !getRootFrame()->IsHideRedlines() )
6797 RedlineType eType = static_cast<const SwRowFrame*>(this)->GetTabLine()->GetRedlineType();
6798 if ( RedlineType::Delete == eType || RedlineType::Insert == eType )
6800 pCol = RedlineType::Delete == eType ? COL_AUTHOR_TABLE_DEL : COL_AUTHOR_TABLE_INS;
6801 bBack = true;
6804 else if ( IsCellFrame() && !getRootFrame()->IsHideRedlines() )
6806 RedlineType eType = static_cast<const SwCellFrame*>(this)->GetTabBox()->GetRedlineType();
6807 if ( RedlineType::Delete == eType || RedlineType::Insert == eType )
6809 pCol = RedlineType::Delete == eType ? COL_AUTHOR_TABLE_DEL : COL_AUTHOR_TABLE_INS;
6810 bBack = true;
6814 if ( bBack && IsCellFrame() && !getRootFrame()->IsHideRedlines() &&
6815 // skip cell background to show the row colored according to its tracked change
6816 RedlineType::None != static_cast<const SwRowFrame*>(GetUpper())->GetTabLine()->GetRedlineType() )
6818 return;
6821 //- Output if a separate background is used.
6822 bool bNoFlyBackground = !gProp.bSFlyMetafile && !bBack && IsFlyFrame();
6823 if ( bNoFlyBackground )
6825 // Fly frame has no background.
6826 // Try to find background brush at parents, if previous call of
6827 // <GetBackgroundBrush> disabled this option with the parameter <bLowerMode>
6828 if ( bLowerMode )
6830 bBack = GetBackgroundBrush( aFillAttributes, pItem, pCol, aOrigBackRect, false, /*bConsiderTextBox=*/false );
6832 // If still no background found for the fly frame, initialize the
6833 // background brush <pItem> with global retouche color and set <bBack>
6834 // to true, that fly frame will paint its background using this color.
6835 if ( !bBack )
6837 // #i6467# - on print output, pdf output and in embedded mode not editing color COL_WHITE is used
6838 // instead of the global retouche color.
6839 if ( pSh->GetOut()->GetOutDevType() == OUTDEV_PRINTER ||
6840 pSh->GetViewOptions()->IsPDFExport() ||
6841 ( pSh->GetDoc()->GetDocShell()->GetCreateMode() == SfxObjectCreateMode::EMBEDDED &&
6842 !pSh->GetDoc()->GetDocShell()->IsInPlaceActive()
6846 pTmpBackBrush.reset(new SvxBrushItem( COL_WHITE, RES_BACKGROUND ));
6848 //UUU
6849 aFillAttributes = std::make_shared<drawinglayer::attribute::SdrAllFillAttributesHelper>(COL_WHITE);
6851 else
6853 pTmpBackBrush.reset(new SvxBrushItem( aGlobalRetoucheColor, RES_BACKGROUND));
6855 //UUU
6856 aFillAttributes = std::make_shared<drawinglayer::attribute::SdrAllFillAttributesHelper>(aGlobalRetoucheColor);
6859 pItem = pTmpBackBrush.get();
6860 bBack = true;
6864 SwRect aPaintRect( getFrameArea() );
6865 if( IsTextFrame() || IsSctFrame() )
6866 aPaintRect = UnionFrame( true );
6868 // bOnlyTextBackground means background that's on top of background shapes,
6869 // this includes both text and cell frames.
6870 if ( (!bOnlyTextBackground || IsTextFrame() || IsCellFrame()) && aPaintRect.Overlaps( rRect ) )
6872 if ( bBack || bPageFrame || !bLowerMode )
6874 const bool bBrowse = pSh->GetViewOptions()->getBrowseMode();
6875 SwRect aRect;
6876 if ( (bPageFrame && bBrowse) ||
6877 (IsTextFrame() && getFramePrintArea().SSize() == getFrameArea().SSize()) )
6879 aRect = getFrameArea();
6880 ::SwAlignRect( aRect, gProp.pSGlobalShell, gProp.pSGlobalShell->GetOut() );
6882 else
6884 if (bPageFrame && GetAttrSet()->GetItem<SfxBoolItem>(RES_BACKGROUND_FULL_SIZE)->GetValue())
6886 aRect = getFrameArea();
6887 ::SwAlignRect(aRect, gProp.pSGlobalShell, gProp.pSGlobalShell->GetOut());
6889 else
6891 ::lcl_CalcBorderRect( aRect, this, rAttrs, false, gProp);
6894 if ( (IsTextFrame() || IsTabFrame()) && GetPrev() )
6896 if ( GetPrev()->GetAttrSet()->GetBackground() == GetAttrSet()->GetBackground() &&
6897 lcl_compareFillAttributes(GetPrev()->getSdrAllFillAttributesHelper(), getSdrAllFillAttributesHelper()))
6899 aRect.Top( getFrameArea().Top() );
6903 aRect.Intersection( rRect );
6905 OutputDevice *pOut = pSh->GetOut();
6907 if ( aRect.HasArea() )
6909 std::unique_ptr<SvxBrushItem> pNewItem;
6911 if( pCol )
6913 pNewItem.reset(new SvxBrushItem( *pCol, RES_BACKGROUND ));
6914 pItem = pNewItem.get();
6915 aFillAttributes = std::make_shared<drawinglayer::attribute::SdrAllFillAttributesHelper>(*pCol);
6918 SwRegionRects aRegion( aRect );
6919 basegfx::B2DPolygon aB2DPolygon{tools::Polygon(aRect.SVRect()).getB2DPolygon()};
6920 basegfx::utils::B2DClipState aClipState{basegfx::B2DPolyPolygon(aB2DPolygon)};
6921 if (pPage->GetSortedObjs() &&
6922 pSh->GetDoc()->getIDocumentSettingAccess().get(DocumentSettingId::SUBTRACT_FLYS))
6924 ::lcl_SubtractFlys( this, pPage, aRect, aRegion, aClipState, gProp );
6927 // Determine, if background transparency
6928 // have to be considered for drawing.
6929 // Status Quo: background transparency have to be
6930 // considered for fly frames
6931 const bool bConsiderBackgroundTransparency = IsFlyFrame();
6932 bool bDone(false);
6934 // #i125189# We are also done when the new DrawingLayer FillAttributes are used
6935 // or the FillStyle is set (different from drawing::FillStyle_NONE)
6936 if (aFillAttributes)
6938 if(aFillAttributes->isUsed())
6940 // check if really something is painted
6941 bDone = DrawFillAttributes(aFillAttributes, aOrigBackRect, aRegion, aClipState, *pOut);
6944 if(!bDone)
6946 // if not, still a FillStyle could be set but the transparency is at 100%,
6947 // thus need to check the model data itself for FillStyle (do not rely on
6948 // SdrAllFillAttributesHelper since it already contains optimized information,
6949 // e.g. transparency leads to no fill)
6950 const drawing::FillStyle eFillStyle(GetAttrSet()->Get(XATTR_FILLSTYLE).GetValue());
6952 if(drawing::FillStyle_NONE != eFillStyle)
6954 bDone = true;
6959 if(!bDone)
6961 for (size_t i = 0; i < aRegion.size(); ++i)
6963 if (1 < aRegion.size())
6965 ::SwAlignRect( aRegion[i], gProp.pSGlobalShell, gProp.pSGlobalShell->GetOut() );
6966 if( !aRegion[i].HasArea() )
6967 continue;
6969 // add 6th parameter to indicate, if background transparency have to be considered
6970 // Set missing 5th parameter to the default value GRFNUM_NO
6971 // - see declaration in /core/inc/frmtool.hxx.
6972 ::DrawGraphic(
6973 pItem,
6974 *pOut,
6975 aOrigBackRect,
6976 aRegion[i],
6977 GRFNUM_NO,
6978 bConsiderBackgroundTransparency );
6983 else
6984 bLowMode = bLowerMode;
6987 // delete temporary background brush.
6988 pTmpBackBrush.reset();
6990 //Now process lower and his neighbour.
6991 //We end this as soon as a Frame leaves the chain and therefore is not a lower
6992 //of me anymore
6993 const SwFrame *pFrame = GetLower();
6994 if ( !pFrame )
6995 return;
6997 SwRect aFrameRect;
6998 SwRect aRect( GetPaintArea() );
6999 aRect.Intersection_( rRect );
7000 SwRect aBorderRect( aRect );
7002 pFrame = SkipFrame(pFrame, ePaintFrameMode);
7003 if (!pFrame)
7004 return;
7006 SwShortCut aShortCut( *pFrame, aBorderRect );
7008 { if ( gProp.pSProgress )
7009 SfxProgress::Reschedule();
7011 aFrameRect = pFrame->GetPaintArea();
7012 if ( aFrameRect.Overlaps( aBorderRect ) )
7014 SwBorderAttrAccess aAccess( SwFrame::GetCache(), pFrame );
7015 const SwBorderAttrs &rTmpAttrs = *aAccess.Get();
7016 if ( ( pFrame->IsLayoutFrame() && bLowerBorder ) || aFrameRect.Overlaps( aRect ) )
7018 pFrame->PaintSwFrameBackground( aRect, pPage, rTmpAttrs, bLowMode,
7019 bLowerBorder, bOnlyTextBackground );
7022 if ( bLowerBorder )
7024 pFrame->PaintSwFrameShadowAndBorder( aBorderRect, pPage, rTmpAttrs );
7027 pFrame = pFrame->GetNext();
7028 pFrame = SkipFrame(pFrame, ePaintFrameMode);
7029 } while ( pFrame && pFrame->GetUpper() == this &&
7030 !aShortCut.Stop( aFrameRect ) );
7033 /// Refreshes all subsidiary lines of a page.
7034 void SwPageFrame::RefreshSubsidiary( const SwRect &rRect ) const
7036 if ( !(isSubsidiaryLinesEnabled() ||
7037 gProp.pSGlobalShell->GetViewOptions()->IsTextBoundaries() ||
7038 gProp.pSGlobalShell->GetViewOptions()->IsSectionBoundaries() ||
7039 gProp.pSGlobalShell->GetViewOptions()->IsTableBoundaries()) )
7040 return;
7042 if ( !rRect.HasArea() )
7043 return;
7045 //During paint using the root, the array is controlled from there.
7046 //Otherwise we'll handle it for our self.
7047 bool bDelSubs = false;
7048 if ( !gProp.pSSubsLines )
7050 gProp.pSSubsLines.reset(new SwSubsRects);
7051 // create container for special subsidiary lines
7052 gProp.pSSpecSubsLines.reset(new SwSubsRects);
7053 bDelSubs = true;
7056 RefreshLaySubsidiary( this, rRect );
7058 if ( bDelSubs )
7060 // paint special subsidiary lines and delete its container
7061 gProp.pSSpecSubsLines->PaintSubsidiary( gProp.pSGlobalShell->GetOut(), nullptr, gProp );
7062 gProp.pSSpecSubsLines.reset();
7064 gProp.pSSubsLines->PaintSubsidiary(gProp.pSGlobalShell->GetOut(), gProp.pSLines.get(), gProp);
7065 gProp.pSSubsLines.reset();
7069 void SwLayoutFrame::RefreshLaySubsidiary( const SwPageFrame *pPage,
7070 const SwRect &rRect ) const
7072 if (isSubsidiaryLinesEnabled())
7073 PaintSubsidiaryLines( pPage, rRect );
7075 const SwFrame *pLow = Lower();
7076 if( !pLow )
7077 return;
7078 SwShortCut aShortCut( *pLow, rRect );
7079 while( pLow && !aShortCut.Stop( pLow->getFrameArea() ) )
7081 if ( pLow->getFrameArea().Overlaps( rRect ) && pLow->getFrameArea().HasArea() )
7083 if ( pLow->IsLayoutFrame() )
7084 static_cast<const SwLayoutFrame*>(pLow)->RefreshLaySubsidiary( pPage, rRect);
7085 else if ( pLow->GetDrawObjs() )
7087 const SwSortedObjs& rObjs = *(pLow->GetDrawObjs());
7088 for (SwAnchoredObject* pAnchoredObj : rObjs)
7090 if ( pPage->GetFormat()->GetDoc()->getIDocumentDrawModelAccess().IsVisibleLayerId(
7091 pAnchoredObj->GetDrawObj()->GetLayer() ) )
7092 if (auto pFly = pAnchoredObj->DynCastFlyFrame() )
7094 if ( pFly->IsFlyInContentFrame() && pFly->getFrameArea().Overlaps( rRect ) )
7096 SwFrame* pLower = pFly->Lower();
7097 if ( !pLower || !pLower->IsNoTextFrame() ||
7098 !static_cast<const SwNoTextFrame*>(pLower)->HasAnimation())
7099 pFly->RefreshLaySubsidiary( pPage, rRect );
7105 pLow = pLow->GetNext();
7110 * Subsidiary lines to paint the PrtAreas
7111 * Only the LayoutFrames which directly contain Content
7112 * Paints the desired line and pays attention to not overpaint any flys
7114 static void lcl_RefreshLine( const SwLayoutFrame *pLay,
7115 const SwPageFrame *pPage,
7116 const Point &rP1,
7117 const Point &rP2,
7118 const SubColFlags nSubColor,
7119 SwLineRects* pSubsLines )
7121 //In which direction do we loop? Can only be horizontal or vertical.
7122 OSL_ENSURE( ((rP1.X() == rP2.X()) || (rP1.Y() == rP2.Y())),
7123 "Sloped subsidiary lines are not allowed." );
7125 const bool bHori = rP1.Y() == rP2.Y();
7127 // use pointers to member function in order to unify flow
7128 typedef tools::Long (Point::*pmfPtGet)() const;
7129 typedef void (Point::*pmfPtSet)(tools::Long);
7130 const pmfPtGet pDirPtX = &Point::X;
7131 const pmfPtGet pDirPtY = &Point::Y;
7132 const pmfPtGet pDirPt = bHori ? pDirPtX : pDirPtY;
7133 const pmfPtSet pDirPtSetX = &Point::setX;
7134 const pmfPtSet pDirPtSetY = &Point::setY;
7135 const pmfPtSet pDirPtSet = bHori ? pDirPtSetX : pDirPtSetY;
7137 Point aP1( rP1 );
7138 Point aP2( rP2 );
7140 while ( (aP1.*pDirPt)() < (aP2.*pDirPt)() )
7142 //If the starting point lies in a fly, it is directly set behind the
7143 //fly.
7144 //The end point moves to the start if the end point lies in a fly or we
7145 //have a fly between starting point and end point.
7146 // In this way, every position is output one by one.
7148 //If I'm a fly I'll only avoid those flys which are places 'above' me;
7149 //this means those who are behind me in the array.
7150 //Even if I'm inside a fly or inside a fly inside a fly a.s.o I won't
7151 //avoid any of those flys.
7152 SwOrderIter aIter( pPage );
7153 const SwFlyFrame *pMyFly = pLay->FindFlyFrame();
7154 if ( pMyFly )
7156 aIter.Current( pMyFly->GetVirtDrawObj() );
7157 while ( nullptr != (pMyFly = pMyFly->GetAnchorFrame()->FindFlyFrame()) )
7159 if ( aIter()->GetOrdNum() > pMyFly->GetVirtDrawObj()->GetOrdNum() )
7160 aIter.Current( pMyFly->GetVirtDrawObj() );
7163 else
7164 aIter.Bottom();
7166 while ( aIter() )
7168 const SwVirtFlyDrawObj *pObj = static_cast<const SwVirtFlyDrawObj*>(aIter());
7169 const SwFlyFrame *pFly = pObj ? pObj->GetFlyFrame() : nullptr;
7171 //I certainly won't avoid myself, even if I'm placed _inside_ the
7172 //fly I won't avoid it.
7173 if ( !pFly || (pFly == pLay || pFly->IsAnLower( pLay )) )
7175 aIter.Next();
7176 continue;
7179 // do *not* consider fly frames with a transparent background.
7180 // do *not* consider fly frame, which belongs to an invisible layer
7181 if ( pFly->IsBackgroundTransparent() ||
7182 !pFly->GetFormat()->GetDoc()->getIDocumentDrawModelAccess().IsVisibleLayerId( pObj->GetLayer() ) )
7184 aIter.Next();
7185 continue;
7188 //Is the Obj placed on the line
7189 const tools::Long nP1OthPt = !bHori ? rP1.X() : rP1.Y();
7190 const tools::Rectangle &rBound = pObj->GetCurrentBoundRect();
7191 const Point aDrPt( rBound.TopLeft() );
7192 const tools::Long nDrOthPt = !bHori ? aDrPt.X() : aDrPt.Y();
7193 const Size aDrSz( rBound.GetSize() );
7194 const tools::Long nDrOthSz = !bHori ? aDrSz.Width() : aDrSz.Height();
7196 if ( nP1OthPt >= nDrOthPt && nP1OthPt <= nDrOthPt + nDrOthSz )
7198 const tools::Long nDrDirPt = bHori ? aDrPt.X() : aDrPt.Y();
7199 const tools::Long nDrDirSz = bHori ? aDrSz.Width() : aDrSz.Height();
7201 if ( (aP1.*pDirPt)() >= nDrDirPt && (aP1.*pDirPt)() <= nDrDirPt + nDrDirSz )
7202 (aP1.*pDirPtSet)( nDrDirPt + nDrDirSz );
7204 if ( (aP2.*pDirPt)() >= nDrDirPt && (aP1.*pDirPt)() < (nDrDirPt - 1) )
7205 (aP2.*pDirPtSet)( nDrDirPt - 1 );
7207 aIter.Next();
7210 if ( (aP1.*pDirPt)() < (aP2.*pDirPt)() )
7212 SwRect aRect( aP1, aP2 );
7213 // use parameter <pSubsLines> instead of global variable <gProp.pSSubsLines>.
7214 pSubsLines->AddLineRect( aRect, nullptr, SvxBorderLineStyle::SOLID,
7215 nullptr, nSubColor, gProp );
7217 aP1 = aP2;
7218 (aP1.*pDirPtSet)( (aP1.*pDirPt)() + 1 );
7219 aP2 = rP2;
7223 static std::vector<basegfx::B2DPolygon> lcl_CreatePageAreaDelimiterPolygons(const SwRect& rRect, bool bHeaderFooter)
7225 std::vector<basegfx::B2DPolygon> aPolygons;
7227 // Hide text boundaries by default - cool#3491
7228 if (!bHeaderFooter && comphelper::LibreOfficeKit::isActive())
7229 return aPolygons;
7231 double nLineLength = 200.0; // in Twips
7233 Point aPoints[] = { rRect.TopLeft(), rRect.TopRight(), rRect.BottomRight(), rRect.BottomLeft() };
7234 double const aXOffDirs[] = { -1.0, 1.0, 1.0, -1.0 };
7235 double const aYOffDirs[] = { -1.0, -1.0, 1.0, 1.0 };
7237 // Actually loop over the corners to create the two lines
7238 for ( int i = 0; i < 4; i++ )
7240 basegfx::B2DVector aHorizVector( aXOffDirs[i], 0.0 );
7241 basegfx::B2DVector aVertVector( 0.0, aYOffDirs[i] );
7243 basegfx::B2DPoint aBPoint( aPoints[i].getX(), aPoints[i].getY() );
7245 basegfx::B2DPolygon aPolygon;
7246 aPolygon.append( aBPoint + aHorizVector * nLineLength );
7247 aPolygon.append( aBPoint );
7248 aPolygon.append( aBPoint + aVertVector * nLineLength );
7250 aPolygons.emplace_back(aPolygon);
7253 return aPolygons;
7256 static drawinglayer::primitive2d::Primitive2DContainer lcl_CreateDelimiterPrimitives(
7257 const std::vector<basegfx::B2DPolygon>& rPolygons)
7259 drawinglayer::primitive2d::Primitive2DContainer aSeq(rPolygons.size());
7261 basegfx::BColor aLineColor = SwViewOption::GetCurrentViewOptions().GetDocBoundariesColor().getBColor();
7262 for (size_t i = 0; i < rPolygons.size(); ++i)
7263 aSeq[i] = new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(rPolygons[i], aLineColor);
7265 return aSeq;
7268 static std::vector<basegfx::B2DPolygon> lcl_CreateRectangleDelimiterPolygons(const SwRect& rRect)
7270 std::vector<basegfx::B2DPolygon> aRet(1);
7271 aRet[0].append( basegfx::B2DPoint( rRect.Left(), rRect.Top() ) );
7272 aRet[0].append( basegfx::B2DPoint( rRect.Right(), rRect.Top() ) );
7273 aRet[0].append( basegfx::B2DPoint( rRect.Right(), rRect.Bottom() ) );
7274 aRet[0].append( basegfx::B2DPoint( rRect.Left(), rRect.Bottom() ) );
7275 aRet[0].setClosed( true );
7276 return aRet;
7279 static drawinglayer::primitive2d::Primitive2DContainer lcl_CreateRectangleDelimiterPrimitives (
7280 const SwRect& rRect )
7282 return lcl_CreateDelimiterPrimitives(lcl_CreateRectangleDelimiterPolygons(rRect));
7285 static drawinglayer::primitive2d::Primitive2DContainer lcl_CreateColumnAreaDelimiterPrimitives(
7286 const SwRect& rRect )
7288 drawinglayer::primitive2d::Primitive2DContainer aSeq( 4 );
7290 basegfx::BColor aLineColor = SwViewOption::GetCurrentViewOptions().GetDocBoundariesColor().getBColor();
7291 double nLineLength = 100.0; // in Twips
7293 Point aPoints[] = { rRect.TopLeft(), rRect.TopRight(), rRect.BottomRight(), rRect.BottomLeft() };
7294 double const aXOffDirs[] = { 1.0, -1.0, -1.0, 1.0 };
7295 double const aYOffDirs[] = { 1.0, 1.0, -1.0, -1.0 };
7297 // Actually loop over the corners to create the two lines
7298 for ( int i = 0; i < 4; i++ )
7300 basegfx::B2DVector aHorizVector( aXOffDirs[i], 0.0 );
7301 basegfx::B2DVector aVertVector( 0.0, aYOffDirs[i] );
7303 basegfx::B2DPoint aBPoint( aPoints[i].getX(), aPoints[i].getY() );
7305 basegfx::B2DPolygon aPolygon;
7306 aPolygon.append( aBPoint + aHorizVector * nLineLength );
7307 aPolygon.append( aBPoint );
7308 aPolygon.append( aBPoint + aVertVector * nLineLength );
7310 aSeq[i] = new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(
7311 std::move(aPolygon), aLineColor );
7314 return aSeq;
7317 std::vector<basegfx::B2DPolygon> SwPageFrame::GetSubsidiaryLinesPolygons(const SwViewShell& rViewShell) const
7319 std::vector<basegfx::B2DPolygon> aPolygons;
7321 if (!rViewShell.GetViewOptions()->IsTextBoundaries())
7322 return aPolygons;
7324 const SwFrame* pLay = Lower();
7325 const SwFrame* pFootnoteCont = nullptr;
7326 const SwFrame* pPageBody = nullptr;
7328 if (!pLay)
7329 return aPolygons;
7331 while ( pLay && !( pFootnoteCont && pPageBody ) )
7333 if ( pLay->IsFootnoteContFrame( ) )
7334 pFootnoteCont = pLay;
7335 if ( pLay->IsBodyFrame() )
7336 pPageBody = pLay;
7337 pLay = pLay->GetNext();
7340 if (!pPageBody)
7341 return aPolygons;
7343 SwRect aArea( pPageBody->getFrameArea() );
7344 if ( pFootnoteCont )
7345 aArea.AddBottom( pFootnoteCont->getFrameArea().Bottom() - aArea.Bottom() );
7347 if (aArea.IsEmpty())
7348 return aPolygons;
7350 if (!rViewShell.GetViewOptions()->IsTextBoundariesFull())
7351 aPolygons = lcl_CreatePageAreaDelimiterPolygons(aArea, false /* body */);
7352 else
7353 aPolygons = lcl_CreateRectangleDelimiterPolygons(aArea);
7355 return aPolygons;
7358 void SwPageFrame::PaintSubsidiaryLines(const SwPageFrame*, const SwRect&) const
7360 if (gProp.pSGlobalShell->IsHeaderFooterEdit())
7361 return;
7363 std::vector<basegfx::B2DPolygon> aPolygons = GetSubsidiaryLinesPolygons(*gProp.pSGlobalShell);
7364 if (aPolygons.empty())
7365 return;
7367 ProcessPrimitives(lcl_CreateDelimiterPrimitives(aPolygons));
7370 static void lclAddSubsidiaryLinesBounds(const std::vector<basegfx::B2DPolygon>& rPolygons, RectangleVector& rRects)
7372 for (const auto& rPolygon : rPolygons)
7374 tools::Rectangle aRect(vcl::unotools::rectangleFromB2DRectangle(rPolygon.getB2DRange()));
7375 aRect.expand(1);
7376 if (basegfx::utils::isRectangle(rPolygon) && aRect.GetWidth() > 4 && aRect.GetHeight() > 4)
7378 // turn hairline rectangle into four non-overlapping blocks that cover the borders
7379 rRects.emplace_back(tools::Rectangle(Point(aRect.Left(), aRect.Top()), Size(aRect.GetWidth(), 2)));
7380 rRects.emplace_back(tools::Rectangle(Point(aRect.Left(), aRect.Top() + 2), Size(2, aRect.GetHeight() - 4)));
7381 rRects.emplace_back(tools::Rectangle(Point(aRect.Right() - 2, aRect.Top() + 2), Size(2, aRect.GetHeight() - 4)));
7382 rRects.emplace_back(tools::Rectangle(Point(aRect.Left(), aRect.Top() + aRect.GetHeight() - 2), Size(aRect.GetWidth(), 2)));
7384 else
7385 rRects.emplace_back(aRect);
7389 void SwPageFrame::AddSubsidiaryLinesBounds(const SwViewShell& rViewShell, RectangleVector& rRects) const
7391 lclAddSubsidiaryLinesBounds(GetSubsidiaryLinesPolygons(rViewShell), rRects);
7393 const SwFrame *pLow = Lower();
7394 while (pLow)
7396 if (pLow->getFrameArea().HasArea())
7398 if (pLow->IsHeaderFrame() || pLow->IsFooterFrame())
7400 static_cast<const SwHeadFootFrame*>(pLow)->AddSubsidiaryLinesBounds(rViewShell, rRects);
7403 pLow = pLow->GetNext();
7407 void SwColumnFrame::PaintSubsidiaryLines( const SwPageFrame *,
7408 const SwRect & ) const
7410 const SwFrame* pLay = Lower();
7411 const SwFrame* pFootnoteCont = nullptr;
7412 const SwFrame* pColBody = nullptr;
7413 while ( pLay && !( pFootnoteCont && pColBody ) )
7415 if ( pLay->IsFootnoteContFrame( ) )
7416 pFootnoteCont = pLay;
7417 if ( pLay->IsBodyFrame() )
7418 pColBody = pLay;
7419 pLay = pLay->GetNext();
7422 assert(pColBody && "presumably this is impossible");
7424 SwRect aArea( pColBody->getFrameArea() );
7426 // #i3662# - enlarge top of column body frame's printing area
7427 // in sections to top of section frame.
7428 const bool bColInSection = GetUpper()->IsSctFrame();
7429 if ( bColInSection )
7431 if ( IsVertical() )
7432 aArea.Right( GetUpper()->getFrameArea().Right() );
7433 else
7434 aArea.Top( GetUpper()->getFrameArea().Top() );
7437 if ( pFootnoteCont )
7438 aArea.AddBottom( pFootnoteCont->getFrameArea().Bottom() - aArea.Bottom() );
7440 ::SwAlignRect( aArea, gProp.pSGlobalShell, gProp.pSGlobalShell->GetOut() );
7442 if ( !gProp.pSGlobalShell->GetViewOptions()->IsTextBoundariesFull( ) )
7443 ProcessPrimitives( lcl_CreateColumnAreaDelimiterPrimitives( aArea ) );
7444 else
7445 ProcessPrimitives( lcl_CreateRectangleDelimiterPrimitives( aArea ) );
7448 void SwSectionFrame::PaintSubsidiaryLines( const SwPageFrame * pPage,
7449 const SwRect & rRect ) const
7451 if (!gProp.pSGlobalShell->GetViewOptions()->IsSectionBoundaries())
7452 return;
7454 SwLayoutFrame::PaintSubsidiaryLines( pPage, rRect );
7458 * The SwBodyFrame doesn't print any subsidiary line: it's bounds are painted
7459 * either by the parent page or the parent column frame.
7461 void SwBodyFrame::PaintSubsidiaryLines( const SwPageFrame *,
7462 const SwRect & ) const
7466 std::vector<basegfx::B2DPolygon> SwHeadFootFrame::GetSubsidiaryLinesPolygons(const SwViewShell& rViewShell) const
7468 std::vector<basegfx::B2DPolygon> aPolygons;
7470 if (!rViewShell.GetViewOptions()->IsTextBoundaries())
7471 return aPolygons;
7473 SwRect aArea( getFramePrintArea() );
7474 aArea.Pos() += getFrameArea().Pos();
7475 if (!rViewShell.GetViewOptions()->IsTextBoundariesFull( ))
7476 aPolygons = lcl_CreatePageAreaDelimiterPolygons(aArea, true /* header/footer*/);
7477 else
7478 aPolygons = lcl_CreateRectangleDelimiterPolygons(aArea);
7480 return aPolygons;
7483 void SwHeadFootFrame::PaintSubsidiaryLines(const SwPageFrame*, const SwRect&) const
7485 if (!gProp.pSGlobalShell->IsHeaderFooterEdit())
7486 return;
7488 std::vector<basegfx::B2DPolygon> aPolygons = GetSubsidiaryLinesPolygons(*gProp.pSGlobalShell);
7489 if (aPolygons.empty())
7490 return;
7492 ProcessPrimitives(lcl_CreateDelimiterPrimitives(aPolygons));
7495 void SwHeadFootFrame::AddSubsidiaryLinesBounds(const SwViewShell& rViewShell, RectangleVector& rRects) const
7497 lclAddSubsidiaryLinesBounds(GetSubsidiaryLinesPolygons(rViewShell), rRects);
7501 * This method is overridden in order to have no subsidiary lines
7502 * around the footnotes.
7504 void SwFootnoteFrame::PaintSubsidiaryLines( const SwPageFrame *,
7505 const SwRect & ) const
7510 * This method is overridden in order to have no subsidiary lines
7511 * around the footnotes containers.
7513 void SwFootnoteContFrame::PaintSubsidiaryLines( const SwPageFrame *,
7514 const SwRect & ) const
7518 void SwLayoutFrame::PaintSubsidiaryLines( const SwPageFrame *pPage,
7519 const SwRect &rRect ) const
7521 bool bNewTableModel = false;
7523 // #i29550#
7524 if ( IsTabFrame() || IsCellFrame() || IsRowFrame() )
7526 const SwTabFrame* pTabFrame = FindTabFrame();
7527 if ( pTabFrame->IsCollapsingBorders() )
7528 return;
7530 bNewTableModel = pTabFrame->GetTable()->IsNewModel();
7531 // in the new table model, we have an early return for all cell-related
7532 // frames, except from non-covered table cells
7533 if ( bNewTableModel )
7534 if ( IsTabFrame() ||
7535 IsRowFrame() ||
7536 ( IsCellFrame() && IsCoveredCell() ) )
7537 return;
7540 const bool bFlys = pPage->GetSortedObjs() != nullptr;
7542 const bool bCell = IsCellFrame();
7544 if ( (IsSctFrame() || IsFlyFrame()) &&
7545 !gProp.pSGlobalShell->GetViewOptions()->IsSectionBoundaries() )
7546 return;
7547 if ( IsTextFrame() &&
7548 !gProp.pSGlobalShell->GetViewOptions()->IsTextBoundaries() )
7549 return;
7551 // #i3662# - use frame area for cells for section use also frame area
7552 const bool bUseFrameArea = bCell || IsSctFrame();
7553 SwRect aOriginal( bUseFrameArea ? getFrameArea() : getFramePrintArea() );
7554 if ( !bUseFrameArea )
7555 aOriginal.Pos() += getFrameArea().Pos();
7557 ::SwAlignRect( aOriginal, gProp.pSGlobalShell, gProp.pSGlobalShell->GetOut() );
7559 if ( !aOriginal.Overlaps( rRect ) )
7560 return;
7562 SwRect aOut( aOriginal );
7563 aOut.Intersection_( rRect );
7565 const SwTwips nRight = aOut.Right();
7566 const SwTwips nBottom= aOut.Bottom();
7568 const Point aRT( nRight, aOut.Top() );
7569 const Point aRB( nRight, nBottom );
7570 const Point aLB( aOut.Left(), nBottom );
7572 SubColFlags nSubColor = ( bCell || IsRowFrame() )
7573 ? SubColFlags::Tab
7574 : ( IsInSct()
7575 ? SubColFlags::Sect
7576 : ( IsInFly() ? SubColFlags::Fly : SubColFlags::Page ) );
7578 // collect body, header, footer, footnote and section
7579 // sub-lines in <pSpecSubsLine> array.
7580 const bool bSpecialSublines = IsBodyFrame() || IsHeaderFrame() || IsFooterFrame() ||
7581 IsFootnoteFrame() || IsSctFrame();
7582 SwLineRects *const pUsedSubsLines = bSpecialSublines
7583 ? gProp.pSSpecSubsLines.get() : gProp.pSSubsLines.get();
7585 // NOTE: for cell frames only left and right (horizontal layout) respectively
7586 // top and bottom (vertical layout) lines painted.
7587 // NOTE2: this does not hold for the new table model!!! We paint the top border
7588 // of each non-covered table cell.
7589 const bool bVert = IsVertical();
7590 if ( bFlys )
7592 // add control for drawing left and right lines
7593 if ( !bCell || bNewTableModel || !bVert )
7595 if ( aOriginal.Left() == aOut.Left() )
7596 ::lcl_RefreshLine( this, pPage, aOut.Pos(), aLB, nSubColor, pUsedSubsLines );
7597 // in vertical layout set page/column break at right
7598 if ( aOriginal.Right() == nRight )
7599 ::lcl_RefreshLine( this, pPage, aRT, aRB, nSubColor, pUsedSubsLines );
7601 // adjust control for drawing top and bottom lines
7602 if ( !bCell || bNewTableModel || bVert )
7604 if ( aOriginal.Top() == aOut.Top() )
7605 // in horizontal layout set page/column break at top
7606 ::lcl_RefreshLine( this, pPage, aOut.Pos(), aRT, nSubColor, pUsedSubsLines );
7607 if ( aOriginal.Bottom() == nBottom )
7608 ::lcl_RefreshLine( this, pPage, aLB, aRB, nSubColor,
7609 pUsedSubsLines );
7612 else
7614 // add control for drawing left and right lines
7615 if ( !bCell || bNewTableModel || !bVert )
7617 if ( aOriginal.Left() == aOut.Left() )
7619 const SwRect aRect( aOut.Pos(), aLB );
7620 pUsedSubsLines->AddLineRect( aRect, nullptr,
7621 SvxBorderLineStyle::SOLID, nullptr, nSubColor, gProp );
7623 // in vertical layout set page/column break at right
7624 if ( aOriginal.Right() == nRight )
7626 const SwRect aRect( aRT, aRB );
7627 pUsedSubsLines->AddLineRect( aRect, nullptr,
7628 SvxBorderLineStyle::SOLID, nullptr, nSubColor, gProp );
7631 // adjust control for drawing top and bottom lines
7632 if ( !bCell || bNewTableModel || bVert )
7634 if ( aOriginal.Top() == aOut.Top() )
7636 // in horizontal layout set page/column break at top
7637 const SwRect aRect( aOut.Pos(), aRT );
7638 pUsedSubsLines->AddLineRect( aRect, nullptr,
7639 SvxBorderLineStyle::SOLID, nullptr, nSubColor, gProp );
7641 if ( aOriginal.Bottom() == nBottom )
7643 const SwRect aRect( aLB, aRB );
7644 pUsedSubsLines->AddLineRect( aRect, nullptr,
7645 SvxBorderLineStyle::SOLID, nullptr, nSubColor, gProp );
7652 * Refreshes all extra data (line breaks a.s.o) of the page. Basically only those objects
7653 * are considered which horizontally overlap the Rect.
7655 void SwPageFrame::RefreshExtraData( const SwRect &rRect ) const
7657 const SwLineNumberInfo &rInfo = GetFormat()->GetDoc()->GetLineNumberInfo();
7658 bool bLineInFly = (rInfo.IsPaintLineNumbers() && rInfo.IsCountInFlys())
7659 || static_cast<sal_Int16>(SwModule::get()->GetRedlineMarkPos()) != text::HoriOrientation::NONE;
7661 SwRect aRect( rRect );
7662 ::SwAlignRect( aRect, gProp.pSGlobalShell, gProp.pSGlobalShell->GetOut() );
7663 if ( !aRect.HasArea() )
7664 return;
7666 SwLayoutFrame::RefreshExtraData( aRect );
7668 if ( bLineInFly && GetSortedObjs() )
7669 for (SwAnchoredObject* pAnchoredObj : *GetSortedObjs())
7671 if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
7673 if ( pFly->getFrameArea().Top() <= aRect.Bottom() &&
7674 pFly->getFrameArea().Bottom() >= aRect.Top() )
7675 pFly->RefreshExtraData( aRect );
7680 void SwLayoutFrame::RefreshExtraData( const SwRect &rRect ) const
7683 const SwLineNumberInfo &rInfo = GetFormat()->GetDoc()->GetLineNumberInfo();
7684 bool bLineInBody = rInfo.IsPaintLineNumbers(),
7685 bLineInFly = bLineInBody && rInfo.IsCountInFlys(),
7686 bRedLine = static_cast<sal_Int16>(SwModule::get()->GetRedlineMarkPos())!=text::HoriOrientation::NONE;
7688 const SwContentFrame *pCnt = ContainsContent();
7689 while ( pCnt && IsAnLower( pCnt ) )
7691 if ( pCnt->IsTextFrame() && ( bRedLine ||
7692 ( !pCnt->IsInTab() &&
7693 ((bLineInBody && pCnt->IsInDocBody()) ||
7694 (bLineInFly && pCnt->IsInFly())) ) ) &&
7695 pCnt->getFrameArea().Top() <= rRect.Bottom() &&
7696 pCnt->getFrameArea().Bottom() >= rRect.Top() )
7698 static_cast<const SwTextFrame*>(pCnt)->PaintExtraData( rRect );
7700 if ( bLineInFly && pCnt->GetDrawObjs() )
7701 for (SwAnchoredObject* pAnchoredObj : *pCnt->GetDrawObjs())
7703 if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
7705 if ( pFly->IsFlyInContentFrame() &&
7706 pFly->getFrameArea().Top() <= rRect.Bottom() &&
7707 pFly->getFrameArea().Bottom() >= rRect.Top() )
7708 pFly->RefreshExtraData( rRect );
7711 pCnt = pCnt->GetNextContentFrame();
7716 * For #102450#
7717 * Determine the color, that is respectively will be drawn as background
7718 * for the page frame.
7719 * Using existing method SwFrame::GetBackgroundBrush to determine the color
7720 * that is set at the page frame respectively is parent. If none is found
7721 * return the global retouche color
7723 * @return Color
7725 Color SwPageFrame::GetDrawBackgroundColor() const
7727 const SvxBrushItem* pBrushItem;
7728 std::optional<Color> xDummyColor;
7729 SwRect aDummyRect;
7730 drawinglayer::attribute::SdrAllFillAttributesHelperPtr aFillAttributes;
7732 if ( GetBackgroundBrush( aFillAttributes, pBrushItem, xDummyColor, aDummyRect, true, /*bConsiderTextBox=*/false) )
7734 if(aFillAttributes && aFillAttributes->isUsed())
7736 // let SdrAllFillAttributesHelper do the average color calculation
7737 return Color(aFillAttributes->getAverageColor(aGlobalRetoucheColor.getBColor()));
7739 else if(pBrushItem)
7741 OUString referer;
7742 SwViewShell * sh1 = getRootFrame()->GetCurrShell();
7743 if (sh1 != nullptr) {
7744 SfxObjectShell * sh2 = sh1->GetDoc()->GetPersist();
7745 if (sh2 != nullptr && sh2->HasName()) {
7746 referer = sh2->GetMedium()->GetName();
7749 const Graphic* pGraphic = pBrushItem->GetGraphic(referer);
7751 if(pGraphic)
7753 // #29105# when a graphic is set, it may be possible to calculate a single
7754 // color which looks good in all places of the graphic. Since it is
7755 // planned to have text edit on the overlay one day and the fallback
7756 // to aGlobalRetoucheColor returns something useful, just use that
7757 // for now.
7759 else
7761 // not a graphic, use (hopefully) initialized color
7762 return pBrushItem->GetColor();
7767 return aGlobalRetoucheColor;
7770 /// create/return font used to paint the "empty page" string
7771 const vcl::Font& SwPageFrame::GetEmptyPageFont()
7773 static vcl::Font aEmptyPgFont = []()
7775 vcl::Font tmp;
7776 tmp.SetFontSize( Size( 0, 80 * 20 )); // == 80 pt
7777 tmp.SetWeight( WEIGHT_BOLD );
7778 tmp.SetStyleName(OUString());
7779 tmp.SetFamilyName(u"Noto Sans"_ustr);
7780 tmp.SetFamily( FAMILY_SWISS );
7781 tmp.SetTransparent( true );
7782 tmp.SetColor( COL_GRAY );
7783 return tmp;
7784 }();
7786 return aEmptyPgFont;
7790 * Retouch for a section
7792 * Retouch will only be done, if the Frame is the last one in his chain.
7793 * The whole area of the upper which is located below the Frame will be
7794 * cleared using PaintSwFrameBackground.
7796 void SwFrame::Retouch( const SwPageFrame * pPage, const SwRect &rRect ) const
7798 if ( gProp.bSFlyMetafile )
7799 return;
7801 OSL_ENSURE( GetUpper(), "Retouche try without Upper." );
7802 OSL_ENSURE( getRootFrame()->GetCurrShell() && gProp.pSGlobalShell->GetWin(), "Retouche on a printer?" );
7804 SwRect aRetouche( GetUpper()->GetPaintArea() );
7805 aRetouche.Top( getFrameArea().Top() + getFrameArea().Height() );
7806 aRetouche.Intersection( gProp.pSGlobalShell->VisArea() );
7808 if ( aRetouche.HasArea() )
7810 //Omit the passed Rect. To do this, we unfortunately need a region to
7811 //cut out.
7812 SwRegionRects aRegion( aRetouche );
7813 aRegion -= rRect;
7814 SwViewShell *pSh = getRootFrame()->GetCurrShell();
7816 // #i16816# tagged pdf support
7817 SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, nullptr, *pSh->GetOut() );
7819 for ( size_t i = 0; i < aRegion.size(); ++i )
7821 const SwRect &rRetouche = aRegion[i];
7823 GetUpper()->PaintBaBo( rRetouche, pPage );
7825 //Hell and Heaven need to be refreshed too.
7826 //To avoid recursion my retouch flag needs to be reset first!
7827 ResetRetouche();
7828 if ( rRetouche.HasArea() )
7830 const Color aPageBackgrdColor(pPage->GetDrawBackgroundColor());
7831 const IDocumentDrawModelAccess& rIDDMA = pSh->getIDocumentDrawModelAccess();
7832 // --> OD #i76669#
7833 SwViewObjectContactRedirector aSwRedirector( *pSh );
7834 // <--
7836 pSh->Imp()->PaintLayer( rIDDMA.GetHellId(), nullptr,
7837 *pPage, rRetouche, &aPageBackgrdColor,
7838 pPage->IsRightToLeft(),
7839 &aSwRedirector );
7840 pSh->Imp()->PaintLayer( rIDDMA.GetHeavenId(), nullptr,
7841 *pPage, rRetouche, &aPageBackgrdColor,
7842 pPage->IsRightToLeft(),
7843 &aSwRedirector );
7846 SetRetouche();
7848 //Because we leave all paint areas, we need to refresh the
7849 //subsidiary lines.
7850 pPage->RefreshSubsidiary( rRetouche );
7853 if ( SwViewShell::IsLstEndAction() )
7854 ResetRetouche();
7858 * Determine the background brush for the frame:
7859 * the background brush is taken from it-self or from its parent (anchor/upper).
7860 * Normally, the background brush is taken, which has no transparent color or
7861 * which has a background graphic. But there are some special cases:
7862 * (1) No background brush is taken from a page frame, if view option "IsPageBack"
7863 * isn't set.
7864 * (2) Background brush from an index section is taken under special conditions.
7865 * In this case parameter <rpCol> is set to the index shading color.
7866 * (3) New (OD 20.08.2002) - Background brush is taken, if on background drawing
7867 * of the frame transparency is considered and its color is not "no fill"/"auto fill"
7869 * Old description in German:
7870 * Returns the Backgroundbrush for the area of the Frame.
7871 * The Brush is defined by the Frame or by an upper, the first Brush is
7872 * used. If no Brush is defined for a Frame, false is returned.
7874 * @param rpBrush
7875 * output parameter - constant reference pointer the found background brush
7877 * @param rpFillStyle
7878 * output parameter - constant reference pointer the found background fill style
7880 * @param rpFillGradient
7881 * output parameter - constant reference pointer the found background fill gradient
7883 * @param rpCol
7884 * output parameter - constant reference pointer to the color of the index shading
7885 * set under special conditions, if background brush is taken from an index section.
7887 * @param rOrigRect
7888 * in-/output parameter - reference to the rectangle the background brush is
7889 * considered for - adjusted to the frame, from which the background brush is
7890 * taken.
7892 * @param bLowerMode
7893 * input parameter - boolean indicating, if background brush should *not* be
7894 * taken from parent.
7896 * @param bConsiderTextBox
7897 * consider the TextBox of this fly frame (if there is any) when determining
7898 * the background color, useful for automatic font color.
7900 * @return true, if a background brush for the frame is found
7902 bool SwFrame::GetBackgroundBrush(
7903 drawinglayer::attribute::SdrAllFillAttributesHelperPtr& rFillAttributes,
7904 const SvxBrushItem* & rpBrush,
7905 std::optional<Color>& rxCol,
7906 SwRect &rOrigRect,
7907 bool bLowerMode,
7908 bool bConsiderTextBox ) const
7910 const SwFrame *pFrame = this;
7911 SwViewShell *pSh = getRootFrame()->GetCurrShell();
7912 const SwViewOption *pOpt = pSh->GetViewOptions();
7913 rpBrush = nullptr;
7914 rxCol.reset();
7917 if ( pFrame->IsPageFrame() && !pOpt->IsPageBack() )
7918 return false;
7920 if (pFrame->supportsFullDrawingLayerFillAttributeSet())
7922 bool bHandledTextBox = false;
7923 if (pFrame->IsFlyFrame() && bConsiderTextBox)
7925 const SwFlyFrame* pFlyFrame = static_cast<const SwFlyFrame*>(pFrame);
7926 SwFrameFormat* pShape
7927 = SwTextBoxHelper::getOtherTextBoxFormat(pFlyFrame->GetFormat(), RES_FLYFRMFMT);
7928 if (pShape)
7930 SdrObject* pObject = pShape->FindRealSdrObject();
7931 if (pObject)
7933 // Work with the fill attributes of the shape of the fly frame.
7934 rFillAttributes =
7935 std::make_shared<drawinglayer::attribute::SdrAllFillAttributesHelper>(
7936 pObject->GetMergedItemSet());
7937 bHandledTextBox = true;
7942 if (!bHandledTextBox)
7943 rFillAttributes = pFrame->getSdrAllFillAttributesHelper();
7945 const SvxBrushItem &rBack = pFrame->GetAttrSet()->GetBackground();
7947 if( pFrame->IsSctFrame() )
7949 const SwSection* pSection = static_cast<const SwSectionFrame*>(pFrame)->GetSection();
7950 // Note: If frame <pFrame> is a section of the index and
7951 // it its background color is "no fill"/"auto fill" and
7952 // it has no background graphic and
7953 // we are not in the page preview and
7954 // we are not in read-only mode and
7955 // option "index shadings" is set and
7956 // the output is not the printer
7957 // then set <rpCol> to the color of the index shading
7958 if( pSection && ( SectionType::ToxHeader == pSection->GetType() ||
7959 SectionType::ToxContent == pSection->GetType() ) &&
7960 (rBack.GetColor() == COL_TRANSPARENT) &&
7961 rBack.GetGraphicPos() == GPOS_NONE &&
7962 !pOpt->IsPagePreview() &&
7963 !pOpt->IsReadonly() &&
7964 // #114856# Form view
7965 !pOpt->IsFormView() &&
7966 pOpt->IsIndexShadings() &&
7967 !pOpt->IsPDFExport() &&
7968 pSh->GetOut()->GetOutDevType() != OUTDEV_PRINTER )
7970 rxCol = pOpt->GetIndexShadingsColor();
7974 // determine, if background draw of frame <pFrame> considers transparency
7975 // Status Quo: background transparency have to be
7976 // considered for fly frames
7977 const bool bConsiderBackgroundTransparency = pFrame->IsFlyFrame();
7979 // #i125189# Do not base the decision for using the parent's fill style for this
7980 // frame when the new DrawingLayer FillAttributes are used on the SdrAllFillAttributesHelper
7981 // information. There the data is already optimized to no fill in the case that the
7982 // transparence is at 100% while no fill is the criteria for derivation
7983 bool bNewDrawingLayerFillStyleIsUsedAndNotNoFill(false);
7985 if(rFillAttributes)
7987 // the new DrawingLayer FillStyle is used
7988 if(rFillAttributes->isUsed())
7990 // it's not drawing::FillStyle_NONE
7991 bNewDrawingLayerFillStyleIsUsedAndNotNoFill = true;
7993 else
7995 // maybe optimized already when 100% transparency is used somewhere, need to test
7996 // XFillStyleItem directly from the model data
7997 const drawing::FillStyle eFillStyle(pFrame->GetAttrSet()->Get(XATTR_FILLSTYLE).GetValue());
7999 if(drawing::FillStyle_NONE != eFillStyle)
8001 bNewDrawingLayerFillStyleIsUsedAndNotNoFill = true;
8006 // add condition:
8007 // If <bConsiderBackgroundTransparency> is set - see above -,
8008 // return brush of frame <pFrame>, if its color is *not* "no fill"/"auto fill"
8009 if (
8010 // #i125189# Done when the new DrawingLayer FillAttributes are used and
8011 // not drawing::FillStyle_NONE (see above)
8012 bNewDrawingLayerFillStyleIsUsedAndNotNoFill ||
8014 // done when SvxBrushItem is used
8015 rBack.GetColor().GetAlpha() == 255 || rBack.GetGraphicPos() != GPOS_NONE ||
8017 // done when direct color is forced
8018 rxCol ||
8020 // done when consider BG transparency and color is not completely transparent
8021 (bConsiderBackgroundTransparency && (rBack.GetColor() != COL_TRANSPARENT))
8024 rpBrush = &rBack;
8025 if ( pFrame->IsPageFrame() && pSh->GetViewOptions()->getBrowseMode() )
8027 rOrigRect = pFrame->getFrameArea();
8028 ::SwAlignRect(rOrigRect, pSh, pSh->GetOut());
8030 else
8032 if (pFrame->IsPageFrame()
8033 && pFrame->GetAttrSet()->GetItem<SfxBoolItem>(RES_BACKGROUND_FULL_SIZE)->GetValue())
8035 rOrigRect = pFrame->getFrameArea();
8037 else if (pFrame->getFrameArea().SSize() != pFrame->getFramePrintArea().SSize())
8039 SwBorderAttrAccess aAccess( SwFrame::GetCache(), pFrame );
8040 const SwBorderAttrs &rAttrs = *aAccess.Get();
8041 ::lcl_CalcBorderRect( rOrigRect, pFrame, rAttrs, false, gProp );
8043 else
8045 rOrigRect = pFrame->getFramePrintArea();
8046 rOrigRect += pFrame->getFrameArea().Pos();
8050 return true;
8053 if ( bLowerMode )
8055 // Do not try to get background brush from parent (anchor/upper)
8056 return false;
8059 // get parent frame - anchor or upper - for next loop
8060 if ( pFrame->IsFlyFrame() )
8062 pFrame = static_cast<const SwFlyFrame*>(pFrame)->GetAnchorFrame();
8064 else
8066 pFrame = pFrame->GetUpper();
8068 } while ( pFrame );
8070 return false;
8073 void SetOutDevAndWin( SwViewShell *pSh, OutputDevice *pO,
8074 vcl::Window *pW, sal_uInt16 nZoom )
8076 pSh->mpOut = pO;
8077 pSh->mpWin = pW;
8078 pSh->mpOpt->SetZoom( nZoom );
8081 Graphic SwFrameFormat::MakeGraphic( ImageMap*, const sal_uInt32 /*nMaximumQuadraticPixels*/, const std::optional<Size>& /*rTargetDPI*/ )
8083 return Graphic();
8086 Graphic SwFlyFrameFormat::MakeGraphic( ImageMap* pMap, const sal_uInt32 /*nMaximumQuadraticPixels*/, const std::optional<Size>& /*rTargetDPI*/ )
8088 Graphic aRet;
8089 //search any Fly!
8090 SwIterator<SwFrame,SwFormat> aIter( *this );
8091 SwFrame *pFirst = aIter.First();
8092 SwViewShell *const pSh =
8093 pFirst ? pFirst->getRootFrame()->GetCurrShell() : nullptr;
8094 if (nullptr != pSh)
8096 SwViewShell *pOldGlobal = gProp.pSGlobalShell;
8097 gProp.pSGlobalShell = pSh;
8099 bool bNoteURL = pMap &&
8100 SfxItemState::SET != GetAttrSet().GetItemState( RES_URL );
8101 if( bNoteURL )
8103 assert(!pNoteURL);
8104 pNoteURL = new SwNoteURL;
8106 SwFlyFrame *pFly = static_cast<SwFlyFrame*>(pFirst);
8108 OutputDevice *pOld = pSh->GetOut();
8109 ScopedVclPtrInstance< VirtualDevice > pDev( *pOld );
8110 pDev->EnableOutput( false );
8112 GDIMetaFile aMet;
8113 MapMode aMap( pOld->GetMapMode().GetMapUnit() );
8114 pDev->SetMapMode( aMap );
8115 aMet.SetPrefMapMode( aMap );
8117 ::SwCalcPixStatics( pSh->GetOut() );
8118 aMet.SetPrefSize( pFly->getFrameArea().SSize() );
8120 aMet.Record( pDev.get() );
8121 pDev->SetLineColor();
8122 pDev->SetFillColor();
8123 pDev->SetFont( pOld->GetFont() );
8125 //Enlarge the rectangle if needed, so the border is painted too.
8126 SwRect aOut( pFly->getFrameArea() );
8127 SwBorderAttrAccess aAccess( SwFrame::GetCache(), pFly );
8128 const SwBorderAttrs &rAttrs = *aAccess.Get();
8129 if ( rAttrs.CalcRightLine() )
8130 aOut.AddWidth(2*gProp.nSPixelSzW );
8131 if ( rAttrs.CalcBottomLine() )
8132 aOut.AddHeight(2*gProp.nSPixelSzH );
8134 // #i92711# start Pre/PostPaint encapsulation before pOut is changed to the buffering VDev
8135 const vcl::Region aRepaintRegion(aOut.SVRect());
8136 pSh->DLPrePaint2(aRepaintRegion);
8138 vcl::Window *pWin = pSh->GetWin();
8139 sal_uInt16 nZoom = pSh->GetViewOptions()->GetZoom();
8140 ::SetOutDevAndWin( pSh, pDev, nullptr, 100 );
8141 gProp.bSFlyMetafile = true;
8142 gProp.pSFlyMetafileOut = pWin->GetOutDev();
8144 SwViewShellImp *pImp = pSh->Imp();
8145 gProp.pSFlyOnlyDraw = pFly;
8146 gProp.pSLines.reset(new SwLineRects);
8148 // determine page, fly frame is on
8149 const SwPageFrame* pFlyPage = pFly->FindPageFrame();
8150 const Color aPageBackgrdColor(pFlyPage->GetDrawBackgroundColor());
8151 const IDocumentDrawModelAccess& rIDDMA = pSh->getIDocumentDrawModelAccess();
8152 // --> OD #i76669#
8153 SwViewObjectContactRedirector aSwRedirector( *pSh );
8154 // <--
8155 pImp->PaintLayer( rIDDMA.GetHellId(), nullptr,
8156 *pFlyPage, aOut, &aPageBackgrdColor,
8157 pFlyPage->IsRightToLeft(),
8158 &aSwRedirector );
8159 gProp.pSLines->PaintLines( pDev, gProp );
8160 if ( pFly->IsFlyInContentFrame() )
8161 pFly->PaintSwFrame( *pDev, aOut );
8162 gProp.pSLines->PaintLines( pDev, gProp );
8163 pImp->PaintLayer( rIDDMA.GetHeavenId(), nullptr,
8164 *pFlyPage, aOut, &aPageBackgrdColor,
8165 pFlyPage->IsRightToLeft(),
8166 &aSwRedirector );
8167 gProp.pSLines->PaintLines( pDev, gProp );
8168 gProp.pSLines.reset();
8169 gProp.pSFlyOnlyDraw = nullptr;
8171 gProp.pSFlyMetafileOut = nullptr;
8172 gProp.bSFlyMetafile = false;
8173 ::SetOutDevAndWin( pSh, pOld, pWin, nZoom );
8175 // #i92711# end Pre/PostPaint encapsulation when pOut is back and content is painted
8176 pSh->DLPostPaint2(true);
8178 aMet.Stop();
8179 aMet.Move( -pFly->getFrameArea().Left(), -pFly->getFrameArea().Top() );
8180 aRet = Graphic( aMet );
8182 if( bNoteURL )
8184 assert(pNoteURL);
8185 pNoteURL->FillImageMap(pMap, pFly->getFrameArea().Pos(), aMap);
8186 delete pNoteURL;
8187 pNoteURL = nullptr;
8189 gProp.pSGlobalShell = pOldGlobal;
8191 return aRet;
8194 Graphic SwDrawFrameFormat::MakeGraphic( ImageMap*, const sal_uInt32 nMaximumQuadraticPixels, const std::optional<Size>& rTargetDPI )
8196 Graphic aRet;
8197 SwDrawModel* pMod = getIDocumentDrawModelAccess().GetDrawModel();
8198 if ( pMod )
8200 SdrObject *pObj = FindSdrObject();
8201 SdrView aView( *pMod );
8202 SdrPageView *pPgView = aView.ShowSdrPage(aView.GetModel().GetPage(0));
8203 aView.MarkObj( pObj, pPgView );
8204 aRet = aView.GetMarkedObjBitmapEx(/*bNoVDevIfOneBmpMarked=*/false, nMaximumQuadraticPixels, rTargetDPI);
8205 aView.HideSdrPage();
8207 return aRet;
8210 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */