Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / sw / source / core / layout / paintfrm.cxx
blob8242a99634f2b1ca9348c34faef66ede85e063a9
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/lazydelete.hxx>
22 #include <sfx2/docfile.hxx>
23 #include <sfx2/printer.hxx>
24 #include <sfx2/progress.hxx>
25 #include <editeng/brushitem.hxx>
26 #include <editeng/prntitem.hxx>
27 #include <editeng/boxitem.hxx>
28 #include <editeng/shaditem.hxx>
29 #include <svx/ctredlin.hxx>
30 #include <svx/framelink.hxx>
31 #include <drawdoc.hxx>
32 #include <tgrditem.hxx>
33 #include <calbck.hxx>
34 #include <fmtsrnd.hxx>
35 #include <fmtclds.hxx>
36 #include <fmturl.hxx>
37 #include <strings.hrc>
38 #include <swmodule.hxx>
39 #include <rootfrm.hxx>
40 #include <pagefrm.hxx>
41 #include <section.hxx>
42 #include <sectfrm.hxx>
43 #include <viewimp.hxx>
44 #include <dflyobj.hxx>
45 #include <flyfrm.hxx>
46 #include <frmatr.hxx>
47 #include <frmtool.hxx>
48 #include <viewopt.hxx>
49 #include <dview.hxx>
50 #include <dcontact.hxx>
51 #include <txtfrm.hxx>
52 #include <ftnfrm.hxx>
53 #include <tabfrm.hxx>
54 #include <rowfrm.hxx>
55 #include <cellfrm.hxx>
56 #include <notxtfrm.hxx>
57 #include <layact.hxx>
58 #include <pagedesc.hxx>
59 #include <ptqueue.hxx>
60 #include <noteurl.hxx>
61 #include "virtoutp.hxx"
62 #include <lineinfo.hxx>
63 #include <dbg_lay.hxx>
64 #include <docsh.hxx>
65 #include <svx/svdogrp.hxx>
66 #include <sortedobjs.hxx>
67 #include <EnhancedPDFExportHelper.hxx>
68 #include <bodyfrm.hxx>
69 #include <hffrm.hxx>
70 #include <colfrm.hxx>
71 #include <sw_primitivetypes2d.hxx>
72 #include <swfont.hxx>
74 #include <svx/sdr/primitive2d/sdrframeborderprimitive2d.hxx>
75 #include <svx/sdr/contact/viewobjectcontactredirector.hxx>
76 #include <svx/sdr/contact/viewobjectcontact.hxx>
77 #include <svx/sdr/contact/viewcontact.hxx>
78 #include <DocumentSettingManager.hxx>
79 #include <IDocumentDeviceAccess.hxx>
80 #include <IDocumentDrawModelAccess.hxx>
82 #include <ndole.hxx>
83 #include <PostItMgr.hxx>
84 #include <FrameControlsManager.hxx>
85 #include <vcl/settings.hxx>
87 #include <svx/sdr/attribute/sdrallfillattributeshelper.hxx>
89 #include <svtools/borderhelper.hxx>
91 #include <bitmaps.hlst>
92 #include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
93 #include <drawinglayer/primitive2d/PolyPolygonStrokePrimitive2D.hxx>
94 #include <drawinglayer/primitive2d/discreteshadowprimitive2d.hxx>
95 #include <drawinglayer/primitive2d/maskprimitive2d.hxx>
96 #include <drawinglayer/primitive2d/textprimitive2d.hxx>
97 #include <drawinglayer/primitive2d/textlayoutdevice.hxx>
98 #include <drawinglayer/processor2d/baseprocessor2d.hxx>
99 #include <drawinglayer/processor2d/processor2dtools.hxx>
100 #include <svx/unoapi.hxx>
101 #include <svx/svdpagv.hxx>
102 #include <svx/xfillit0.hxx>
103 #include <basegfx/matrix/b2dhommatrixtools.hxx>
104 #include <basegfx/color/bcolortools.hxx>
105 #include <basegfx/utils/b2dclipstate.hxx>
106 #include <sal/log.hxx>
108 #include <memory>
109 #include <vector>
110 #include <algorithm>
111 #include <wrtsh.hxx>
112 #include <edtwin.hxx>
113 #include <view.hxx>
114 #include <paintfrm.hxx>
115 #include <textboxhelper.hxx>
116 #include <o3tl/typed_flags_set.hxx>
118 #include <vcl/BitmapTools.hxx>
119 #include <comphelper/lok.hxx>
120 #include <svtools/optionsdrawinglayer.hxx>
121 #include <vcl/GraphicLoader.hxx>
122 #include <basegfx/polygon/b2dpolygontools.hxx>
124 #include <svl/style.hxx>
125 #include <ndtxt.hxx>
126 #include <unotools/configmgr.hxx>
127 #include <vcl/hatch.hxx>
129 using namespace ::editeng;
130 using namespace ::com::sun::star;
132 namespace {
134 struct SwPaintProperties;
136 //Class declaration; here because they are only used in this file
137 enum class SubColFlags {
138 Page = 0x01, //Helplines of the page
139 Tab = 0x08, //Helplines inside tables
140 Fly = 0x10, //Helplines inside fly frames
141 Sect = 0x20, //Helplines inside sections
146 namespace o3tl {
147 template<> struct typed_flags<SubColFlags> : is_typed_flags<SubColFlags, 0x39> {};
150 namespace {
152 // Classes collecting the border lines and help lines
153 class SwLineRect : public SwRect
155 Color m_aColor;
156 SvxBorderLineStyle m_nStyle;
157 const SwTabFrame* m_pTabFrame;
158 SubColFlags m_nSubColor; //colorize subsidiary lines
159 bool m_bPainted; //already painted?
160 sal_uInt8 m_nLock; //To distinguish the line and the hell layer.
161 public:
162 SwLineRect( const SwRect &rRect, const Color *pCol, const SvxBorderLineStyle nStyle,
163 const SwTabFrame *pT , const SubColFlags nSCol );
165 const Color& GetColor() const { return m_aColor; }
166 SvxBorderLineStyle GetStyle() const { return m_nStyle; }
167 const SwTabFrame* GetTab() const { return m_pTabFrame; }
168 void SetPainted() { m_bPainted = true; }
169 void Lock(bool bLock)
171 if (bLock)
172 ++m_nLock;
173 else if (m_nLock)
174 --m_nLock;
176 bool IsPainted() const { return m_bPainted; }
177 bool IsLocked() const { return m_nLock != 0; }
178 SubColFlags GetSubColor() const { return m_nSubColor; }
180 bool MakeUnion(const SwRect& rRect, SwPaintProperties const& properties);
185 #ifdef IOS
186 static void dummy_function()
188 pid_t pid = getpid();
189 (void) pid;
191 #endif
193 namespace {
195 class SwLineRects
197 public:
198 std::vector<SwLineRect> m_aLineRects;
199 typedef std::vector< SwLineRect >::const_iterator const_iterator;
200 typedef std::vector< SwLineRect >::iterator iterator;
201 typedef std::vector< SwLineRect >::reverse_iterator reverse_iterator;
202 typedef std::vector< SwLineRect >::size_type size_type;
203 size_t m_nLastCount; //avoid unnecessary cycles in PaintLines
204 SwLineRects()
205 : m_nLastCount(0)
207 #ifdef IOS
208 // Work around what is either a compiler bug in Xcode 5.1.1,
209 // or some unknown problem in this file. If I ifdef out this
210 // call, I get a crash in SwSubsRects::PaintSubsidiary: the
211 // address of the rLi reference variable is claimed to be
212 // 0x4000000!
213 dummy_function();
214 #endif
216 void AddLineRect( const SwRect& rRect, const Color *pColor, const SvxBorderLineStyle nStyle,
217 const SwTabFrame *pTab, const SubColFlags nSCol, SwPaintProperties const &properties );
218 void ConnectEdges( OutputDevice const *pOut, SwPaintProperties const &properties );
219 void PaintLines ( OutputDevice *pOut, SwPaintProperties const &properties );
220 void LockLines( bool bLock );
222 //Limit lines to 100
223 bool isFull() const { return m_aLineRects.size() > 100; }
226 class SwSubsRects : public SwLineRects
228 void RemoveSuperfluousSubsidiaryLines( const SwLineRects &rRects, SwPaintProperties const &properties );
229 public:
230 void PaintSubsidiary( OutputDevice *pOut, const SwLineRects *pRects, SwPaintProperties const &properties );
233 class BorderLines
235 drawinglayer::primitive2d::Primitive2DContainer m_Lines;
236 public:
237 void AddBorderLines(drawinglayer::primitive2d::Primitive2DContainer&& rContainer);
238 drawinglayer::primitive2d::Primitive2DContainer GetBorderLines_Clear()
240 drawinglayer::primitive2d::Primitive2DContainer lines;
241 lines.swap(m_Lines);
242 return lines;
248 // Default zoom factor
249 const double aEdgeScale = 0.5;
251 //To optimize the expensive RetouchColor determination
252 Color aGlobalRetoucheColor;
254 namespace sw
256 Color* GetActiveRetoucheColor()
258 return &aGlobalRetoucheColor;
262 namespace {
265 * Container for static properties
267 struct SwPaintProperties {
268 // Only repaint the Fly content as well as the background of the Fly content if
269 // a metafile is taken of the Fly.
270 bool bSFlyMetafile;
271 VclPtr<OutputDevice> pSFlyMetafileOut;
272 SwViewShell *pSGlobalShell;
274 // Retouch for transparent Flys is done by the background of the Flys.
275 // The Fly itself should certainly not be spared out. See PaintSwFrameBackground and
276 // lcl_SubtractFlys()
277 SwFlyFrame *pSRetoucheFly;
278 SwFlyFrame *pSRetoucheFly2;
279 SwFlyFrame *pSFlyOnlyDraw;
281 // The borders will be collected in pSLines during the Paint and later
282 // possibly merge them.
283 // The help lines will be collected and merged in gProp.pSSubsLines. These will
284 // be compared with pSLines before the work in order to avoid help lines
285 // to hide borders.
286 std::unique_ptr<BorderLines> pBLines;
287 std::unique_ptr<SwLineRects> pSLines;
288 std::unique_ptr<SwSubsRects> pSSubsLines;
290 // global variable for sub-lines of body, header, footer, section and footnote frames.
291 std::unique_ptr<SwSubsRects> pSSpecSubsLines;
292 SfxProgress *pSProgress;
294 // Sizes of a pixel and the corresponding halves. Will be reset when
295 // entering SwRootFrame::PaintSwFrame
296 tools::Long nSPixelSzW;
297 tools::Long nSPixelSzH;
298 tools::Long nSHalfPixelSzW;
299 tools::Long nSHalfPixelSzH;
300 tools::Long nSMinDistPixelW;
301 tools::Long nSMinDistPixelH;
303 Color aSGlobalRetoucheColor;
305 // Current zoom factor
306 double aSScaleX;
307 double aSScaleY;
309 SwPaintProperties()
310 : bSFlyMetafile(false)
311 , pSFlyMetafileOut(nullptr)
312 , pSGlobalShell(nullptr)
313 , pSRetoucheFly(nullptr)
314 , pSRetoucheFly2(nullptr)
315 , pSFlyOnlyDraw(nullptr)
316 , pSProgress(nullptr)
317 , nSPixelSzW(0)
318 , nSPixelSzH(0)
319 , nSHalfPixelSzW(0)
320 , nSHalfPixelSzH(0)
321 , nSMinDistPixelW(0)
322 , nSMinDistPixelH(0)
323 , aSScaleX(1)
324 , aSScaleY(1)
332 static SwPaintProperties gProp;
334 static bool isSubsidiaryLinesFlysEnabled()
336 return !gProp.pSGlobalShell->GetViewOptions()->IsPagePreview() &&
337 !gProp.pSGlobalShell->GetViewOptions()->IsReadonly() &&
338 !gProp.pSGlobalShell->GetViewOptions()->IsFormView() &&
339 gProp.pSGlobalShell->GetViewOptions()->IsObjectBoundaries();
341 //other subsidiary lines enabled?
342 static bool isSubsidiaryLinesEnabled()
344 return !gProp.pSGlobalShell->GetViewOptions()->IsPagePreview() &&
345 !gProp.pSGlobalShell->GetViewOptions()->IsReadonly() &&
346 !gProp.pSGlobalShell->GetViewOptions()->IsFormView() &&
347 !gProp.pSGlobalShell->GetViewOptions()->IsWhitespaceHidden() &&
348 gProp.pSGlobalShell->GetViewOptions()->IsDocBoundaries();
350 //subsidiary lines for sections
351 static bool isSubsidiaryLinesForSectionsEnabled()
353 return !gProp.pSGlobalShell->GetViewOptions()->IsPagePreview() &&
354 !gProp.pSGlobalShell->GetViewOptions()->IsReadonly() &&
355 !gProp.pSGlobalShell->GetViewOptions()->IsFormView() &&
356 gProp.pSGlobalShell->GetViewOptions()->IsSectionBoundaries();
360 namespace {
362 bool isTableBoundariesEnabled()
364 if (!gProp.pSGlobalShell->GetViewOptions()->IsTable())
365 return false;
367 if (gProp.pSGlobalShell->GetViewOptions()->IsPagePreview())
368 return false;
370 if (gProp.pSGlobalShell->GetViewOptions()->IsReadonly())
371 return false;
373 if (gProp.pSGlobalShell->GetViewOptions()->IsFormView())
374 return false;
376 return gProp.pSGlobalShell->GetViewOptions()->IsTableBoundaries();
382 * Set borders alignment statics
383 * Adjustment for 'small' twip-to-pixel relations:
384 * For 'small' twip-to-pixel relations (less than 2:1)
385 * values of <gProp.nSHalfPixelSzW> and <gProp.nSHalfPixelSzH> are set to ZERO
387 void SwCalcPixStatics( vcl::RenderContext const *pOut )
389 // determine 'small' twip-to-pixel relation
390 bool bSmallTwipToPxRelW = false;
391 bool bSmallTwipToPxRelH = false;
393 Size aCheckTwipToPxRelSz( pOut->PixelToLogic( Size( 100, 100 )) );
394 if ( (aCheckTwipToPxRelSz.Width()/100.0) < 2.0 )
396 bSmallTwipToPxRelW = true;
398 if ( (aCheckTwipToPxRelSz.Height()/100.0) < 2.0 )
400 bSmallTwipToPxRelH = true;
404 Size aSz( pOut->PixelToLogic( Size( 1,1 )) );
406 gProp.nSPixelSzW = aSz.Width();
407 if( !gProp.nSPixelSzW )
408 gProp.nSPixelSzW = 1;
409 gProp.nSPixelSzH = aSz.Height();
410 if( !gProp.nSPixelSzH )
411 gProp.nSPixelSzH = 1;
413 // consider 'small' twip-to-pixel relations
414 if ( !bSmallTwipToPxRelW )
416 gProp.nSHalfPixelSzW = gProp.nSPixelSzW / 2 + 1;
418 else
420 gProp.nSHalfPixelSzW = 0;
422 // consider 'small' twip-to-pixel relations
423 if ( !bSmallTwipToPxRelH )
425 gProp.nSHalfPixelSzH = gProp.nSPixelSzH / 2 + 1;
427 else
429 gProp.nSHalfPixelSzH = 0;
432 gProp.nSMinDistPixelW = gProp.nSPixelSzW * 2 + 1;
433 gProp.nSMinDistPixelH = gProp.nSPixelSzH * 2 + 1;
435 const MapMode &rMap = pOut->GetMapMode();
436 gProp.aSScaleX = double(rMap.GetScaleX());
437 gProp.aSScaleY = double(rMap.GetScaleY());
440 namespace {
443 * To be able to save the statics so the paint is more or less reentrant
445 class SwSavePaintStatics : public SwPaintProperties
447 public:
448 SwSavePaintStatics();
449 ~SwSavePaintStatics();
454 SwSavePaintStatics::SwSavePaintStatics()
456 // Saving globales
457 bSFlyMetafile = gProp.bSFlyMetafile;
458 pSGlobalShell = gProp.pSGlobalShell;
459 pSFlyMetafileOut = gProp.pSFlyMetafileOut;
460 pSRetoucheFly = gProp.pSRetoucheFly;
461 pSRetoucheFly2 = gProp.pSRetoucheFly2;
462 pSFlyOnlyDraw = gProp.pSFlyOnlyDraw;
463 pBLines = std::move(gProp.pBLines);
464 pSLines = std::move(gProp.pSLines);
465 pSSubsLines = std::move(gProp.pSSubsLines);
466 pSSpecSubsLines = std::move(gProp.pSSpecSubsLines);
467 pSProgress = gProp.pSProgress;
468 nSPixelSzW = gProp.nSPixelSzW;
469 nSPixelSzH = gProp.nSPixelSzH;
470 nSHalfPixelSzW = gProp.nSHalfPixelSzW;
471 nSHalfPixelSzH = gProp.nSHalfPixelSzH;
472 nSMinDistPixelW = gProp.nSMinDistPixelW;
473 nSMinDistPixelH = gProp.nSMinDistPixelH ;
474 aSGlobalRetoucheColor = aGlobalRetoucheColor;
475 aSScaleX = gProp.aSScaleX;
476 aSScaleY = gProp.aSScaleY;
478 // Restoring globales to default
479 gProp.bSFlyMetafile = false;
480 gProp.pSFlyMetafileOut = nullptr;
481 gProp.pSRetoucheFly = nullptr;
482 gProp.pSRetoucheFly2 = nullptr;
483 gProp.nSPixelSzW = gProp.nSPixelSzH =
484 gProp.nSHalfPixelSzW = gProp.nSHalfPixelSzH =
485 gProp.nSMinDistPixelW = gProp.nSMinDistPixelH = 0;
486 gProp.aSScaleX = gProp.aSScaleY = 1.0;
487 gProp.pSProgress = nullptr;
490 SwSavePaintStatics::~SwSavePaintStatics()
492 // Restoring globales to saved one
493 gProp.pSGlobalShell = pSGlobalShell;
494 gProp.bSFlyMetafile = bSFlyMetafile;
495 gProp.pSFlyMetafileOut = pSFlyMetafileOut;
496 gProp.pSRetoucheFly = pSRetoucheFly;
497 gProp.pSRetoucheFly2 = pSRetoucheFly2;
498 gProp.pSFlyOnlyDraw = pSFlyOnlyDraw;
499 gProp.pBLines = std::move(pBLines);
500 gProp.pSLines = std::move(pSLines);
501 gProp.pSSubsLines = std::move(pSSubsLines);
502 gProp.pSSpecSubsLines = std::move(pSSpecSubsLines);
503 gProp.pSProgress = pSProgress;
504 gProp.nSPixelSzW = nSPixelSzW;
505 gProp.nSPixelSzH = nSPixelSzH;
506 gProp.nSHalfPixelSzW = nSHalfPixelSzW;
507 gProp.nSHalfPixelSzH = nSHalfPixelSzH;
508 gProp.nSMinDistPixelW = nSMinDistPixelW;
509 gProp.nSMinDistPixelH = nSMinDistPixelH;
510 aGlobalRetoucheColor = aSGlobalRetoucheColor;
511 gProp.aSScaleX = aSScaleX;
512 gProp.aSScaleY = aSScaleY;
515 void BorderLines::AddBorderLines(drawinglayer::primitive2d::Primitive2DContainer&& rContainer)
517 if(!rContainer.empty())
519 m_Lines.append(std::move(rContainer));
523 SwLineRect::SwLineRect(const SwRect& rRect, const Color* pCol, const SvxBorderLineStyle nStyl,
524 const SwTabFrame* pT, const SubColFlags nSCol)
525 : SwRect(rRect)
526 , m_nStyle(nStyl)
527 , m_pTabFrame(pT)
528 , m_nSubColor(nSCol)
529 , m_bPainted(false)
530 , m_nLock(0)
532 if ( pCol != nullptr )
533 m_aColor = *pCol;
536 bool SwLineRect::MakeUnion( const SwRect &rRect, SwPaintProperties const & properties)
538 // It has already been tested outside, whether the rectangles have
539 // the same orientation (horizontal or vertical), color, etc.
540 if ( Height() > Width() ) //Vertical line
542 if ( Left() == rRect.Left() && Width() == rRect.Width() )
544 // Merge when there is no gap between the lines
545 const tools::Long nAdd = properties.nSPixelSzW + properties.nSHalfPixelSzW;
546 if ( Bottom() + nAdd >= rRect.Top() &&
547 Top() - nAdd <= rRect.Bottom() )
549 Bottom( std::max( Bottom(), rRect.Bottom() ) );
550 Top ( std::min( Top(), rRect.Top() ) );
551 return true;
555 else
557 if ( Top() == rRect.Top() && Height() == rRect.Height() )
559 // Merge when there is no gap between the lines
560 const tools::Long nAdd = properties.nSPixelSzW + properties.nSHalfPixelSzW;
561 if ( Right() + nAdd >= rRect.Left() &&
562 Left() - nAdd <= rRect.Right() )
564 Right( std::max( Right(), rRect.Right() ) );
565 Left ( std::min( Left(), rRect.Left() ) );
566 return true;
570 return false;
573 void SwLineRects::AddLineRect( const SwRect &rRect, const Color *pCol, const SvxBorderLineStyle nStyle,
574 const SwTabFrame *pTab, const SubColFlags nSCol, SwPaintProperties const & properties )
576 // Loop backwards because lines which can be combined, can usually be painted
577 // in the same context
578 for (reverse_iterator it = m_aLineRects.rbegin(); it != m_aLineRects.rend(); ++it)
580 SwLineRect &rLRect = *it;
581 // Test for the orientation, color, table
582 if ( rLRect.GetTab() == pTab &&
583 !rLRect.IsPainted() && rLRect.GetSubColor() == nSCol &&
584 (rLRect.Height() > rLRect.Width()) == (rRect.Height() > rRect.Width()) &&
585 (pCol && rLRect.GetColor() == *pCol) )
587 if ( rLRect.MakeUnion( rRect, properties ) )
588 return;
591 m_aLineRects.emplace_back(rRect, pCol, nStyle, pTab, nSCol);
594 void SwLineRects::ConnectEdges( OutputDevice const *pOut, SwPaintProperties const & properties )
596 if ( pOut->GetOutDevType() != OUTDEV_PRINTER )
598 // I'm not doing anything for a too small zoom
599 if ( properties.aSScaleX < aEdgeScale || properties.aSScaleY < aEdgeScale )
600 return;
603 static const tools::Long nAdd = 20;
605 std::vector<SwLineRect*> aCheck;
607 for (size_t i = 0; i < m_aLineRects.size(); ++i)
609 SwLineRect& rL1 = m_aLineRects[i];
610 if ( !rL1.GetTab() || rL1.IsPainted() || rL1.IsLocked() )
611 continue;
613 aCheck.clear();
615 const bool bVert = rL1.Height() > rL1.Width();
616 tools::Long nL1a, nL1b, nL1c, nL1d;
618 if ( bVert )
620 nL1a = rL1.Top(); nL1b = rL1.Left();
621 nL1c = rL1.Right(); nL1d = rL1.Bottom();
623 else
625 nL1a = rL1.Left(); nL1b = rL1.Top();
626 nL1c = rL1.Bottom(); nL1d = rL1.Right();
629 // Collect all lines to possibly link with i1
630 for (iterator it2 = m_aLineRects.begin(); it2 != m_aLineRects.end(); ++it2)
632 SwLineRect &rL2 = *it2;
633 if ( rL2.GetTab() != rL1.GetTab() ||
634 rL2.IsPainted() ||
635 rL2.IsLocked() ||
636 (bVert == (rL2.Height() > rL2.Width())) )
637 continue;
639 tools::Long nL2a, nL2b, nL2c, nL2d;
640 if ( bVert )
642 nL2a = rL2.Top(); nL2b = rL2.Left();
643 nL2c = rL2.Right(); nL2d = rL2.Bottom();
645 else
647 nL2a = rL2.Left(); nL2b = rL2.Top();
648 nL2c = rL2.Bottom(); nL2d = rL2.Right();
651 if ( (nL1a - nAdd < nL2d && nL1d + nAdd > nL2a) &&
652 ((nL1b > nL2b && nL1c < nL2c) ||
653 (nL1c >= nL2c && nL1b - nAdd < nL2c) ||
654 (nL1b <= nL2b && nL1c + nAdd > nL2b)) )
656 aCheck.push_back( &rL2 );
659 if ( aCheck.size() < 2 )
660 continue;
662 bool bRemove = false;
664 // For each line test all following ones.
665 for ( size_t k = 0; !bRemove && k < aCheck.size(); ++k )
667 SwLineRect &rR1 = *aCheck[k];
669 for ( size_t k2 = k+1; !bRemove && k2 < aCheck.size(); ++k2 )
671 SwLineRect &rR2 = *aCheck[k2];
672 if ( bVert )
674 SwLineRect *pLA = nullptr;
675 SwLineRect *pLB = nullptr;
676 if ( rR1.Top() < rR2.Top() )
678 pLA = &rR1; pLB = &rR2;
680 else if ( rR1.Top() > rR2.Top() )
682 pLA = &rR2; pLB = &rR1;
684 // are k1 and k2 describing a double line?
685 if ( pLA && pLA->Bottom() + 60 > pLB->Top() )
687 if ( rL1.Top() < pLA->Top() )
689 if ( rL1.Bottom() == pLA->Bottom() )
690 continue; //Small mistake (where?)
692 SwRect aIns( rL1 );
693 aIns.Bottom( pLA->Bottom() );
694 if ( !rL1.Contains( aIns ) )
695 continue;
696 m_aLineRects.emplace_back(aIns, &rL1.GetColor(),
697 SvxBorderLineStyle::SOLID, rL1.GetTab(),
698 SubColFlags::Tab);
699 if ( isFull() )
701 --i;
702 k = aCheck.size();
703 break;
707 if ( rL1.Bottom() > pLB->Bottom() )
708 rL1.Top( pLB->Top() ); // extend i1 on the top
709 else
710 bRemove = true; //stopping, remove i1
713 else
715 SwLineRect *pLA = nullptr;
716 SwLineRect *pLB = nullptr;
717 if ( rR1.Left() < rR2.Left() )
719 pLA = &rR1; pLB = &rR2;
721 else if ( rR1.Left() > rR2.Left() )
723 pLA = &rR2; pLB = &rR1;
725 // Is it double line?
726 if ( pLA && pLA->Right() + 60 > pLB->Left() )
728 if ( rL1.Left() < pLA->Left() )
730 if ( rL1.Right() == pLA->Right() )
731 continue; //small error
733 SwRect aIns( rL1 );
734 aIns.Right( pLA->Right() );
735 if ( !rL1.Contains( aIns ) )
736 continue;
737 m_aLineRects.emplace_back(aIns, &rL1.GetColor(),
738 SvxBorderLineStyle::SOLID, rL1.GetTab(),
739 SubColFlags::Tab);
740 if ( isFull() )
742 --i;
743 k = aCheck.size();
744 break;
747 if ( rL1.Right() > pLB->Right() )
748 rL1.Left( pLB->Left() );
749 else
750 bRemove = true;
755 if ( bRemove )
757 m_aLineRects.erase(m_aLineRects.begin() + i);
758 --i;
763 void SwSubsRects::RemoveSuperfluousSubsidiaryLines( const SwLineRects &rRects, SwPaintProperties const & properties )
765 // All help lines that are covered by any border will be removed or split
766 for (size_t i = 0; i < m_aLineRects.size(); ++i)
768 // get a copy instead of a reference, because an <insert> may destroy
769 // the object due to a necessary array resize.
770 const SwLineRect aSubsLineRect(m_aLineRects[i]);
772 // add condition <aSubsLineRect.IsLocked()> in order to consider only
773 // border lines, which are *not* locked.
774 if ( aSubsLineRect.IsPainted() ||
775 aSubsLineRect.IsLocked() )
776 continue;
778 const bool bVerticalSubs = aSubsLineRect.Height() > aSubsLineRect.Width();
779 SwRect aSubsRect( aSubsLineRect );
780 if ( bVerticalSubs )
782 aSubsRect.AddLeft ( - (properties.nSPixelSzW+properties.nSHalfPixelSzW) );
783 aSubsRect.AddRight ( properties.nSPixelSzW+properties.nSHalfPixelSzW );
785 else
787 aSubsRect.AddTop ( - (properties.nSPixelSzH+properties.nSHalfPixelSzH) );
788 aSubsRect.AddBottom( properties.nSPixelSzH+properties.nSHalfPixelSzH );
790 for (const_iterator itK = rRects.m_aLineRects.begin(); itK != rRects.m_aLineRects.end();
791 ++itK)
793 const SwLineRect &rLine = *itK;
795 // do *not* consider painted or locked border lines.
796 // #i1837# - locked border lines have to be considered.
797 if ( rLine.IsLocked () )
798 continue;
800 if ( !bVerticalSubs == ( rLine.Height() > rLine.Width() ) ) //same direction?
801 continue;
803 if ( aSubsRect.Overlaps( rLine ) )
805 if ( bVerticalSubs ) // Vertical?
807 if ( aSubsRect.Left() <= rLine.Right() &&
808 aSubsRect.Right() >= rLine.Left() )
810 tools::Long nTmp = rLine.Top()-(properties.nSPixelSzH+1);
811 if ( aSubsLineRect.Top() < nTmp )
813 SwRect aNewSubsRect( aSubsLineRect );
814 aNewSubsRect.Bottom( nTmp );
815 m_aLineRects.emplace_back(aNewSubsRect, nullptr,
816 aSubsLineRect.GetStyle(), nullptr,
817 aSubsLineRect.GetSubColor());
819 nTmp = rLine.Bottom()+properties.nSPixelSzH+1;
820 if ( aSubsLineRect.Bottom() > nTmp )
822 SwRect aNewSubsRect( aSubsLineRect );
823 aNewSubsRect.Top( nTmp );
824 m_aLineRects.emplace_back(aNewSubsRect, nullptr,
825 aSubsLineRect.GetStyle(), nullptr,
826 aSubsLineRect.GetSubColor());
828 m_aLineRects.erase(m_aLineRects.begin() + i);
829 --i;
830 break;
833 else // Horizontal
835 if ( aSubsRect.Top() <= rLine.Bottom() &&
836 aSubsRect.Bottom() >= rLine.Top() )
838 tools::Long nTmp = rLine.Left()-(properties.nSPixelSzW+1);
839 if ( aSubsLineRect.Left() < nTmp )
841 SwRect aNewSubsRect( aSubsLineRect );
842 aNewSubsRect.Right( nTmp );
843 m_aLineRects.emplace_back(aNewSubsRect, nullptr,
844 aSubsLineRect.GetStyle(), nullptr,
845 aSubsLineRect.GetSubColor());
847 nTmp = rLine.Right()+properties.nSPixelSzW+1;
848 if ( aSubsLineRect.Right() > nTmp )
850 SwRect aNewSubsRect( aSubsLineRect );
851 aNewSubsRect.Left( nTmp );
852 m_aLineRects.emplace_back(aNewSubsRect, nullptr,
853 aSubsLineRect.GetStyle(), nullptr,
854 aSubsLineRect.GetSubColor());
856 m_aLineRects.erase(m_aLineRects.begin() + i);
857 --i;
858 break;
866 void SwLineRects::LockLines( bool bLock )
868 for (SwLineRect& rLRect : m_aLineRects)
869 rLRect.Lock(bLock);
872 static void lcl_DrawDashedRect( OutputDevice * pOut, SwLineRect const & rLRect )
874 tools::Long startX = rLRect.Left( ), endX;
875 tools::Long startY = rLRect.Top( ), endY;
877 // Discriminate vertically stretched rect from horizontally stretched
878 // and restrict minimum nHalfLWidth to 1
879 tools::Long nHalfLWidth = std::max( std::min( rLRect.Width( ), rLRect.Height( ) ) / 2, tools::Long(1) );
881 if ( rLRect.Height( ) > rLRect.Width( ) )
883 startX += nHalfLWidth;
884 endX = startX;
885 endY = startY + rLRect.Height( );
887 else
889 startY += nHalfLWidth;
890 endY = startY;
891 endX = startX + rLRect.Width( );
894 svtools::DrawLine( *pOut, Point( startX, startY ), Point( endX, endY ),
895 sal_uInt32( nHalfLWidth * 2 ), rLRect.GetStyle( ) );
898 void SwLineRects::PaintLines( OutputDevice *pOut, SwPaintProperties const &properties )
900 // Paint the borders. Sadly two passes are needed.
901 // Once for the inside and once for the outside edges of tables
902 if (m_aLineRects.size() == m_nLastCount)
903 return;
905 // #i16816# tagged pdf support
906 SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, nullptr, *pOut );
908 pOut->Push( vcl::PushFlags::FILLCOLOR|vcl::PushFlags::LINECOLOR );
909 pOut->SetFillColor();
910 pOut->SetLineColor();
911 ConnectEdges( pOut, properties );
912 const Color *pLast = nullptr;
914 bool bPaint2nd = false;
915 size_t nMinCount = m_aLineRects.size();
917 for (size_t i = 0; i < m_aLineRects.size(); ++i)
919 SwLineRect& rLRect = m_aLineRects[i];
921 if ( rLRect.IsPainted() )
922 continue;
924 if ( rLRect.IsLocked() )
926 nMinCount = std::min( nMinCount, i );
927 continue;
930 // Paint it now or in the second pass?
931 bool bPaint = true;
932 if ( rLRect.GetTab() )
934 if ( rLRect.Height() > rLRect.Width() )
936 // Vertical edge, overlapping with the table edge?
937 SwTwips nLLeft = rLRect.Left() - 30,
938 nLRight = rLRect.Right() + 30,
939 nTLeft = rLRect.GetTab()->getFrameArea().Left() + rLRect.GetTab()->getFramePrintArea().Left(),
940 nTRight = rLRect.GetTab()->getFrameArea().Left() + rLRect.GetTab()->getFramePrintArea().Right();
941 if ( (nTLeft >= nLLeft && nTLeft <= nLRight) ||
942 (nTRight>= nLLeft && nTRight<= nLRight) )
943 bPaint = false;
945 else
947 // Horizontal edge, overlapping with the table edge?
948 SwTwips nLTop = rLRect.Top() - 30,
949 nLBottom = rLRect.Bottom() + 30,
950 nTTop = rLRect.GetTab()->getFrameArea().Top() + rLRect.GetTab()->getFramePrintArea().Top(),
951 nTBottom = rLRect.GetTab()->getFrameArea().Top() + rLRect.GetTab()->getFramePrintArea().Bottom();
952 if ( (nTTop >= nLTop && nTTop <= nLBottom) ||
953 (nTBottom >= nLTop && nTBottom <= nLBottom) )
954 bPaint = false;
957 if ( bPaint )
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() )
966 pOut->SetDrawMode( DrawModeFlags::Default );
968 pOut->SetLineColor( *pLast );
969 pOut->SetFillColor( *pLast );
970 pOut->SetDrawMode( nOldDrawMode );
973 if( !rLRect.IsEmpty() )
974 lcl_DrawDashedRect( pOut, rLRect );
975 rLRect.SetPainted();
977 else
978 bPaint2nd = true;
980 if ( bPaint2nd )
982 for (size_t i = 0; i < m_aLineRects.size(); ++i)
984 SwLineRect& rLRect = m_aLineRects[i];
985 if ( rLRect.IsPainted() )
986 continue;
988 if ( rLRect.IsLocked() )
990 nMinCount = std::min( nMinCount, i );
991 continue;
994 if ( !pLast || *pLast != rLRect.GetColor() )
996 pLast = &rLRect.GetColor();
998 DrawModeFlags nOldDrawMode = pOut->GetDrawMode();
999 if( properties.pSGlobalShell->GetWin() &&
1000 Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
1002 pOut->SetDrawMode( DrawModeFlags::Default );
1005 pOut->SetFillColor( *pLast );
1006 pOut->SetDrawMode( nOldDrawMode );
1008 if( !rLRect.IsEmpty() )
1009 lcl_DrawDashedRect( pOut, rLRect );
1010 rLRect.SetPainted();
1013 m_nLastCount = nMinCount;
1014 pOut->Pop();
1018 void SwSubsRects::PaintSubsidiary( OutputDevice *pOut,
1019 const SwLineRects *pRects,
1020 SwPaintProperties const & properties )
1022 if (m_aLineRects.empty())
1023 return;
1025 // #i16816# tagged pdf support
1026 SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, nullptr, *pOut );
1028 // Remove all help line that are almost covered (tables)
1029 for (size_type i = 0; i != m_aLineRects.size(); ++i)
1031 SwLineRect& rLi = m_aLineRects[i];
1032 const bool bVerticalSubs = rLi.Height() > rLi.Width();
1034 for (size_type k = i + 1; k != m_aLineRects.size(); ++k)
1036 SwLineRect& rLk = m_aLineRects[k];
1037 if ( rLi.SSize() == rLk.SSize() )
1039 if ( bVerticalSubs == ( rLk.Height() > rLk.Width() ) )
1041 if ( bVerticalSubs )
1043 tools::Long nLi = rLi.Right();
1044 tools::Long nLk = rLk.Right();
1045 if ( rLi.Top() == rLk.Top() &&
1046 ((nLi < rLk.Left() && nLi+21 > rLk.Left()) ||
1047 (nLk < rLi.Left() && nLk+21 > rLi.Left())))
1049 m_aLineRects.erase(m_aLineRects.begin() + i);
1050 // don't continue with inner loop any more:
1051 // the array may shrink!
1052 --i;
1053 break;
1056 else
1058 tools::Long nLi = rLi.Bottom();
1059 tools::Long nLk = rLk.Bottom();
1060 if ( rLi.Left() == rLk.Left() &&
1061 ((nLi < rLk.Top() && nLi+21 > rLk.Top()) ||
1062 (nLk < rLi.Top() && nLk+21 > rLi.Top())))
1064 m_aLineRects.erase(m_aLineRects.begin() + i);
1065 // don't continue with inner loop any more:
1066 // the array may shrink!
1067 --i;
1068 break;
1076 if (pRects && (!pRects->m_aLineRects.empty()))
1077 RemoveSuperfluousSubsidiaryLines( *pRects, properties );
1079 if (m_aLineRects.empty())
1080 return;
1082 pOut->Push( vcl::PushFlags::FILLCOLOR|vcl::PushFlags::LINECOLOR );
1083 pOut->SetLineColor();
1085 // Reset draw mode in high contrast mode in order to get fill color
1086 // set at output device. Recover draw mode after draw of lines.
1087 // Necessary for the subsidiary lines painted by the fly frames.
1088 DrawModeFlags nOldDrawMode = pOut->GetDrawMode();
1089 if( gProp.pSGlobalShell->GetWin() &&
1090 Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
1092 pOut->SetDrawMode( DrawModeFlags::Default );
1095 for (SwLineRect& rLRect : m_aLineRects)
1097 // Add condition <!rLRect.IsLocked()> to prevent paint of locked subsidiary lines.
1098 if ( !rLRect.IsPainted() &&
1099 !rLRect.IsLocked() )
1101 const Color *pCol = nullptr;
1102 SwViewShell *pShell = properties.pSGlobalShell;
1103 const SwViewOption *pOpt = pShell->GetViewOptions();
1104 switch ( rLRect.GetSubColor() )
1106 case SubColFlags::Page: pCol = &pOpt->GetDocBoundariesColor(); break;
1107 case SubColFlags::Fly: pCol = &pOpt->GetObjectBoundariesColor(); break;
1108 case SubColFlags::Tab: pCol = &pOpt->GetTableBoundariesColor(); break;
1109 case SubColFlags::Sect: pCol = &pOpt->GetSectionBoundColor(); break;
1112 if (pCol && pOut->GetFillColor() != *pCol)
1113 pOut->SetFillColor( *pCol );
1114 pOut->DrawRect( rLRect.SVRect() );
1116 rLRect.SetPainted();
1120 pOut->SetDrawMode( nOldDrawMode );
1122 pOut->Pop();
1125 // Various functions that are use in this file.
1128 * Function <SwAlignRect(..)> is also used outside this file
1130 * Correction: adjust rectangle on pixel level in order to make sure,
1131 * that the border "leaves its original pixel", if it has to
1132 * No prior adjustments for odd relation between pixel and twip
1134 void SwAlignRect( SwRect &rRect, const SwViewShell *pSh, const vcl::RenderContext* pRenderContext )
1136 if( !rRect.HasArea() )
1137 return;
1139 // Make sure that view shell (parameter <pSh>) exists, if the output device
1140 // is taken from this view shell --> no output device, no alignment
1141 // Output device taken from view shell <pSh>, if <gProp.bSFlyMetafile> not set
1142 if ( !gProp.bSFlyMetafile && !pSh )
1144 return;
1147 const vcl::RenderContext *pOut = gProp.bSFlyMetafile ?
1148 gProp.pSFlyMetafileOut.get() : pRenderContext;
1150 // Hold original rectangle in pixel
1151 const tools::Rectangle aOrgPxRect = pOut->LogicToPixel( rRect.SVRect() );
1152 // Determine pixel-center rectangle in twip
1153 const SwRect aPxCenterRect( pOut->PixelToLogic( aOrgPxRect ) );
1155 // Perform adjustments on pixel level.
1156 SwRect aAlignedPxRect( aOrgPxRect );
1157 if ( rRect.Top() > aPxCenterRect.Top() )
1159 // 'leave pixel overlapping on top'
1160 aAlignedPxRect.AddTop( 1 );
1163 if ( rRect.Bottom() < aPxCenterRect.Bottom() )
1165 // 'leave pixel overlapping on bottom'
1166 aAlignedPxRect.AddBottom( - 1 );
1169 if ( rRect.Left() > aPxCenterRect.Left() )
1171 // 'leave pixel overlapping on left'
1172 aAlignedPxRect.AddLeft( 1 );
1175 if ( rRect.Right() < aPxCenterRect.Right() )
1177 // 'leave pixel overlapping on right'
1178 aAlignedPxRect.AddRight( - 1 );
1181 // Consider negative width/height check, if aligned SwRect has negative width/height.
1182 // If Yes, adjust it to width/height = 0 twip.
1183 // NOTE: A SwRect with negative width/height can occur, if the width/height
1184 // of the given SwRect in twip was less than a pixel in twip and that
1185 // the alignment calculates that the aligned SwRect should not contain
1186 // the pixels the width/height is on.
1187 if ( aAlignedPxRect.Width() < 0 )
1189 aAlignedPxRect.Width(0);
1191 if ( aAlignedPxRect.Height() < 0 )
1193 aAlignedPxRect.Height(0);
1195 // Consider zero width/height for converting a rectangle from
1196 // pixel to logic it needs a width/height. Thus, set width/height
1197 // to one, if it's zero and correct this on the twip level after the conversion.
1198 bool bZeroWidth = false;
1199 if ( aAlignedPxRect.Width() == 0 )
1201 aAlignedPxRect.Width(1);
1202 bZeroWidth = true;
1204 bool bZeroHeight = false;
1205 if ( aAlignedPxRect.Height() == 0 )
1207 aAlignedPxRect.Height(1);
1208 bZeroHeight = true;
1211 rRect = SwRect(pOut->PixelToLogic( aAlignedPxRect.SVRect() ));
1213 // Consider zero width/height and adjust calculated aligned twip rectangle.
1214 // Reset width/height to zero; previous negative width/height haven't to be considered.
1215 if ( bZeroWidth )
1217 rRect.Width(0);
1219 if ( bZeroHeight )
1221 rRect.Height(0);
1226 * Method to pixel-align rectangle for drawing graphic object
1228 * Because we are drawing graphics from the left-top-corner in conjunction
1229 * with size coordinates, these coordinates have to be calculated at a pixel
1230 * level.
1231 * Thus, we convert the rectangle to pixel and then convert to left-top-corner
1232 * and then get size of pixel rectangle back to logic.
1233 * This calculation is necessary, because there's a different between
1234 * the conversion from logic to pixel of a normal rectangle with its left-top-
1235 * and right-bottom-corner and the same conversion of the same rectangle
1236 * with left-top-corner and size.
1238 * NOTE: Call this method before each <GraphicObject.Draw(...)>
1240 void SwAlignGrfRect( SwRect *pGrfRect, const vcl::RenderContext &rOut )
1242 tools::Rectangle aPxRect = rOut.LogicToPixel( pGrfRect->SVRect() );
1243 pGrfRect->Pos( rOut.PixelToLogic( aPxRect.TopLeft() ) );
1244 pGrfRect->SSize( rOut.PixelToLogic( aPxRect.GetSize() ) );
1247 static tools::Long lcl_AlignWidth( const tools::Long nWidth, SwPaintProperties const & properties )
1249 if ( nWidth )
1251 const tools::Long nW = nWidth % properties.nSPixelSzW;
1253 if ( !nW || nW > properties.nSHalfPixelSzW )
1254 return std::max(tools::Long(1), nWidth - properties.nSHalfPixelSzW);
1256 return nWidth;
1259 static tools::Long lcl_AlignHeight( const tools::Long nHeight, SwPaintProperties const & properties )
1261 if ( nHeight )
1263 const tools::Long nH = nHeight % properties.nSPixelSzH;
1265 if ( !nH || nH > properties.nSHalfPixelSzH )
1266 return std::max(tools::Long(1), nHeight - properties.nSHalfPixelSzH);
1268 return nHeight;
1272 * Calculate PrtArea plus surrounding plus shadow
1274 static void lcl_CalcBorderRect( SwRect &rRect, const SwFrame *pFrame,
1275 const SwBorderAttrs &rAttrs,
1276 const bool bShadow,
1277 SwPaintProperties const & properties)
1279 // Special handling for cell frames.
1280 // The printing area of a cell frame is completely enclosed in the frame area
1281 // and a cell frame has no shadow. Thus, for cell frames the calculated
1282 // area equals the frame area.
1283 // Notes: Borders of cell frames in R2L text direction will switch its side
1284 // - left border is painted on the right; right border on the left.
1285 // See <lcl_PaintLeftLine> and <lcl_PaintRightLine>.
1286 if( pFrame->IsSctFrame() )
1288 rRect = pFrame->getFramePrintArea();
1289 rRect.Pos() += pFrame->getFrameArea().Pos();
1291 else if ( pFrame->IsCellFrame() )
1292 rRect = pFrame->getFrameArea();
1293 else
1295 rRect = pFrame->getFramePrintArea();
1296 rRect.Pos() += pFrame->getFrameArea().Pos();
1298 SwRectFn fnRect = pFrame->IsVertical() ? ( pFrame->IsVertLR() ? (pFrame->IsVertLRBT() ? fnRectVertL2RB2T : fnRectVertL2R) : fnRectVert ) : fnRectHori;
1300 const SvxBoxItem &rBox = rAttrs.GetBox();
1301 const bool bTop = 0 != (pFrame->*fnRect->fnGetTopMargin)();
1302 if ( bTop || rBox.GetTop() )
1304 SwTwips nDiff = rBox.GetTop() ?
1305 rBox.CalcLineSpace( SvxBoxItemLine::TOP, /*bEvenIfNoLine=*/false, /*bAllowNegative=*/true ) :
1306 rBox.GetDistance( SvxBoxItemLine::TOP );
1307 if( nDiff )
1308 (rRect.*fnRect->fnSubTop)( nDiff );
1311 const bool bBottom = 0 != (pFrame->*fnRect->fnGetBottomMargin)();
1312 if ( bBottom )
1314 SwTwips nDiff = 0;
1315 // #i29550#
1316 if ( pFrame->IsTabFrame() &&
1317 static_cast<const SwTabFrame*>(pFrame)->IsCollapsingBorders() )
1319 // For collapsing borders, we have to add the height of
1320 // the height of the last line
1321 nDiff = static_cast<const SwTabFrame*>(pFrame)->GetBottomLineSize();
1323 else
1325 nDiff = rBox.GetBottom() ?
1326 rBox.CalcLineSpace( SvxBoxItemLine::BOTTOM ) :
1327 rBox.GetDistance( SvxBoxItemLine::BOTTOM );
1329 if( nDiff )
1330 (rRect.*fnRect->fnAddBottom)( nDiff );
1333 if ( rBox.GetLeft() )
1334 (rRect.*fnRect->fnSubLeft)( rBox.CalcLineSpace( SvxBoxItemLine::LEFT ) );
1335 else
1336 (rRect.*fnRect->fnSubLeft)( rBox.GetDistance( SvxBoxItemLine::LEFT ) );
1338 if ( rBox.GetRight() )
1339 (rRect.*fnRect->fnAddRight)( rBox.CalcLineSpace( SvxBoxItemLine::RIGHT ) );
1340 else
1341 (rRect.*fnRect->fnAddRight)( rBox.GetDistance( SvxBoxItemLine::RIGHT ) );
1343 if ( bShadow && rAttrs.GetShadow().GetLocation() != SvxShadowLocation::NONE )
1345 const SvxShadowItem &rShadow = rAttrs.GetShadow();
1346 if ( bTop )
1347 (rRect.*fnRect->fnSubTop)(rShadow.CalcShadowSpace(SvxShadowItemSide::TOP));
1348 (rRect.*fnRect->fnSubLeft)(rShadow.CalcShadowSpace(SvxShadowItemSide::LEFT));
1349 if ( bBottom )
1350 (rRect.*fnRect->fnAddBottom)
1351 (rShadow.CalcShadowSpace( SvxShadowItemSide::BOTTOM ));
1352 (rRect.*fnRect->fnAddRight)(rShadow.CalcShadowSpace(SvxShadowItemSide::RIGHT));
1356 ::SwAlignRect( rRect, properties.pSGlobalShell, properties.pSGlobalShell ? properties.pSGlobalShell->GetOut() : nullptr );
1360 * Extend left/right border/shadow rectangle to bottom of previous frame/to
1361 * top of next frame, if border/shadow is joined with previous/next frame
1363 static void lcl_ExtendLeftAndRight( SwRect& _rRect,
1364 const SwFrame& _rFrame,
1365 const SwBorderAttrs& _rAttrs,
1366 const SwRectFn& _rRectFn )
1368 if ( _rAttrs.JoinedWithPrev( _rFrame ) )
1370 const SwFrame* pPrevFrame = _rFrame.GetPrev();
1371 (_rRect.*_rRectFn->fnSetTop)( (pPrevFrame->*_rRectFn->fnGetPrtBottom)() );
1373 if ( _rAttrs.JoinedWithNext( _rFrame ) )
1375 const SwFrame* pNextFrame = _rFrame.GetNext();
1376 (_rRect.*_rRectFn->fnSetBottom)( (pNextFrame->*_rRectFn->fnGetPrtTop)() );
1380 /// Returns a range suitable for subtraction when lcl_SubtractFlys() is used.
1381 /// Otherwise DrawFillAttributes() expands the clip path itself.
1382 static basegfx::B2DRange lcl_ShrinkFly(const SwRect& rRect)
1384 static MapMode aMapMode(MapUnit::MapTwip);
1385 static const Size aSingleUnit = Application::GetDefaultDevice()->PixelToLogic(Size(1, 1), aMapMode);
1387 double x1 = rRect.Left() + aSingleUnit.getWidth();
1388 double y1 = rRect.Top() + aSingleUnit.getHeight();
1389 double x2 = rRect.Right() - aSingleUnit.getWidth();
1390 double y2 = rRect.Bottom() - aSingleUnit.getHeight();
1392 return basegfx::B2DRange(x1, y1, x2, y2);
1395 static void lcl_SubtractFlys( const SwFrame *pFrame, const SwPageFrame *pPage,
1396 const SwRect &rRect, SwRegionRects &rRegion, basegfx::utils::B2DClipState& rClipState, SwPaintProperties const & rProperties)
1398 const SwSortedObjs& rObjs = *pPage->GetSortedObjs();
1399 const SwFlyFrame* pSelfFly = pFrame->IsInFly() ? pFrame->FindFlyFrame() : gProp.pSRetoucheFly2;
1400 if (!gProp.pSRetoucheFly)
1401 gProp.pSRetoucheFly = gProp.pSRetoucheFly2;
1403 for (size_t j = 0; (j < rObjs.size()) && !rRegion.empty(); ++j)
1405 const SwAnchoredObject* pAnchoredObj = rObjs[j];
1406 const SdrObject* pSdrObj = pAnchoredObj->GetDrawObj();
1408 // Do not consider invisible objects
1409 if (!pPage->GetFormat()->GetDoc()->getIDocumentDrawModelAccess().IsVisibleLayerId(pSdrObj->GetLayer()))
1410 continue;
1412 const SwFlyFrame *pFly = pAnchoredObj->DynCastFlyFrame();
1413 if (!pFly)
1414 continue;
1416 if (pSelfFly == pFly || gProp.pSRetoucheFly == pFly || !rRect.Overlaps(pFly->getFrameArea()))
1417 continue;
1419 if (!pFly->GetFormat()->GetPrint().GetValue() &&
1420 (OUTDEV_PRINTER == gProp.pSGlobalShell->GetOut()->GetOutDevType() ||
1421 gProp.pSGlobalShell->IsPreview()))
1422 continue;
1424 const bool bLowerOfSelf = pSelfFly && pFly->IsLowerOf( pSelfFly );
1426 //For character bound Flys only examine those Flys in which it is not
1427 //anchored itself.
1428 //Why only for character bound ones you may ask? It never makes sense to
1429 //subtract frames in which it is anchored itself right?
1430 if (pSelfFly && pSelfFly->IsLowerOf(pFly))
1431 continue;
1433 //Any why does it not apply for the RetoucheFly too?
1434 if (gProp.pSRetoucheFly && gProp.pSRetoucheFly->IsLowerOf(pFly))
1435 continue;
1437 #if OSL_DEBUG_LEVEL > 0
1438 //Flys who are anchored inside their own one, must have a bigger OrdNum
1439 //or be character bound.
1440 if (pSelfFly && bLowerOfSelf)
1442 OSL_ENSURE( pFly->IsFlyInContentFrame() ||
1443 pSdrObj->GetOrdNumDirect() > pSelfFly->GetVirtDrawObj()->GetOrdNumDirect(),
1444 "Fly with wrong z-Order" );
1446 #endif
1448 bool bStopOnHell = true;
1449 if (pSelfFly)
1451 const SdrObject *pTmp = pSelfFly->GetVirtDrawObj();
1452 if (pSdrObj->GetLayer() == pTmp->GetLayer())
1454 if (pSdrObj->GetOrdNumDirect() < pTmp->GetOrdNumDirect())
1455 //In the same layer we only observe those that are above.
1456 continue;
1458 else
1460 if (!bLowerOfSelf && !pFly->GetFormat()->GetOpaque().GetValue())
1461 //From other layers we are only interested in non
1462 //transparent ones or those that are internal
1463 continue;
1464 bStopOnHell = false;
1467 if (gProp.pSRetoucheFly)
1469 const SdrObject *pTmp = gProp.pSRetoucheFly->GetVirtDrawObj();
1470 if ( pSdrObj->GetLayer() == pTmp->GetLayer() )
1472 if ( pSdrObj->GetOrdNumDirect() < pTmp->GetOrdNumDirect() )
1473 //In the same layer we only observe those that are above.
1474 continue;
1476 else
1478 if (!pFly->IsLowerOf( gProp.pSRetoucheFly ) && !pFly->GetFormat()->GetOpaque().GetValue())
1479 //From other layers we are only interested in non
1480 //transparent ones or those that are internal
1481 continue;
1482 bStopOnHell = false;
1486 //If the content of the Fly is transparent, we subtract it only if it's
1487 //contained in the hell layer.
1488 const IDocumentDrawModelAccess& rIDDMA = pFly->GetFormat()->getIDocumentDrawModelAccess();
1489 bool bHell = pSdrObj->GetLayer() == rIDDMA.GetHellId();
1490 if ( (bStopOnHell && bHell) ||
1491 /// Change internal order of condition
1492 /// first check "!bHell", then "..->Lower()" and "..->IsNoTextFrame()"
1493 /// have not to be performed, if frame is in "Hell"
1494 ( !bHell && pFly->Lower() && pFly->Lower()->IsNoTextFrame() &&
1495 (static_cast<SwNoTextFrame const*>(pFly->Lower())->IsTransparent() ||
1496 static_cast<SwNoTextFrame const*>(pFly->Lower())->HasAnimation() ||
1497 pFly->GetFormat()->GetSurround().IsContour()
1501 continue;
1503 // Own if-statements for transparent background/shadow of fly frames
1504 // in order to handle special conditions.
1505 if (pFly->IsBackgroundTransparent())
1507 // Background <pFly> is transparent drawn. Thus normally, its region
1508 // have not to be subtracted from given region.
1509 // But, if method is called for a fly frame and
1510 // <pFly> is a direct lower of this fly frame and
1511 // <pFly> inherites its transparent background brush from its parent,
1512 // then <pFly> frame area have to be subtracted from given region.
1513 // NOTE: Because in Status Quo transparent backgrounds can only be
1514 // assigned to fly frames, the handle of this special case
1515 // avoids drawing of transparent areas more than once, if
1516 // a fly frame inherites a transparent background from its
1517 // parent fly frame.
1518 if (pFrame->IsFlyFrame() &&
1519 (pFly->GetAnchorFrame()->FindFlyFrame() == pFrame) &&
1520 pFly->GetFormat()->IsBackgroundBrushInherited()
1523 SwRect aRect;
1524 SwBorderAttrAccess aAccess( SwFrame::GetCache(), static_cast<SwFrame const *>(pFly) );
1525 const SwBorderAttrs &rAttrs = *aAccess.Get();
1526 ::lcl_CalcBorderRect( aRect, pFly, rAttrs, true, rProperties );
1527 rRegion -= aRect;
1528 rClipState.subtractRange(lcl_ShrinkFly(aRect));
1529 continue;
1531 else
1533 continue;
1537 if (bHell && pFly->GetAnchorFrame()->IsInFly())
1539 //So the border won't get dismantled by the background of the other
1540 //Fly.
1541 SwRect aRect;
1542 SwBorderAttrAccess aAccess( SwFrame::GetCache(), static_cast<SwFrame const *>(pFly) );
1543 const SwBorderAttrs &rAttrs = *aAccess.Get();
1544 ::lcl_CalcBorderRect( aRect, pFly, rAttrs, true, rProperties );
1545 rRegion -= aRect;
1546 rClipState.subtractRange(lcl_ShrinkFly(aRect));
1548 else
1550 SwRect aRect( pFly->getFramePrintArea() );
1551 aRect += pFly->getFrameArea().Pos();
1552 rRegion -= aRect;
1553 rClipState.subtractRange(lcl_ShrinkFly(aRect));
1556 if (gProp.pSRetoucheFly == gProp.pSRetoucheFly2)
1557 gProp.pSRetoucheFly = nullptr;
1560 static void lcl_implDrawGraphicBackground(const SvxBrushItem& _rBackgrdBrush,
1561 vcl::RenderContext& _rOut,
1562 const SwRect& _rAlignedPaintRect,
1563 const GraphicObject& _rGraphicObj,
1564 SwPaintProperties const & properties)
1566 /// determine color of background
1567 /// If color of background brush is not "no fill"/"auto fill" or
1568 /// <SwPaintProperties.bSFlyMetafile> is set, use color of background brush, otherwise
1569 /// use global retouche color.
1570 const Color aColor( ( (_rBackgrdBrush.GetColor() != COL_TRANSPARENT) || properties.bSFlyMetafile )
1571 ? _rBackgrdBrush.GetColor()
1572 : aGlobalRetoucheColor );
1574 /// determine, if background color have to be drawn transparent
1575 /// and calculate transparency percent value
1576 sal_Int8 nTransparencyPercent = 0;
1577 bool bDrawTransparent = false;
1578 if ( aColor.IsTransparent() )
1579 /// background color is transparent --> draw transparent.
1581 bDrawTransparent = true;
1582 nTransparencyPercent = ((255 - aColor.GetAlpha())*100 + 0x7F)/0xFF;
1584 else if ( (_rGraphicObj.GetAttr().IsTransparent()) &&
1585 (_rBackgrdBrush.GetColor() == COL_TRANSPARENT) )
1586 /// graphic is drawn transparent and background color is
1587 /// "no fill"/"auto fill" --> draw transparent
1589 bDrawTransparent = true;
1590 nTransparencyPercent = 100 - (_rGraphicObj.GetAttr().GetAlpha() * 100 + 127) / 255;
1593 if ( bDrawTransparent )
1595 /// draw background transparent
1596 if( _rOut.GetFillColor() != aColor.GetRGBColor() )
1597 _rOut.SetFillColor( aColor.GetRGBColor() );
1598 tools::PolyPolygon aPoly( _rAlignedPaintRect.SVRect() );
1599 _rOut.DrawTransparent( aPoly, nTransparencyPercent );
1601 else
1603 /// draw background opaque
1604 if ( _rOut.GetFillColor() != aColor )
1605 _rOut.SetFillColor( aColor );
1606 _rOut.DrawRect( _rAlignedPaintRect.SVRect() );
1611 * This is a local help method to draw a background for a graphic
1613 * Under certain circumstances we have to draw a background for a graphic.
1614 * This method takes care of the conditions and draws the background with the
1615 * corresponding color.
1616 * Method introduced for bug fix #103876# in order to optimize drawing tiled
1617 * background graphics. Previously, this code was integrated in method
1618 * <lcl_DrawGraphic>.
1619 * Method implemented as an inline, checking the conditions and calling method
1620 * method <lcl_implDrawGraphicBackground(..)> for the intrinsic drawing.
1622 * @param _rBackgrdBrush
1623 * background brush contain the color the background has to be drawn.
1625 * @param _rOut
1626 * output device the background has to be drawn in.
1628 * @param _rAlignedPaintRect
1629 * paint rectangle in the output device, which has to be drawn with the background.
1630 * rectangle have to be aligned by method ::SwAlignRect
1632 * @param _rGraphicObj
1633 * graphic object, for which the background has to be drawn. Used for checking
1634 * the transparency of its bitmap, its type and if the graphic is drawn transparent
1636 * @param _bNumberingGraphic
1637 * boolean indicating that graphic is used as a numbering.
1639 * @param _bBackgrdAlreadyDrawn
1640 * boolean (optional; default: false) indicating, if the background is already drawn.
1642 static void lcl_DrawGraphicBackground( const SvxBrushItem& _rBackgrdBrush,
1643 OutputDevice& _rOut,
1644 const SwRect& _rAlignedPaintRect,
1645 const GraphicObject& _rGraphicObj,
1646 bool _bNumberingGraphic,
1647 SwPaintProperties const & properties,
1648 bool _bBackgrdAlreadyDrawn = false)
1650 // draw background with background color, if
1651 // (1) graphic is not used as a numbering AND
1652 // (2) background is not already drawn AND
1653 // (3) intrinsic graphic is transparent OR intrinsic graphic doesn't exists
1654 if ( !_bNumberingGraphic &&
1655 !_bBackgrdAlreadyDrawn &&
1656 ( _rGraphicObj.IsTransparent() || _rGraphicObj.GetType() == GraphicType::NONE )
1659 lcl_implDrawGraphicBackground( _rBackgrdBrush, _rOut, _rAlignedPaintRect, _rGraphicObj, properties );
1664 * NNOTE: the transparency of the background graphic is saved in
1665 * SvxBrushItem.GetGraphicObject(<shell>).GetAttr().Set/GetTransparency()
1666 * and is considered in the drawing of the graphic
1668 * Thus, to provide transparent background graphic for text frames nothing
1669 * has to be coded
1671 * Use align rectangle for drawing graphic Pixel-align coordinates for
1672 * drawing graphic
1673 * Outsource code for drawing background of the graphic
1674 * with a background color in method <lcl_DrawGraphicBackground>
1676 * Also, change type of <bGrfNum> and <bClip> from <bool> to <bool>
1678 static void lcl_DrawGraphic( const SvxBrushItem& rBrush, vcl::RenderContext &rOutDev,
1679 const SwViewShell &rSh, const SwRect &rGrf, const SwRect &rOut,
1680 bool bGrfNum,
1681 SwPaintProperties const & properties,
1682 bool bBackgrdAlreadyDrawn )
1683 // add parameter <bBackgrdAlreadyDrawn> to indicate
1684 // that the background is already drawn.
1686 // Calculate align rectangle from parameter <rGrf> and use aligned
1687 // rectangle <aAlignedGrfRect> in the following code
1688 SwRect aAlignedGrfRect = rGrf;
1689 ::SwAlignRect( aAlignedGrfRect, &rSh, &rOutDev );
1691 // Change type from <bool> to <bool>.
1692 const bool bNotInside = !rOut.Contains( aAlignedGrfRect );
1693 if ( bNotInside )
1695 rOutDev.Push( vcl::PushFlags::CLIPREGION );
1696 rOutDev.IntersectClipRegion( rOut.SVRect() );
1699 GraphicObject *pGrf = const_cast<GraphicObject*>(rBrush.GetGraphicObject());
1701 OUString aOriginURL = pGrf->GetGraphic().getOriginURL();
1702 if (pGrf->GetGraphic().GetType() == GraphicType::Default && !aOriginURL.isEmpty())
1704 Graphic aGraphic = vcl::graphic::loadFromURL(aOriginURL);
1705 pGrf->SetGraphic(aGraphic);
1708 // Outsource drawing of background with a background color
1709 ::lcl_DrawGraphicBackground( rBrush, rOutDev, aAlignedGrfRect, *pGrf, bGrfNum, properties, bBackgrdAlreadyDrawn );
1711 // Because for drawing a graphic left-top-corner and size coordinates are
1712 // used, these coordinates have to be determined on pixel level.
1713 ::SwAlignGrfRect( &aAlignedGrfRect, rOutDev );
1715 const basegfx::B2DHomMatrix aGraphicTransform(
1716 basegfx::utils::createScaleTranslateB2DHomMatrix(
1717 aAlignedGrfRect.Width(), aAlignedGrfRect.Height(),
1718 aAlignedGrfRect.Left(), aAlignedGrfRect.Top()));
1720 paintGraphicUsingPrimitivesHelper(
1721 rOutDev,
1722 *pGrf,
1723 pGrf->GetAttr(),
1724 aGraphicTransform,
1725 OUString(),
1726 OUString(),
1727 OUString());
1729 if ( bNotInside )
1730 rOutDev.Pop();
1733 bool DrawFillAttributes(
1734 const drawinglayer::attribute::SdrAllFillAttributesHelperPtr& rFillAttributes,
1735 const SwRect& rOriginalLayoutRect,
1736 const SwRegionRects& rPaintRegion,
1737 const basegfx::utils::B2DClipState& rClipState,
1738 vcl::RenderContext& rOut)
1740 if(rFillAttributes && rFillAttributes->isUsed())
1742 basegfx::B2DRange aPaintRange(
1743 rPaintRegion.GetOrigin().Left(),
1744 rPaintRegion.GetOrigin().Top(),
1745 rPaintRegion.GetOrigin().Right(),
1746 rPaintRegion.GetOrigin().Bottom());
1748 if (!aPaintRange.isEmpty() &&
1749 !rPaintRegion.empty() &&
1750 !basegfx::fTools::equalZero(aPaintRange.getWidth()) &&
1751 !basegfx::fTools::equalZero(aPaintRange.getHeight()))
1753 // need to expand for correct AAed and non-AAed visualization as primitive.
1754 // This must probably be removed again when we will be able to get all Writer visualization
1755 // as primitives and Writer prepares all it's stuff in high precision coordinates (also
1756 // needs to avoid moving boundaries around to better show overlapping stuff...)
1757 if (utl::ConfigManager::IsFuzzing() || SvtOptionsDrawinglayer::IsAntiAliasing())
1759 // if AAed in principle expand by 0.5 in all directions. Since painting edges of
1760 // AAed regions does not add to no transparence (0.5 opacity covered by 0.5 opacity
1761 // is not full opacity but 0.75 opacity) we need some overlap here to avoid paint
1762 // artifacts. Checked experimentally - a little bit more in Y is needed, probably
1763 // due to still existing integer alignment and crunching in writer.
1764 static const double fExpandX = 0.55;
1765 static const double fExpandY = 0.70;
1766 const basegfx::B2DVector aSingleUnit(rOut.GetInverseViewTransformation() * basegfx::B2DVector(fExpandX, fExpandY));
1768 aPaintRange.expand(aPaintRange.getMinimum() - aSingleUnit);
1769 aPaintRange.expand(aPaintRange.getMaximum() + aSingleUnit);
1771 else
1773 // if not AAed expand by one unit to bottom right due to the missing unit
1774 // from SwRect/Rectangle integer handling
1775 const basegfx::B2DVector aSingleUnit(rOut.GetInverseViewTransformation() * basegfx::B2DVector(1.0, 1.0));
1777 aPaintRange.expand(aPaintRange.getMaximum() + aSingleUnit);
1780 const basegfx::B2DRange aDefineRange(
1781 rOriginalLayoutRect.Left(),
1782 rOriginalLayoutRect.Top(),
1783 rOriginalLayoutRect.Right(),
1784 rOriginalLayoutRect.Bottom());
1786 const drawinglayer::primitive2d::Primitive2DContainer& rSequence = rFillAttributes->getPrimitive2DSequence(
1787 aPaintRange,
1788 aDefineRange);
1790 if(rSequence.size())
1792 drawinglayer::primitive2d::Primitive2DContainer const*
1793 pPrimitives(&rSequence);
1794 drawinglayer::primitive2d::Primitive2DContainer primitives;
1795 // tdf#86578 the awful lcl_SubtractFlys hack
1796 if (rPaintRegion.size() > 1 || rPaintRegion[0] != rPaintRegion.GetOrigin())
1798 basegfx::B2DPolyPolygon const& maskRegion(rClipState.getClipPoly());
1799 primitives.resize(1);
1800 primitives[0] = new drawinglayer::primitive2d::MaskPrimitive2D(
1801 maskRegion, drawinglayer::primitive2d::Primitive2DContainer(rSequence));
1802 pPrimitives = &primitives;
1804 assert(pPrimitives && pPrimitives->size());
1806 drawinglayer::geometry::ViewInformation2D aViewInformation2D;
1807 aViewInformation2D.setViewTransformation(rOut.GetViewTransformation());
1808 aViewInformation2D.setViewport(aPaintRange);
1810 std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor(drawinglayer::processor2d::createProcessor2DFromOutputDevice(
1811 rOut,
1812 aViewInformation2D) );
1813 pProcessor->process(*pPrimitives);
1814 return true;
1819 return false;
1822 void DrawGraphic(
1823 const SvxBrushItem *pBrush,
1824 vcl::RenderContext &rOutDev,
1825 const SwRect &rOrg,
1826 const SwRect &rOut,
1827 const sal_uInt8 nGrfNum,
1828 const bool bConsiderBackgroundTransparency )
1829 // Add 6th parameter to indicate that method should
1830 // consider background transparency, saved in the color of the brush item
1832 SwViewShell &rSh = *gProp.pSGlobalShell;
1833 bool bReplaceGrfNum = GRFNUM_REPLACE == nGrfNum;
1834 bool bGrfNum = GRFNUM_NO != nGrfNum;
1835 Size aGrfSize;
1836 SvxGraphicPosition ePos = GPOS_NONE;
1837 if( pBrush && !bReplaceGrfNum )
1839 if( rSh.GetViewOptions()->IsGraphic() )
1841 OUString referer;
1842 SfxObjectShell * sh = rSh.GetDoc()->GetPersist();
1843 if (sh != nullptr && sh->HasName()) {
1844 referer = sh->GetMedium()->GetName();
1846 const Graphic* pGrf = pBrush->GetGraphic(referer);
1847 if( pGrf && GraphicType::NONE != pGrf->GetType() )
1849 ePos = pBrush->GetGraphicPos();
1850 if( pGrf->IsSupportedGraphic() )
1851 // don't the use the specific output device! Bug 94802
1852 aGrfSize = ::GetGraphicSizeTwip( *pGrf, nullptr );
1855 else
1856 bReplaceGrfNum = bGrfNum;
1859 SwRect aGrf;
1860 aGrf.SSize( aGrfSize );
1861 bool bDraw = true;
1862 bool bRetouche = true;
1863 switch ( ePos )
1865 case GPOS_LT:
1866 aGrf.Pos() = rOrg.Pos();
1867 break;
1869 case GPOS_MT:
1870 aGrf.Pos().setY( rOrg.Top() );
1871 aGrf.Pos().setX( rOrg.Left() + rOrg.Width()/2 - aGrfSize.Width()/2 );
1872 break;
1874 case GPOS_RT:
1875 aGrf.Pos().setY( rOrg.Top() );
1876 aGrf.Pos().setX( rOrg.Right() - aGrfSize.Width() );
1877 break;
1879 case GPOS_LM:
1880 aGrf.Pos().setY( rOrg.Top() + rOrg.Height()/2 - aGrfSize.Height()/2 );
1881 aGrf.Pos().setX( rOrg.Left() );
1882 break;
1884 case GPOS_MM:
1885 aGrf.Pos().setY( rOrg.Top() + rOrg.Height()/2 - aGrfSize.Height()/2 );
1886 aGrf.Pos().setX( rOrg.Left() + rOrg.Width()/2 - aGrfSize.Width()/2 );
1887 break;
1889 case GPOS_RM:
1890 aGrf.Pos().setY( rOrg.Top() + rOrg.Height()/2 - aGrfSize.Height()/2 );
1891 aGrf.Pos().setX( rOrg.Right() - aGrfSize.Width() );
1892 break;
1894 case GPOS_LB:
1895 aGrf.Pos().setY( rOrg.Bottom() - aGrfSize.Height() );
1896 aGrf.Pos().setX( rOrg.Left() );
1897 break;
1899 case GPOS_MB:
1900 aGrf.Pos().setY( rOrg.Bottom() - aGrfSize.Height() );
1901 aGrf.Pos().setX( rOrg.Left() + rOrg.Width()/2 - aGrfSize.Width()/2 );
1902 break;
1904 case GPOS_RB:
1905 aGrf.Pos().setY( rOrg.Bottom() - aGrfSize.Height() );
1906 aGrf.Pos().setX( rOrg.Right() - aGrfSize.Width() );
1907 break;
1909 case GPOS_AREA:
1910 aGrf = rOrg;
1911 // Despite the fact that the background graphic has to fill the complete
1912 // area, we already checked, whether the graphic will completely fill out
1913 // the region the <rOut> that is to be painted. Thus, nothing has to be
1914 // touched again.
1915 // E.g. this is the case for a Fly Frame without a background
1916 // brush positioned on the border of the page which inherited the background
1917 // brush from the page.
1918 bRetouche = !rOut.Contains( aGrf );
1919 break;
1921 case GPOS_TILED:
1923 // draw background of tiled graphic before drawing tiled graphic in loop
1924 // determine graphic object
1925 GraphicObject* pGraphicObj = const_cast< GraphicObject* >(pBrush->GetGraphicObject());
1926 // calculate aligned paint rectangle
1927 SwRect aAlignedPaintRect = rOut;
1928 ::SwAlignRect( aAlignedPaintRect, &rSh, &rOutDev );
1929 // draw background color for aligned paint rectangle
1930 lcl_DrawGraphicBackground( *pBrush, rOutDev, aAlignedPaintRect, *pGraphicObj, bGrfNum, gProp );
1932 // set left-top-corner of background graphic to left-top-corner of the
1933 // area, from which the background brush is determined.
1934 aGrf.Pos() = rOrg.Pos();
1935 // setup clipping at output device
1936 rOutDev.Push( vcl::PushFlags::CLIPREGION );
1937 rOutDev.IntersectClipRegion( rOut.SVRect() );
1938 // use new method <GraphicObject::DrawTiled(::)>
1940 // calculate paint offset
1941 Point aPaintOffset( aAlignedPaintRect.Pos() - aGrf.Pos() );
1942 // draw background graphic tiled for aligned paint rectangle
1943 // #i42643#
1944 // For PDF export, every draw operation for bitmaps takes a
1945 // noticeable amount of place (~50 characters). Thus, optimize
1946 // between tile bitmap size and number of drawing operations here.
1948 // A_out
1949 // n_chars = k1 * ---------- + k2 * A_bitmap
1950 // A_bitmap
1952 // minimum n_chars is obtained for (derive for A_bitmap,
1953 // set to 0, take positive solution):
1954 // k1
1955 // A_bitmap = Sqrt( ---- A_out )
1956 // k2
1958 // where k1 is the number of chars per draw operation, and
1959 // k2 is the number of chars per bitmap pixel.
1960 // This is approximately 50 and 7 for current PDF writer, respectively.
1962 const double k1( 50 );
1963 const double k2( 7 );
1964 const Size aSize( aAlignedPaintRect.SSize() );
1965 const double Abitmap( k1/k2 * static_cast<double>(aSize.Width())*aSize.Height() );
1967 pGraphicObj->DrawTiled( rOutDev,
1968 aAlignedPaintRect.SVRect(),
1969 aGrf.SSize(),
1970 Size( aPaintOffset.X(), aPaintOffset.Y() ),
1971 std::max( 128, static_cast<int>( sqrt(sqrt( Abitmap)) + .5 ) ) );
1973 // reset clipping at output device
1974 rOutDev.Pop();
1975 // set <bDraw> and <bRetouche> to false, indicating that background
1976 // graphic and background are already drawn.
1977 bDraw = bRetouche = false;
1979 break;
1981 case GPOS_NONE:
1982 bDraw = false;
1983 break;
1985 default: OSL_ENSURE( false, "new Graphic position?" );
1988 /// init variable <bGrfBackgrdAlreadDrawn> to indicate, if background of
1989 /// graphic is already drawn or not.
1990 bool bGrfBackgrdAlreadyDrawn = false;
1991 if ( bRetouche )
1993 rOutDev.Push( vcl::PushFlags::FILLCOLOR|vcl::PushFlags::LINECOLOR );
1994 rOutDev.SetLineColor();
1996 // check, if an existing background graphic (not filling the complete
1997 // background) is transparent drawn and the background color is
1998 // "no fill" respectively "auto fill", if background transparency
1999 // has to be considered.
2000 // If YES, memorize transparency of background graphic.
2001 // check also, if background graphic bitmap is transparent.
2002 bool bTransparentGrfWithNoFillBackgrd = false;
2003 sal_Int32 nGrfTransparency = 0;
2004 bool bGrfIsTransparent = false;
2005 if ( (ePos != GPOS_NONE) &&
2006 (ePos != GPOS_TILED) && (ePos != GPOS_AREA)
2009 GraphicObject *pGrf = const_cast<GraphicObject*>(pBrush->GetGraphicObject());
2010 if ( bConsiderBackgroundTransparency )
2012 GraphicAttr aGrfAttr = pGrf->GetAttr();
2013 if ( (aGrfAttr.IsTransparent()) &&
2014 (pBrush->GetColor() == COL_TRANSPARENT)
2017 bTransparentGrfWithNoFillBackgrd = true;
2018 nGrfTransparency = 255 - aGrfAttr.GetAlpha();
2021 if ( pGrf->IsTransparent() )
2023 bGrfIsTransparent = true;
2027 // to get color of brush, check background color against COL_TRANSPARENT ("no fill"/"auto fill")
2028 // instead of checking, if transparency is not set.
2029 const Color aColor( pBrush &&
2030 ( (pBrush->GetColor() != COL_TRANSPARENT) ||
2031 gProp.bSFlyMetafile )
2032 ? pBrush->GetColor()
2033 : aGlobalRetoucheColor );
2035 // determine, if background region have to be
2036 // drawn transparent.
2037 // background region has to be drawn transparent, if
2038 // background transparency have to be considered
2039 // AND
2040 // ( background color is transparent OR
2041 // background graphic is transparent and background color is "no fill"
2042 // )
2044 enum DrawStyle {
2045 Default,
2046 Transparent,
2047 } eDrawStyle = Default;
2049 if (bConsiderBackgroundTransparency &&
2050 ( ( aColor.IsTransparent()) ||
2051 bTransparentGrfWithNoFillBackgrd ) )
2053 eDrawStyle = Transparent;
2056 // #i75614# reset draw mode in high contrast mode in order to get fill color set
2057 const DrawModeFlags nOldDrawMode = rOutDev.GetDrawMode();
2058 if ( gProp.pSGlobalShell->GetWin() &&
2059 Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
2061 rOutDev.SetDrawMode( DrawModeFlags::Default );
2064 // If background region has to be drawn transparent, set only the RGB values of the background color as
2065 // the fill color for the output device.
2066 switch (eDrawStyle)
2068 case Transparent:
2070 if( rOutDev.GetFillColor() != aColor.GetRGBColor() )
2071 rOutDev.SetFillColor( aColor.GetRGBColor() );
2072 break;
2074 default:
2076 if( rOutDev.GetFillColor() != aColor )
2077 rOutDev.SetFillColor( aColor );
2078 break;
2082 // #i75614#
2083 // restore draw mode
2084 rOutDev.SetDrawMode( nOldDrawMode );
2086 switch (eDrawStyle)
2088 case Transparent:
2090 // background region have to be drawn transparent.
2091 // Thus, create a poly-polygon from the region and draw it with
2092 // the corresponding transparency percent.
2093 tools::PolyPolygon aDrawPoly( rOut.SVRect() );
2094 if ( aGrf.HasArea() )
2096 if ( !bGrfIsTransparent )
2098 // subtract area of background graphic from draw area
2099 // Consider only that part of the graphic area that is overlapping with draw area.
2100 SwRect aTmpGrf = aGrf;
2101 aTmpGrf.Intersection( rOut );
2102 if ( aTmpGrf.HasArea() )
2104 tools::Polygon aGrfPoly( aTmpGrf.SVRect() );
2105 aDrawPoly.Insert( aGrfPoly );
2108 else
2109 bGrfBackgrdAlreadyDrawn = true;
2111 // calculate transparency percent:
2112 // ( <transparency value[0x01..0xFF]>*100 + 0x7F ) / 0xFF
2113 // If there is a background graphic with a background color "no fill"/"auto fill",
2114 // the transparency value is taken from the background graphic,
2115 // otherwise take the transparency value from the color.
2116 sal_Int8 nTransparencyPercent = static_cast<sal_Int8>(
2117 (( bTransparentGrfWithNoFillBackgrd ? nGrfTransparency : (255 - aColor.GetAlpha())
2118 )*100 + 0x7F)/0xFF);
2119 // draw poly-polygon transparent
2120 rOutDev.DrawTransparent( aDrawPoly, nTransparencyPercent );
2122 break;
2124 case Default:
2125 default:
2127 SwRegionRects aRegion( rOut, 4 );
2128 if ( !bGrfIsTransparent )
2129 aRegion -= aGrf;
2130 else
2131 bGrfBackgrdAlreadyDrawn = true;
2132 // loop rectangles of background region, which has to be drawn
2133 for( size_t i = 0; i < aRegion.size(); ++i )
2135 rOutDev.DrawRect( aRegion[i].SVRect() );
2139 rOutDev.Pop();
2142 if( bDraw && aGrf.Overlaps( rOut ) )
2143 lcl_DrawGraphic( *pBrush, rOutDev, rSh, aGrf, rOut, bGrfNum, gProp,
2144 bGrfBackgrdAlreadyDrawn );
2146 if( bReplaceGrfNum )
2148 const BitmapEx& rBmp = rSh.GetReplacementBitmap(false);
2149 vcl::Font aTmp( rOutDev.GetFont() );
2150 Graphic::DrawEx(rOutDev, OUString(), aTmp, rBmp, rOrg.Pos(), rOrg.SSize());
2155 * Local helper for SwRootFrame::PaintSwFrame(..) - Adjust given rectangle to pixel size
2157 * By OD at 27.09.2002 for #103636#
2158 * In order to avoid paint errors caused by multiple alignments (e.g. ::SwAlignRect(..))
2159 * and other changes to the to be painted rectangle, this method is called for the
2160 * rectangle to be painted in order to adjust it to the pixel it is overlapping
2162 static void lcl_AdjustRectToPixelSize( SwRect& io_aSwRect, const vcl::RenderContext &aOut )
2164 // local constant object of class <Size> to determine number of Twips
2165 // representing a pixel.
2166 const Size aTwipToPxSize( aOut.PixelToLogic( Size( 1,1 )) );
2168 // local object of class <Rectangle> in Twip coordinates
2169 // calculated from given rectangle aligned to pixel centers.
2170 const tools::Rectangle aPxCenterRect = aOut.PixelToLogic(
2171 aOut.LogicToPixel( io_aSwRect.SVRect() ) );
2173 // local constant object of class <Rectangle> representing given rectangle
2174 // in pixel.
2175 const tools::Rectangle aOrgPxRect = aOut.LogicToPixel( io_aSwRect.SVRect() );
2177 // calculate adjusted rectangle from pixel centered rectangle.
2178 // Due to rounding differences <aPxCenterRect> doesn't exactly represents
2179 // the Twip-centers. Thus, adjust borders by half of pixel width/height plus 1.
2180 // Afterwards, adjust calculated Twip-positions of the all borders.
2181 tools::Rectangle aSizedRect = aPxCenterRect;
2182 aSizedRect.AdjustLeft( -(aTwipToPxSize.Width()/2 + 1) );
2183 aSizedRect.AdjustRight( aTwipToPxSize.Width()/2 + 1 );
2184 aSizedRect.AdjustTop( -(aTwipToPxSize.Height()/2 + 1) );
2185 aSizedRect.AdjustBottom(aTwipToPxSize.Height()/2 + 1);
2187 // adjust left()
2188 while ( aOut.LogicToPixel(aSizedRect).Left() < aOrgPxRect.Left() )
2190 aSizedRect.AdjustLeft( 1 );
2192 // adjust right()
2193 while ( aOut.LogicToPixel(aSizedRect).Right() > aOrgPxRect.Right() )
2195 aSizedRect.AdjustRight( -1 );
2197 // adjust top()
2198 while ( aOut.LogicToPixel(aSizedRect).Top() < aOrgPxRect.Top() )
2200 aSizedRect.AdjustTop( 1 );
2202 // adjust bottom()
2203 while ( aOut.LogicToPixel(aSizedRect).Bottom() > aOrgPxRect.Bottom() )
2205 aSizedRect.AdjustBottom( -1 );
2208 io_aSwRect = SwRect( aSizedRect );
2210 #if OSL_DEBUG_LEVEL > 0
2211 tools::Rectangle aTestOrgPxRect = aOut.LogicToPixel( io_aSwRect.SVRect() );
2212 tools::Rectangle aTestNewPxRect = aOut.LogicToPixel( aSizedRect );
2213 OSL_ENSURE( aTestOrgPxRect == aTestNewPxRect,
2214 "Error in lcl_AlignRectToPixelSize(..): Adjusted rectangle has incorrect position or size");
2215 // check Left()
2216 aSizedRect.AdjustLeft( -1 );
2217 aTestNewPxRect = aOut.LogicToPixel( aSizedRect );
2218 OSL_ENSURE( aTestOrgPxRect.Left() >= (aTestNewPxRect.Left()+1),
2219 "Error in lcl_AlignRectToPixelSize(..): Left() not correct adjusted");
2220 aSizedRect.AdjustLeft( 1 );
2221 // check Right()
2222 aSizedRect.AdjustRight( 1 );
2223 aTestNewPxRect = aOut.LogicToPixel( aSizedRect );
2224 OSL_ENSURE( aTestOrgPxRect.Right() <= (aTestNewPxRect.Right()-1),
2225 "Error in lcl_AlignRectToPixelSize(..): Right() not correct adjusted");
2226 aSizedRect.AdjustRight( -1 );
2227 // check Top()
2228 aSizedRect.AdjustTop( -1 );
2229 aTestNewPxRect = aOut.LogicToPixel( aSizedRect );
2230 OSL_ENSURE( aTestOrgPxRect.Top() >= (aTestNewPxRect.Top()+1),
2231 "Error in lcl_AlignRectToPixelSize(..): Top() not correct adjusted");
2232 aSizedRect.AdjustTop( 1 );
2233 // check Bottom()
2234 aSizedRect.AdjustBottom( 1 );
2235 aTestNewPxRect = aOut.LogicToPixel( aSizedRect );
2236 OSL_ENSURE( aTestOrgPxRect.Bottom() <= (aTestNewPxRect.Bottom()-1),
2237 "Error in lcl_AlignRectToPixelSize(..): Bottom() not correct adjusted");
2238 aSizedRect.AdjustBottom( -1 );
2239 #endif
2242 // FUNCTIONS USED FOR COLLAPSING TABLE BORDER LINES START
2244 namespace {
2246 struct SwLineEntry
2248 SwTwips mnKey;
2249 SwTwips mnStartPos;
2250 SwTwips mnEndPos;
2251 SwTwips mnLimitedEndPos;
2252 bool mbOuter;
2254 svx::frame::Style maAttribute;
2256 enum OverlapType { NO_OVERLAP, OVERLAP1, OVERLAP2, OVERLAP3 };
2258 enum class VerticalType { LEFT, RIGHT };
2260 public:
2261 SwLineEntry( SwTwips nKey,
2262 SwTwips nStartPos,
2263 SwTwips nEndPos,
2264 bool bOuter,
2265 const svx::frame::Style& rAttribute );
2267 OverlapType Overlaps( const SwLineEntry& rComp ) const;
2270 * Assuming that this entry is for a Word-style covering cell and the border matching eType is
2271 * set, limit the end position of this border in case covered cells have no borders set.
2273 void LimitVerticalEndPos(const SwFrame& rFrame, VerticalType eType);
2278 SwLineEntry::SwLineEntry( SwTwips nKey,
2279 SwTwips nStartPos,
2280 SwTwips nEndPos,
2281 bool bOuter,
2282 const svx::frame::Style& rAttribute )
2283 : mnKey( nKey ),
2284 mnStartPos( nStartPos ),
2285 mnEndPos( nEndPos ),
2286 mnLimitedEndPos(0),
2287 mbOuter(bOuter),
2288 maAttribute( rAttribute )
2294 1. ---------- rOld
2295 ---------- rNew
2297 2. ---------- rOld
2298 ------------- rNew
2300 3. ------- rOld
2301 ------------- rNew
2303 4. ------------- rOld
2304 ---------- rNew
2306 5. ---------- rOld
2307 ---- rNew
2309 6. ---------- rOld
2310 ---------- rNew
2312 7. ------------- rOld
2313 ---------- rNew
2315 8. ---------- rOld
2316 ------------- rNew
2318 9. ---------- rOld
2319 ---------- rNew
2322 SwLineEntry::OverlapType SwLineEntry::Overlaps( const SwLineEntry& rNew ) const
2324 SwLineEntry::OverlapType eRet = OVERLAP3;
2326 if ( mnStartPos >= rNew.mnEndPos || mnEndPos <= rNew.mnStartPos )
2327 eRet = NO_OVERLAP;
2329 // 1, 2, 3
2330 else if ( mnEndPos < rNew.mnEndPos )
2331 eRet = OVERLAP1;
2333 // 4, 5, 6, 7
2334 else if (mnStartPos <= rNew.mnStartPos)
2335 eRet = OVERLAP2;
2337 // 8, 9
2338 return eRet;
2341 void SwLineEntry::LimitVerticalEndPos(const SwFrame& rFrame, VerticalType eType)
2343 if (!rFrame.IsCellFrame())
2345 return;
2348 const auto& rCellFrame = static_cast<const SwCellFrame&>(rFrame);
2349 std::vector<const SwCellFrame*> aCoveredCells = rCellFrame.GetCoveredCells();
2350 // Iterate in reverse order, so we can stop at the first cell that has a border. This can
2351 // determine what is the minimal end position that is safe to use as a limit.
2352 for (auto it = aCoveredCells.rbegin(); it != aCoveredCells.rend(); ++it)
2354 const SwCellFrame* pCoveredCell = *it;
2355 SwBorderAttrAccess aAccess( SwFrame::GetCache(), pCoveredCell );
2356 const SwBorderAttrs& rAttrs = *aAccess.Get();
2357 const SvxBoxItem& rBox = rAttrs.GetBox();
2358 if (eType == VerticalType::LEFT && rBox.GetLeft())
2360 break;
2363 if (eType == VerticalType::RIGHT && rBox.GetRight())
2365 break;
2368 mnLimitedEndPos = pCoveredCell->getFrameArea().Top();
2372 namespace {
2374 struct lt_SwLineEntry
2376 bool operator()( const SwLineEntry& e1, const SwLineEntry& e2 ) const
2378 return e1.mnStartPos < e2.mnStartPos;
2384 typedef std::set< SwLineEntry, lt_SwLineEntry > SwLineEntrySet;
2385 typedef std::map< SwTwips, SwLineEntrySet > SwLineEntryMap;
2387 namespace {
2389 class SwTabFramePainter
2391 SwLineEntryMap maVertLines;
2392 SwLineEntryMap maHoriLines;
2393 const SwTabFrame& mrTabFrame;
2395 void Insert( SwLineEntry&, bool bHori );
2396 void Insert(const SwFrame& rFrame, const SvxBoxItem& rBoxItem, const SwRect &rPaintArea);
2398 /// Inserts top border at the top of a follow table.
2399 void InsertFollowTopBorder(const SwFrame& rFrame, const SvxBoxItem& rBoxItem,
2400 bool bWordTableCell, SwTwips nTop, SwTwips nLeft, SwTwips nRight,
2401 bool bTopIsOuter);
2402 void InsertMasterBottomBorder(const SwFrame& rFrame, const SvxBoxItem& rBoxItem,
2403 bool bWordTableCell, SwTwips nBottom, SwTwips nLeft, SwTwips nRight,
2404 bool bBottomIsOuter);
2406 void HandleFrame(const SwLayoutFrame& rFrame, const SwRect& rPaintArea);
2407 void FindStylesForLine( Point&,
2408 Point&,
2409 svx::frame::Style*,
2410 bool bHori,
2411 bool bOuter ) const;
2413 public:
2414 explicit SwTabFramePainter( const SwTabFrame& rTabFrame );
2416 void PaintLines( OutputDevice& rDev, const SwRect& rRect ) const;
2421 SwTabFramePainter::SwTabFramePainter( const SwTabFrame& rTabFrame )
2422 : mrTabFrame( rTabFrame )
2424 SwRect aPaintArea = rTabFrame.GetUpper()->GetPaintArea();
2425 HandleFrame(rTabFrame, aPaintArea);
2428 void SwTabFramePainter::HandleFrame(const SwLayoutFrame& rLayoutFrame, const SwRect& rPaintArea)
2430 // Add border lines of cell frames. Skip covered cells. Skip cells
2431 // in special row span row, which do not have a negative row span:
2432 if ( rLayoutFrame.IsCellFrame() && !rLayoutFrame.IsCoveredCell() )
2434 const SwCellFrame* pThisCell = static_cast<const SwCellFrame*>(&rLayoutFrame);
2435 const SwRowFrame* pRowFrame = static_cast<const SwRowFrame*>(pThisCell->GetUpper());
2436 const tools::Long nRowSpan = pThisCell->GetTabBox()->getRowSpan();
2437 if ( !pRowFrame->IsRowSpanLine() || nRowSpan > 1 || nRowSpan < -1 )
2439 SwBorderAttrAccess aAccess( SwFrame::GetCache(), &rLayoutFrame );
2440 const SwBorderAttrs& rAttrs = *aAccess.Get();
2441 const SvxBoxItem& rBox = rAttrs.GetBox();
2442 Insert(rLayoutFrame, rBox, rPaintArea);
2446 // Recurse into lower layout frames, but do not recurse into lower tabframes.
2447 const SwFrame* pLower = rLayoutFrame.Lower();
2448 while ( pLower )
2450 if (pLower->IsLayoutFrame() && !pLower->IsTabFrame())
2452 const SwLayoutFrame* pLowerLayFrame = static_cast<const SwLayoutFrame*>(pLower);
2453 HandleFrame(*pLowerLayFrame, rPaintArea);
2455 pLower = pLower->GetNext();
2459 void SwTabFramePainter::PaintLines(OutputDevice& rDev, const SwRect& rRect) const
2461 // #i16816# tagged pdf support
2462 SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, nullptr, rDev );
2464 SwLineEntryMap::const_iterator aIter = maHoriLines.begin();
2465 bool bHori = true;
2467 // color for subsidiary lines:
2468 const Color& rCol( gProp.pSGlobalShell->GetViewOptions()->GetTableBoundariesColor() );
2470 // high contrast mode:
2471 // overrides the color of non-subsidiary lines.
2472 const Color* pHCColor = nullptr;
2473 DrawModeFlags nOldDrawMode = rDev.GetDrawMode();
2474 if( gProp.pSGlobalShell->GetWin() &&
2475 Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
2477 pHCColor = &gProp.pSGlobalShell->GetViewOptions()->GetFontColor();
2478 rDev.SetDrawMode( DrawModeFlags::Default );
2481 const SwFrame* pUpper = mrTabFrame.GetUpper();
2482 SwRect aUpper( pUpper->getFramePrintArea() );
2483 aUpper.Pos() += pUpper->getFrameArea().Pos();
2484 SwRect aUpperAligned( aUpper );
2485 ::SwAlignRect( aUpperAligned, gProp.pSGlobalShell, &rDev );
2487 // prepare SdrFrameBorderDataVector
2488 drawinglayer::primitive2d::SdrFrameBorderDataVector aData;
2490 while ( true )
2492 if ( bHori && aIter == maHoriLines.end() )
2494 aIter = maVertLines.begin();
2495 bHori = false;
2498 if ( !bHori && aIter == maVertLines.end() )
2499 break;
2501 const SwLineEntrySet& rEntrySet = (*aIter).second;
2502 for (const SwLineEntry& rEntry : rEntrySet)
2504 const svx::frame::Style& rEntryStyle( rEntry.maAttribute );
2506 Point aStart, aEnd;
2507 if ( bHori )
2509 aStart.setX( rEntry.mnStartPos );
2510 aStart.setY( rEntry.mnKey );
2511 aEnd.setX( rEntry.mnEndPos );
2512 aEnd.setY( rEntry.mnKey );
2514 else
2516 aStart.setX( rEntry.mnKey );
2517 aStart.setY( rEntry.mnStartPos );
2518 aEnd.setX( rEntry.mnKey );
2519 aEnd.setY( rEntry.mnEndPos );
2522 svx::frame::Style aStyles[ 7 ];
2523 aStyles[ 0 ] = rEntryStyle;
2524 FindStylesForLine(aStart, aEnd, aStyles, bHori, rEntry.mbOuter);
2526 if (!bHori && rEntry.mnLimitedEndPos)
2528 aEnd.setY(rEntry.mnLimitedEndPos);
2531 SwRect aRepaintRect( aStart, aEnd );
2533 // the repaint rectangle has to be moved a bit for the centered lines:
2534 SwTwips nRepaintRectSize = !rEntryStyle.GetWidth() ? 1 : rEntryStyle.GetWidth();
2535 if ( bHori )
2537 aRepaintRect.Height( 2 * nRepaintRectSize );
2538 aRepaintRect.Pos().AdjustY( -nRepaintRectSize );
2540 // To decide on visibility it is also necessary to expand the RepaintRect
2541 // to left/right according existing BorderLine overlap matchings, else there
2542 // will be repaint errors when scrolling in e.t TripleLine BorderLines.
2543 // aStyles[1] == aLFromT, aStyles[3] == aLFromB, aStyles[4] == aRFromT, aStyles[6] == aRFromB
2544 if(aStyles[1].IsUsed() || aStyles[3].IsUsed() || aStyles[4].IsUsed() || aStyles[6].IsUsed())
2546 const double fLineWidthMaxLeft(std::max(aStyles[1].GetWidth(), aStyles[3].GetWidth()));
2547 const double fLineWidthMaxRight(std::max(aStyles[4].GetWidth(), aStyles[6].GetWidth()));
2548 aRepaintRect.Width(aRepaintRect.Width() + (fLineWidthMaxLeft + fLineWidthMaxRight));
2549 aRepaintRect.Pos().AdjustX( -fLineWidthMaxLeft );
2552 else
2554 aRepaintRect.Width( 2 * nRepaintRectSize );
2555 aRepaintRect.Pos().AdjustX( -nRepaintRectSize );
2557 // Accordingly to horizontal case, but for top/bottom
2558 // aStyles[3] == aTFromR, aStyles[1] == aTFromL, aStyles[6] == aBFromR, aStyles[4] == aBFromL
2559 if(aStyles[3].IsUsed() || aStyles[1].IsUsed() || aStyles[6].IsUsed() || aStyles[4].IsUsed())
2561 const double fLineWidthMaxTop(std::max(aStyles[3].GetWidth(), aStyles[1].GetWidth()));
2562 const double fLineWidthMaxBottom(std::max(aStyles[6].GetWidth(), aStyles[4].GetWidth()));
2563 aRepaintRect.Height(aRepaintRect.Height() + (fLineWidthMaxTop + fLineWidthMaxBottom));
2564 aRepaintRect.Pos().AdjustY( -fLineWidthMaxTop );
2568 if (!rRect.Overlaps(aRepaintRect))
2570 continue;
2573 // subsidiary lines
2574 const Color* pTmpColor = nullptr;
2575 if (0 == aStyles[ 0 ].GetWidth())
2577 if (isTableBoundariesEnabled() && gProp.pSGlobalShell->GetWin())
2578 aStyles[ 0 ].Set( rCol, rCol, rCol, false, 1, 0, 0 );
2579 else
2580 aStyles[0].SetType(SvxBorderLineStyle::NONE);
2582 else
2583 pTmpColor = pHCColor;
2585 // The (twip) positions will be adjusted to meet these requirements:
2586 // 1. The y coordinates are located in the middle of the pixel grid
2587 // 2. The x coordinated are located at the beginning of the pixel grid
2588 // This is done, because the horizontal lines are painted "at
2589 // beginning", whereas the vertical lines are painted "centered".
2590 // By making the line sizes a multiple of one pixel size, we can
2591 // assure that all lines having the same twip size have the same
2592 // pixel size, independent of their position on the screen.
2593 Point aPaintStart = rDev.PixelToLogic( rDev.LogicToPixel(aStart) );
2594 Point aPaintEnd = rDev.PixelToLogic( rDev.LogicToPixel(aEnd) );
2596 if (gProp.pSGlobalShell->GetWin())
2598 // The table borders do not use SwAlignRect, but all the other frames do.
2599 // Therefore we tweak the outer borders a bit to achieve that the outer
2600 // borders match the subsidiary lines of the upper:
2601 if (aStart.X() == aUpper.Left())
2602 aPaintStart.setX( aUpperAligned.Left() );
2603 else if (aStart.X() == aUpper.Right_())
2604 aPaintStart.setX( aUpperAligned.Right_() );
2605 if (aStart.Y() == aUpper.Top())
2606 aPaintStart.setY( aUpperAligned.Top() );
2607 else if (aStart.Y() == aUpper.Bottom_())
2608 aPaintStart.setY( aUpperAligned.Bottom_() );
2610 if (aEnd.X() == aUpper.Left())
2611 aPaintEnd.setX( aUpperAligned.Left() );
2612 else if (aEnd.X() == aUpper.Right_())
2613 aPaintEnd.setX( aUpperAligned.Right_() );
2614 if (aEnd.Y() == aUpper.Top())
2615 aPaintEnd.setY( aUpperAligned.Top() );
2616 else if (aEnd.Y() == aUpper.Bottom_())
2617 aPaintEnd.setY( aUpperAligned.Bottom_() );
2620 if(aStyles[0].IsUsed())
2622 if (bHori)
2624 const basegfx::B2DPoint aOrigin(aPaintStart.X(), aPaintStart.Y());
2625 const basegfx::B2DVector aX(basegfx::B2DPoint(aPaintEnd.X(), aPaintEnd.Y()) - aOrigin);
2627 if(!aX.equalZero())
2629 const basegfx::B2DVector aY(basegfx::getNormalizedPerpendicular(aX));
2630 aData.emplace_back(
2631 aOrigin,
2633 aStyles[0],
2634 pTmpColor);
2635 drawinglayer::primitive2d::SdrFrameBorderData& rInstance(aData.back());
2637 rInstance.addSdrConnectStyleData(true, aStyles[1], -aY, true); // aLFromT
2638 rInstance.addSdrConnectStyleData(true, aStyles[2], -aX, true); // aLFromL
2639 rInstance.addSdrConnectStyleData(true, aStyles[3], aY, false); // aLFromB
2641 rInstance.addSdrConnectStyleData(false, aStyles[4], -aY, true); // aRFromT
2642 rInstance.addSdrConnectStyleData(false, aStyles[5], aX, false); // aRFromR
2643 rInstance.addSdrConnectStyleData(false, aStyles[6], aY, false); // aRFromB
2646 else // vertical
2648 const basegfx::B2DPoint aOrigin(aPaintStart.X(), aPaintStart.Y());
2649 const basegfx::B2DVector aX(basegfx::B2DPoint(aPaintEnd.X(), aPaintEnd.Y()) - aOrigin);
2651 if(!aX.equalZero())
2653 const basegfx::B2DVector aY(basegfx::getNormalizedPerpendicular(aX));
2654 aData.emplace_back(
2655 aOrigin,
2657 aStyles[0],
2658 pTmpColor);
2659 drawinglayer::primitive2d::SdrFrameBorderData& rInstance(aData.back());
2661 rInstance.addSdrConnectStyleData(true, aStyles[3], -aY, false); // aTFromR
2662 rInstance.addSdrConnectStyleData(true, aStyles[2], -aX, true); // aTFromT
2663 rInstance.addSdrConnectStyleData(true, aStyles[1], aY, true); // aTFromL
2665 rInstance.addSdrConnectStyleData(false, aStyles[6], -aY, false); // aBFromR
2666 rInstance.addSdrConnectStyleData(false, aStyles[5], aX, false); // aBFromB
2667 rInstance.addSdrConnectStyleData(false, aStyles[4], aY, true); // aBFromL
2672 ++aIter;
2675 // create instance of SdrFrameBorderPrimitive2D if
2676 // SdrFrameBorderDataVector is used
2677 if(!aData.empty())
2679 drawinglayer::primitive2d::Primitive2DContainer aSequence;
2680 aSequence.append(
2681 drawinglayer::primitive2d::Primitive2DReference(
2682 new drawinglayer::primitive2d::SdrFrameBorderPrimitive2D(
2683 std::move(aData),
2684 true))); // force visualization to minimal one discrete unit (pixel)
2685 // paint
2686 mrTabFrame.ProcessPrimitives(aSequence);
2689 // restore output device:
2690 rDev.SetDrawMode( nOldDrawMode );
2694 * Finds the lines that join the line defined by (StartPoint, EndPoint) in either
2695 * StartPoint or Endpoint. The styles of these lines are required for DR's magic
2696 * line painting functions
2698 void SwTabFramePainter::FindStylesForLine( Point& rStartPoint,
2699 Point& rEndPoint,
2700 svx::frame::Style* pStyles,
2701 bool bHori, bool bOuter ) const
2703 // For example, aLFromB means: this vertical line intersects my horizontal line at its left end,
2704 // from bottom.
2705 // pStyles[ 1 ] = bHori ? aLFromT : TFromL
2706 // pStyles[ 2 ] = bHori ? aLFromL : TFromT,
2707 // pStyles[ 3 ] = bHori ? aLFromB : TFromR,
2708 // pStyles[ 4 ] = bHori ? aRFromT : BFromL,
2709 // pStyles[ 5 ] = bHori ? aRFromR : BFromB,
2710 // pStyles[ 6 ] = bHori ? aRFromB : BFromR,
2712 bool bWordTableCell = false;
2713 SwViewShell* pShell = mrTabFrame.getRootFrame()->GetCurrShell();
2714 if (pShell)
2716 const IDocumentSettingAccess& rIDSA = pShell->GetDoc()->getIDocumentSettingAccess();
2717 bWordTableCell = rIDSA.get(DocumentSettingId::TABLE_ROW_KEEP);
2720 SwLineEntryMap::const_iterator aMapIter = maVertLines.find( rStartPoint.X() );
2721 OSL_ENSURE( aMapIter != maVertLines.end(), "FindStylesForLine: Error" );
2722 const SwLineEntrySet& rVertSet = (*aMapIter).second;
2724 for ( const SwLineEntry& rEntry : rVertSet )
2726 if ( bHori )
2728 if ( rStartPoint.Y() == rEntry.mnStartPos )
2729 pStyles[ 3 ] = rEntry.maAttribute;
2730 else if ( rStartPoint.Y() == rEntry.mnEndPos )
2731 pStyles[ 1 ] = rEntry.maAttribute;
2733 if (bWordTableCell && rStartPoint.X() == rEntry.mnKey && !bOuter && rEntry.mbOuter)
2735 rStartPoint.AdjustX(rEntry.maAttribute.GetWidth());
2738 else
2740 if ( rStartPoint.Y() == rEntry.mnEndPos )
2741 pStyles[ 2 ] = rEntry.maAttribute;
2742 else if ( rEndPoint.Y() == rEntry.mnStartPos )
2743 pStyles[ 5 ] = rEntry.maAttribute;
2747 aMapIter = maHoriLines.find( rStartPoint.Y() );
2748 OSL_ENSURE( aMapIter != maHoriLines.end(), "FindStylesForLine: Error" );
2749 const SwLineEntrySet& rHoriSet = (*aMapIter).second;
2751 for ( const SwLineEntry& rEntry : rHoriSet )
2753 if ( bHori )
2755 if ( rStartPoint.X() == rEntry.mnEndPos )
2756 pStyles[ 2 ] = rEntry.maAttribute;
2757 else if ( rEndPoint.X() == rEntry.mnStartPos )
2758 pStyles[ 5 ] = rEntry.maAttribute;
2760 else
2762 if ( rStartPoint.X() == rEntry.mnEndPos )
2763 pStyles[ 1 ] = rEntry.maAttribute;
2764 else if ( rStartPoint.X() == rEntry.mnStartPos )
2765 pStyles[ 3 ] = rEntry.maAttribute;
2767 if (bWordTableCell && rStartPoint.Y() == rEntry.mnKey && !bOuter && rEntry.mbOuter)
2769 rStartPoint.AdjustY(rEntry.maAttribute.GetWidth());
2774 if ( bHori )
2776 aMapIter = maVertLines.find( rEndPoint.X() );
2777 OSL_ENSURE( aMapIter != maVertLines.end(), "FindStylesForLine: Error" );
2778 const SwLineEntrySet& rVertSet2 = (*aMapIter).second;
2780 for ( const SwLineEntry& rEntry : rVertSet2 )
2782 if ( rEndPoint.Y() == rEntry.mnStartPos )
2783 pStyles[ 6 ] = rEntry.maAttribute;
2784 else if ( rEndPoint.Y() == rEntry.mnEndPos )
2785 pStyles[ 4 ] = rEntry.maAttribute;
2787 if (bWordTableCell && rEndPoint.X() == rEntry.mnKey && !bOuter && rEntry.mbOuter)
2789 rEndPoint.AdjustX(-rEntry.maAttribute.GetWidth());
2793 else
2795 aMapIter = maHoriLines.find( rEndPoint.Y() );
2796 OSL_ENSURE( aMapIter != maHoriLines.end(), "FindStylesForLine: Error" );
2797 const SwLineEntrySet& rHoriSet2 = (*aMapIter).second;
2799 for ( const SwLineEntry& rEntry : rHoriSet2 )
2801 if ( rEndPoint.X() == rEntry.mnEndPos )
2802 pStyles[ 4 ] = rEntry.maAttribute;
2803 else if ( rEndPoint.X() == rEntry.mnStartPos )
2804 pStyles[ 6 ] = rEntry.maAttribute;
2806 if (bWordTableCell && rEndPoint.Y() == rEntry.mnKey && !bOuter && rEntry.mbOuter)
2808 rEndPoint.AdjustY(-rEntry.maAttribute.GetWidth());
2815 * Special case: #i9860#
2816 * first line in follow table without repeated headlines
2817 * Special case: tdf#150308
2818 * first visible line of a table with preceding hidden deleted rows
2820 static bool lcl_IsFirstRowInFollowTableWithoutRepeatedHeadlines(
2821 SwTabFrame const& rTabFrame, SwFrame const& rFrame, SvxBoxItem const& rBoxItem)
2823 SwRowFrame const*const pThisRowFrame =
2824 dynamic_cast<const SwRowFrame*>(rFrame.GetUpper());
2825 return (pThisRowFrame
2826 && (pThisRowFrame->GetUpper() == &rTabFrame)
2827 && ( rTabFrame.IsFollow()
2828 // tdf#150308 first table row isn't equal to the table row of the first
2829 // row frame of the first table frame: there are invisible deleted rows
2830 // in Hide Changes mode before the first visible table row
2831 || rTabFrame.GetTable()->GetTabLines().front() != pThisRowFrame->GetTabLine() )
2832 && !rTabFrame.GetTable()->GetRowsToRepeat()
2833 && ( !pThisRowFrame->GetPrev()
2834 || static_cast<const SwRowFrame*>(pThisRowFrame->GetPrev())
2835 ->IsRowSpanLine())
2836 && !rBoxItem.GetTop()
2837 && rBoxItem.GetBottom());
2841 * Special case:
2842 * last visible cell of a table row followed with a hidden deleted cell,
2843 * and the right border of the visible cell was painted by its left border
2845 static bool lcl_IsLastVisibleCellBeforeHiddenCellAtTheEndOfRow(
2846 SwFrame const& rFrame, SvxBoxItem const& rBoxItem)
2848 SwRowFrame const*const pThisRowFrame =
2849 dynamic_cast<const SwRowFrame*>(rFrame.GetUpper());
2850 const SwCellFrame* pThisCell = static_cast<const SwCellFrame*>(&rFrame);
2852 return pThisRowFrame
2853 // last visible cell
2854 && !rFrame.GetNext()
2855 // it has only left border
2856 && !rBoxItem.GetRight()
2857 && rBoxItem.GetLeft()
2858 // last visible table cell isn't equal to the last cell:
2859 // there are invisible deleted cells in Hide Changes mode
2860 && pThisRowFrame->GetTabLine()->GetTabBoxes().back() != pThisCell->GetTabBox();
2863 void SwTabFramePainter::InsertFollowTopBorder(const SwFrame& rFrame, const SvxBoxItem& rBoxItem,
2864 bool bWordTableCell, SwTwips nTop, SwTwips nLeft,
2865 SwTwips nRight, bool bTopIsOuter)
2867 // Figure out which cell to copy.
2868 int nCol = 0;
2869 const SwFrame* pCell = &rFrame;
2870 while (pCell)
2872 if (!pCell->GetPrev())
2874 break;
2877 ++nCol;
2878 pCell = pCell->GetPrev();
2881 auto pThisRow = dynamic_cast<const SwRowFrame*>(rFrame.GetUpper());
2882 if (!pThisRow || pThisRow->GetUpper() != &mrTabFrame)
2884 return;
2887 if (!mrTabFrame.IsFollow() || mrTabFrame.GetTable()->GetRowsToRepeat())
2889 return;
2892 if (pThisRow->GetPrev() || rBoxItem.GetTop() || rBoxItem.GetBottom())
2894 return;
2897 // This is then a first row in a follow table, without repeated headlines.
2898 auto pLastRow = dynamic_cast<const SwRowFrame*>(mrTabFrame.GetLastLower());
2899 if (!pLastRow || pLastRow == pThisRow)
2901 return;
2904 const SwFrame* pLastCell = pLastRow->GetLower();
2905 for (int i = 0; i < nCol; ++i)
2907 if (!pLastCell)
2909 break;
2912 pLastCell = pLastCell->GetNext();
2914 if (!pLastCell)
2916 return;
2919 SwBorderAttrAccess aAccess(SwFrame::GetCache(), pLastCell);
2920 const SwBorderAttrs& rAttrs = *aAccess.Get();
2921 const SvxBoxItem& rLastBoxItem = rAttrs.GetBox();
2922 if (!rLastBoxItem.GetBottom())
2924 return;
2927 // The matching (same column) row in the last row has a bottom border for us.
2928 svx::frame::Style aFollowB(rLastBoxItem.GetBottom(), 1.0);
2929 aFollowB.SetWordTableCell(bWordTableCell);
2930 SwLineEntry aFollowTop(nTop, nLeft, nRight, bTopIsOuter, aFollowB);
2931 aFollowB.SetRefMode(svx::frame::RefMode::Begin);
2932 Insert(aFollowTop, true);
2935 void SwTabFramePainter::InsertMasterBottomBorder(const SwFrame& rFrame, const SvxBoxItem& rBoxItem,
2936 bool bWordTableCell, SwTwips nBottom, SwTwips nLeft,
2937 SwTwips nRight, bool bBottomIsOuter)
2939 // Figure out which cell to copy.
2940 int nCol = 0;
2941 const SwFrame* pCell = &rFrame;
2942 while (pCell)
2944 if (!pCell->GetPrev())
2946 break;
2949 ++nCol;
2950 pCell = pCell->GetPrev();
2953 auto pThisRow = dynamic_cast<const SwRowFrame*>(rFrame.GetUpper());
2954 if (!pThisRow || pThisRow->GetUpper() != &mrTabFrame)
2956 return;
2959 if (mrTabFrame.IsFollow() || !mrTabFrame.GetFollow())
2961 return;
2964 // This is a master table that is split.
2965 if (pThisRow->GetNext() || rBoxItem.GetTop() || rBoxItem.GetBottom())
2967 return;
2970 // This is then a last row in a master table.
2971 auto pFirstRow = dynamic_cast<const SwRowFrame*>(mrTabFrame.GetLower());
2972 if (!pFirstRow || pFirstRow == pThisRow)
2974 return;
2977 const SwFrame* pFirstCell = pFirstRow->GetLower();
2978 for (int i = 0; i < nCol; ++i)
2980 if (!pFirstCell)
2982 break;
2985 pFirstCell = pFirstCell->GetNext();
2987 if (!pFirstCell)
2989 return;
2992 SwBorderAttrAccess aAccess(SwFrame::GetCache(), pFirstCell);
2993 const SwBorderAttrs& rAttrs = *aAccess.Get();
2994 const SvxBoxItem& rFirstBoxItem = rAttrs.GetBox();
2995 if (!rFirstBoxItem.GetTop())
2997 return;
3000 // The matching (same column) row in the first row has a top border for us.
3001 svx::frame::Style aMasterT(rFirstBoxItem.GetTop(), 1.0);
3002 aMasterT.SetWordTableCell(bWordTableCell);
3003 SwLineEntry aMasterBottom(nBottom, nLeft, nRight, bBottomIsOuter, aMasterT);
3004 aMasterT.SetRefMode(svx::frame::RefMode::Begin);
3005 Insert(aMasterBottom, true);
3008 void SwTabFramePainter::Insert(const SwFrame& rFrame, const SvxBoxItem& rBoxItem, const SwRect& rPaintArea)
3010 // build 4 line entries for the 4 borders:
3011 SwRect aBorderRect = rFrame.getFrameArea();
3013 aBorderRect.Intersection(rPaintArea);
3015 bool const bBottomAsTop(lcl_IsFirstRowInFollowTableWithoutRepeatedHeadlines(
3016 mrTabFrame, rFrame, rBoxItem));
3017 bool const bLeftAsRight(lcl_IsLastVisibleCellBeforeHiddenCellAtTheEndOfRow(
3018 rFrame, rBoxItem));
3020 bool const bVert = mrTabFrame.IsVertical();
3021 bool const bR2L = mrTabFrame.IsRightToLeft();
3023 bool bWordTableCell = false;
3024 SwViewShell* pShell = rFrame.getRootFrame()->GetCurrShell();
3025 if (pShell)
3027 const IDocumentSettingAccess& rIDSA = pShell->GetDoc()->getIDocumentSettingAccess();
3028 bWordTableCell = rIDSA.get(DocumentSettingId::TABLE_ROW_KEEP);
3031 // no scaling needed, it's all in the primitives and the target device
3032 svx::frame::Style aL(rBoxItem.GetLeft(), 1.0);
3033 aL.SetWordTableCell(bWordTableCell);
3034 svx::frame::Style aR(rBoxItem.GetRight(), 1.0);
3035 aR.SetWordTableCell(bWordTableCell);
3036 svx::frame::Style aT(rBoxItem.GetTop(), 1.0);
3037 aT.SetWordTableCell(bWordTableCell);
3038 svx::frame::Style aB(rBoxItem.GetBottom(), 1.0);
3039 aB.SetWordTableCell(bWordTableCell);
3041 // First cell in a row.
3042 bool bLeftIsOuter = rFrame.IsCellFrame() && rFrame.GetUpper()->GetLower() == &rFrame;
3043 // Last cell in a row.
3044 bool bRightIsOuter = rFrame.IsCellFrame() && rFrame.GetNext() == nullptr;
3045 // First row in a table.
3046 bool bTopIsOuter = rFrame.IsCellFrame() && rFrame.GetUpper()->GetUpper()->GetLower() == rFrame.GetUpper();
3047 // Last row in a table.
3048 bool bBottomIsOuter = rFrame.IsCellFrame() && rFrame.GetUpper()->GetNext() == nullptr;
3050 aR.MirrorSelf();
3051 if (!bWordTableCell || !bBottomIsOuter)
3053 // Outer horizontal lines are never mirrored in Word.
3054 aB.MirrorSelf();
3057 const SwTwips nLeft = aBorderRect.Left_();
3058 const SwTwips nRight = aBorderRect.Right_();
3059 const SwTwips nTop = aBorderRect.Top_();
3060 const SwTwips nBottom = aBorderRect.Bottom_();
3062 aL.SetRefMode( svx::frame::RefMode::Centered );
3063 aR.SetRefMode( svx::frame::RefMode::Centered );
3064 aT.SetRefMode( !bVert ? svx::frame::RefMode::Begin : svx::frame::RefMode::End );
3065 aB.SetRefMode( !bVert ? svx::frame::RefMode::Begin : svx::frame::RefMode::End );
3067 if (bWordTableCell && bLeftIsOuter)
3069 // Outer vertical lines are always mirrored in Word.
3070 aL.MirrorSelf();
3073 SwLineEntry aLeft (nLeft, nTop, nBottom, bLeftIsOuter,
3074 bVert ? aB : (bR2L ? aR : aL));
3075 if (bWordTableCell && rBoxItem.GetLeft())
3077 aLeft.LimitVerticalEndPos(rFrame, SwLineEntry::VerticalType::LEFT);
3080 SwLineEntry aRight (nRight, nTop, nBottom, bRightIsOuter,
3081 bVert ? (bBottomAsTop ? aB : aT) : ((bR2L || bLeftAsRight) ? aL : aR));
3082 if (bWordTableCell && rBoxItem.GetRight())
3084 aRight.LimitVerticalEndPos(rFrame, SwLineEntry::VerticalType::RIGHT);
3087 SwLineEntry aTop (nTop, nLeft, nRight, bTopIsOuter,
3088 bVert ? aL : (bBottomAsTop ? aB : aT));
3090 SwLineEntry aBottom(nBottom, nLeft, nRight, bBottomIsOuter,
3091 bVert ? aR : aB);
3093 Insert( aLeft, false );
3094 Insert( aRight, false );
3095 Insert( aTop, true );
3096 Insert( aBottom, true );
3098 InsertMasterBottomBorder(rFrame, rBoxItem, bWordTableCell, nBottom, nLeft, nRight,
3099 bBottomIsOuter);
3100 InsertFollowTopBorder(rFrame, rBoxItem, bWordTableCell, nTop, nLeft, nRight, bTopIsOuter);
3103 void SwTabFramePainter::Insert( SwLineEntry& rNew, bool bHori )
3105 // get all lines from structure, that have key entry of pLE
3106 SwLineEntryMap* pLine2 = bHori ? &maHoriLines : &maVertLines;
3107 const SwTwips nKey = rNew.mnKey;
3108 SwLineEntryMap::iterator aMapIter = pLine2->find( nKey );
3110 SwLineEntrySet* pLineSet = aMapIter != pLine2->end() ? &((*aMapIter).second) : nullptr;
3111 if ( !pLineSet )
3113 SwLineEntrySet aNewSet;
3114 (*pLine2)[ nKey ] = aNewSet;
3115 pLineSet = &(*pLine2)[ nKey ];
3117 SwLineEntrySet::iterator aIter = pLineSet->begin();
3119 bool bWordTableCell = false;
3120 SwViewShell* pShell = mrTabFrame.getRootFrame()->GetCurrShell();
3121 if (pShell)
3123 const IDocumentSettingAccess& rIDSA = pShell->GetDoc()->getIDocumentSettingAccess();
3124 bWordTableCell = rIDSA.get(DocumentSettingId::TABLE_ROW_KEEP);
3126 bool bR2L = mrTabFrame.IsRightToLeft();
3127 while ( aIter != pLineSet->end() && rNew.mnStartPos < rNew.mnEndPos )
3129 const SwLineEntry& rOld = *aIter;
3131 // The bWordTableCell code only works for LTR at the moment, avoid it for RTL.
3132 if (rOld.mnLimitedEndPos || (bWordTableCell && (rOld.mbOuter != rNew.mbOuter) && !bR2L))
3134 // Don't merge with this line entry as it ends sooner than mnEndPos.
3135 ++aIter;
3136 continue;
3139 const SwLineEntry::OverlapType nOverlapType = rOld.Overlaps( rNew );
3141 const svx::frame::Style& rOldAttr = rOld.maAttribute;
3142 const svx::frame::Style& rNewAttr = rNew.maAttribute;
3143 const svx::frame::Style& rCmpAttr = std::max(rNewAttr, rOldAttr);
3145 if ( SwLineEntry::OVERLAP1 == nOverlapType )
3147 OSL_ENSURE( rNew.mnStartPos >= rOld.mnStartPos, "Overlap type 3? How this?" );
3149 // new left segment
3150 const SwLineEntry aLeft(nKey, rOld.mnStartPos, rNew.mnStartPos, rOld.mbOuter, rOldAttr);
3152 // new middle segment
3153 const SwLineEntry aMiddle(nKey, rNew.mnStartPos, rOld.mnEndPos, rOld.mbOuter, rCmpAttr);
3155 // new right segment
3156 rNew.mnStartPos = rOld.mnEndPos;
3158 // update current lines set
3159 pLineSet->erase( aIter );
3160 if ( aLeft.mnStartPos < aLeft.mnEndPos ) pLineSet->insert( aLeft );
3161 if ( aMiddle.mnStartPos < aMiddle.mnEndPos ) pLineSet->insert( aMiddle );
3163 aIter = pLineSet->begin();
3165 continue; // start over
3167 else if ( SwLineEntry::OVERLAP2 == nOverlapType )
3169 // new left segment
3170 const SwLineEntry aLeft(nKey, rOld.mnStartPos, rNew.mnStartPos, rOld.mbOuter, rOldAttr);
3172 // new middle segment
3173 const SwLineEntry aMiddle(nKey, rNew.mnStartPos, rNew.mnEndPos, rOld.mbOuter, rCmpAttr);
3175 // new right segment
3176 const SwLineEntry aRight(nKey, rNew.mnEndPos, rOld.mnEndPos, rOld.mbOuter, rOldAttr);
3178 // update current lines set
3179 pLineSet->erase( aIter );
3180 if ( aLeft.mnStartPos < aLeft.mnEndPos ) pLineSet->insert( aLeft );
3181 if ( aMiddle.mnStartPos < aMiddle.mnEndPos ) pLineSet->insert( aMiddle );
3182 if ( aRight.mnStartPos < aRight.mnEndPos ) pLineSet->insert( aRight );
3184 rNew.mnStartPos = rNew.mnEndPos; // rNew should not be inserted!
3186 break; // we are finished
3188 else if ( SwLineEntry::OVERLAP3 == nOverlapType )
3190 // new left segment
3191 const SwLineEntry aLeft(nKey, rNew.mnStartPos, rOld.mnStartPos, rOld.mbOuter, rNewAttr);
3193 // new middle segment
3194 const SwLineEntry aMiddle(nKey, rOld.mnStartPos, rNew.mnEndPos, rOld.mbOuter, rCmpAttr);
3196 // new right segment
3197 const SwLineEntry aRight(nKey, rNew.mnEndPos, rOld.mnEndPos, rOld.mbOuter, rOldAttr);
3199 // update current lines set
3200 pLineSet->erase( aIter );
3201 if ( aLeft.mnStartPos < aLeft.mnEndPos ) pLineSet->insert( aLeft );
3202 if ( aMiddle.mnStartPos < aMiddle.mnEndPos ) pLineSet->insert( aMiddle );
3203 if ( aRight.mnStartPos < aRight.mnEndPos ) pLineSet->insert( aRight );
3205 rNew.mnStartPos = rNew.mnEndPos; // rNew should not be inserted!
3207 break; // we are finished
3210 ++aIter;
3213 if ( rNew.mnStartPos < rNew.mnEndPos ) // insert rest
3214 pLineSet->insert( rNew );
3218 * FUNCTIONS USED FOR COLLAPSING TABLE BORDER LINES END
3219 * --> OD #i76669#
3221 namespace
3223 class SwViewObjectContactRedirector : public sdr::contact::ViewObjectContactRedirector
3225 private:
3226 const SwViewShell& mrViewShell;
3228 public:
3229 explicit SwViewObjectContactRedirector( const SwViewShell& rSh )
3230 : mrViewShell( rSh )
3233 virtual void createRedirectedPrimitive2DSequence(
3234 const sdr::contact::ViewObjectContact& rOriginal,
3235 const sdr::contact::DisplayInfo& rDisplayInfo,
3236 drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) override
3238 bool bPaint( true );
3240 SdrObject* pObj = rOriginal.GetViewContact().TryToGetSdrObject();
3241 if ( pObj )
3243 bPaint = SwFlyFrame::IsPaint( pObj, &mrViewShell );
3246 if ( !bPaint )
3248 return;
3251 sdr::contact::ViewObjectContactRedirector::createRedirectedPrimitive2DSequence(
3252 rOriginal, rDisplayInfo, rVisitor );
3256 } // end of anonymous namespace
3257 // <--
3260 * Paint once for every visible page which is touched by Rect
3262 * 1. Paint borders and backgrounds
3263 * 2. Paint the draw layer (frames and drawing objects) that is
3264 * below the document (hell)
3265 * 3. Paint the document content (text)
3266 * 4. Paint the draw layer that is above the document
3268 void SwRootFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const& rRect, SwPrintData const*const pPrintData) const
3270 OSL_ENSURE( Lower() && Lower()->IsPageFrame(), "Lower of root is no page." );
3272 PROTOCOL( this, PROT::FileInit, DbgAction::NONE, nullptr)
3274 bool bResetRootPaint = false;
3275 SwViewShell *pSh = mpCurrShell;
3277 if ( pSh->GetWin() )
3279 if ( pSh->GetOut() == pSh->GetWin()->GetOutDev() && !pSh->GetWin()->IsVisible() )
3281 return;
3283 if (SwRootFrame::s_isInPaint)
3285 SwPaintQueue::Add( pSh, rRect );
3286 return;
3289 else
3290 SwRootFrame::s_isInPaint = bResetRootPaint = true;
3292 std::unique_ptr<SwSavePaintStatics> pStatics;
3293 if ( gProp.pSGlobalShell )
3294 pStatics.reset(new SwSavePaintStatics());
3295 gProp.pSGlobalShell = pSh;
3297 if( !pSh->GetWin() )
3298 gProp.pSProgress = SfxProgress::GetActiveProgress( static_cast<SfxObjectShell*>(pSh->GetDoc()->GetDocShell()) );
3300 ::SwCalcPixStatics( pSh->GetOut() );
3301 aGlobalRetoucheColor = pSh->Imp()->GetRetoucheColor();
3303 // Copy rRect; for one, rRect could become dangling during the below action, and for another it
3304 // needs to be copied to aRect anyway as that is modified further down below:
3305 SwRect aRect( rRect );
3307 //Trigger an action to clear things up if needed.
3308 //Using this trick we can ensure that all values are valid in all paints -
3309 //no problems, no special case(s).
3310 // #i92745#
3311 // Extend check on certain states of the 'current' <SwViewShell> instance to
3312 // all existing <SwViewShell> instances.
3313 bool bPerformLayoutAction( true );
3315 for(SwViewShell& rTmpViewShell : pSh->GetRingContainer())
3317 if ( rTmpViewShell.IsInEndAction() ||
3318 rTmpViewShell.IsPaintInProgress() ||
3319 ( rTmpViewShell.Imp()->IsAction() &&
3320 rTmpViewShell.Imp()->GetLayAction().IsActionInProgress() ) )
3322 bPerformLayoutAction = false;
3325 if(!bPerformLayoutAction)
3326 break;
3329 if ( bPerformLayoutAction )
3331 const_cast<SwRootFrame*>(this)->ResetTurbo();
3332 SwLayAction aAction( const_cast<SwRootFrame*>(this), pSh->Imp() );
3333 aAction.SetPaint( false );
3334 aAction.SetComplete( false );
3335 aAction.SetReschedule( gProp.pSProgress != nullptr );
3336 aAction.Action(&rRenderContext);
3337 ResetTurboFlag();
3338 if ( !pSh->ActionPend() )
3339 pSh->Imp()->DeletePaintRegion();
3342 aRect.Intersection( pSh->VisArea() );
3344 const bool bExtraData = ::IsExtraData( GetFormat()->GetDoc() );
3346 gProp.pSLines.reset(new SwLineRects); // Container for borders.
3348 // #104289#. During painting, something (OLE) can
3349 // load the linguistic, which in turn can cause a reformat
3350 // of the document. Dangerous! We better set this flag to
3351 // avoid the reformat.
3352 const bool bOldAction = IsCallbackActionEnabled();
3353 const_cast<SwRootFrame*>(this)->SetCallbackActionEnabled( false );
3355 const SwPageFrame *pPage = pSh->Imp()->GetFirstVisPage(&rRenderContext);
3357 // #126222. The positions of headers and footers of the previous
3358 // pages have to be updated, else these headers and footers could
3359 // get visible at a wrong position.
3360 const SwPageFrame *pPageDeco = static_cast<const SwPageFrame*>(pPage->GetPrev());
3361 while (pPageDeco)
3363 pPageDeco->PaintDecorators();
3364 OSL_ENSURE(!pPageDeco->GetPrev() || pPageDeco->GetPrev()->IsPageFrame(),
3365 "Neighbour of page is not a page.");
3366 pPageDeco = static_cast<const SwPageFrame*>(pPageDeco->GetPrev());
3369 const bool bBookMode = gProp.pSGlobalShell->GetViewOptions()->IsViewLayoutBookMode();
3370 if ( bBookMode && pPage->GetPrev() && static_cast<const SwPageFrame*>(pPage->GetPrev())->IsEmptyPage() )
3371 pPage = static_cast<const SwPageFrame*>(pPage->GetPrev());
3373 // #i68597#
3374 const bool bGridPainting(pSh->GetWin() && pSh->Imp()->HasDrawView() && pSh->Imp()->GetDrawView()->IsGridVisible());
3376 // Hide all page break controls before showing them again
3377 SwWrtShell* pWrtSh = dynamic_cast< SwWrtShell* >( gProp.pSGlobalShell );
3378 if ( pWrtSh )
3380 SwEditWin& rEditWin = pWrtSh->GetView().GetEditWin();
3381 SwFrameControlsManager& rMngr = rEditWin.GetFrameControlsManager();
3382 const SwPageFrame* pHiddenPage = pPage;
3383 while ( pHiddenPage->GetPrev() != nullptr )
3385 pHiddenPage = static_cast< const SwPageFrame* >( pHiddenPage->GetPrev() );
3386 SwFrameControlPtr pControl = rMngr.GetControl( FrameControlType::PageBreak, pHiddenPage );
3387 if ( pControl )
3388 pControl->ShowAll( false );
3392 // #i76669#
3393 SwViewObjectContactRedirector aSwRedirector( *pSh );
3395 while ( pPage )
3397 const bool bPaintRightShadow = pPage->IsRightShadowNeeded();
3398 const bool bPaintLeftShadow = pPage->IsLeftShadowNeeded();
3399 const bool bRightSidebar = pPage->SidebarPosition() == sw::sidebarwindows::SidebarPosition::RIGHT;
3401 if ( !pPage->IsEmptyPage() )
3403 SwRect aPaintRect;
3404 SwPageFrame::GetBorderAndShadowBoundRect( pPage->getFrameArea(), pSh, &rRenderContext, aPaintRect,
3405 bPaintLeftShadow, bPaintRightShadow, bRightSidebar );
3407 if ( aRect.Overlaps( aPaintRect ) )
3409 if ( pSh->GetWin() )
3411 gProp.pSSubsLines.reset(new SwSubsRects);
3412 gProp.pSSpecSubsLines.reset(new SwSubsRects);
3414 gProp.pBLines.reset(new BorderLines);
3416 aPaintRect.Intersection_( aRect );
3418 if ( bExtraData &&
3419 pSh->GetWin() && pSh->IsInEndAction() )
3421 // enlarge paint rectangle to complete page width, subtract
3422 // current paint area and invalidate the resulting region.
3423 SwRectFnSet aRectFnSet(pPage);
3424 SwRect aPageRectTemp( aPaintRect );
3425 aRectFnSet.SetLeftAndWidth( aPageRectTemp,
3426 aRectFnSet.GetLeft(pPage->getFrameArea()),
3427 aRectFnSet.GetWidth(pPage->getFrameArea()) );
3428 aPageRectTemp.Intersection_( pSh->VisArea() );
3429 vcl::Region aPageRectRegion( aPageRectTemp.SVRect() );
3430 aPageRectRegion.Exclude( aPaintRect.SVRect() );
3431 pSh->GetWin()->Invalidate( aPageRectRegion, InvalidateFlags::Children );
3434 // #i80793#
3435 // enlarge paint rectangle for objects overlapping the same pixel
3436 // in all cases and before the DrawingLayer overlay is initialized.
3437 lcl_AdjustRectToPixelSize( aPaintRect, *(pSh->GetOut()) );
3439 // #i68597#
3440 // moved paint pre-process for DrawingLayer overlay here since the above
3441 // code dependent from bExtraData may expand the PaintRect
3443 // #i75172# if called from SwViewShell::ImplEndAction it should no longer
3444 // really be used but handled by SwViewShell::ImplEndAction already
3445 const vcl::Region aDLRegion(aPaintRect.SVRect());
3446 pSh->DLPrePaint2(aDLRegion);
3449 if(OUTDEV_WINDOW == gProp.pSGlobalShell->GetOut()->GetOutDevType())
3451 // changed method SwLayVout::Enter(..)
3452 // 2nd parameter is no longer <const> and will be set to the
3453 // rectangle the virtual output device is calculated from <aPaintRect>,
3454 // if the virtual output is used.
3455 s_pVout->Enter(pSh, aPaintRect, !s_isNoVirDev);
3457 // Adjust paint rectangle to pixel size
3458 // Thus, all objects overlapping on pixel level with the unadjusted
3459 // paint rectangle will be considered in the paint.
3460 lcl_AdjustRectToPixelSize( aPaintRect, *(pSh->GetOut()) );
3463 // maybe this can be put in the above scope. Since we are not sure, just leave it ATM
3464 s_pVout->SetOrgRect( aPaintRect );
3466 // determine background color of page for <PaintLayer> method
3467 // calls, paint <hell> or <heaven>
3468 const Color aPageBackgrdColor(pPage->GetDrawBackgroundColor());
3470 pPage->PaintBaBo( aPaintRect, pPage );
3472 if ( pSh->Imp()->HasDrawView() )
3474 gProp.pSLines->LockLines( true );
3475 const IDocumentDrawModelAccess& rIDDMA = pSh->getIDocumentDrawModelAccess();
3476 pSh->Imp()->PaintLayer( rIDDMA.GetHellId(),
3477 pPrintData,
3478 *pPage, pPage->getFrameArea(),
3479 &aPageBackgrdColor,
3480 pPage->IsRightToLeft(),
3481 &aSwRedirector );
3482 gProp.pSLines->PaintLines( pSh->GetOut(), gProp );
3483 gProp.pSLines->LockLines( false );
3486 if ( pSh->GetDoc()->GetDocumentSettingManager().get( DocumentSettingId::BACKGROUND_PARA_OVER_DRAWINGS ) )
3487 pPage->PaintBaBo( aPaintRect, pPage, /*bOnlyTextBackground=*/true );
3489 if( pSh->GetWin() )
3491 // collect sub-lines
3492 pPage->RefreshSubsidiary( aPaintRect );
3493 // paint special sub-lines
3494 gProp.pSSpecSubsLines->PaintSubsidiary( pSh->GetOut(), nullptr, gProp );
3497 pPage->PaintSwFrame( rRenderContext, aPaintRect );
3499 // no paint of page border and shadow, if writer is in place mode.
3500 if( pSh->GetWin() && pSh->GetDoc()->GetDocShell() &&
3501 !pSh->GetDoc()->GetDocShell()->IsInPlaceActive() )
3503 SwPageFrame::PaintBorderAndShadow( pPage->getFrameArea(), pSh, bPaintLeftShadow, bPaintRightShadow, bRightSidebar );
3504 SwPageFrame::PaintNotesSidebar( pPage->getFrameArea(), pSh, pPage->GetPhyPageNum(), bRightSidebar);
3507 gProp.pSLines->PaintLines( pSh->GetOut(), gProp );
3508 if ( pSh->GetWin() )
3510 gProp.pSSubsLines->PaintSubsidiary( pSh->GetOut(), gProp.pSLines.get(), gProp );
3511 gProp.pSSubsLines.reset();
3512 gProp.pSSpecSubsLines.reset();
3514 // fdo#42750: delay painting these until after subsidiary lines
3515 // fdo#45562: delay painting these until after hell layer
3516 // fdo#47717: but do it before heaven layer
3518 SwTaggedPDFHelper tag(nullptr, nullptr, nullptr, rRenderContext);
3519 ProcessPrimitives(gProp.pBLines->GetBorderLines_Clear());
3522 if ( pSh->Imp()->HasDrawView() )
3524 pSh->Imp()->PaintLayer( pSh->GetDoc()->getIDocumentDrawModelAccess().GetHeavenId(),
3525 pPrintData,
3526 *pPage, pPage->getFrameArea(),
3527 &aPageBackgrdColor,
3528 pPage->IsRightToLeft(),
3529 &aSwRedirector );
3532 if ( bExtraData )
3533 pPage->RefreshExtraData( aPaintRect );
3535 gProp.pBLines.reset();
3536 s_pVout->Leave();
3538 // #i68597#
3539 // needed to move grid painting inside Begin/EndDrawLayer bounds and to change
3540 // output rect for it accordingly
3541 if(bGridPainting)
3543 SdrPaintView* pPaintView = pSh->Imp()->GetDrawView();
3544 SdrPageView* pPageView = pPaintView->GetSdrPageView();
3545 pPageView->DrawPageViewGrid(*pSh->GetOut(), aPaintRect.SVRect(), pSh->GetViewOptions()->GetTextGridColor() );
3548 // #i68597#
3549 // moved paint post-process for DrawingLayer overlay here, see above
3551 pSh->DLPostPaint2(true);
3555 pPage->PaintDecorators( );
3556 pPage->PaintBreak();
3558 else if ( bBookMode && pSh->GetWin() && !pSh->GetDoc()->GetDocShell()->IsInPlaceActive() )
3560 // paint empty page
3561 SwRect aPaintRect;
3562 SwRect aEmptyPageRect( pPage->getFrameArea() );
3564 // code from vprint.cxx
3565 const SwPageFrame& rFormatPage = pPage->GetFormatPage();
3566 aEmptyPageRect.SSize( rFormatPage.getFrameArea().SSize() );
3568 SwPageFrame::GetBorderAndShadowBoundRect( aEmptyPageRect, pSh, &rRenderContext, aPaintRect,
3569 bPaintLeftShadow, bPaintRightShadow, bRightSidebar );
3570 aPaintRect.Intersection_( aRect );
3572 if ( aRect.Overlaps( aEmptyPageRect ) )
3574 // #i75172# if called from SwViewShell::ImplEndAction it should no longer
3575 // really be used but handled by SwViewShell::ImplEndAction already
3577 const vcl::Region aDLRegion(aPaintRect.SVRect());
3578 pSh->DLPrePaint2(aDLRegion);
3581 if( pSh->GetOut()->GetFillColor() != aGlobalRetoucheColor )
3582 pSh->GetOut()->SetFillColor( aGlobalRetoucheColor );
3583 // No line color
3584 pSh->GetOut()->SetLineColor();
3585 // Use aligned page rectangle
3587 SwRect aTmpPageRect( aEmptyPageRect );
3588 ::SwAlignRect( aTmpPageRect, pSh, &rRenderContext );
3589 aEmptyPageRect = aTmpPageRect;
3592 pSh->GetOut()->DrawRect( aEmptyPageRect.SVRect() );
3594 // paint empty page text
3595 const vcl::Font& rEmptyPageFont = SwPageFrame::GetEmptyPageFont();
3596 const vcl::Font aOldFont( pSh->GetOut()->GetFont() );
3598 pSh->GetOut()->SetFont( rEmptyPageFont );
3599 pSh->GetOut()->DrawText( aEmptyPageRect.SVRect(), SwResId( STR_EMPTYPAGE ),
3600 DrawTextFlags::VCenter |
3601 DrawTextFlags::Center |
3602 DrawTextFlags::Clip );
3604 pSh->GetOut()->SetFont( aOldFont );
3605 // paint shadow and border for empty page
3606 SwPageFrame::PaintBorderAndShadow( aEmptyPageRect, pSh, bPaintLeftShadow, bPaintRightShadow, bRightSidebar );
3607 SwPageFrame::PaintNotesSidebar( aEmptyPageRect, pSh, pPage->GetPhyPageNum(), bRightSidebar);
3610 pSh->DLPostPaint2(true);
3615 OSL_ENSURE( !pPage->GetNext() || pPage->GetNext()->IsPageFrame(),
3616 "Neighbour of page is not a page." );
3617 pPage = static_cast<const SwPageFrame*>(pPage->GetNext());
3620 gProp.pSLines.reset();
3622 if ( bResetRootPaint )
3623 SwRootFrame::s_isInPaint = false;
3624 if ( pStatics )
3625 pStatics.reset();
3626 else
3628 gProp.pSProgress = nullptr;
3629 gProp.pSGlobalShell = nullptr;
3632 const_cast<SwRootFrame*>(this)->SetCallbackActionEnabled( bOldAction );
3635 static void lcl_EmergencyFormatFootnoteCont( SwFootnoteContFrame *pCont )
3637 vcl::RenderContext* pRenderContext = pCont->getRootFrame()->GetCurrShell()->GetOut();
3639 //It's possible that the Cont will get destroyed.
3640 SwContentFrame *pCnt = pCont->ContainsContent();
3641 while ( pCnt && pCnt->IsInFootnote() )
3643 pCnt->Calc(pRenderContext);
3644 pCnt = pCnt->GetNextContentFrame();
3648 namespace {
3650 class SwShortCut
3652 SwRectDist m_fnCheck;
3653 tools::Long m_nLimit;
3655 public:
3656 SwShortCut( const SwFrame& rFrame, const SwRect& rRect );
3657 bool Stop(const SwRect& rRect) const { return (rRect.*m_fnCheck)(m_nLimit) > 0; }
3662 SwShortCut::SwShortCut( const SwFrame& rFrame, const SwRect& rRect )
3664 bool bVert = rFrame.IsVertical();
3665 bool bR2L = rFrame.IsRightToLeft();
3666 if( rFrame.IsNeighbourFrame() && bVert == bR2L )
3668 if( bVert )
3670 m_fnCheck = &SwRect::GetBottomDistance;
3671 m_nLimit = rRect.Top();
3673 else
3675 m_fnCheck = &SwRect::GetLeftDistance;
3676 m_nLimit = rRect.Left() + rRect.Width();
3679 else if( bVert == rFrame.IsNeighbourFrame() )
3681 m_fnCheck = &SwRect::GetTopDistance;
3682 m_nLimit = rRect.Top() + rRect.Height();
3684 else
3686 if ( rFrame.IsVertLR() )
3688 m_fnCheck = &SwRect::GetLeftDistance;
3689 m_nLimit = rRect.Right();
3691 else
3693 m_fnCheck = &SwRect::GetRightDistance;
3694 m_nLimit = rRect.Left();
3699 void SwLayoutFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const& rRect, SwPrintData const*const) const
3701 // #i16816# tagged pdf support
3702 Frame_Info aFrameInfo(*this, false);
3703 SwTaggedPDFHelper aTaggedPDFHelper( nullptr, &aFrameInfo, nullptr, rRenderContext );
3704 ::std::optional<SwTaggedPDFHelper> oTaggedLink;
3705 if (IsFlyFrame())
3707 // tdf#154939 Link nested inside Figure
3708 auto const pItem(GetFormat()->GetAttrSet().GetItemIfSet(RES_URL));
3709 if (pItem && !pItem->GetURL().isEmpty())
3711 Frame_Info linkInfo(*this, true);
3712 oTaggedLink.emplace(nullptr, &linkInfo, nullptr, rRenderContext);
3716 const SwFrame *pFrame = Lower();
3717 if ( !pFrame )
3718 return;
3720 SwFrameDeleteGuard g(const_cast<SwLayoutFrame*>(this)); // lock because Calc() and recursion
3721 SwShortCut aShortCut( *pFrame, rRect );
3722 bool bCnt = pFrame->IsContentFrame();
3723 if ( bCnt )
3724 pFrame->Calc(&rRenderContext);
3726 if ( pFrame->IsFootnoteContFrame() )
3728 ::lcl_EmergencyFormatFootnoteCont( const_cast<SwFootnoteContFrame*>(static_cast<const SwFootnoteContFrame*>(pFrame)) );
3729 pFrame = Lower();
3732 const SwPageFrame *pPage = nullptr;
3733 bool bWin = gProp.pSGlobalShell->GetWin() != nullptr;
3734 if (comphelper::LibreOfficeKit::isTiledPainting())
3735 // Tiled rendering is similar to printing in this case: painting transparently multiple
3736 // times will result in darker colors: avoid that.
3737 bWin = false;
3739 while ( IsAnLower( pFrame ) )
3741 SwRect aPaintRect( pFrame->GetPaintArea() );
3742 if( aShortCut.Stop( aPaintRect ) )
3743 break;
3744 if ( bCnt && gProp.pSProgress )
3745 SfxProgress::Reschedule();
3747 //We need to retouch if a frame explicitly requests it.
3748 //First do the retouch, because this could flatten the borders.
3749 if ( pFrame->IsRetouche() )
3751 if ( pFrame->IsRetoucheFrame() && bWin && !pFrame->GetNext() )
3753 if ( !pPage )
3754 pPage = FindPageFrame();
3755 pFrame->Retouch( pPage, rRect );
3757 pFrame->ResetRetouche();
3760 if ( rRect.Overlaps( aPaintRect ) )
3762 if ( bCnt && pFrame->IsCompletePaint() &&
3763 !rRect.Contains( aPaintRect ) && Application::AnyInput( VclInputFlags::KEYBOARD ) )
3765 //fix(8104): It may happen, that the processing wasn't complete
3766 //but some parts of the paragraph were still repainted.
3767 //This could lead to the situation, that other parts of the
3768 //paragraph won't be repainted at all. The only solution seems
3769 //to be an invalidation of the window.
3770 //To not make it too severe the rectangle is limited by
3771 //painting the desired part and only invalidating the
3772 //remaining paragraph parts.
3773 if ( aPaintRect.Left() == rRect.Left() &&
3774 aPaintRect.Right() == rRect.Right() )
3776 aPaintRect.Bottom( rRect.Top() - 1 );
3777 if ( aPaintRect.Height() > 0 )
3778 gProp.pSGlobalShell->InvalidateWindows(aPaintRect);
3779 aPaintRect.Top( rRect.Bottom() + 1 );
3780 aPaintRect.Bottom( pFrame->getFrameArea().Bottom() );
3781 if ( aPaintRect.Height() > 0 )
3782 gProp.pSGlobalShell->InvalidateWindows(aPaintRect);
3783 aPaintRect.Top( pFrame->getFrameArea().Top() );
3784 aPaintRect.Bottom( pFrame->getFrameArea().Bottom() );
3786 else
3788 gProp.pSGlobalShell->InvalidateWindows( aPaintRect );
3789 pFrame = pFrame->GetNext();
3790 if ( pFrame )
3792 bCnt = pFrame->IsContentFrame();
3793 if ( bCnt )
3794 pFrame->Calc(&rRenderContext);
3796 continue;
3799 pFrame->ResetCompletePaint();
3800 aPaintRect.Intersection_( rRect );
3802 pFrame->PaintSwFrame( rRenderContext, aPaintRect );
3804 if ( Lower() && Lower()->IsColumnFrame() )
3806 //Paint the column separator line if needed. The page is
3807 //responsible for the page frame - not the upper.
3808 const SwFrameFormat *pFormat = GetUpper() && GetUpper()->IsPageFrame()
3809 ? GetUpper()->GetFormat()
3810 : GetFormat();
3811 const SwFormatCol &rCol = pFormat->GetCol();
3812 if ( rCol.GetLineAdj() != COLADJ_NONE )
3814 if ( !pPage )
3815 pPage = pFrame->FindPageFrame();
3817 PaintColLines( aPaintRect, rCol, pPage );
3821 if ( !bCnt && pFrame->GetNext() && pFrame->GetNext()->IsFootnoteContFrame() )
3822 ::lcl_EmergencyFormatFootnoteCont( const_cast<SwFootnoteContFrame*>(static_cast<const SwFootnoteContFrame*>(pFrame->GetNext())) );
3824 pFrame = pFrame->GetNext();
3826 if ( pFrame )
3828 bCnt = pFrame->IsContentFrame();
3829 if ( bCnt )
3830 pFrame->Calc(&rRenderContext);
3835 static drawinglayer::primitive2d::Primitive2DContainer lcl_CreateDashedIndicatorPrimitive(
3836 const basegfx::B2DPoint& rStart, const basegfx::B2DPoint& rEnd,
3837 basegfx::BColor aColor )
3839 drawinglayer::primitive2d::Primitive2DContainer aSeq( 1 );
3841 std::vector< double > aStrokePattern;
3842 basegfx::B2DPolygon aLinePolygon;
3843 aLinePolygon.append(rStart);
3844 aLinePolygon.append(rEnd);
3846 const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
3847 if ( rSettings.GetHighContrastMode( ) )
3849 // Only a solid line in high contrast mode
3850 aColor = rSettings.GetDialogTextColor().getBColor();
3852 else
3854 // Get a color for the contrast
3855 basegfx::BColor aHslLine = basegfx::utils::rgb2hsl( aColor );
3856 double nLuminance = aHslLine.getZ() * 2.5;
3857 if ( nLuminance == 0 )
3858 nLuminance = 0.5;
3859 else if ( nLuminance >= 1.0 )
3860 nLuminance = aHslLine.getZ() * 0.4;
3861 aHslLine.setZ( nLuminance );
3862 const basegfx::BColor aOtherColor = basegfx::utils::hsl2rgb( aHslLine );
3864 // Compute the plain line
3865 aSeq[0] =
3866 new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(
3867 aLinePolygon, aOtherColor );
3869 // Dashed line in twips
3870 aStrokePattern.push_back( 40 );
3871 aStrokePattern.push_back( 40 );
3873 aSeq.resize( 2 );
3876 // Compute the dashed line primitive
3877 aSeq[ aSeq.size( ) - 1 ] =
3878 new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D (
3879 basegfx::B2DPolyPolygon( aLinePolygon ),
3880 drawinglayer::attribute::LineAttribute( aColor ),
3881 drawinglayer::attribute::StrokeAttribute( std::move(aStrokePattern) ) );
3884 return aSeq;
3887 void SwPageFrame::PaintBreak( ) const
3889 if ( gProp.pSGlobalShell->GetOut()->GetOutDevType() == OUTDEV_PRINTER ||
3890 gProp.pSGlobalShell->GetViewOptions()->IsPDFExport() ||
3891 gProp.pSGlobalShell->GetViewOptions()->IsReadonly() ||
3892 gProp.pSGlobalShell->IsPreview() )
3893 return;
3895 const SwFrame* pBodyFrame = Lower();
3896 while ( pBodyFrame && !pBodyFrame->IsBodyFrame() )
3897 pBodyFrame = pBodyFrame->GetNext();
3899 if ( pBodyFrame )
3901 const SwLayoutFrame* pLayBody = static_cast< const SwLayoutFrame* >( pBodyFrame );
3902 const SwFlowFrame *pFlowFrame = pLayBody->ContainsContent();
3904 // Test if the first node is a table
3905 const SwFrame* pFirstFrame = pLayBody->Lower();
3906 if ( pFirstFrame && pFirstFrame->IsTabFrame() )
3907 pFlowFrame = static_cast< const SwTabFrame* >( pFirstFrame );
3909 SwWrtShell* pWrtSh = dynamic_cast< SwWrtShell* >( gProp.pSGlobalShell );
3910 if ( pWrtSh )
3912 SwEditWin& rEditWin = pWrtSh->GetView().GetEditWin();
3913 SwFrameControlsManager& rMngr = rEditWin.GetFrameControlsManager();
3915 if ( pFlowFrame && pFlowFrame->IsPageBreak( true ) )
3916 rMngr.SetPageBreakControl( this );
3917 else
3918 rMngr.RemoveControlsByType( FrameControlType::PageBreak, this );
3921 SwLayoutFrame::PaintBreak( );
3924 void SwColumnFrame::PaintBreak( ) const
3926 if ( gProp.pSGlobalShell->GetOut()->GetOutDevType() == OUTDEV_PRINTER ||
3927 gProp.pSGlobalShell->GetViewOptions()->IsPDFExport() ||
3928 gProp.pSGlobalShell->GetViewOptions()->IsReadonly() ||
3929 gProp.pSGlobalShell->IsPreview() )
3930 return;
3932 const SwFrame* pBodyFrame = Lower();
3933 while ( pBodyFrame && !pBodyFrame->IsBodyFrame() )
3934 pBodyFrame = pBodyFrame->GetNext();
3936 if ( !pBodyFrame )
3937 return;
3939 const SwContentFrame *pCnt = static_cast< const SwLayoutFrame* >( pBodyFrame )->ContainsContent();
3940 if ( !(pCnt && pCnt->IsColBreak( true )) )
3941 return;
3943 // Paint the break only if:
3944 // * Not in header footer edition, to avoid conflicts with the
3945 // header/footer marker
3946 // * Non-printing characters are shown, as this is more consistent
3947 // with other formatting marks
3948 if ( !(!gProp.pSGlobalShell->IsShowHeaderFooterSeparator( FrameControlType::Header ) &&
3949 !gProp.pSGlobalShell->IsShowHeaderFooterSeparator( FrameControlType::Footer ) &&
3950 gProp.pSGlobalShell->GetViewOptions()->IsLineBreak()) )
3951 return;
3953 SwRect aRect( pCnt->getFramePrintArea() );
3954 aRect.Pos() += pCnt->getFrameArea().Pos();
3956 // Draw the line
3957 basegfx::B2DPoint aStart( double( aRect.Left() ), aRect.Top() );
3958 basegfx::B2DPoint aEnd( double( aRect.Right() ), aRect.Top() );
3959 double nWidth = aRect.Width();
3960 if ( IsVertical( ) )
3962 aStart = basegfx::B2DPoint( double( aRect.Right() ), double( aRect.Top() ) );
3963 aEnd = basegfx::B2DPoint( double( aRect.Right() ), double( aRect.Bottom() ) );
3964 nWidth = aRect.Height();
3967 basegfx::BColor aLineColor = gProp.pSGlobalShell->GetViewOptions()->GetPageBreakColor().getBColor();
3969 drawinglayer::primitive2d::Primitive2DContainer aSeq =
3970 lcl_CreateDashedIndicatorPrimitive( aStart, aEnd, aLineColor );
3972 // Add the text above
3973 OUString aBreakText = SwResId(STR_COLUMN_BREAK);
3975 basegfx::B2DVector aFontSize;
3976 OutputDevice* pOut = gProp.pSGlobalShell->GetOut();
3977 vcl::Font aFont = pOut->GetSettings().GetStyleSettings().GetToolFont();
3978 aFont.SetFontHeight( 8 * 20 );
3979 pOut->SetFont( aFont );
3980 drawinglayer::attribute::FontAttribute aFontAttr = drawinglayer::primitive2d::getFontAttributeFromVclFont(
3981 aFontSize, aFont, IsRightToLeft(), false );
3983 tools::Rectangle aTextRect;
3984 pOut->GetTextBoundRect( aTextRect, aBreakText );
3985 tools::Long nTextOff = ( nWidth - aTextRect.GetWidth() ) / 2;
3987 basegfx::B2DHomMatrix aTextMatrix( basegfx::utils::createScaleTranslateB2DHomMatrix(
3988 aFontSize.getX(), aFontSize.getY(),
3989 aRect.Left() + nTextOff, aRect.Top() ) );
3990 if ( IsVertical() )
3992 aTextMatrix = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix (
3993 aFontSize.getX(), aFontSize.getY(), 0.0, M_PI_2,
3994 aRect.Right(), aRect.Top() + nTextOff );
3997 aSeq.push_back(
3998 new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
3999 aTextMatrix,
4000 aBreakText, 0, aBreakText.getLength(),
4001 std::vector< double >(),
4003 std::move(aFontAttr),
4004 lang::Locale(),
4005 aLineColor ) );
4007 ProcessPrimitives( aSeq );
4010 void SwLayoutFrame::PaintBreak( ) const
4012 const SwFrame* pFrame = Lower();
4013 while ( pFrame )
4015 if ( pFrame->IsLayoutFrame() )
4016 static_cast< const SwLayoutFrame*>( pFrame )->PaintBreak( );
4017 pFrame = pFrame->GetNext();
4021 void SwPageFrame::PaintDecorators( ) const
4023 SwWrtShell* pWrtSh = dynamic_cast< SwWrtShell* >( gProp.pSGlobalShell );
4024 if ( !pWrtSh )
4025 return;
4027 SwEditWin& rEditWin = pWrtSh->GetView().GetEditWin();
4029 const SwLayoutFrame* pBody = FindBodyCont();
4030 if ( !pBody )
4031 return;
4033 SwRect aBodyRect( pBody->getFrameArea() );
4035 if ( !(gProp.pSGlobalShell->GetOut()->GetOutDevType() != OUTDEV_PRINTER &&
4036 !gProp.pSGlobalShell->GetViewOptions()->IsPDFExport() &&
4037 !gProp.pSGlobalShell->IsPreview() &&
4038 !gProp.pSGlobalShell->GetViewOptions()->IsReadonly() &&
4039 !gProp.pSGlobalShell->GetViewOptions()->getBrowseMode() &&
4040 ( gProp.pSGlobalShell->IsShowHeaderFooterSeparator( FrameControlType::Header ) ||
4041 gProp.pSGlobalShell->IsShowHeaderFooterSeparator( FrameControlType::Footer ) )) )
4042 return;
4044 bool bRtl = AllSettings::GetLayoutRTL();
4045 const SwRect& rVisArea = gProp.pSGlobalShell->VisArea();
4046 tools::Long nXOff = std::min( aBodyRect.Right(), rVisArea.Right() );
4047 if ( bRtl )
4048 nXOff = std::max( aBodyRect.Left(), rVisArea.Left() );
4050 // Header
4051 if ( gProp.pSGlobalShell->IsShowHeaderFooterSeparator( FrameControlType::Header ) )
4053 const SwFrame* pHeaderFrame = Lower();
4054 if ( !pHeaderFrame->IsHeaderFrame() )
4055 pHeaderFrame = nullptr;
4057 tools::Long nHeaderYOff = aBodyRect.Top();
4058 Point nOutputOff = rEditWin.LogicToPixel( Point( nXOff, nHeaderYOff ) );
4059 rEditWin.GetFrameControlsManager().SetHeaderFooterControl( this, FrameControlType::Header, nOutputOff );
4062 // Footer
4063 if ( !gProp.pSGlobalShell->IsShowHeaderFooterSeparator( FrameControlType::Footer ) )
4064 return;
4066 const SwFrame* pFootnoteContFrame = Lower();
4067 while ( pFootnoteContFrame )
4069 if ( pFootnoteContFrame->IsFootnoteContFrame() )
4070 aBodyRect.AddBottom( pFootnoteContFrame->getFrameArea().Bottom() - aBodyRect.Bottom() );
4071 pFootnoteContFrame = pFootnoteContFrame->GetNext();
4074 tools::Long nFooterYOff = aBodyRect.Bottom();
4075 Point nOutputOff = rEditWin.LogicToPixel( Point( nXOff, nFooterYOff ) );
4076 rEditWin.GetFrameControlsManager().SetHeaderFooterControl( this, FrameControlType::Footer, nOutputOff );
4080 * For feature #99657#
4082 * OD 12.08.2002
4083 * determines, if background of fly frame has to be drawn transparent
4084 * declaration found in /core/inc/flyfrm.cxx
4086 * OD 08.10.2002 #103898# - If the background of the fly frame itself is not
4087 * transparent and the background is inherited from its parent/grandparent,
4088 * the background brush, used for drawing, has to be investigated for transparency.
4090 * @return true, if background is transparent drawn
4092 bool SwFlyFrame::IsBackgroundTransparent() const
4094 bool bBackgroundTransparent = GetFormat()->IsBackgroundTransparent();
4095 if ( !bBackgroundTransparent &&
4096 GetFormat()->IsBackgroundBrushInherited() )
4098 const SvxBrushItem* pBackgroundBrush = nullptr;
4099 std::optional<Color> xSectionTOXColor;
4100 SwRect aDummyRect;
4101 drawinglayer::attribute::SdrAllFillAttributesHelperPtr aFillAttributes;
4103 if ( GetBackgroundBrush( aFillAttributes, pBackgroundBrush, xSectionTOXColor, aDummyRect, false, /*bConsiderTextBox=*/false) )
4105 if ( xSectionTOXColor &&
4106 (xSectionTOXColor->IsTransparent()) &&
4107 (xSectionTOXColor != COL_TRANSPARENT) )
4109 bBackgroundTransparent = true;
4111 else if(aFillAttributes && aFillAttributes->isUsed())
4113 bBackgroundTransparent = aFillAttributes->isTransparent();
4115 else if ( pBackgroundBrush )
4117 if ( (pBackgroundBrush->GetColor().IsTransparent()) &&
4118 (pBackgroundBrush->GetColor() != COL_TRANSPARENT) )
4120 bBackgroundTransparent = true;
4122 else
4124 const GraphicObject *pTmpGrf =
4125 pBackgroundBrush->GetGraphicObject();
4126 if ( pTmpGrf &&
4127 (pTmpGrf->GetAttr().IsTransparent())
4130 bBackgroundTransparent = true;
4137 return bBackgroundTransparent;
4140 bool SwFlyFrame::IsPaint( SdrObject *pObj, const SwViewShell *pSh )
4142 SdrObjUserCall *pUserCall = GetUserCall(pObj);
4144 if ( nullptr == pUserCall )
4145 return true;
4147 //Attribute dependent, don't paint for printer or Preview
4148 bool bPaint = gProp.pSFlyOnlyDraw ||
4149 static_cast<SwContact*>(pUserCall)->GetFormat()->GetPrint().GetValue();
4150 if ( !bPaint )
4151 bPaint = pSh->GetWin() && !pSh->IsPreview();
4153 if ( bPaint )
4155 //The paint may be prevented by the superior Flys.
4156 SwFrame *pAnch = nullptr;
4157 if ( dynamic_cast< const SwFlyDrawObj *>( pObj ) != nullptr ) // i#117962#
4159 bPaint = false;
4161 if ( auto pFlyDraw = dynamic_cast<SwVirtFlyDrawObj *>( pObj ) )
4163 SwFlyFrame *pFly = pFlyDraw->GetFlyFrame();
4164 if ( gProp.pSFlyOnlyDraw && gProp.pSFlyOnlyDraw == pFly )
4165 return true;
4167 //Try to avoid displaying the intermediate stage, Flys which don't
4168 //overlap with the page on which they are anchored won't be
4169 //painted.
4170 //HACK: exception: printing of frames in tables, those can overlap
4171 //a page once in a while when dealing with oversized tables (HTML).
4172 SwPageFrame *pPage = pFly->FindPageFrame();
4173 if ( pPage && pPage->getFrameArea().Overlaps( pFly->getFrameArea() ) )
4175 pAnch = pFly->AnchorFrame();
4179 else
4181 // Consider 'virtual' drawing objects
4182 SwDrawContact* pDrawContact = dynamic_cast<SwDrawContact*>(pUserCall);
4183 pAnch = pDrawContact ? pDrawContact->GetAnchorFrame(pObj) : nullptr;
4184 if ( pAnch )
4186 if ( !pAnch->isFrameAreaPositionValid() )
4187 pAnch = nullptr;
4188 else if ( pSh->GetOut() == pSh->getIDocumentDeviceAccess().getPrinter( false ))
4190 //HACK: we have to omit some of the objects for printing,
4191 //otherwise they would be printed twice.
4192 //The objects should get printed if the TableHack is active
4193 //right now. Afterwards they must not be printed if the
4194 //page over which they float position wise gets printed.
4195 const SwPageFrame *pPage = pAnch->FindPageFrame();
4196 if ( !pPage->getFrameArea().Overlaps( SwRect(pObj->GetCurrentBoundRect()) ) )
4197 pAnch = nullptr;
4200 else
4202 if ( dynamic_cast< const SdrObjGroup *>( pObj ) == nullptr )
4204 OSL_FAIL( "<SwFlyFrame::IsPaint(..)> - paint of drawing object without anchor frame!?" );
4208 if ( pAnch )
4210 if ( pAnch->IsInFly() )
4211 bPaint = SwFlyFrame::IsPaint( pAnch->FindFlyFrame()->GetVirtDrawObj(),
4212 pSh );
4213 else if ( gProp.pSFlyOnlyDraw )
4214 bPaint = false;
4216 else
4217 bPaint = false;
4219 return bPaint;
4222 void SwCellFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const& rRect, SwPrintData const*const) const
4224 if ( GetLayoutRowSpan() >= 1 )
4225 SwLayoutFrame::PaintSwFrame( rRenderContext, rRect );
4228 namespace {
4230 struct BorderLinesGuard
4232 explicit BorderLinesGuard() : m_pBorderLines(std::move(gProp.pBLines))
4234 gProp.pBLines.reset(new BorderLines);
4236 ~BorderLinesGuard()
4238 gProp.pBLines = std::move(m_pBorderLines);
4240 private:
4241 std::unique_ptr<BorderLines> m_pBorderLines;
4246 // set strikethrough for deleted objects anchored to character
4247 void SwFrame::SetDrawObjsAsDeleted( bool bDeleted )
4249 if ( SwSortedObjs *pObjs = GetDrawObjs() )
4251 for (SwAnchoredObject* pAnchoredObj : *pObjs)
4253 if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
4255 pFly->SetDeleted(bDeleted);
4261 void SwFlyFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const& rRect, SwPrintData const*const) const
4263 //optimize thumbnail generation and store procedure to improve odt saving performance, #i120030#
4264 SwViewShell *pShell = getRootFrame()->GetCurrShell();
4265 if (pShell && pShell->GetDoc() && pShell->GetDoc()->GetDocShell())
4267 bool bInGenerateThumbnail = pShell->GetDoc()->GetDocShell()->IsInGenerateAndStoreThumbnail();
4268 if (bInGenerateThumbnail)
4270 const SwRect& aVisRect = pShell->VisArea();
4271 if (!aVisRect.Overlaps(getFrameArea()))
4272 return;
4276 //because of the overlapping of frames and drawing objects the flys have to
4277 //paint their borders (and those of the internal ones) directly.
4278 //e.g. #33066#
4279 gProp.pSLines->LockLines(true);
4280 BorderLinesGuard blg; // this should not paint borders added from PaintBaBo
4282 SwRect aRect( rRect );
4283 aRect.Intersection_( getFrameArea() );
4285 rRenderContext.Push( vcl::PushFlags::CLIPREGION );
4286 rRenderContext.SetClipRegion();
4287 const SwPageFrame* pPage = FindPageFrame();
4289 const SwNoTextFrame *pNoText = Lower() && Lower()->IsNoTextFrame()
4290 ? static_cast<const SwNoTextFrame*>(Lower()) : nullptr;
4292 bool bIsChart = false; //#i102950# don't paint additional borders for charts
4293 //check whether we have a chart
4294 if(pNoText)
4296 const SwNoTextNode* pNoTNd = dynamic_cast<const SwNoTextNode*>(pNoText->GetNode());
4297 if( pNoTNd )
4299 SwOLENode* pOLENd = const_cast<SwOLENode*>(pNoTNd->GetOLENode());
4300 if( pOLENd && pOLENd->GetOLEObj().GetObject().IsChart() )
4301 bIsChart = true;
4306 bool bContour = GetFormat()->GetSurround().IsContour();
4307 tools::PolyPolygon aPoly;
4308 if ( bContour )
4310 // add 2nd parameter with value <true>
4311 // to indicate that method is called for paint in order to avoid
4312 // load of the intrinsic graphic.
4313 bContour = GetContour( aPoly, true );
4316 // #i47804# - distinguish complete background paint
4317 // and margin paint.
4318 // paint complete background for Writer text fly frames
4319 bool bPaintCompleteBack( !pNoText );
4320 // paint complete background for transparent graphic and contour,
4321 // if own background color exists.
4322 const bool bIsGraphicTransparent = pNoText && pNoText->IsTransparent();
4323 if ( !bPaintCompleteBack &&
4324 ( bIsGraphicTransparent|| bContour ) )
4326 const SwFlyFrameFormat* pSwFrameFormat = GetFormat();
4328 if (pSwFrameFormat && pSwFrameFormat->supportsFullDrawingLayerFillAttributeSet())
4330 // check for transparency
4331 const drawinglayer::attribute::SdrAllFillAttributesHelperPtr aFillAttributes(pSwFrameFormat->getSdrAllFillAttributesHelper());
4333 // check if the new fill attributes are used
4334 if(aFillAttributes && aFillAttributes->isUsed())
4336 bPaintCompleteBack = true;
4339 else
4341 std::unique_ptr<SvxBrushItem> aBack = GetFormat()->makeBackgroundBrushItem();
4342 // to determine, if background has to be painted, by checking, if
4343 // background color is not COL_TRANSPARENT ("no fill"/"auto fill")
4344 // or a background graphic exists.
4345 bPaintCompleteBack =
4346 aBack->GetColor() != COL_TRANSPARENT ||
4347 aBack->GetGraphicPos() != GPOS_NONE;
4350 // paint of margin needed.
4351 const bool bPaintMarginOnly( !bPaintCompleteBack &&
4352 getFramePrintArea().SSize() != getFrameArea().SSize() );
4354 // #i47804# - paint background of parent fly frame
4355 // for transparent graphics in layer Hell, if parent fly frame isn't
4356 // in layer Hell. It's only painted the intersection between the
4357 // parent fly frame area and the paint area <aRect>
4358 const IDocumentDrawModelAccess& rIDDMA = GetFormat()->getIDocumentDrawModelAccess();
4360 if (bIsGraphicTransparent &&
4361 GetFormat()->GetDoc()->getIDocumentSettingAccess().get(DocumentSettingId::SUBTRACT_FLYS) &&
4362 GetVirtDrawObj()->GetLayer() == rIDDMA.GetHellId() &&
4363 GetAnchorFrame()->FindFlyFrame() )
4365 const SwFlyFrame* pParentFlyFrame = GetAnchorFrame()->FindFlyFrame();
4366 if ( pParentFlyFrame->GetDrawObj()->GetLayer() !=
4367 rIDDMA.GetHellId() )
4369 SwFlyFrame* pOldRet = gProp.pSRetoucheFly2;
4370 gProp.pSRetoucheFly2 = const_cast<SwFlyFrame*>(this);
4372 SwBorderAttrAccess aAccess( SwFrame::GetCache(), pParentFlyFrame );
4373 const SwBorderAttrs &rAttrs = *aAccess.Get();
4374 SwRect aPaintRect( aRect );
4375 aPaintRect.Intersection_( pParentFlyFrame->getFrameArea() );
4376 pParentFlyFrame->PaintSwFrameBackground( aPaintRect, pPage, rAttrs );
4378 gProp.pSRetoucheFly2 = pOldRet;
4382 if ( bPaintCompleteBack || bPaintMarginOnly )
4384 //#24926# JP 01.02.96, PaintBaBo is here partially so PaintSwFrameShadowAndBorder
4385 //receives the original Rect but PaintSwFrameBackground only the limited
4386 //one.
4388 rRenderContext.Push( vcl::PushFlags::FILLCOLOR|vcl::PushFlags::LINECOLOR );
4389 rRenderContext.SetLineColor();
4391 pPage = FindPageFrame();
4393 SwBorderAttrAccess aAccess( SwFrame::GetCache(), static_cast<SwFrame const *>(this) );
4394 const SwBorderAttrs &rAttrs = *aAccess.Get();
4396 // paint background
4398 SwRegionRects aRegion( aRect );
4399 // #i80822#
4400 // suppress painting of background in printing area for
4401 // non-transparent graphics.
4402 if ( bPaintMarginOnly ||
4403 ( pNoText && !bIsGraphicTransparent ) )
4405 //What we actually want to paint is the small stripe between
4406 //PrtArea and outer border.
4407 SwRect aTmp( getFramePrintArea() ); aTmp += getFrameArea().Pos();
4408 aRegion -= aTmp;
4410 if ( bContour )
4412 rRenderContext.Push();
4413 // #i80822#
4414 // apply clip region under the same conditions, which are
4415 // used in <SwNoTextFrame::PaintSwFrame(..)> to set the clip region
4416 // for painting the graphic/OLE. Thus, the clip region is
4417 // also applied for the PDF export.
4418 SwViewShell *pSh = getRootFrame()->GetCurrShell();
4420 if ( !rRenderContext.GetConnectMetaFile() || !pSh || !pSh->GetWin() )
4422 rRenderContext.SetClipRegion(vcl::Region(aPoly));
4425 for ( size_t i = 0; i < aRegion.size(); ++i )
4427 PaintSwFrameBackground( aRegion[i], pPage, rAttrs, false, true );
4430 rRenderContext.Pop();
4432 else
4434 for ( size_t i = 0; i < aRegion.size(); ++i )
4436 PaintSwFrameBackground( aRegion[i], pPage, rAttrs, false, true );
4441 // paint border before painting background
4442 PaintSwFrameShadowAndBorder(rRect, pPage, rAttrs);
4444 rRenderContext.Pop();
4448 // fly frame will paint it's subsidiary lines and
4449 // the subsidiary lines of its lowers on its own, due to overlapping with
4450 // other fly frames or other objects.
4451 if( gProp.pSGlobalShell->GetWin()
4452 && !bIsChart ) //#i102950# don't paint additional borders for charts
4454 bool bSubsLineRectsCreated;
4455 if ( gProp.pSSubsLines )
4457 // Lock already existing subsidiary lines
4458 gProp.pSSubsLines->LockLines( true );
4459 bSubsLineRectsCreated = false;
4461 else
4463 // create new subsidiary lines
4464 gProp.pSSubsLines.reset(new SwSubsRects);
4465 bSubsLineRectsCreated = true;
4468 bool bSpecSubsLineRectsCreated;
4469 if ( gProp.pSSpecSubsLines )
4471 // Lock already existing special subsidiary lines
4472 gProp.pSSpecSubsLines->LockLines( true );
4473 bSpecSubsLineRectsCreated = false;
4475 else
4477 // create new special subsidiary lines
4478 gProp.pSSpecSubsLines.reset(new SwSubsRects);
4479 bSpecSubsLineRectsCreated = true;
4481 // Add subsidiary lines of fly frame and its lowers
4482 RefreshLaySubsidiary( pPage, aRect );
4483 // paint subsidiary lines of fly frame and its lowers
4484 gProp.pSSpecSubsLines->PaintSubsidiary( &rRenderContext, nullptr, gProp );
4485 gProp.pSSubsLines->PaintSubsidiary(&rRenderContext, gProp.pSLines.get(), gProp);
4486 if ( !bSubsLineRectsCreated )
4487 // unlock subsidiary lines
4488 gProp.pSSubsLines->LockLines( false );
4489 else
4491 // delete created subsidiary lines container
4492 gProp.pSSubsLines.reset();
4495 if ( !bSpecSubsLineRectsCreated )
4496 // unlock special subsidiary lines
4497 gProp.pSSpecSubsLines->LockLines( false );
4498 else
4500 // delete created special subsidiary lines container
4501 gProp.pSSpecSubsLines.reset();
4505 SwLayoutFrame::PaintSwFrame( rRenderContext, aRect );
4507 Validate();
4510 SwTaggedPDFHelper tag(nullptr, nullptr, nullptr, rRenderContext);
4511 // first paint lines added by fly frame paint
4512 // and then unlock other lines.
4513 gProp.pSLines->PaintLines( &rRenderContext, gProp );
4514 gProp.pSLines->LockLines( false );
4515 // have to paint frame borders added in heaven layer here...
4516 ProcessPrimitives(gProp.pBLines->GetBorderLines_Clear());
4519 PaintDecorators();
4521 // crossing out for tracked deletion
4522 if ( GetAuthor() != std::string::npos && IsDeleted() )
4524 tools::Long startX = aRect.Left( ), endX = aRect.Right();
4525 tools::Long startY = aRect.Top( ), endY = aRect.Bottom();
4526 rRenderContext.SetLineColor( SwPostItMgr::GetColorAnchor(GetAuthor()) );
4527 rRenderContext.DrawLine(Point(startX, startY), Point(endX, endY));
4528 rRenderContext.DrawLine(Point(startX, endY), Point(endX, startY));
4531 rRenderContext.Pop();
4533 if ( gProp.pSProgress && pNoText )
4534 SfxProgress::Reschedule();
4537 void SwFlyFrame::PaintDecorators() const
4539 // Show the un-float button
4540 SwWrtShell* pWrtSh = dynamic_cast< SwWrtShell* >( gProp.pSGlobalShell );
4541 if ( pWrtSh )
4543 UpdateUnfloatButton(pWrtSh, IsShowUnfloatButton(pWrtSh));
4547 SwView* SwTextFrame::GetView()
4549 SwWrtShell* pWrtSh = dynamic_cast<SwWrtShell*>(gProp.pSGlobalShell);
4551 if (!pWrtSh)
4552 return nullptr;
4554 return &pWrtSh->GetView();
4557 void SwTextFrame::PaintParagraphStylesHighlighting() const
4559 // Maybe avoid the dynamic_cast and just use GetActiveWrtShell()
4560 // NO! Multiple windows will not display the highlighting correctly if GetActiveWrtShell is used.
4561 SwWrtShell* pWrtSh = dynamic_cast<SwWrtShell*>(gProp.pSGlobalShell);
4563 if (!pWrtSh)
4564 return;
4566 vcl::RenderContext* pRenderContext = pWrtSh->GetOut();
4567 if (!pRenderContext)
4568 return;
4570 StylesHighlighterColorMap& rParaStylesColorMap
4571 = pWrtSh->GetView().GetStylesHighlighterParaColorMap();
4573 if (rParaStylesColorMap.empty())
4574 return;
4576 // draw styles highlighter
4577 OUString sStyleName = GetTextNodeFirst()->GetTextColl()->GetName();
4578 if (rParaStylesColorMap.find(sStyleName) != rParaStylesColorMap.end())
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& rRect = 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(rParaStylesColorMap[sStyleName].first);
4607 pRenderContext->SetLineColor(rParaStylesColorMap[sStyleName].first);
4609 pRenderContext->DrawRect(rRect);
4611 // draw hatch pattern if paragraph has direct formatting
4612 if (SwDoc::HasParagraphDirectFormatting(SwPosition(*GetTextNodeForParaProps())))
4614 Color aHatchColor(rParaStylesColorMap[sStyleName].first);
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(rRect), aHatch);
4621 pRenderContext->SetFont(aFont);
4622 pRenderContext->SetLayoutMode(vcl::text::ComplexTextLayoutFlags::Default);
4623 pRenderContext->SetTextFillColor(rParaStylesColorMap[sStyleName].first);
4624 pRenderContext->DrawText(rRect, OUString::number(rParaStylesColorMap[sStyleName].second),
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, SwPrintData const*const) const
4640 const SwViewOption* pViewOption = gProp.pSGlobalShell->GetViewOptions();
4641 if (pViewOption->IsTable())
4643 // #i29550#
4644 if ( IsCollapsingBorders() )
4646 SwBorderAttrAccess aAccess( SwFrame::GetCache(), static_cast<SwFrame const *>(this) );
4647 const SwBorderAttrs &rAttrs = *aAccess.Get();
4649 // paint shadow
4650 if ( rAttrs.GetShadow().GetLocation() != SvxShadowLocation::NONE )
4652 SwRect aRect;
4653 ::lcl_CalcBorderRect( aRect, this, rAttrs, true, gProp );
4654 PaintShadow( rRect, aRect, rAttrs );
4657 SwTabFramePainter aHelper(*this);
4658 aHelper.PaintLines(rRenderContext, rRect);
4661 SwLayoutFrame::PaintSwFrame( rRenderContext, rRect );
4663 // #i6467# - no light grey rectangle for page preview
4664 else if ( gProp.pSGlobalShell->GetWin() && !gProp.pSGlobalShell->IsPreview() )
4666 // #i6467# - intersect output rectangle with table frame
4667 SwRect aTabRect( getFramePrintArea() );
4668 aTabRect.Pos() += getFrameArea().Pos();
4669 SwRect aTabOutRect( rRect );
4670 aTabOutRect.Intersection( aTabRect );
4671 SwViewOption::DrawRect( &rRenderContext, aTabOutRect, COL_LIGHTGRAY );
4673 const_cast<SwTabFrame*>(this)->ResetComplete();
4677 * Paint border shadow
4679 * @param[in] rRect aligned rect to clip the result
4680 * @param[in,out] rOutRect full painting area as input
4681 * painting area reduced by shadow space for border and background as output
4682 * @param[in] rShadow includes shadow attributes
4683 * @param[in] bDrawFullShadowRectangle paint full rect of shadow
4684 * @param[in] bTop paint top part of the shadow
4685 * @param[in] bBottom paint bottom part of the shadow
4686 * @param[in] bLeft paint left part of the shadow
4687 * @param[in] bRight paint right part of the shadow
4689 static void lcl_PaintShadow( const SwRect& rRect, SwRect& rOutRect,
4690 const SvxShadowItem& rShadow, const bool bDrawFullShadowRectangle,
4691 const bool bTop, const bool bBottom,
4692 const bool bLeft, const bool bRight,
4693 SwPaintProperties const & properties)
4695 const tools::Long nWidth = ::lcl_AlignWidth ( rShadow.GetWidth(), properties );
4696 const tools::Long nHeight = ::lcl_AlignHeight( rShadow.GetWidth(), properties );
4698 SwRects aRegion;
4699 SwRect aOut( rOutRect );
4701 switch ( rShadow.GetLocation() )
4703 case SvxShadowLocation::BottomRight:
4705 if ( bDrawFullShadowRectangle )
4707 // draw full shadow rectangle
4708 aOut.Top( rOutRect.Top() + nHeight );
4709 aOut.Left( rOutRect.Left() + nWidth );
4710 aRegion.push_back( aOut );
4712 else
4714 if( bBottom )
4716 aOut.Top( rOutRect.Bottom() - nHeight );
4717 if( bLeft )
4718 aOut.Left( rOutRect.Left() + nWidth );
4719 aRegion.push_back( aOut );
4721 if( bRight )
4723 aOut.Left( rOutRect.Right() - nWidth );
4724 if( bTop )
4725 aOut.Top( rOutRect.Top() + nHeight );
4726 else
4727 aOut.Top( rOutRect.Top() );
4728 if( bBottom )
4729 aOut.Bottom( rOutRect.Bottom() - nHeight );
4730 aRegion.push_back( aOut );
4734 if( bRight )
4735 rOutRect.AddRight(- nWidth );
4736 if( bBottom )
4737 rOutRect.AddBottom(- nHeight );
4739 break;
4740 case SvxShadowLocation::TopLeft:
4742 if ( bDrawFullShadowRectangle )
4744 // draw full shadow rectangle
4745 aOut.Bottom( rOutRect.Bottom() - nHeight );
4746 aOut.Right( rOutRect.Right() - nWidth );
4747 aRegion.push_back( aOut );
4749 else
4751 if( bTop )
4753 aOut.Bottom( rOutRect.Top() + nHeight );
4754 if( bRight )
4755 aOut.Right( rOutRect.Right() - nWidth );
4756 aRegion.push_back( aOut );
4758 if( bLeft )
4760 aOut.Right( rOutRect.Left() + nWidth );
4761 if( bBottom )
4762 aOut.Bottom( rOutRect.Bottom() - nHeight );
4763 else
4764 aOut.Bottom( rOutRect.Bottom() );
4765 if( bTop )
4766 aOut.Top( rOutRect.Top() + nHeight );
4767 aRegion.push_back( aOut );
4771 if( bLeft )
4772 rOutRect.AddLeft( nWidth );
4773 if( bTop )
4774 rOutRect.AddTop( nHeight );
4776 break;
4777 case SvxShadowLocation::TopRight:
4779 if ( bDrawFullShadowRectangle )
4781 // draw full shadow rectangle
4782 aOut.Bottom( rOutRect.Bottom() - nHeight);
4783 aOut.Left( rOutRect.Left() + nWidth );
4784 aRegion.push_back( aOut );
4786 else
4788 if( bTop )
4790 aOut.Bottom( rOutRect.Top() + nHeight );
4791 if( bLeft )
4792 aOut.Left( rOutRect.Left() + nWidth );
4793 aRegion.push_back( aOut );
4795 if( bRight )
4797 aOut.Left( rOutRect.Right() - nWidth );
4798 if( bBottom )
4799 aOut.Bottom( rOutRect.Bottom() - nHeight );
4800 else
4801 aOut.Bottom( rOutRect.Bottom() );
4802 if( bTop )
4803 aOut.Top( rOutRect.Top() + nHeight );
4804 aRegion.push_back( aOut );
4808 if( bRight )
4809 rOutRect.AddRight( - nWidth );
4810 if( bTop )
4811 rOutRect.AddTop( nHeight );
4813 break;
4814 case SvxShadowLocation::BottomLeft:
4816 if ( bDrawFullShadowRectangle )
4818 // draw full shadow rectangle
4819 aOut.Top( rOutRect.Top() + nHeight );
4820 aOut.Right( rOutRect.Right() - nWidth );
4821 aRegion.push_back( aOut );
4823 else
4825 if( bBottom )
4827 aOut.Top( rOutRect.Bottom()- nHeight );
4828 if( bRight )
4829 aOut.Right( rOutRect.Right() - nWidth );
4830 aRegion.push_back( aOut );
4832 if( bLeft )
4834 aOut.Right( rOutRect.Left() + nWidth );
4835 if( bTop )
4836 aOut.Top( rOutRect.Top() + nHeight );
4837 else
4838 aOut.Top( rOutRect.Top() );
4839 if( bBottom )
4840 aOut.Bottom( rOutRect.Bottom() - nHeight );
4841 aRegion.push_back( aOut );
4845 if( bLeft )
4846 rOutRect.AddLeft( nWidth );
4847 if( bBottom )
4848 rOutRect.AddBottom( - nHeight );
4850 break;
4851 default:
4852 assert(false);
4853 break;
4856 vcl::RenderContext *pOut = properties.pSGlobalShell->GetOut();
4858 DrawModeFlags nOldDrawMode = pOut->GetDrawMode();
4859 Color aShadowColor( rShadow.GetColor().GetRGBColor() );
4860 if( !aRegion.empty() && properties.pSGlobalShell->GetWin() &&
4861 Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
4863 // In high contrast mode, the output device has already set the
4864 // DrawModeFlags::SettingsFill flag. This causes the SetFillColor function
4865 // to ignore the setting of a new color. Therefore we have to reset
4866 // the drawing mode
4867 pOut->SetDrawMode( DrawModeFlags::Default );
4868 aShadowColor = properties.pSGlobalShell->GetViewOptions()->GetFontColor();
4871 if ( pOut->GetFillColor() != aShadowColor )
4872 pOut->SetFillColor( aShadowColor );
4874 pOut->SetLineColor();
4876 pOut->SetDrawMode( nOldDrawMode );
4878 for (const SwRect & rOut : aRegion)
4880 aOut = rOut;
4881 if ( rRect.Overlaps( aOut ) && aOut.Height() > 0 && aOut.Width() > 0 )
4883 aOut.Intersection_( rRect );
4884 pOut->DrawRect( aOut.SVRect() );
4890 * Paints a shadow if the format requests so.
4892 * The shadow is always painted on the outer edge of the OutRect.
4893 * If needed, the OutRect is shrunk so the painting of the border can be
4894 * done on it.
4896 * @note: draw full shadow rectangle for frames with transparent drawn backgrounds (OD 23.08.2002 #99657#)
4898 void SwFrame::PaintShadow( const SwRect& rRect, SwRect& rOutRect,
4899 const SwBorderAttrs &rAttrs ) const
4901 SvxShadowItem rShadow = rAttrs.GetShadow();
4903 const bool bCnt = IsContentFrame();
4904 const bool bTop = !bCnt || rAttrs.GetTopLine ( *(this) );
4905 const bool bBottom = !bCnt || rAttrs.GetBottomLine( *(this) );
4907 if( IsVertical() )
4909 switch( rShadow.GetLocation() )
4911 case SvxShadowLocation::BottomRight: rShadow.SetLocation(SvxShadowLocation::BottomLeft); break;
4912 case SvxShadowLocation::TopLeft: rShadow.SetLocation(SvxShadowLocation::TopRight); break;
4913 case SvxShadowLocation::TopRight: rShadow.SetLocation(SvxShadowLocation::BottomRight); break;
4914 case SvxShadowLocation::BottomLeft: rShadow.SetLocation(SvxShadowLocation::TopLeft); break;
4915 default: break;
4919 // determine, if full shadow rectangle have to be drawn or only two shadow rectangles beside the frame.
4920 // draw full shadow rectangle, if frame background is drawn transparent.
4921 // Status Quo:
4922 // SwLayoutFrame can have transparent drawn backgrounds. Thus,
4923 // "asked" their frame format.
4924 const bool bDrawFullShadowRectangle =
4925 ( IsLayoutFrame() &&
4926 static_cast<const SwLayoutFrame*>(this)->GetFormat()->IsBackgroundTransparent()
4929 SwRectFnSet aRectFnSet(this);
4930 ::lcl_ExtendLeftAndRight( rOutRect, *(this), rAttrs, aRectFnSet.FnRect() );
4932 lcl_PaintShadow(rRect, rOutRect, rShadow, bDrawFullShadowRectangle, bTop, bBottom, true, true, gProp);
4935 void SwFrame::PaintBorderLine( const SwRect& rRect,
4936 const SwRect& rOutRect,
4937 const SwPageFrame * pPage,
4938 const Color *pColor,
4939 const SvxBorderLineStyle nStyle ) const
4941 if ( !rOutRect.Overlaps( rRect ) )
4942 return;
4944 SwRect aOut( rOutRect );
4945 aOut.Intersection_( rRect );
4947 const SwTabFrame *pTab = IsCellFrame() ? FindTabFrame() : nullptr;
4948 SubColFlags nSubCol = ( IsCellFrame() || IsRowFrame() )
4949 ? SubColFlags::Tab
4950 : ( IsInSct()
4951 ? SubColFlags::Sect
4952 : ( IsInFly() ? SubColFlags::Fly : SubColFlags::Page ) );
4953 if( pColor && gProp.pSGlobalShell->GetWin() &&
4954 Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
4956 SwViewShell *pSh = getRootFrame()->GetCurrShell();
4957 const SwViewOption *pOpt = pSh->GetViewOptions();
4958 pColor = &pOpt->GetFontColor();
4961 if (pPage->GetSortedObjs() &&
4962 pPage->GetFormat()->GetDoc()->getIDocumentSettingAccess().get(DocumentSettingId::SUBTRACT_FLYS))
4964 SwRegionRects aRegion( aOut, 4 );
4965 basegfx::utils::B2DClipState aClipState;
4966 ::lcl_SubtractFlys( this, pPage, aOut, aRegion, aClipState, gProp );
4967 for ( size_t i = 0; i < aRegion.size(); ++i )
4968 gProp.pSLines->AddLineRect( aRegion[i], pColor, nStyle, pTab, nSubCol, gProp );
4970 else
4971 gProp.pSLines->AddLineRect( aOut, pColor, nStyle, pTab, nSubCol, gProp );
4974 namespace drawinglayer::primitive2d
4976 namespace {
4978 class SwBorderRectanglePrimitive2D : public BufferedDecompositionPrimitive2D
4980 private:
4981 /// the transformation defining the geometry of this BorderRectangle
4982 basegfx::B2DHomMatrix maB2DHomMatrix;
4984 /// the four styles to be used
4985 svx::frame::Style maStyleTop;
4986 svx::frame::Style maStyleRight;
4987 svx::frame::Style maStyleBottom;
4988 svx::frame::Style maStyleLeft;
4990 protected:
4991 /// local decomposition.
4992 virtual void create2DDecomposition(
4993 Primitive2DContainer& rContainer,
4994 const geometry::ViewInformation2D& rViewInformation) const override;
4996 public:
4997 /// constructor
4998 SwBorderRectanglePrimitive2D(
4999 basegfx::B2DHomMatrix aB2DHomMatrix,
5000 const svx::frame::Style& rStyleTop,
5001 const svx::frame::Style& rStyleRight,
5002 const svx::frame::Style& rStyleBottom,
5003 const svx::frame::Style& rStyleLeft);
5005 /// data read access
5006 const basegfx::B2DHomMatrix& getB2DHomMatrix() const { return maB2DHomMatrix; }
5007 const svx::frame::Style& getStyleTop() const { return maStyleTop; }
5008 const svx::frame::Style& getStyleRight() const { return maStyleRight; }
5009 const svx::frame::Style& getStyleBottom() const { return maStyleBottom; }
5010 const svx::frame::Style& getStyleLeft() const { return maStyleLeft; }
5012 /// compare operator
5013 virtual bool operator==(const BasePrimitive2D& rPrimitive) const override;
5015 /// get range
5016 virtual basegfx::B2DRange getB2DRange(const geometry::ViewInformation2D& rViewInformation) const override;
5018 /// provide unique ID
5019 virtual sal_uInt32 getPrimitive2DID() const override;
5024 void SwBorderRectanglePrimitive2D::create2DDecomposition(
5025 Primitive2DContainer& rContainer,
5026 const geometry::ViewInformation2D& /*rViewInformation*/) const
5028 basegfx::B2DPoint aTopLeft(getB2DHomMatrix() * basegfx::B2DPoint(0.0, 0.0));
5029 basegfx::B2DPoint aTopRight(getB2DHomMatrix() * basegfx::B2DPoint(1.0, 0.0));
5030 basegfx::B2DPoint aBottomLeft(getB2DHomMatrix() * basegfx::B2DPoint(0.0, 1.0));
5031 basegfx::B2DPoint aBottomRight(getB2DHomMatrix() * basegfx::B2DPoint(1.0, 1.0));
5033 // prepare SdrFrameBorderDataVector
5034 drawinglayer::primitive2d::SdrFrameBorderDataVector aData;
5036 if(getStyleTop().IsUsed())
5038 // move top left/right inwards half border width
5039 basegfx::B2DVector aDown(getB2DHomMatrix() * basegfx::B2DVector(0.0, 1.0));
5040 aDown.setLength(getStyleTop().GetWidth() * 0.5);
5041 aTopLeft += aDown;
5042 aTopRight += aDown;
5045 if(getStyleBottom().IsUsed())
5047 // move bottom left/right inwards half border width
5048 basegfx::B2DVector aUp(getB2DHomMatrix() * basegfx::B2DVector(0.0, -1.0));
5049 aUp.setLength(getStyleBottom().GetWidth() * 0.5);
5050 aBottomLeft += aUp;
5051 aBottomRight += aUp;
5054 if(getStyleLeft().IsUsed())
5056 // move left top/bottom inwards half border width
5057 basegfx::B2DVector aRight(getB2DHomMatrix() * basegfx::B2DVector(1.0, 0.0));
5058 aRight.setLength(getStyleLeft().GetWidth() * 0.5);
5059 aTopLeft += aRight;
5060 aBottomLeft += aRight;
5063 if(getStyleRight().IsUsed())
5065 // move right top/bottom inwards half border width
5066 basegfx::B2DVector aLeft(getB2DHomMatrix() * basegfx::B2DVector(-1.0, 0.0));
5067 aLeft.setLength(getStyleRight().GetWidth() * 0.5);
5068 aTopRight += aLeft;
5069 aBottomRight += aLeft;
5072 // go round-robin, from TopLeft to TopRight, down, left and back up. That
5073 // way, the borders will not need to be mirrored in any way
5074 if(getStyleTop().IsUsed())
5076 // create BorderPrimitive(s) for top border
5077 const basegfx::B2DVector aVector(aTopRight - aTopLeft);
5078 aData.emplace_back(
5079 aTopLeft,
5080 aVector,
5081 getStyleTop(),
5082 nullptr);
5083 drawinglayer::primitive2d::SdrFrameBorderData& rInstance(aData.back());
5085 if(getStyleLeft().IsUsed())
5087 rInstance.addSdrConnectStyleData(true, getStyleLeft(), basegfx::B2DVector(aBottomLeft - aTopLeft), false);
5090 if(getStyleRight().IsUsed())
5092 rInstance.addSdrConnectStyleData(false, getStyleRight(), basegfx::B2DVector(aBottomRight - aTopRight), false);
5096 if(getStyleRight().IsUsed())
5098 // create BorderPrimitive(s) for right border
5099 const basegfx::B2DVector aVector(aBottomRight - aTopRight);
5100 aData.emplace_back(
5101 aTopRight,
5102 aVector,
5103 getStyleRight(),
5104 nullptr);
5105 drawinglayer::primitive2d::SdrFrameBorderData& rInstance(aData.back());
5107 if(getStyleTop().IsUsed())
5109 rInstance.addSdrConnectStyleData(true, getStyleTop(), basegfx::B2DVector(aTopLeft - aTopRight), false);
5112 if(getStyleBottom().IsUsed())
5114 rInstance.addSdrConnectStyleData(false, getStyleBottom(), basegfx::B2DVector(aBottomLeft - aBottomRight), false);
5118 if(getStyleBottom().IsUsed())
5120 // create BorderPrimitive(s) for bottom border
5121 const basegfx::B2DVector aVector(aBottomLeft - aBottomRight);
5122 aData.emplace_back(
5123 aBottomRight,
5124 aVector,
5125 getStyleBottom(),
5126 nullptr);
5127 drawinglayer::primitive2d::SdrFrameBorderData& rInstance(aData.back());
5129 if(getStyleRight().IsUsed())
5131 rInstance.addSdrConnectStyleData(true, getStyleRight(), basegfx::B2DVector(aTopRight - aBottomRight), false);
5134 if(getStyleLeft().IsUsed())
5136 rInstance.addSdrConnectStyleData(false, getStyleLeft(), basegfx::B2DVector(aTopLeft - aBottomLeft), false);
5140 if(getStyleLeft().IsUsed())
5142 // create BorderPrimitive(s) for left border
5143 const basegfx::B2DVector aVector(aTopLeft - aBottomLeft);
5144 aData.emplace_back(
5145 aBottomLeft,
5146 aVector,
5147 getStyleLeft(),
5148 nullptr);
5149 drawinglayer::primitive2d::SdrFrameBorderData& rInstance(aData.back());
5151 if(getStyleBottom().IsUsed())
5153 rInstance.addSdrConnectStyleData(true, getStyleBottom(), basegfx::B2DVector(aBottomRight - aBottomLeft), false);
5156 if(getStyleTop().IsUsed())
5158 rInstance.addSdrConnectStyleData(false, getStyleTop(), basegfx::B2DVector(aTopRight - aTopLeft), false);
5162 // create instance of SdrFrameBorderPrimitive2D if
5163 // SdrFrameBorderDataVector is used
5164 if(!aData.empty())
5166 rContainer.append(
5167 drawinglayer::primitive2d::Primitive2DReference(
5168 new drawinglayer::primitive2d::SdrFrameBorderPrimitive2D(
5169 std::move(aData),
5170 true))); // force visualization to minimal one discrete unit (pixel)
5174 SwBorderRectanglePrimitive2D::SwBorderRectanglePrimitive2D(
5175 basegfx::B2DHomMatrix aB2DHomMatrix,
5176 const svx::frame::Style& rStyleTop,
5177 const svx::frame::Style& rStyleRight,
5178 const svx::frame::Style& rStyleBottom,
5179 const svx::frame::Style& rStyleLeft)
5180 : maB2DHomMatrix(std::move(aB2DHomMatrix)),
5181 maStyleTop(rStyleTop),
5182 maStyleRight(rStyleRight),
5183 maStyleBottom(rStyleBottom),
5184 maStyleLeft(rStyleLeft)
5188 bool SwBorderRectanglePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
5190 if(BasePrimitive2D::operator==(rPrimitive))
5192 const SwBorderRectanglePrimitive2D& rCompare = static_cast<const SwBorderRectanglePrimitive2D&>(rPrimitive);
5194 return (getB2DHomMatrix() == rCompare.getB2DHomMatrix() &&
5195 getStyleTop() == rCompare.getStyleTop() &&
5196 getStyleRight() == rCompare.getStyleRight() &&
5197 getStyleBottom() == rCompare.getStyleBottom() &&
5198 getStyleLeft() == rCompare.getStyleLeft());
5201 return false;
5204 basegfx::B2DRange SwBorderRectanglePrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
5206 basegfx::B2DRange aRetval(0.0, 0.0, 1.0, 1.0);
5208 aRetval.transform(getB2DHomMatrix());
5209 return aRetval;
5212 // provide unique ID
5213 sal_uInt32 SwBorderRectanglePrimitive2D::getPrimitive2DID() const
5215 return PRIMITIVE2D_ID_SWBORDERRECTANGLERIMITIVE;
5218 } // end of namespace drawinglayer::primitive2d
5220 namespace {
5222 editeng::SvxBorderLine const * get_ptr(std::optional<editeng::SvxBorderLine> const & opt) {
5223 return opt ? &*opt : nullptr;
5228 void PaintCharacterBorder(
5229 const SwFont& rFont,
5230 const SwRect& rPaintArea,
5231 const bool bVerticalLayout,
5232 const bool bVerticalLayoutLRBT,
5233 const bool bJoinWithPrev,
5234 const bool bJoinWithNext )
5236 SwRect aAlignedRect(rPaintArea);
5237 SwAlignRect(aAlignedRect, gProp.pSGlobalShell, gProp.pSGlobalShell->GetOut());
5239 bool bTop = true;
5240 bool bBottom = true;
5241 bool bLeft = true;
5242 bool bRight = true;
5244 switch (rFont.GetOrientation(bVerticalLayout, bVerticalLayoutLRBT).get())
5246 case 0 :
5247 bLeft = !bJoinWithPrev;
5248 bRight = !bJoinWithNext;
5249 break;
5250 case 900 :
5251 bBottom = !bJoinWithPrev;
5252 bTop = !bJoinWithNext;
5253 break;
5254 case 1800 :
5255 bRight = !bJoinWithPrev;
5256 bLeft = !bJoinWithNext;
5257 break;
5258 case 2700 :
5259 bTop = !bJoinWithPrev;
5260 bBottom = !bJoinWithNext;
5261 break;
5264 // Paint shadow (reduce painting rect)
5266 const SvxShadowItem aShadow(
5267 0, &rFont.GetShadowColor(), rFont.GetShadowWidth(),
5268 rFont.GetAbsShadowLocation(bVerticalLayout, bVerticalLayoutLRBT));
5270 if( aShadow.GetLocation() != SvxShadowLocation::NONE )
5272 lcl_PaintShadow( rPaintArea, aAlignedRect, aShadow,
5273 false, bTop, bBottom, bLeft, bRight, gProp);
5277 const basegfx::B2DHomMatrix aBorderTransform(
5278 basegfx::utils::createScaleTranslateB2DHomMatrix(
5279 aAlignedRect.Width(), aAlignedRect.Height(),
5280 aAlignedRect.Left(), aAlignedRect.Top()));
5281 const svx::frame::Style aStyleTop(
5282 bTop ? get_ptr(rFont.GetAbsTopBorder(bVerticalLayout, bVerticalLayoutLRBT)) : nullptr,
5283 1.0);
5284 const svx::frame::Style aStyleRight(
5285 bRight ? get_ptr(rFont.GetAbsRightBorder(bVerticalLayout, bVerticalLayoutLRBT)) : nullptr,
5286 1.0);
5287 const svx::frame::Style aStyleBottom(
5288 bBottom ? get_ptr(rFont.GetAbsBottomBorder(bVerticalLayout, bVerticalLayoutLRBT))
5289 : nullptr,
5290 1.0);
5291 const svx::frame::Style aStyleLeft(
5292 bLeft ? get_ptr(rFont.GetAbsLeftBorder(bVerticalLayout, bVerticalLayoutLRBT)) : nullptr,
5293 1.0);
5294 drawinglayer::primitive2d::Primitive2DContainer aBorderLineTarget;
5296 aBorderLineTarget.append(
5297 drawinglayer::primitive2d::Primitive2DReference(
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 const bool bLine = rAttrs.IsLine();
5536 const bool bShadow = rAttrs.GetShadow().GetLocation() != SvxShadowLocation::NONE;
5538 // - flag to control,
5539 //-hack has to be used.
5540 const bool bb4779636HackActive = true;
5542 const SwFrame* pCellFrameForBottomBorderAttrs = nullptr;
5543 const SwFrame* pCellFrameForTopBorderAttrs = nullptr;
5544 bool bFoundCellForTopOrBorderAttrs = false;
5545 if ( bb4779636HackActive && IsCellFrame() )
5547 pCellFrameForBottomBorderAttrs = lcl_GetCellFrameForBorderAttrs( this, rAttrs, false );
5548 if ( pCellFrameForBottomBorderAttrs != this )
5549 bFoundCellForTopOrBorderAttrs = true;
5550 pCellFrameForTopBorderAttrs = lcl_GetCellFrameForBorderAttrs( this, rAttrs, true );
5551 if ( pCellFrameForTopBorderAttrs != this )
5552 bFoundCellForTopOrBorderAttrs = true;
5555 // - add condition <bFoundCellForTopOrBorderAttrs>
5556 //-hack
5557 if ( !(bLine || bShadow || bFoundCellForTopOrBorderAttrs) )
5558 return;
5560 //If the rectangle is completely inside the PrtArea, no border needs to
5561 //be painted.
5562 //For the PrtArea the aligned value needs to be used, otherwise it could
5563 //happen, that some parts won't be processed.
5564 SwRect aRect( getFramePrintArea() );
5565 aRect += getFrameArea().Pos();
5566 ::SwAlignRect( aRect, gProp.pSGlobalShell, gProp.pSGlobalShell->GetOut() );
5567 // new local boolean variable in order to
5568 // suspend border paint under special cases - see below.
5569 // NOTE: This is a fix for the implementation of feature #99657#.
5570 bool bDrawOnlyShadowForTransparentFrame = false;
5571 if ( aRect.Contains( rRect ) )
5573 // paint shadow, if background is transparent.
5574 // Because of introduced transparent background for fly frame #99657#,
5575 // the shadow have to be drawn if the background is transparent,
5576 // in spite the fact that the paint rectangle <rRect> lies fully
5577 // in the printing area.
5578 // NOTE to chosen solution:
5579 // On transparent background, continue processing, but suspend
5580 // drawing of border by setting <bDrawOnlyShadowForTransparentFrame>
5581 // to true.
5582 if ( IsLayoutFrame() &&
5583 static_cast<const SwLayoutFrame*>(this)->GetFormat()->IsBackgroundTransparent() )
5585 bDrawOnlyShadowForTransparentFrame = true;
5587 else
5589 return;
5593 ::lcl_CalcBorderRect( aRect, this, rAttrs, true, gProp );
5594 rAttrs.SetGetCacheLine( true );
5596 if(bShadow)
5598 PaintShadow(rRect, aRect, rAttrs);
5601 // suspend drawing of border
5602 // add condition < NOT bDrawOnlyShadowForTransparentFrame > - see above
5603 // - add condition <bFoundCellForTopOrBorderAttrs>
5604 //-hack.
5605 if((bLine || bFoundCellForTopOrBorderAttrs) && !bDrawOnlyShadowForTransparentFrame)
5607 // define SvxBorderLine(s) to use
5608 const SvxBoxItem& rBox(rAttrs.GetBox());
5609 const SvxBorderLine* pLeftBorder(rBox.GetLeft());
5610 const SvxBorderLine* pRightBorder(rBox.GetRight());
5611 const SvxBorderLine* pTopBorder(rBox.GetTop());
5612 const SvxBorderLine* pBottomBorder(rBox.GetBottom());
5614 // if R2L, exchange Right/Left
5615 const bool bR2L(IsCellFrame() && IsRightToLeft());
5617 if(bR2L)
5619 std::swap(pLeftBorder, pRightBorder);
5622 // if ContentFrame and joined Prev/Next, reset top/bottom as needed
5623 if(IsContentFrame())
5625 const SwFrame* pDirRefFrame(IsCellFrame() ? FindTabFrame() : this);
5626 const SwRectFnSet aRectFnSet(pDirRefFrame);
5627 const SwRectFn& _rRectFn(aRectFnSet.FnRect());
5629 if(rAttrs.JoinedWithPrev(*this))
5631 // tdf#115296 re-add adaptation of vert distance to close the evtl.
5632 // existing gap to previous frame
5633 const SwFrame* pPrevFrame(GetPrev());
5634 (aRect.*_rRectFn->fnSetTop)( (pPrevFrame->*_rRectFn->fnGetPrtBottom)() );
5636 // ...and disable top border paint/creation
5637 pTopBorder = nullptr;
5640 if(rAttrs.JoinedWithNext(*this))
5642 // tdf#115296 re-add adaptation of vert distance to close the evtl.
5643 // existing gap to next frame
5644 const SwFrame* pNextFrame(GetNext());
5645 (aRect.*_rRectFn->fnSetBottom)( (pNextFrame->*_rRectFn->fnGetPrtTop)() );
5647 // ...and disable bottom border paint/creation
5648 pBottomBorder = nullptr;
5652 // necessary to replace TopBorder?
5653 if((!IsContentFrame() || rAttrs.GetTopLine(*this)) && IsCellFrame() && pCellFrameForTopBorderAttrs != this)
5655 SwBorderAttrAccess aAccess(SwFrame::GetCache(), pCellFrameForTopBorderAttrs);
5656 pTopBorder = aAccess.Get()->GetBox().GetTop();
5659 // necessary to replace BottomBorder?
5660 if((!IsContentFrame() || rAttrs.GetBottomLine(*this)) && IsCellFrame() && pCellFrameForBottomBorderAttrs != this)
5662 SwBorderAttrAccess aAccess(SwFrame::GetCache(), pCellFrameForBottomBorderAttrs);
5663 pBottomBorder = aAccess.Get()->GetBox().GetBottom();
5666 bool bWordBorder = false;
5667 SwViewShell* pShell = getRootFrame()->GetCurrShell();
5668 if (pShell)
5670 const IDocumentSettingAccess& rIDSA = pShell->GetDoc()->getIDocumentSettingAccess();
5671 bWordBorder = rIDSA.get(DocumentSettingId::TABLE_ROW_KEEP);
5673 bool bInWordTableCell = IsContentFrame() && GetUpper()->IsCellFrame() && bWordBorder;
5674 if (bInWordTableCell)
5676 // Compat mode: don't paint bottom border if we know the bottom of the content was cut
5677 // off.
5678 auto pContentFrame = static_cast<const SwContentFrame*>(this);
5679 if (pContentFrame->IsUndersized())
5681 pBottomBorder = nullptr;
5685 if(nullptr != pLeftBorder || nullptr != pRightBorder || nullptr != pTopBorder || nullptr != pBottomBorder)
5687 // now we have all SvxBorderLine(s) sorted out, create geometry
5688 const basegfx::B2DHomMatrix aBorderTransform(
5689 basegfx::utils::createScaleTranslateB2DHomMatrix(
5690 aRect.Width(), aRect.Height(),
5691 aRect.Left(), aRect.Top()));
5692 const svx::frame::Style aStyleTop(pTopBorder, 1.0);
5693 svx::frame::Style aStyleRight(pRightBorder, 1.0);
5695 // Right/bottom page borders are always mirrored in Word.
5696 if (IsPageFrame() && bWordBorder)
5698 aStyleRight.MirrorSelf();
5701 svx::frame::Style aStyleBottom(pBottomBorder, 1.0);
5703 if (IsPageFrame() && bWordBorder)
5705 aStyleBottom.MirrorSelf();
5708 const svx::frame::Style aStyleLeft(pLeftBorder, 1.0);
5709 drawinglayer::primitive2d::Primitive2DContainer aBorderLineTarget;
5711 drawinglayer::primitive2d::Primitive2DReference aRetval(
5712 new drawinglayer::primitive2d::SwBorderRectanglePrimitive2D(
5713 aBorderTransform,
5714 aStyleTop,
5715 aStyleRight,
5716 aStyleBottom,
5717 aStyleLeft));
5719 if (bInWordTableCell)
5721 // Compat mode: cut off the borders which are outside of our own area.
5722 const SwRect& rClip = getFrameArea();
5723 basegfx::B2DRectangle aClip(rClip.Left(), rClip.Top(), rClip.Right(),
5724 rClip.Bottom());
5725 basegfx::B2DPolyPolygon aPolyPolygon(
5726 basegfx::utils::createPolygonFromRect(aClip));
5727 const drawinglayer::primitive2d::Primitive2DReference xClipped(
5728 new drawinglayer::primitive2d::MaskPrimitive2D(std::move(aPolyPolygon), { aRetval }));
5729 aRetval = xClipped;
5732 aBorderLineTarget.append(aRetval);
5733 gProp.pBLines->AddBorderLines(std::move(aBorderLineTarget));
5737 rAttrs.SetGetCacheLine( false );
5741 * Special implementation because of the footnote line
5743 * Currently only the top frame needs to be taken into account
5744 * Other lines and shadows are set aside
5746 void SwFootnoteContFrame::PaintSwFrameShadowAndBorder(
5747 const SwRect& rRect,
5748 const SwPageFrame* pPage,
5749 const SwBorderAttrs&) const
5751 //If the rectangle is completely inside the PrtArea, no border needs to
5752 //be painted.
5753 SwRect aRect( getFramePrintArea() );
5754 aRect.Pos() += getFrameArea().Pos();
5755 if ( !aRect.Contains( rRect ) )
5756 PaintLine( rRect, pPage );
5759 /// Paint footnote lines.
5760 void SwFootnoteContFrame::PaintLine( const SwRect& rRect,
5761 const SwPageFrame *pPage ) const
5763 //The length of the line is derived from the percentual indication on the
5764 //PageDesc. The position is also stated on the PageDesc.
5765 //The pen can directly be taken from the PageDesc.
5767 if ( !pPage )
5768 pPage = FindPageFrame();
5769 const SwPageFootnoteInfo &rInf = pPage->GetPageDesc()->GetFootnoteInfo();
5771 SwRectFnSet aRectFnSet(this);
5772 SwTwips nPrtWidth = aRectFnSet.GetWidth(getFramePrintArea());
5773 Fraction aFract( nPrtWidth, 1 );
5774 aFract *= rInf.GetWidth();
5775 const SwTwips nWidth = static_cast<tools::Long>(aFract);
5777 SwTwips nX = aRectFnSet.GetPrtLeft(*this);
5778 switch ( rInf.GetAdj() )
5780 case css::text::HorizontalAdjust_CENTER:
5781 nX += nPrtWidth/2 - nWidth/2; break;
5782 case css::text::HorizontalAdjust_RIGHT:
5783 nX += nPrtWidth - nWidth; break;
5784 case css::text::HorizontalAdjust_LEFT:
5785 /* do nothing */; break;
5786 default:
5787 SAL_WARN("sw.core", "New adjustment for footnote lines?");
5788 assert(false);
5790 SwTwips nLineWidth = rInf.GetLineWidth();
5791 const SwRect aLineRect = aRectFnSet.IsVert() ?
5792 SwRect( Point(getFrameArea().Left()+getFrameArea().Width()-rInf.GetTopDist()-nLineWidth,
5793 nX), Size( nLineWidth, nWidth ) )
5794 : SwRect( Point( nX, getFrameArea().Pos().Y() + rInf.GetTopDist() ),
5795 Size( nWidth, rInf.GetLineWidth()));
5796 if ( aLineRect.HasArea() && rInf.GetLineStyle() != SvxBorderLineStyle::NONE)
5797 PaintBorderLine( rRect, aLineRect , pPage, &rInf.GetLineColor(),
5798 rInf.GetLineStyle() );
5801 void SwFootnoteContFrame::dumpAsXml(xmlTextWriterPtr writer) const
5803 (void)xmlTextWriterStartElement(writer, reinterpret_cast<const xmlChar*>("ftncont"));
5804 dumpAsXmlAttributes(writer);
5806 (void)xmlTextWriterStartElement(writer, BAD_CAST("infos"));
5807 dumpInfosAsXml(writer);
5808 (void)xmlTextWriterEndElement(writer);
5809 dumpChildrenAsXml(writer);
5811 (void)xmlTextWriterEndElement(writer);
5814 /// Paints the separator line for inside columns
5815 void SwLayoutFrame::PaintColLines( const SwRect &rRect, const SwFormatCol &rFormatCol,
5816 const SwPageFrame *pPage ) const
5818 const SwFrame *pCol = Lower();
5819 if ( !pCol || !pCol->IsColumnFrame() )
5820 return;
5822 SwRectFn fnRect = pCol->IsVertical() ? ( pCol->IsVertLR() ? (pCol->IsVertLRBT() ? fnRectVertL2RB2T : fnRectVertL2R) : fnRectVert ) : fnRectHori;
5824 SwRect aLineRect = getFramePrintArea();
5825 aLineRect += getFrameArea().Pos();
5827 SwTwips nTop = ((aLineRect.*fnRect->fnGetHeight)()*rFormatCol.GetLineHeight())
5828 / 100 - (aLineRect.*fnRect->fnGetHeight)();
5829 SwTwips nBottom = 0;
5831 switch ( rFormatCol.GetLineAdj() )
5833 case COLADJ_CENTER:
5834 nBottom = nTop / 2; nTop -= nBottom; break;
5835 case COLADJ_TOP:
5836 nBottom = nTop; nTop = 0; break;
5837 case COLADJ_BOTTOM:
5838 break;
5839 default:
5840 OSL_ENSURE( false, "New adjustment for column lines?" );
5843 if( nTop )
5844 (aLineRect.*fnRect->fnSubTop)( nTop );
5845 if( nBottom )
5846 (aLineRect.*fnRect->fnAddBottom)( nBottom );
5848 SwTwips nPenHalf = rFormatCol.GetLineWidth();
5849 (aLineRect.*fnRect->fnSetWidth)( nPenHalf );
5850 nPenHalf /= 2;
5852 //We need to be a bit generous here, to not lose something.
5853 SwRect aRect( rRect );
5854 (aRect.*fnRect->fnSubLeft)( nPenHalf + gProp.nSPixelSzW );
5855 (aRect.*fnRect->fnAddRight)( nPenHalf + gProp.nSPixelSzW );
5856 SwRectGet fnGetX = IsRightToLeft() ? fnRect->fnGetLeft : fnRect->fnGetRight;
5857 while ( pCol->GetNext() )
5859 (aLineRect.*fnRect->fnSetPosX)
5860 ( (pCol->getFrameArea().*fnGetX)() - nPenHalf );
5861 if ( aRect.Overlaps( aLineRect ) )
5862 PaintBorderLine( aRect, aLineRect , pPage, &rFormatCol.GetLineColor(),
5863 rFormatCol.GetLineStyle() );
5864 pCol = pCol->GetNext();
5868 void SwPageFrame::PaintGrid( OutputDevice const * pOut, SwRect const &rRect ) const
5870 if( !m_bHasGrid || gProp.pSRetoucheFly || gProp.pSRetoucheFly2 )
5871 return;
5872 SwTextGridItem const*const pGrid(GetGridItem(this));
5873 if( !(pGrid && ( OUTDEV_PRINTER != pOut->GetOutDevType() ?
5874 pGrid->GetDisplayGrid() : pGrid->GetPrintGrid() )) )
5875 return;
5877 const SwLayoutFrame* pBody = FindBodyCont();
5878 if( !pBody )
5879 return;
5881 SwRect aGrid( pBody->getFramePrintArea() );
5882 aGrid += pBody->getFrameArea().Pos();
5884 SwRect aInter( aGrid );
5885 aInter.Intersection( rRect );
5886 if( !aInter.HasArea() )
5887 return;
5889 bool bGrid = pGrid->GetRubyTextBelow();
5890 bool bCell = GRID_LINES_CHARS == pGrid->GetGridType();
5891 tools::Long nGrid = pGrid->GetBaseHeight();
5892 const SwDoc* pDoc = GetFormat()->GetDoc();
5893 tools::Long nGridWidth = GetGridWidth(*pGrid, *pDoc);
5894 tools::Long nRuby = pGrid->GetRubyHeight();
5895 tools::Long nSum = nGrid + nRuby;
5896 const Color *pCol = &pGrid->GetColor();
5898 SwTwips nRight = aInter.Left() + aInter.Width();
5899 SwTwips nBottom = aInter.Top() + aInter.Height();
5900 if( IsVertical() )
5902 SwTwips nOrig = aGrid.Left() + aGrid.Width();
5903 SwTwips nY = nOrig + nSum *
5904 ( ( nOrig - aInter.Left() ) / nSum );
5905 SwRect aTmp( Point( nY, aInter.Top() ),
5906 Size( 1, aInter.Height() ) );
5907 SwTwips nX = aGrid.Top() + nGrid *
5908 ( ( aInter.Top() - aGrid.Top() )/ nGrid );
5909 if( nX < aInter.Top() )
5910 nX += nGrid;
5911 SwTwips nGridBottom = aGrid.Top() + aGrid.Height();
5912 bool bLeft = aGrid.Top() >= aInter.Top();
5913 bool bRight = nGridBottom <= nBottom;
5914 bool bBorder = bLeft || bRight;
5915 while( nY > nRight )
5917 aTmp.Pos().setX( nY );
5918 if( bGrid )
5920 nY -= nGrid;
5921 SwTwips nPosY = std::max( SwTwips(aInter.Left()), nY );
5922 SwTwips nHeight = std::min(nRight, SwTwips(aTmp.Pos().X()))-nPosY;
5923 if( nHeight > 0 )
5925 if( bCell )
5927 SwRect aVert( Point( nPosY, nX ),
5928 Size( nHeight, 1 ) );
5929 while( aVert.Top() <= nBottom )
5931 PaintBorderLine(rRect,aVert,this,pCol);
5932 aVert.Pos().AdjustY(nGrid );
5935 else if( bBorder )
5937 SwRect aVert( Point( nPosY, aGrid.Top() ),
5938 Size( nHeight, 1 ) );
5939 if( bLeft )
5940 PaintBorderLine(rRect,aVert,this,pCol);
5941 if( bRight )
5943 aVert.Pos().setY( nGridBottom );
5944 PaintBorderLine(rRect,aVert,this,pCol);
5949 else
5951 nY -= nRuby;
5952 if( bBorder )
5954 SwTwips nPos = std::max( SwTwips(aInter.Left()), nY );
5955 SwTwips nW = std::min(nRight, SwTwips(aTmp.Pos().X())) - nPos;
5956 SwRect aVert( Point( nPos, aGrid.Top() ),
5957 Size( nW, 1 ) );
5958 if( nW > 0 )
5960 if( bLeft )
5961 PaintBorderLine(rRect,aVert,this,pCol);
5962 if( bRight )
5964 aVert.Pos().setY( nGridBottom );
5965 PaintBorderLine(rRect,aVert,this,pCol);
5970 bGrid = !bGrid;
5972 while( nY >= aInter.Left() )
5974 aTmp.Pos().setX( nY );
5975 PaintBorderLine( rRect, aTmp, this, pCol);
5976 if( bGrid )
5978 nY -= nGrid;
5979 SwTwips nHeight = aTmp.Pos().X()
5980 - std::max(SwTwips(aInter.Left()), nY );
5981 if( nHeight > 0 )
5983 if( bCell )
5985 SwRect aVert( Point(aTmp.Pos().X()-nHeight,
5986 nX ), Size( nHeight, 1 ) );
5987 while( aVert.Top() <= nBottom )
5989 PaintBorderLine(rRect,aVert,this,pCol);
5990 aVert.Pos().AdjustY(nGrid );
5993 else if( bBorder )
5995 SwRect aVert( Point(aTmp.Pos().X()-nHeight,
5996 aGrid.Top() ), Size( nHeight, 1 ) );
5997 if( bLeft )
5998 PaintBorderLine(rRect,aVert,this,pCol);
5999 if( bRight )
6001 aVert.Pos().setY( nGridBottom );
6002 PaintBorderLine(rRect,aVert,this,pCol);
6007 else
6009 nY -= nRuby;
6010 if( bBorder )
6012 SwTwips nPos = std::max( SwTwips(aInter.Left()), nY );
6013 SwTwips nW = std::min(nRight, SwTwips(aTmp.Pos().X())) - nPos;
6014 SwRect aVert( Point( nPos, aGrid.Top() ),
6015 Size( nW, 1 ) );
6016 if( nW > 0 )
6018 if( bLeft )
6019 PaintBorderLine(rRect,aVert,this,pCol);
6020 if( bRight )
6022 aVert.Pos().setY( nGridBottom );
6023 PaintBorderLine(rRect,aVert,this,pCol);
6028 bGrid = !bGrid;
6031 else
6033 SwTwips nOrig = aGrid.Top();
6034 SwTwips nY = nOrig + nSum *( (aInter.Top()-nOrig)/nSum );
6035 SwRect aTmp( Point( aInter.Left(), nY ),
6036 Size( aInter.Width(), 1 ) );
6037 //for textgrid refactor
6038 SwTwips nX = aGrid.Left() + nGridWidth *
6039 ( ( aInter.Left() - aGrid.Left() )/ nGridWidth );
6040 if( nX < aInter.Left() )
6041 nX += nGridWidth;
6042 SwTwips nGridRight = aGrid.Left() + aGrid.Width();
6043 bool bLeft = aGrid.Left() >= aInter.Left();
6044 bool bRight = nGridRight <= nRight;
6045 bool bBorder = bLeft || bRight;
6046 while( nY < aInter.Top() )
6048 aTmp.Pos().setY(nY);
6049 if( bGrid )
6051 nY += nGrid;
6052 SwTwips nPosY = std::max( aInter.Top(), aTmp.Pos().getY() );
6053 SwTwips nHeight = std::min(nBottom, nY ) - nPosY;
6054 if( nHeight )
6056 if( bCell )
6058 SwRect aVert( Point( nX, nPosY ),
6059 Size( 1, nHeight ) );
6060 while( aVert.Left() <= nRight )
6062 PaintBorderLine(rRect,aVert,this,pCol);
6063 aVert.Pos().AdjustX(nGridWidth ); //for textgrid refactor
6066 else if ( bBorder )
6068 SwRect aVert( Point( aGrid.Left(), nPosY ),
6069 Size( 1, nHeight ) );
6070 if( bLeft )
6071 PaintBorderLine(rRect,aVert,this,pCol);
6072 if( bRight )
6074 aVert.Pos().setX( nGridRight );
6075 PaintBorderLine(rRect,aVert,this,pCol);
6080 else
6082 nY += nRuby;
6083 if( bBorder )
6085 SwTwips nPos = std::max(aInter.Top(),aTmp.Pos().getY());
6086 SwTwips nH = std::min( nBottom, nY ) - nPos;
6087 SwRect aVert( Point( aGrid.Left(), nPos ),
6088 Size( 1, nH ) );
6089 if( nH > 0 )
6091 if( bLeft )
6092 PaintBorderLine(rRect,aVert,this,pCol);
6093 if( bRight )
6095 aVert.Pos().setX(nGridRight);
6096 PaintBorderLine(rRect,aVert,this,pCol);
6101 bGrid = !bGrid;
6103 while( nY <= nBottom )
6105 aTmp.Pos().setY(nY);
6106 PaintBorderLine( rRect, aTmp, this, pCol);
6107 if( bGrid )
6109 nY += nGrid;
6110 SwTwips nHeight = std::min(nBottom, nY) - aTmp.Pos().getY();
6111 if( nHeight )
6113 if( bCell )
6115 SwRect aVert( Point( nX, aTmp.Pos().getY() ),
6116 Size( 1, nHeight ) );
6117 while( aVert.Left() <= nRight )
6119 PaintBorderLine( rRect, aVert, this, pCol);
6120 aVert.Pos().setX(aVert.Pos().getX() + nGridWidth); //for textgrid refactor
6123 else if( bBorder )
6125 SwRect aVert( Point( aGrid.Left(),
6126 aTmp.Pos().getY() ), Size( 1, nHeight ) );
6127 if( bLeft )
6128 PaintBorderLine(rRect,aVert,this,pCol);
6129 if( bRight )
6131 aVert.Pos().setX(nGridRight);
6132 PaintBorderLine(rRect,aVert,this,pCol);
6137 else
6139 nY += nRuby;
6140 if( bBorder )
6142 SwTwips nPos = std::max(aInter.Top(),aTmp.Pos().Y());
6143 SwTwips nH = std::min( nBottom, nY ) - nPos;
6144 SwRect aVert( Point( aGrid.Left(), nPos ),
6145 Size( 1, nH ) );
6146 if( nH > 0 )
6148 if( bLeft )
6149 PaintBorderLine(rRect,aVert,this,pCol);
6150 if( bRight )
6152 aVert.Pos().setX(nGridRight);
6153 PaintBorderLine(rRect,aVert,this,pCol);
6158 bGrid = !bGrid;
6164 * Paint margin area of a page
6166 * OD 20.11.2002 for #104598#:
6167 * implement paint of margin area; margin area will be painted for a
6168 * view shell with a window and if the document is not in online layout.
6170 * @param _rOutputRect
6171 * input parameter - constant instance reference of the rectangle, for
6172 * which an output has to be generated.
6174 * @param _pViewShell
6175 * input parameter - instance of the view shell, on which the output
6176 * has to be generated.
6178 void SwPageFrame::PaintMarginArea( const SwRect& _rOutputRect,
6179 SwViewShell const * _pViewShell ) const
6181 if ( !_pViewShell->GetWin() || _pViewShell->GetViewOptions()->getBrowseMode() )
6182 return;
6184 // Simplified paint with DrawingLayer FillStyle
6185 SwRect aPgRect = getFrameArea();
6186 aPgRect.Intersection_( _rOutputRect );
6188 if(!aPgRect.IsEmpty())
6190 OutputDevice *pOut = _pViewShell->GetOut();
6192 if(pOut->GetFillColor() != aGlobalRetoucheColor)
6194 pOut->SetFillColor(aGlobalRetoucheColor);
6197 pOut->DrawRect(aPgRect.SVRect());
6201 const sal_Int8 SwPageFrame::snShadowPxWidth = 9;
6203 bool SwPageFrame::IsRightShadowNeeded() const
6205 const SwViewShell *pSh = getRootFrame()->GetCurrShell();
6206 const bool bIsLTR = getRootFrame()->IsLeftToRightViewLayout();
6208 // We paint the right shadow if we're not in book mode
6209 // or if we've no sibling or are the last page of the "row"
6210 return !pSh || (!pSh->GetViewOptions()->IsViewLayoutBookMode()) || !GetNext()
6211 || (this == Lower()) || (bIsLTR && OnRightPage())
6212 || (!bIsLTR && !OnRightPage());
6216 bool SwPageFrame::IsLeftShadowNeeded() const
6218 const SwViewShell *pSh = getRootFrame()->GetCurrShell();
6219 const bool bIsLTR = getRootFrame()->IsLeftToRightViewLayout();
6221 // We paint the left shadow if we're not in book mode
6222 // or if we've no sibling or are the last page of the "row"
6223 return !pSh || (!pSh->GetViewOptions()->IsViewLayoutBookMode()) || !GetPrev()
6224 || (bIsLTR && !OnRightPage())
6225 || (!bIsLTR && OnRightPage());
6229 * Determine rectangle for bottom page shadow
6230 * for #i9719#
6232 /*static*/ void SwPageFrame::GetHorizontalShadowRect( const SwRect& _rPageRect,
6233 const SwViewShell* _pViewShell,
6234 OutputDevice const * pRenderContext,
6235 SwRect& _orHorizontalShadowRect,
6236 bool bPaintLeftShadow,
6237 bool bPaintRightShadow,
6238 bool bRightSidebar )
6240 const SwPostItMgr *pMgr = _pViewShell->GetPostItMgr();
6241 SwRect aAlignedPageRect( _rPageRect );
6242 ::SwAlignRect( aAlignedPageRect, _pViewShell, pRenderContext );
6243 SwRect aPagePxRect(pRenderContext->LogicToPixel( aAlignedPageRect.SVRect() ));
6245 tools::Long lShadowAdjustment = snShadowPxWidth - 1; // TODO: extract this
6247 _orHorizontalShadowRect.Chg(
6248 Point( aPagePxRect.Left() + (bPaintLeftShadow ? lShadowAdjustment : 0), 0 ),
6249 Size( aPagePxRect.Width() - ( (bPaintLeftShadow ? lShadowAdjustment : 0) + (bPaintRightShadow ? lShadowAdjustment : 0) ),
6250 snShadowPxWidth ) );
6252 if(pMgr && pMgr->ShowNotes() && pMgr->HasNotes())
6254 // Notes are displayed, we've to extend borders
6255 SwTwips aSidebarTotalWidth = pMgr->GetSidebarWidth(true) + pMgr->GetSidebarBorderWidth(true);
6256 if(bRightSidebar)
6257 _orHorizontalShadowRect.AddRight( aSidebarTotalWidth );
6258 else
6259 _orHorizontalShadowRect.AddLeft( - aSidebarTotalWidth );
6263 namespace {
6265 enum PaintArea {LEFT, RIGHT, TOP, BOTTOM};
6269 #define BORDER_TILE_SIZE 512
6271 /// Wrapper around pOut->DrawBitmapEx.
6272 static void lcl_paintBitmapExToRect(vcl::RenderContext *pOut, const Point& aPoint, const Size& aSize, const BitmapEx& rBitmapEx, PaintArea eArea)
6274 if(!comphelper::LibreOfficeKit::isActive())
6276 // The problem is that if we get called multiple times and the color is
6277 // partly transparent, then the result will get darker and darker. To avoid
6278 // this, always paint the background color before doing the real paint.
6279 tools::Rectangle aRect(aPoint, aSize);
6281 if (!aRect.IsEmpty())
6283 switch (eArea)
6285 case LEFT: aRect.SetLeft( aRect.Right() - 1 ); break;
6286 case RIGHT: aRect.SetRight( aRect.Left() + 1 ); break;
6287 case TOP: aRect.SetTop( aRect.Bottom() - 1 ); break;
6288 case BOTTOM: aRect.SetBottom( aRect.Top() + 1 ); break;
6292 pOut->SetFillColor(SwViewOption::GetCurrentViewOptions().GetAppBackgroundColor());
6293 pOut->SetLineColor();
6294 pOut->DrawRect(pOut->PixelToLogic(aRect));
6297 // Tiled render if necessary
6298 tools::Rectangle aComplete(aPoint, aSize);
6299 Size aTileSize(BORDER_TILE_SIZE, BORDER_TILE_SIZE);
6301 tools::Long iterX = eArea != RIGHT && eArea != LEFT ? BORDER_TILE_SIZE : 0;
6302 tools::Long iterY = eArea == RIGHT || eArea == LEFT ? BORDER_TILE_SIZE : 0;
6304 for (tools::Rectangle aTile(aPoint, aTileSize); true; aTile.Move(iterX, iterY))
6306 tools::Rectangle aRender = aComplete.GetIntersection(aTile);
6307 if (aRender.IsEmpty())
6308 break;
6309 pOut->DrawBitmapEx(pOut->PixelToLogic(aRender.TopLeft()),
6310 pOut->PixelToLogic(aRender.GetSize()),
6311 Point(0, 0), aRender.GetSize(),
6312 rBitmapEx);
6318 * Paint page border and shadow
6320 * for #i9719#
6321 * implement paint of page border and shadow
6323 /*static*/ void SwPageFrame::PaintBorderAndShadow( const SwRect& _rPageRect,
6324 const SwViewShell* _pViewShell,
6325 bool bPaintLeftShadow,
6326 bool bPaintRightShadow,
6327 bool bRightSidebar )
6329 // No shadow in prefs
6330 if (!_pViewShell->GetViewOptions()->IsShadow())
6331 return;
6333 // #i16816# tagged pdf support
6334 SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, nullptr, *_pViewShell->GetOut() );
6336 static vcl::DeleteOnDeinit<drawinglayer::primitive2d::DiscreteShadow> shadowMaskObj(
6337 vcl::bitmap::loadFromName(BMP_PAGE_SHADOW_MASK,
6338 ImageLoadFlags::IgnoreDarkTheme | ImageLoadFlags::IgnoreScalingFactor));
6340 drawinglayer::primitive2d::DiscreteShadow& shadowMask = *shadowMaskObj.get();
6341 static vcl::DeleteOnDeinit< BitmapEx > aPageTopRightShadowObj {};
6342 static vcl::DeleteOnDeinit< BitmapEx > aPageBottomRightShadowObj {};
6343 static vcl::DeleteOnDeinit< BitmapEx > aPageBottomLeftShadowObj {};
6344 static vcl::DeleteOnDeinit< BitmapEx > aPageBottomShadowBaseObj {};
6345 static vcl::DeleteOnDeinit< BitmapEx > aPageRightShadowBaseObj {};
6346 static vcl::DeleteOnDeinit< BitmapEx > aPageTopShadowBaseObj {};
6347 static vcl::DeleteOnDeinit< BitmapEx > aPageTopLeftShadowObj {};
6348 static vcl::DeleteOnDeinit< BitmapEx > aPageLeftShadowBaseObj {};
6349 BitmapEx& aPageTopRightShadow = *aPageTopRightShadowObj.get();
6350 BitmapEx& aPageBottomRightShadow = *aPageBottomRightShadowObj.get();
6351 BitmapEx& aPageBottomLeftShadow = *aPageBottomLeftShadowObj.get();
6352 BitmapEx& aPageBottomShadow = *aPageBottomShadowBaseObj.get();
6353 BitmapEx& aPageRightShadow = *aPageRightShadowBaseObj.get();
6354 BitmapEx& aPageTopShadow = *aPageTopShadowBaseObj.get();
6355 BitmapEx& aPageTopLeftShadow = *aPageTopLeftShadowObj.get();
6356 BitmapEx& aPageLeftShadow = *aPageLeftShadowBaseObj.get();
6357 static Color aShadowColor( COL_AUTO );
6359 SwRect aAlignedPageRect( _rPageRect );
6360 ::SwAlignRect( aAlignedPageRect, _pViewShell, _pViewShell->GetOut() );
6361 SwRect aPagePxRect(_pViewShell->GetOut()->LogicToPixel( aAlignedPageRect.SVRect() ));
6363 if (aShadowColor != _pViewShell->GetViewOptions()->GetShadowColor())
6365 aShadowColor = _pViewShell->GetViewOptions()->GetShadowColor();
6367 AlphaMask aMask( shadowMask.getBottomRight().GetBitmap() );
6368 Bitmap aFilledSquare(aMask.GetSizePixel(), vcl::PixelFormat::N24_BPP);
6369 aFilledSquare.Erase( aShadowColor );
6370 aPageBottomRightShadow = BitmapEx( aFilledSquare, aMask );
6372 aMask = AlphaMask( shadowMask.getBottomLeft().GetBitmap() );
6373 aFilledSquare = Bitmap(aMask.GetSizePixel(), vcl::PixelFormat::N24_BPP);
6374 aFilledSquare.Erase( aShadowColor );
6375 aPageBottomLeftShadow = BitmapEx( aFilledSquare, aMask );
6377 aMask = AlphaMask( shadowMask.getBottom().GetBitmap() );
6378 aFilledSquare = Bitmap(aMask.GetSizePixel(), vcl::PixelFormat::N24_BPP);
6379 aFilledSquare.Erase( aShadowColor );
6380 aPageBottomShadow = BitmapEx( aFilledSquare, aMask );
6382 aMask = AlphaMask( shadowMask.getTop().GetBitmap() );
6383 aFilledSquare = Bitmap(aMask.GetSizePixel(), vcl::PixelFormat::N24_BPP);
6384 aFilledSquare.Erase( aShadowColor );
6385 aPageTopShadow = BitmapEx( aFilledSquare, aMask );
6387 aMask = AlphaMask( shadowMask.getTopRight().GetBitmap() );
6388 aFilledSquare = Bitmap(aMask.GetSizePixel(), vcl::PixelFormat::N24_BPP);
6389 aFilledSquare.Erase( aShadowColor );
6390 aPageTopRightShadow = BitmapEx( aFilledSquare, aMask );
6392 aMask = AlphaMask( shadowMask.getRight().GetBitmap() );
6393 aFilledSquare = Bitmap(aMask.GetSizePixel(), vcl::PixelFormat::N24_BPP);
6394 aFilledSquare.Erase( aShadowColor );
6395 aPageRightShadow = BitmapEx( aFilledSquare, aMask );
6397 aMask = AlphaMask( shadowMask.getTopLeft().GetBitmap() );
6398 aFilledSquare = Bitmap(aMask.GetSizePixel(), vcl::PixelFormat::N24_BPP);
6399 aFilledSquare.Erase( aShadowColor );
6400 aPageTopLeftShadow = BitmapEx( aFilledSquare, aMask );
6402 aMask = AlphaMask( shadowMask.getLeft().GetBitmap() );
6403 aFilledSquare = Bitmap(aMask.GetSizePixel(), vcl::PixelFormat::N24_BPP);
6404 aFilledSquare.Erase( aShadowColor );
6405 aPageLeftShadow = BitmapEx( aFilledSquare, aMask );
6408 SwRect aPaintRect;
6409 OutputDevice *pOut = _pViewShell->GetOut();
6411 SwPageFrame::GetHorizontalShadowRect( _rPageRect, _pViewShell, pOut, aPaintRect, bPaintLeftShadow, bPaintRightShadow, bRightSidebar );
6413 // Right shadow & corners
6414 if ( bPaintRightShadow )
6416 pOut->DrawBitmapEx( pOut->PixelToLogic( Point( aPaintRect.Right(), aPagePxRect.Bottom() + 1 - (aPageBottomRightShadow.GetSizePixel().Height() - snShadowPxWidth) ) ),
6417 aPageBottomRightShadow );
6418 pOut->DrawBitmapEx( pOut->PixelToLogic( Point( aPaintRect.Right(), aPagePxRect.Top() - snShadowPxWidth ) ),
6419 aPageTopRightShadow );
6421 if (aPagePxRect.Height() > 2 * snShadowPxWidth)
6423 const tools::Long nWidth = aPageRightShadow.GetSizePixel().Width();
6424 const tools::Long nHeight = aPagePxRect.Height() - 2 * (snShadowPxWidth - 1);
6425 if (aPageRightShadow.GetSizePixel().Height() < BORDER_TILE_SIZE)
6426 aPageRightShadow.Scale(Size(nWidth, BORDER_TILE_SIZE), BmpScaleFlag::Fast);
6428 lcl_paintBitmapExToRect(pOut,
6429 Point(aPaintRect.Right() + snShadowPxWidth, aPagePxRect.Top() + snShadowPxWidth - 1),
6430 Size(nWidth, nHeight),
6431 aPageRightShadow, RIGHT);
6435 // Left shadows and corners
6436 if(bPaintLeftShadow)
6438 const tools::Long lLeft = aPaintRect.Left() - aPageBottomLeftShadow.GetSizePixel().Width();
6439 pOut->DrawBitmapEx( pOut->PixelToLogic( Point( lLeft,
6440 aPagePxRect.Bottom() + 1 + snShadowPxWidth - aPageBottomLeftShadow.GetSizePixel().Height() ) ), aPageBottomLeftShadow );
6441 pOut->DrawBitmapEx( pOut->PixelToLogic( Point( lLeft, aPagePxRect.Top() - snShadowPxWidth ) ), aPageTopLeftShadow );
6442 if (aPagePxRect.Height() > 2 * snShadowPxWidth)
6444 const tools::Long nWidth = aPageLeftShadow.GetSizePixel().Width();
6445 const tools::Long nHeight = aPagePxRect.Height() - 2 * (snShadowPxWidth - 1);
6446 if (aPageLeftShadow.GetSizePixel().Height() < BORDER_TILE_SIZE)
6447 aPageLeftShadow.Scale(Size(nWidth, BORDER_TILE_SIZE), BmpScaleFlag::Fast);
6449 lcl_paintBitmapExToRect(pOut,
6450 Point(lLeft, aPagePxRect.Top() + snShadowPxWidth - 1),
6451 Size(nWidth, nHeight),
6452 aPageLeftShadow, LEFT);
6456 // Bottom shadow
6457 const tools::Long nBottomHeight = aPageBottomShadow.GetSizePixel().Height();
6458 if (aPageBottomShadow.GetSizePixel().Width() < BORDER_TILE_SIZE)
6459 aPageBottomShadow.Scale(Size(BORDER_TILE_SIZE, nBottomHeight), BmpScaleFlag::Fast);
6461 lcl_paintBitmapExToRect(pOut,
6462 Point(aPaintRect.Left(), aPagePxRect.Bottom() + 2),
6463 Size(aPaintRect.Width(), nBottomHeight),
6464 aPageBottomShadow, BOTTOM);
6466 // Top shadow
6467 const tools::Long nTopHeight = aPageTopShadow.GetSizePixel().Height();
6468 if (aPageTopShadow.GetSizePixel().Width() < BORDER_TILE_SIZE)
6469 aPageTopShadow.Scale(Size(BORDER_TILE_SIZE, nTopHeight), BmpScaleFlag::Fast);
6471 lcl_paintBitmapExToRect(pOut,
6472 Point(aPaintRect.Left(), aPagePxRect.Top() - snShadowPxWidth),
6473 Size(aPaintRect.Width(), nTopHeight),
6474 aPageTopShadow, TOP);
6478 * mod #i6193# paint sidebar for notes
6479 * IMPORTANT: if you change the rects here, also change SwPostItMgr::ScrollbarHit
6481 /*static*/void SwPageFrame::PaintNotesSidebar(const SwRect& _rPageRect, SwViewShell* _pViewShell, sal_uInt16 nPageNum, bool bRight)
6483 //TODO: cut out scrollbar area and arrows out of sidepane rect, otherwise it could flicker when pressing arrow buttons
6484 if (!_pViewShell )
6485 return;
6487 SwRect aPageRect( _rPageRect );
6488 SwAlignRect( aPageRect, _pViewShell, _pViewShell->GetOut() );
6490 const SwPostItMgr *pMgr = _pViewShell->GetPostItMgr();
6491 if (!(pMgr && pMgr->ShowNotes() && pMgr->HasNotes())) // do not show anything in print preview
6492 return;
6494 sal_Int32 nScrollerHeight = pMgr->GetSidebarScrollerHeight();
6495 const tools::Rectangle &aVisRect = _pViewShell->VisArea().SVRect();
6496 //draw border and sidepane
6497 _pViewShell->GetOut()->SetLineColor();
6498 if (!bRight)
6500 _pViewShell->GetOut()->SetFillColor(_pViewShell->GetViewOptions()->GetObjectBoundariesColor());
6501 _pViewShell->GetOut()->DrawRect(tools::Rectangle(Point(aPageRect.Left()-pMgr->GetSidebarBorderWidth(),aPageRect.Top()),Size(pMgr->GetSidebarBorderWidth(),aPageRect.Height()))) ;
6502 if (Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
6503 _pViewShell->GetOut()->SetFillColor(COL_BLACK);
6504 else
6505 _pViewShell->GetOut()->SetFillColor(_pViewShell->GetViewOptions()->GetSectionBoundColor());
6506 _pViewShell->GetOut()->DrawRect(tools::Rectangle(Point(aPageRect.Left()-pMgr->GetSidebarWidth()-pMgr->GetSidebarBorderWidth(),aPageRect.Top()),Size(pMgr->GetSidebarWidth(),aPageRect.Height()))) ;
6508 else
6510 _pViewShell->GetOut()->SetFillColor(_pViewShell->GetViewOptions()->GetObjectBoundariesColor());
6511 SwRect aSidebarBorder(aPageRect.TopRight(),Size(pMgr->GetSidebarBorderWidth(),aPageRect.Height()));
6512 _pViewShell->GetOut()->DrawRect(aSidebarBorder.SVRect());
6513 if (Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
6514 _pViewShell->GetOut()->SetFillColor(COL_BLACK);
6515 else
6516 _pViewShell->GetOut()->SetFillColor(_pViewShell->GetViewOptions()->GetSectionBoundColor());
6517 SwRect aSidebar(Point(aPageRect.Right()+pMgr->GetSidebarBorderWidth(),aPageRect.Top()),Size(pMgr->GetSidebarWidth(),aPageRect.Height()));
6518 _pViewShell->GetOut()->DrawRect(aSidebar.SVRect());
6520 if (!pMgr->ShowScrollbar(nPageNum))
6521 return;
6523 // draw scrollbar area and arrows
6524 Point aPointBottom;
6525 Point aPointTop;
6526 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()) :
6527 Point(aPageRect.Right() + pMgr->GetSidebarBorderWidth() + _pViewShell->GetOut()->PixelToLogic(Size(2,0)).Width(),aPageRect.Bottom()- _pViewShell->GetOut()->PixelToLogic(Size(0,2+pMgr->GetSidebarScrollerHeight())).Height());
6528 aPointTop = !bRight ? Point(aPageRect.Left() - pMgr->GetSidebarWidth() + _pViewShell->GetOut()->PixelToLogic(Size(2,0)).Width(),aPageRect.Top() + _pViewShell->GetOut()->PixelToLogic(Size(0,2)).Height()) :
6529 Point(aPageRect.Right() + pMgr->GetSidebarBorderWidth() + _pViewShell->GetOut()->PixelToLogic(Size(2,0)).Width(),aPageRect.Top() + _pViewShell->GetOut()->PixelToLogic(Size(0,2)).Height());
6530 Size aSize(pMgr->GetSidebarWidth() - _pViewShell->GetOut()->PixelToLogic(Size(4,0)).Width(), _pViewShell->GetOut()->PixelToLogic(Size(0,nScrollerHeight)).Height()) ;
6531 tools::Rectangle aRectBottom(aPointBottom,aSize);
6532 tools::Rectangle aRectTop(aPointTop,aSize);
6534 if (aRectBottom.Overlaps(aVisRect))
6537 if (Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
6539 _pViewShell->GetOut()->SetLineColor(COL_WHITE);
6540 _pViewShell->GetOut()->SetFillColor(COL_BLACK);
6542 else
6544 _pViewShell->GetOut()->SetLineColor(COL_BLACK);
6545 _pViewShell->GetOut()->SetFillColor(COL_LIGHTGRAY);
6547 _pViewShell->GetOut()->DrawRect(aRectBottom);
6548 _pViewShell->GetOut()->DrawLine(aPointBottom + Point(pMgr->GetSidebarWidth()/3,0), aPointBottom + Point(pMgr->GetSidebarWidth()/3 , _pViewShell->GetOut()->PixelToLogic(Size(0,nScrollerHeight)).Height()));
6550 _pViewShell->GetOut()->SetLineColor();
6551 Point aMiddleFirst(aPointBottom + Point(pMgr->GetSidebarWidth()/6,_pViewShell->GetOut()->PixelToLogic(Size(0,nScrollerHeight)).Height()/2));
6552 Point aMiddleSecond(aPointBottom + Point(pMgr->GetSidebarWidth()/3*2,_pViewShell->GetOut()->PixelToLogic(Size(0,nScrollerHeight)).Height()/2));
6553 PaintNotesSidebarArrows(aMiddleFirst,aMiddleSecond,_pViewShell,pMgr->GetArrowColor(KEY_PAGEUP,nPageNum), pMgr->GetArrowColor(KEY_PAGEDOWN,nPageNum));
6555 if (!aRectTop.Overlaps(aVisRect))
6556 return;
6558 if (Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
6560 _pViewShell->GetOut()->SetLineColor(COL_WHITE);
6561 _pViewShell->GetOut()->SetFillColor(COL_BLACK);
6563 else
6565 _pViewShell->GetOut()->SetLineColor(COL_BLACK);
6566 _pViewShell->GetOut()->SetFillColor(COL_LIGHTGRAY);
6568 _pViewShell->GetOut()->DrawRect(aRectTop);
6569 _pViewShell->GetOut()->DrawLine(aPointTop + Point(pMgr->GetSidebarWidth()/3*2,0), aPointTop + Point(pMgr->GetSidebarWidth()/3*2 , _pViewShell->GetOut()->PixelToLogic(Size(0,nScrollerHeight)).Height()));
6571 _pViewShell->GetOut()->SetLineColor();
6572 Point aMiddleFirst(aPointTop + Point(pMgr->GetSidebarWidth()/3,_pViewShell->GetOut()->PixelToLogic(Size(0,nScrollerHeight)).Height()/2));
6573 Point aMiddleSecond(aPointTop + Point(pMgr->GetSidebarWidth()/6*5,_pViewShell->GetOut()->PixelToLogic(Size(0,nScrollerHeight)).Height()/2));
6574 PaintNotesSidebarArrows(aMiddleFirst,aMiddleSecond,_pViewShell, pMgr->GetArrowColor(KEY_PAGEUP,nPageNum), pMgr->GetArrowColor(KEY_PAGEDOWN,nPageNum));
6577 /*static*/ void SwPageFrame::PaintNotesSidebarArrows(const Point &aMiddleFirst, const Point &aMiddleSecond, SwViewShell const * _pViewShell, const Color& rColorUp, const Color& rColorDown)
6579 tools::Polygon aTriangleUp(3);
6580 tools::Polygon aTriangleDown(3);
6582 aTriangleUp.SetPoint(aMiddleFirst + Point(0,_pViewShell->GetOut()->PixelToLogic(Size(0,-3)).Height()),0);
6583 aTriangleUp.SetPoint(aMiddleFirst + Point(_pViewShell->GetOut()->PixelToLogic(Size(-3,0)).Width(),_pViewShell->GetOut()->PixelToLogic(Size(0,3)).Height()),1);
6584 aTriangleUp.SetPoint(aMiddleFirst + Point(_pViewShell->GetOut()->PixelToLogic(Size(3,0)).Width(),_pViewShell->GetOut()->PixelToLogic(Size(0,3)).Height()),2);
6586 aTriangleDown.SetPoint(aMiddleSecond + Point(_pViewShell->GetOut()->PixelToLogic(Size(-3,0)).Width(),_pViewShell->GetOut()->PixelToLogic(Size(0,-3)).Height()),0);
6587 aTriangleDown.SetPoint(aMiddleSecond + Point(_pViewShell->GetOut()->PixelToLogic(Size(+3,0)).Width(),_pViewShell->GetOut()->PixelToLogic(Size(0,-3)).Height()),1);
6588 aTriangleDown.SetPoint(aMiddleSecond + Point(0,_pViewShell->GetOut()->PixelToLogic(Size(0,3)).Height()),2);
6590 _pViewShell->GetOut()->SetFillColor(rColorUp);
6591 _pViewShell->GetOut()->DrawPolygon(aTriangleUp);
6592 _pViewShell->GetOut()->SetFillColor(rColorDown);
6593 _pViewShell->GetOut()->DrawPolygon(aTriangleDown);
6597 * Get bound rectangle of border and shadow for repaints
6599 * for #i9719#
6601 /*static*/ void SwPageFrame::GetBorderAndShadowBoundRect( const SwRect& _rPageRect,
6602 const SwViewShell* _pViewShell,
6603 OutputDevice const * pRenderContext,
6604 SwRect& _orBorderAndShadowBoundRect,
6605 bool bLeftShadow,
6606 bool bRightShadow,
6607 bool bRightSidebar
6610 SwRect aAlignedPageRect( _rPageRect );
6611 ::SwAlignRect( aAlignedPageRect, _pViewShell, pRenderContext );
6612 SwRect aPagePxRect(pRenderContext->LogicToPixel( aAlignedPageRect.SVRect() ));
6613 aPagePxRect.AddBottom( snShadowPxWidth + 1 );
6614 aPagePxRect.AddTop( - snShadowPxWidth - 1 );
6616 SwRect aTmpRect;
6618 // Always ask for full shadow since we want a bounding rect
6619 // including at least the page frame
6620 SwPageFrame::GetHorizontalShadowRect( _rPageRect, _pViewShell, pRenderContext, aTmpRect, false, false, bRightSidebar );
6622 if(bLeftShadow) aPagePxRect.Left( aTmpRect.Left() - snShadowPxWidth - 1);
6623 if(bRightShadow) aPagePxRect.Right( aTmpRect.Right() + snShadowPxWidth + 1);
6625 _orBorderAndShadowBoundRect = SwRect(pRenderContext->PixelToLogic( aPagePxRect.SVRect() ));
6628 SwRect SwPageFrame::GetBoundRect(OutputDevice const * pOutputDevice) const
6630 const SwViewShell *pSh = getRootFrame()->GetCurrShell();
6631 SwRect aPageRect( getFrameArea() );
6632 SwRect aResult;
6634 if(!pSh) {
6635 return SwRect( Point(0, 0), Size(0, 0) );
6638 SwPageFrame::GetBorderAndShadowBoundRect( aPageRect, pSh, pOutputDevice, aResult,
6639 IsLeftShadowNeeded(), IsRightShadowNeeded(), SidebarPosition() == sw::sidebarwindows::SidebarPosition::RIGHT );
6640 return aResult;
6643 /*static*/ SwTwips SwPageFrame::GetSidebarBorderWidth( const SwViewShell* _pViewShell )
6645 const SwPostItMgr* pPostItMgr = _pViewShell ? _pViewShell->GetPostItMgr() : nullptr;
6646 const SwTwips nRet = pPostItMgr && pPostItMgr->HasNotes() && pPostItMgr->ShowNotes() ? pPostItMgr->GetSidebarWidth() + pPostItMgr->GetSidebarBorderWidth() : 0;
6647 return nRet;
6650 void SwFrame::PaintBaBo( const SwRect& rRect, const SwPageFrame *pPage,
6651 const bool bOnlyTextBackground ) const
6653 if ( !pPage )
6654 pPage = FindPageFrame();
6656 OutputDevice *pOut = gProp.pSGlobalShell->GetOut();
6658 // #i16816# tagged pdf support
6659 SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, nullptr, *pOut );
6661 pOut->Push( vcl::PushFlags::FILLCOLOR|vcl::PushFlags::LINECOLOR );
6662 pOut->SetLineColor();
6664 SwBorderAttrAccess aAccess( SwFrame::GetCache(), this );
6665 const SwBorderAttrs &rAttrs = *aAccess.Get();
6667 // take care of page margin area
6668 // Note: code move from <SwFrame::PaintSwFrameBackground(..)> to new method
6669 // <SwPageFrame::Paintmargin(..)>.
6670 if ( IsPageFrame() && !bOnlyTextBackground)
6672 static_cast<const SwPageFrame*>(this)->PaintMarginArea( rRect, gProp.pSGlobalShell );
6675 // paint background
6677 PaintSwFrameBackground( rRect, pPage, rAttrs, false, true/*bLowerBorder*/, bOnlyTextBackground );
6680 // paint border before painting background
6681 // paint grid for page frame and paint border
6682 if (!bOnlyTextBackground)
6684 SwRect aRect( rRect );
6686 if( IsPageFrame() )
6688 static_cast<const SwPageFrame*>(this)->PaintGrid( pOut, aRect );
6691 PaintSwFrameShadowAndBorder(aRect, pPage, rAttrs);
6694 pOut->Pop();
6697 static bool lcl_compareFillAttributes(const drawinglayer::attribute::SdrAllFillAttributesHelperPtr& pA, const drawinglayer::attribute::SdrAllFillAttributesHelperPtr& pB)
6699 if (pA == pB)
6700 return true;
6701 if (!pA || !pB)
6702 return false;
6703 return pA->getFillAttribute() == pB->getFillAttribute();
6706 /// Do not paint background for fly frames without a background brush by
6707 /// calling <PaintBaBo> at the page or at the fly frame its anchored
6708 void SwFrame::PaintSwFrameBackground( const SwRect &rRect, const SwPageFrame *pPage,
6709 const SwBorderAttrs & rAttrs,
6710 const bool bLowerMode,
6711 const bool bLowerBorder,
6712 const bool bOnlyTextBackground ) const
6714 // #i1837# - no paint of table background, if corresponding option is *not* set.
6715 SwViewShell *pSh = gProp.pSGlobalShell;
6716 if( IsTabFrame() &&
6717 !pSh->GetViewOptions()->IsTable() )
6719 return;
6722 // nothing to do for covered table cells:
6723 if( IsCellFrame() && IsCoveredCell() )
6724 return;
6726 // #i16816# tagged pdf support
6727 SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, nullptr, *pSh->GetOut() );
6729 const SvxBrushItem* pItem;
6730 // temporary background brush for a fly frame without a background brush
6731 std::unique_ptr<SvxBrushItem> pTmpBackBrush;
6732 std::optional<Color> pCol;
6733 SwRect aOrigBackRect;
6734 const bool bPageFrame = IsPageFrame();
6735 bool bLowMode = true;
6736 drawinglayer::attribute::SdrAllFillAttributesHelperPtr aFillAttributes;
6738 bool bBack = GetBackgroundBrush( aFillAttributes, pItem, pCol, aOrigBackRect, bLowerMode, /*bConsiderTextBox=*/false );
6740 // show track changes of table row
6741 if( IsRowFrame() && !getRootFrame()->IsHideRedlines() )
6743 RedlineType eType = static_cast<const SwRowFrame*>(this)->GetTabLine()->GetRedlineType();
6744 if ( RedlineType::Delete == eType || RedlineType::Insert == eType )
6746 pCol = RedlineType::Delete == eType ? COL_AUTHOR_TABLE_DEL : COL_AUTHOR_TABLE_INS;
6747 bBack = true;
6750 else if ( IsCellFrame() && !getRootFrame()->IsHideRedlines() )
6752 RedlineType eType = static_cast<const SwCellFrame*>(this)->GetTabBox()->GetRedlineType();
6753 if ( RedlineType::Delete == eType || RedlineType::Insert == eType )
6755 pCol = RedlineType::Delete == eType ? COL_AUTHOR_TABLE_DEL : COL_AUTHOR_TABLE_INS;
6756 bBack = true;
6760 if ( bBack && IsCellFrame() && !getRootFrame()->IsHideRedlines() &&
6761 // skip cell background to show the row colored according to its tracked change
6762 RedlineType::None != static_cast<const SwRowFrame*>(GetUpper())->GetTabLine()->GetRedlineType() )
6764 return;
6767 //- Output if a separate background is used.
6768 bool bNoFlyBackground = !gProp.bSFlyMetafile && !bBack && IsFlyFrame();
6769 if ( bNoFlyBackground )
6771 // Fly frame has no background.
6772 // Try to find background brush at parents, if previous call of
6773 // <GetBackgroundBrush> disabled this option with the parameter <bLowerMode>
6774 if ( bLowerMode )
6776 bBack = GetBackgroundBrush( aFillAttributes, pItem, pCol, aOrigBackRect, false, /*bConsiderTextBox=*/false );
6778 // If still no background found for the fly frame, initialize the
6779 // background brush <pItem> with global retouche color and set <bBack>
6780 // to true, that fly frame will paint its background using this color.
6781 if ( !bBack )
6783 // #i6467# - on print output, pdf output and in embedded mode not editing color COL_WHITE is used
6784 // instead of the global retouche color.
6785 if ( pSh->GetOut()->GetOutDevType() == OUTDEV_PRINTER ||
6786 pSh->GetViewOptions()->IsPDFExport() ||
6787 ( pSh->GetDoc()->GetDocShell()->GetCreateMode() == SfxObjectCreateMode::EMBEDDED &&
6788 !pSh->GetDoc()->GetDocShell()->IsInPlaceActive()
6792 pTmpBackBrush.reset(new SvxBrushItem( COL_WHITE, RES_BACKGROUND ));
6794 //UUU
6795 aFillAttributes = std::make_shared<drawinglayer::attribute::SdrAllFillAttributesHelper>(COL_WHITE);
6797 else
6799 pTmpBackBrush.reset(new SvxBrushItem( aGlobalRetoucheColor, RES_BACKGROUND));
6801 //UUU
6802 aFillAttributes = std::make_shared<drawinglayer::attribute::SdrAllFillAttributesHelper>(aGlobalRetoucheColor);
6805 pItem = pTmpBackBrush.get();
6806 bBack = true;
6810 SwRect aPaintRect( getFrameArea() );
6811 if( IsTextFrame() || IsSctFrame() )
6812 aPaintRect = UnionFrame( true );
6814 // bOnlyTextBackground means background that's on top of background shapes,
6815 // this includes both text and cell frames.
6816 if ( (!bOnlyTextBackground || IsTextFrame() || IsCellFrame()) && aPaintRect.Overlaps( rRect ) )
6818 if ( bBack || bPageFrame || !bLowerMode )
6820 const bool bBrowse = pSh->GetViewOptions()->getBrowseMode();
6821 SwRect aRect;
6822 if ( (bPageFrame && bBrowse) ||
6823 (IsTextFrame() && getFramePrintArea().SSize() == getFrameArea().SSize()) )
6825 aRect = getFrameArea();
6826 ::SwAlignRect( aRect, gProp.pSGlobalShell, gProp.pSGlobalShell->GetOut() );
6828 else
6830 if (bPageFrame && GetAttrSet()->GetItem<SfxBoolItem>(RES_BACKGROUND_FULL_SIZE)->GetValue())
6832 aRect = getFrameArea();
6833 ::SwAlignRect(aRect, gProp.pSGlobalShell, gProp.pSGlobalShell->GetOut());
6835 else
6837 ::lcl_CalcBorderRect( aRect, this, rAttrs, false, gProp);
6840 if ( (IsTextFrame() || IsTabFrame()) && GetPrev() )
6842 if ( GetPrev()->GetAttrSet()->GetBackground() == GetAttrSet()->GetBackground() &&
6843 lcl_compareFillAttributes(GetPrev()->getSdrAllFillAttributesHelper(), getSdrAllFillAttributesHelper()))
6845 aRect.Top( getFrameArea().Top() );
6849 aRect.Intersection( rRect );
6851 OutputDevice *pOut = pSh->GetOut();
6853 if ( aRect.HasArea() )
6855 std::unique_ptr<SvxBrushItem> pNewItem;
6857 if( pCol )
6859 pNewItem.reset(new SvxBrushItem( *pCol, RES_BACKGROUND ));
6860 pItem = pNewItem.get();
6861 aFillAttributes = std::make_shared<drawinglayer::attribute::SdrAllFillAttributesHelper>(*pCol);
6864 SwRegionRects aRegion( aRect );
6865 basegfx::B2DPolygon aB2DPolygon{tools::Polygon(aRect.SVRect()).getB2DPolygon()};
6866 basegfx::utils::B2DClipState aClipState{basegfx::B2DPolyPolygon(aB2DPolygon)};
6867 if (pPage->GetSortedObjs() &&
6868 pSh->GetDoc()->getIDocumentSettingAccess().get(DocumentSettingId::SUBTRACT_FLYS))
6870 ::lcl_SubtractFlys( this, pPage, aRect, aRegion, aClipState, gProp );
6873 // Determine, if background transparency
6874 // have to be considered for drawing.
6875 // Status Quo: background transparency have to be
6876 // considered for fly frames
6877 const bool bConsiderBackgroundTransparency = IsFlyFrame();
6878 bool bDone(false);
6880 // #i125189# We are also done when the new DrawingLayer FillAttributes are used
6881 // or the FillStyle is set (different from drawing::FillStyle_NONE)
6882 if (aFillAttributes)
6884 if(aFillAttributes->isUsed())
6886 // check if really something is painted
6887 bDone = DrawFillAttributes(aFillAttributes, aOrigBackRect, aRegion, aClipState, *pOut);
6890 if(!bDone)
6892 // if not, still a FillStyle could be set but the transparency is at 100%,
6893 // thus need to check the model data itself for FillStyle (do not rely on
6894 // SdrAllFillAttributesHelper since it already contains optimized information,
6895 // e.g. transparency leads to no fill)
6896 const drawing::FillStyle eFillStyle(GetAttrSet()->Get(XATTR_FILLSTYLE).GetValue());
6898 if(drawing::FillStyle_NONE != eFillStyle)
6900 bDone = true;
6905 if(!bDone)
6907 for (size_t i = 0; i < aRegion.size(); ++i)
6909 if (1 < aRegion.size())
6911 ::SwAlignRect( aRegion[i], gProp.pSGlobalShell, gProp.pSGlobalShell->GetOut() );
6912 if( !aRegion[i].HasArea() )
6913 continue;
6915 // add 6th parameter to indicate, if background transparency have to be considered
6916 // Set missing 5th parameter to the default value GRFNUM_NO
6917 // - see declaration in /core/inc/frmtool.hxx.
6918 ::DrawGraphic(
6919 pItem,
6920 *pOut,
6921 aOrigBackRect,
6922 aRegion[i],
6923 GRFNUM_NO,
6924 bConsiderBackgroundTransparency );
6929 else
6930 bLowMode = bLowerMode;
6933 // delete temporary background brush.
6934 pTmpBackBrush.reset();
6936 //Now process lower and his neighbour.
6937 //We end this as soon as a Frame leaves the chain and therefore is not a lower
6938 //of me anymore
6939 const SwFrame *pFrame = GetLower();
6940 if ( !pFrame )
6941 return;
6943 SwRect aFrameRect;
6944 SwRect aRect( GetPaintArea() );
6945 aRect.Intersection_( rRect );
6946 SwRect aBorderRect( aRect );
6947 SwShortCut aShortCut( *pFrame, aBorderRect );
6949 { if ( gProp.pSProgress )
6950 SfxProgress::Reschedule();
6952 aFrameRect = pFrame->GetPaintArea();
6953 if ( aFrameRect.Overlaps( aBorderRect ) )
6955 SwBorderAttrAccess aAccess( SwFrame::GetCache(), pFrame );
6956 const SwBorderAttrs &rTmpAttrs = *aAccess.Get();
6957 if ( ( pFrame->IsLayoutFrame() && bLowerBorder ) || aFrameRect.Overlaps( aRect ) )
6959 pFrame->PaintSwFrameBackground( aRect, pPage, rTmpAttrs, bLowMode,
6960 bLowerBorder, bOnlyTextBackground );
6963 if ( bLowerBorder )
6965 pFrame->PaintSwFrameShadowAndBorder( aBorderRect, pPage, rTmpAttrs );
6968 pFrame = pFrame->GetNext();
6969 } while ( pFrame && pFrame->GetUpper() == this &&
6970 !aShortCut.Stop( aFrameRect ) );
6973 /// Refreshes all subsidiary lines of a page.
6974 void SwPageFrame::RefreshSubsidiary( const SwRect &rRect ) const
6976 if ( !(isSubsidiaryLinesEnabled() || isTableBoundariesEnabled()
6977 || isSubsidiaryLinesForSectionsEnabled() || isSubsidiaryLinesFlysEnabled()) )
6978 return;
6980 if ( !rRect.HasArea() )
6981 return;
6983 //During paint using the root, the array is controlled from there.
6984 //Otherwise we'll handle it for our self.
6985 bool bDelSubs = false;
6986 if ( !gProp.pSSubsLines )
6988 gProp.pSSubsLines.reset(new SwSubsRects);
6989 // create container for special subsidiary lines
6990 gProp.pSSpecSubsLines.reset(new SwSubsRects);
6991 bDelSubs = true;
6994 RefreshLaySubsidiary( this, rRect );
6996 if ( bDelSubs )
6998 // paint special subsidiary lines and delete its container
6999 gProp.pSSpecSubsLines->PaintSubsidiary( gProp.pSGlobalShell->GetOut(), nullptr, gProp );
7000 gProp.pSSpecSubsLines.reset();
7002 gProp.pSSubsLines->PaintSubsidiary(gProp.pSGlobalShell->GetOut(), gProp.pSLines.get(), gProp);
7003 gProp.pSSubsLines.reset();
7007 void SwLayoutFrame::RefreshLaySubsidiary( const SwPageFrame *pPage,
7008 const SwRect &rRect ) const
7010 const bool bSubsOpt = isSubsidiaryLinesEnabled() || isSubsidiaryLinesForSectionsEnabled();
7011 if ( bSubsOpt )
7012 PaintSubsidiaryLines( pPage, rRect );
7014 const SwFrame *pLow = Lower();
7015 if( !pLow )
7016 return;
7017 SwShortCut aShortCut( *pLow, rRect );
7018 while( pLow && !aShortCut.Stop( pLow->getFrameArea() ) )
7020 if ( pLow->getFrameArea().Overlaps( rRect ) && pLow->getFrameArea().HasArea() )
7022 if ( pLow->IsLayoutFrame() )
7023 static_cast<const SwLayoutFrame*>(pLow)->RefreshLaySubsidiary( pPage, rRect);
7024 else if ( pLow->GetDrawObjs() )
7026 const SwSortedObjs& rObjs = *(pLow->GetDrawObjs());
7027 for (SwAnchoredObject* pAnchoredObj : rObjs)
7029 if ( pPage->GetFormat()->GetDoc()->getIDocumentDrawModelAccess().IsVisibleLayerId(
7030 pAnchoredObj->GetDrawObj()->GetLayer() ) )
7031 if (auto pFly = pAnchoredObj->DynCastFlyFrame() )
7033 if ( pFly->IsFlyInContentFrame() && pFly->getFrameArea().Overlaps( rRect ) )
7035 if ( !pFly->Lower() || !pFly->Lower()->IsNoTextFrame() ||
7036 !static_cast<const SwNoTextFrame*>(pFly->Lower())->HasAnimation())
7037 pFly->RefreshLaySubsidiary( pPage, rRect );
7043 pLow = pLow->GetNext();
7048 * Subsidiary lines to paint the PrtAreas
7049 * Only the LayoutFrames which directly contain Content
7050 * Paints the desired line and pays attention to not overpaint any flys
7052 static void lcl_RefreshLine( const SwLayoutFrame *pLay,
7053 const SwPageFrame *pPage,
7054 const Point &rP1,
7055 const Point &rP2,
7056 const SubColFlags nSubColor,
7057 SwLineRects* pSubsLines )
7059 //In which direction do we loop? Can only be horizontal or vertical.
7060 OSL_ENSURE( ((rP1.X() == rP2.X()) || (rP1.Y() == rP2.Y())),
7061 "Sloped subsidiary lines are not allowed." );
7063 const bool bHori = rP1.Y() == rP2.Y();
7065 // use pointers to member function in order to unify flow
7066 typedef tools::Long (Point::*pmfPtGet)() const;
7067 typedef void (Point::*pmfPtSet)(tools::Long);
7068 const pmfPtGet pDirPtX = &Point::X;
7069 const pmfPtGet pDirPtY = &Point::Y;
7070 const pmfPtGet pDirPt = bHori ? pDirPtX : pDirPtY;
7071 const pmfPtSet pDirPtSetX = &Point::setX;
7072 const pmfPtSet pDirPtSetY = &Point::setY;
7073 const pmfPtSet pDirPtSet = bHori ? pDirPtSetX : pDirPtSetY;
7075 Point aP1( rP1 );
7076 Point aP2( rP2 );
7078 while ( (aP1.*pDirPt)() < (aP2.*pDirPt)() )
7080 //If the starting point lies in a fly, it is directly set behind the
7081 //fly.
7082 //The end point moves to the start if the end point lies in a fly or we
7083 //have a fly between starting point and end point.
7084 // In this way, every position is output one by one.
7086 //If I'm a fly I'll only avoid those flys which are places 'above' me;
7087 //this means those who are behind me in the array.
7088 //Even if I'm inside a fly or inside a fly inside a fly a.s.o I won't
7089 //avoid any of those flys.
7090 SwOrderIter aIter( pPage );
7091 const SwFlyFrame *pMyFly = pLay->FindFlyFrame();
7092 if ( pMyFly )
7094 aIter.Current( pMyFly->GetVirtDrawObj() );
7095 while ( nullptr != (pMyFly = pMyFly->GetAnchorFrame()->FindFlyFrame()) )
7097 if ( aIter()->GetOrdNum() > pMyFly->GetVirtDrawObj()->GetOrdNum() )
7098 aIter.Current( pMyFly->GetVirtDrawObj() );
7101 else
7102 aIter.Bottom();
7104 while ( aIter() )
7106 const SwVirtFlyDrawObj *pObj = static_cast<const SwVirtFlyDrawObj*>(aIter());
7107 const SwFlyFrame *pFly = pObj ? pObj->GetFlyFrame() : nullptr;
7109 //I certainly won't avoid myself, even if I'm placed _inside_ the
7110 //fly I won't avoid it.
7111 if ( !pFly || (pFly == pLay || pFly->IsAnLower( pLay )) )
7113 aIter.Next();
7114 continue;
7117 // do *not* consider fly frames with a transparent background.
7118 // do *not* consider fly frame, which belongs to an invisible layer
7119 if ( pFly->IsBackgroundTransparent() ||
7120 !pFly->GetFormat()->GetDoc()->getIDocumentDrawModelAccess().IsVisibleLayerId( pObj->GetLayer() ) )
7122 aIter.Next();
7123 continue;
7126 //Is the Obj placed on the line
7127 const tools::Long nP1OthPt = !bHori ? rP1.X() : rP1.Y();
7128 const tools::Rectangle &rBound = pObj->GetCurrentBoundRect();
7129 const Point aDrPt( rBound.TopLeft() );
7130 const tools::Long nDrOthPt = !bHori ? aDrPt.X() : aDrPt.Y();
7131 const Size aDrSz( rBound.GetSize() );
7132 const tools::Long nDrOthSz = !bHori ? aDrSz.Width() : aDrSz.Height();
7134 if ( nP1OthPt >= nDrOthPt && nP1OthPt <= nDrOthPt + nDrOthSz )
7136 const tools::Long nDrDirPt = bHori ? aDrPt.X() : aDrPt.Y();
7137 const tools::Long nDrDirSz = bHori ? aDrSz.Width() : aDrSz.Height();
7139 if ( (aP1.*pDirPt)() >= nDrDirPt && (aP1.*pDirPt)() <= nDrDirPt + nDrDirSz )
7140 (aP1.*pDirPtSet)( nDrDirPt + nDrDirSz );
7142 if ( (aP2.*pDirPt)() >= nDrDirPt && (aP1.*pDirPt)() < (nDrDirPt - 1) )
7143 (aP2.*pDirPtSet)( nDrDirPt - 1 );
7145 aIter.Next();
7148 if ( (aP1.*pDirPt)() < (aP2.*pDirPt)() )
7150 SwRect aRect( aP1, aP2 );
7151 // use parameter <pSubsLines> instead of global variable <gProp.pSSubsLines>.
7152 pSubsLines->AddLineRect( aRect, nullptr, SvxBorderLineStyle::SOLID,
7153 nullptr, nSubColor, gProp );
7155 aP1 = aP2;
7156 (aP1.*pDirPtSet)( (aP1.*pDirPt)() + 1 );
7157 aP2 = rP2;
7161 static drawinglayer::primitive2d::Primitive2DContainer lcl_CreatePageAreaDelimiterPrimitives(
7162 const SwRect& rRect )
7164 drawinglayer::primitive2d::Primitive2DContainer aSeq( 4 );
7166 basegfx::BColor aLineColor = SwViewOption::GetCurrentViewOptions().GetDocBoundariesColor().getBColor();
7167 double nLineLength = 200.0; // in Twips
7169 Point aPoints[] = { rRect.TopLeft(), rRect.TopRight(), rRect.BottomRight(), rRect.BottomLeft() };
7170 double const aXOffDirs[] = { -1.0, 1.0, 1.0, -1.0 };
7171 double const aYOffDirs[] = { -1.0, -1.0, 1.0, 1.0 };
7173 // Actually loop over the corners to create the two lines
7174 for ( int i = 0; i < 4; i++ )
7176 basegfx::B2DVector aHorizVector( aXOffDirs[i], 0.0 );
7177 basegfx::B2DVector aVertVector( 0.0, aYOffDirs[i] );
7179 basegfx::B2DPoint aBPoint( aPoints[i].getX(), aPoints[i].getY() );
7181 basegfx::B2DPolygon aPolygon;
7182 aPolygon.append( aBPoint + aHorizVector * nLineLength );
7183 aPolygon.append( aBPoint );
7184 aPolygon.append( aBPoint + aVertVector * nLineLength );
7186 aSeq[i] = new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(
7187 std::move(aPolygon), aLineColor );
7190 return aSeq;
7193 static drawinglayer::primitive2d::Primitive2DContainer lcl_CreateRectangleDelimiterPrimitives (
7194 const SwRect& rRect )
7196 drawinglayer::primitive2d::Primitive2DContainer aSeq( 1 );
7197 basegfx::BColor aLineColor = SwViewOption::GetCurrentViewOptions().GetDocBoundariesColor().getBColor();
7199 basegfx::B2DPolygon aPolygon;
7200 aPolygon.append( basegfx::B2DPoint( rRect.Left(), rRect.Top() ) );
7201 aPolygon.append( basegfx::B2DPoint( rRect.Right(), rRect.Top() ) );
7202 aPolygon.append( basegfx::B2DPoint( rRect.Right(), rRect.Bottom() ) );
7203 aPolygon.append( basegfx::B2DPoint( rRect.Left(), rRect.Bottom() ) );
7204 aPolygon.setClosed( true );
7206 aSeq[0] = new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(
7207 std::move(aPolygon), aLineColor );
7209 return aSeq;
7212 static drawinglayer::primitive2d::Primitive2DContainer lcl_CreateColumnAreaDelimiterPrimitives(
7213 const SwRect& rRect )
7215 drawinglayer::primitive2d::Primitive2DContainer aSeq( 4 );
7217 basegfx::BColor aLineColor = SwViewOption::GetCurrentViewOptions().GetDocBoundariesColor().getBColor();
7218 double nLineLength = 100.0; // in Twips
7220 Point aPoints[] = { rRect.TopLeft(), rRect.TopRight(), rRect.BottomRight(), rRect.BottomLeft() };
7221 double const aXOffDirs[] = { 1.0, -1.0, -1.0, 1.0 };
7222 double const aYOffDirs[] = { 1.0, 1.0, -1.0, -1.0 };
7224 // Actually loop over the corners to create the two lines
7225 for ( int i = 0; i < 4; i++ )
7227 basegfx::B2DVector aHorizVector( aXOffDirs[i], 0.0 );
7228 basegfx::B2DVector aVertVector( 0.0, aYOffDirs[i] );
7230 basegfx::B2DPoint aBPoint( aPoints[i].getX(), aPoints[i].getY() );
7232 basegfx::B2DPolygon aPolygon;
7233 aPolygon.append( aBPoint + aHorizVector * nLineLength );
7234 aPolygon.append( aBPoint );
7235 aPolygon.append( aBPoint + aVertVector * nLineLength );
7237 aSeq[i] = new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(
7238 std::move(aPolygon), aLineColor );
7241 return aSeq;
7244 void SwPageFrame::PaintSubsidiaryLines( const SwPageFrame *,
7245 const SwRect & ) const
7247 if (!gProp.pSGlobalShell->GetViewOptions()->IsDocBoundaries())
7248 return;
7250 if ( gProp.pSGlobalShell->IsHeaderFooterEdit() )
7251 return;
7253 const SwFrame* pLay = Lower();
7254 const SwFrame* pFootnoteCont = nullptr;
7255 const SwFrame* pPageBody = nullptr;
7256 while ( pLay && !( pFootnoteCont && pPageBody ) )
7258 if ( pLay->IsFootnoteContFrame( ) )
7259 pFootnoteCont = pLay;
7260 if ( pLay->IsBodyFrame() )
7261 pPageBody = pLay;
7262 pLay = pLay->GetNext();
7265 if (!pPageBody)
7266 return;
7268 SwRect aArea( pPageBody->getFrameArea() );
7269 if ( pFootnoteCont )
7270 aArea.AddBottom( pFootnoteCont->getFrameArea().Bottom() - aArea.Bottom() );
7272 if ( !gProp.pSGlobalShell->GetViewOptions()->IsViewMetaChars( ) )
7273 ProcessPrimitives( lcl_CreatePageAreaDelimiterPrimitives( aArea ) );
7274 else
7275 ProcessPrimitives( lcl_CreateRectangleDelimiterPrimitives( aArea ) );
7278 void SwColumnFrame::PaintSubsidiaryLines( const SwPageFrame *,
7279 const SwRect & ) const
7281 const SwFrame* pLay = Lower();
7282 const SwFrame* pFootnoteCont = nullptr;
7283 const SwFrame* pColBody = nullptr;
7284 while ( pLay && !( pFootnoteCont && pColBody ) )
7286 if ( pLay->IsFootnoteContFrame( ) )
7287 pFootnoteCont = pLay;
7288 if ( pLay->IsBodyFrame() )
7289 pColBody = pLay;
7290 pLay = pLay->GetNext();
7293 assert(pColBody && "presumably this is impossible");
7295 SwRect aArea( pColBody->getFrameArea() );
7297 // #i3662# - enlarge top of column body frame's printing area
7298 // in sections to top of section frame.
7299 const bool bColInSection = GetUpper()->IsSctFrame();
7300 if ( bColInSection )
7302 if ( IsVertical() )
7303 aArea.Right( GetUpper()->getFrameArea().Right() );
7304 else
7305 aArea.Top( GetUpper()->getFrameArea().Top() );
7308 if ( pFootnoteCont )
7309 aArea.AddBottom( pFootnoteCont->getFrameArea().Bottom() - aArea.Bottom() );
7311 ::SwAlignRect( aArea, gProp.pSGlobalShell, gProp.pSGlobalShell->GetOut() );
7313 if ( !gProp.pSGlobalShell->GetViewOptions()->IsViewMetaChars( ) )
7314 ProcessPrimitives( lcl_CreateColumnAreaDelimiterPrimitives( aArea ) );
7315 else
7316 ProcessPrimitives( lcl_CreateRectangleDelimiterPrimitives( aArea ) );
7319 void SwSectionFrame::PaintSubsidiaryLines( const SwPageFrame * pPage,
7320 const SwRect & rRect ) const
7322 if (!gProp.pSGlobalShell->GetViewOptions()->IsSectionBoundaries())
7323 return;
7325 const bool bNoLowerColumn = !Lower() || !Lower()->IsColumnFrame();
7326 if ( bNoLowerColumn )
7328 SwLayoutFrame::PaintSubsidiaryLines( pPage, rRect );
7333 * The SwBodyFrame doesn't print any subsidiary line: it's bounds are painted
7334 * either by the parent page or the parent column frame.
7336 void SwBodyFrame::PaintSubsidiaryLines( const SwPageFrame *,
7337 const SwRect & ) const
7341 void SwHeadFootFrame::PaintSubsidiaryLines( const SwPageFrame *, const SwRect & ) const
7343 if (!gProp.pSGlobalShell->GetViewOptions()->IsDocBoundaries())
7344 return;
7346 if ( gProp.pSGlobalShell->IsHeaderFooterEdit() )
7348 SwRect aArea( getFramePrintArea() );
7349 aArea.Pos() += getFrameArea().Pos();
7350 if ( !gProp.pSGlobalShell->GetViewOptions()->IsViewMetaChars( ) )
7351 ProcessPrimitives( lcl_CreatePageAreaDelimiterPrimitives( aArea ) );
7352 else
7353 ProcessPrimitives( lcl_CreateRectangleDelimiterPrimitives( aArea ) );
7358 * This method is overridden in order to have no subsidiary lines
7359 * around the footnotes.
7361 void SwFootnoteFrame::PaintSubsidiaryLines( const SwPageFrame *,
7362 const SwRect & ) const
7367 * This method is overridden in order to have no subsidiary lines
7368 * around the footnotes containers.
7370 void SwFootnoteContFrame::PaintSubsidiaryLines( const SwPageFrame *,
7371 const SwRect & ) const
7375 void SwLayoutFrame::PaintSubsidiaryLines( const SwPageFrame *pPage,
7376 const SwRect &rRect ) const
7378 bool bNewTableModel = false;
7380 // #i29550#
7381 if ( IsTabFrame() || IsCellFrame() || IsRowFrame() )
7383 const SwTabFrame* pTabFrame = FindTabFrame();
7384 if ( pTabFrame->IsCollapsingBorders() )
7385 return;
7387 bNewTableModel = pTabFrame->GetTable()->IsNewModel();
7388 // in the new table model, we have an early return for all cell-related
7389 // frames, except from non-covered table cells
7390 if ( bNewTableModel )
7391 if ( IsTabFrame() ||
7392 IsRowFrame() ||
7393 ( IsCellFrame() && IsCoveredCell() ) )
7394 return;
7397 const bool bFlys = pPage->GetSortedObjs() != nullptr;
7399 const bool bCell = IsCellFrame();
7400 // #i3662# - use frame area for cells for section use also frame area
7401 const bool bUseFrameArea = bCell || IsSctFrame();
7402 SwRect aOriginal( bUseFrameArea ? getFrameArea() : getFramePrintArea() );
7403 if ( !bUseFrameArea )
7404 aOriginal.Pos() += getFrameArea().Pos();
7406 ::SwAlignRect( aOriginal, gProp.pSGlobalShell, gProp.pSGlobalShell->GetOut() );
7408 if ( !aOriginal.Overlaps( rRect ) )
7409 return;
7411 SwRect aOut( aOriginal );
7412 aOut.Intersection_( rRect );
7414 const SwTwips nRight = aOut.Right();
7415 const SwTwips nBottom= aOut.Bottom();
7417 const Point aRT( nRight, aOut.Top() );
7418 const Point aRB( nRight, nBottom );
7419 const Point aLB( aOut.Left(), nBottom );
7421 SubColFlags nSubColor = ( bCell || IsRowFrame() )
7422 ? SubColFlags::Tab
7423 : ( IsInSct()
7424 ? SubColFlags::Sect
7425 : ( IsInFly() ? SubColFlags::Fly : SubColFlags::Page ) );
7427 // collect body, header, footer, footnote and section
7428 // sub-lines in <pSpecSubsLine> array.
7429 const bool bSpecialSublines = IsBodyFrame() || IsHeaderFrame() || IsFooterFrame() ||
7430 IsFootnoteFrame() || IsSctFrame();
7431 SwLineRects *const pUsedSubsLines = bSpecialSublines
7432 ? gProp.pSSpecSubsLines.get() : gProp.pSSubsLines.get();
7434 // NOTE: for cell frames only left and right (horizontal layout) respectively
7435 // top and bottom (vertical layout) lines painted.
7436 // NOTE2: this does not hold for the new table model!!! We paint the top border
7437 // of each non-covered table cell.
7438 const bool bVert = IsVertical();
7439 if ( bFlys )
7441 // add control for drawing left and right lines
7442 if ( !bCell || bNewTableModel || !bVert )
7444 if ( aOriginal.Left() == aOut.Left() )
7445 ::lcl_RefreshLine( this, pPage, aOut.Pos(), aLB, nSubColor, pUsedSubsLines );
7446 // in vertical layout set page/column break at right
7447 if ( aOriginal.Right() == nRight )
7448 ::lcl_RefreshLine( this, pPage, aRT, aRB, nSubColor, pUsedSubsLines );
7450 // adjust control for drawing top and bottom lines
7451 if ( !bCell || bNewTableModel || bVert )
7453 if ( aOriginal.Top() == aOut.Top() )
7454 // in horizontal layout set page/column break at top
7455 ::lcl_RefreshLine( this, pPage, aOut.Pos(), aRT, nSubColor, pUsedSubsLines );
7456 if ( aOriginal.Bottom() == nBottom )
7457 ::lcl_RefreshLine( this, pPage, aLB, aRB, nSubColor,
7458 pUsedSubsLines );
7461 else
7463 // add control for drawing left and right lines
7464 if ( !bCell || bNewTableModel || !bVert )
7466 if ( aOriginal.Left() == aOut.Left() )
7468 const SwRect aRect( aOut.Pos(), aLB );
7469 pUsedSubsLines->AddLineRect( aRect, nullptr,
7470 SvxBorderLineStyle::SOLID, nullptr, nSubColor, gProp );
7472 // in vertical layout set page/column break at right
7473 if ( aOriginal.Right() == nRight )
7475 const SwRect aRect( aRT, aRB );
7476 pUsedSubsLines->AddLineRect( aRect, nullptr,
7477 SvxBorderLineStyle::SOLID, nullptr, nSubColor, gProp );
7480 // adjust control for drawing top and bottom lines
7481 if ( !bCell || bNewTableModel || bVert )
7483 if ( aOriginal.Top() == aOut.Top() )
7485 // in horizontal layout set page/column break at top
7486 const SwRect aRect( aOut.Pos(), aRT );
7487 pUsedSubsLines->AddLineRect( aRect, nullptr,
7488 SvxBorderLineStyle::SOLID, nullptr, nSubColor, gProp );
7490 if ( aOriginal.Bottom() == nBottom )
7492 const SwRect aRect( aLB, aRB );
7493 pUsedSubsLines->AddLineRect( aRect, nullptr,
7494 SvxBorderLineStyle::SOLID, nullptr, nSubColor, gProp );
7501 * Refreshes all extra data (line breaks a.s.o) of the page. Basically only those objects
7502 * are considered which horizontally overlap the Rect.
7504 void SwPageFrame::RefreshExtraData( const SwRect &rRect ) const
7506 const SwLineNumberInfo &rInfo = GetFormat()->GetDoc()->GetLineNumberInfo();
7507 bool bLineInFly = (rInfo.IsPaintLineNumbers() && rInfo.IsCountInFlys())
7508 || static_cast<sal_Int16>(SW_MOD()->GetRedlineMarkPos()) != text::HoriOrientation::NONE;
7510 SwRect aRect( rRect );
7511 ::SwAlignRect( aRect, gProp.pSGlobalShell, gProp.pSGlobalShell->GetOut() );
7512 if ( !aRect.HasArea() )
7513 return;
7515 SwLayoutFrame::RefreshExtraData( aRect );
7517 if ( bLineInFly && GetSortedObjs() )
7518 for (SwAnchoredObject* pAnchoredObj : *GetSortedObjs())
7520 if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
7522 if ( pFly->getFrameArea().Top() <= aRect.Bottom() &&
7523 pFly->getFrameArea().Bottom() >= aRect.Top() )
7524 pFly->RefreshExtraData( aRect );
7529 void SwLayoutFrame::RefreshExtraData( const SwRect &rRect ) const
7532 const SwLineNumberInfo &rInfo = GetFormat()->GetDoc()->GetLineNumberInfo();
7533 bool bLineInBody = rInfo.IsPaintLineNumbers(),
7534 bLineInFly = bLineInBody && rInfo.IsCountInFlys(),
7535 bRedLine = static_cast<sal_Int16>(SW_MOD()->GetRedlineMarkPos())!=text::HoriOrientation::NONE;
7537 const SwContentFrame *pCnt = ContainsContent();
7538 while ( pCnt && IsAnLower( pCnt ) )
7540 if ( pCnt->IsTextFrame() && ( bRedLine ||
7541 ( !pCnt->IsInTab() &&
7542 ((bLineInBody && pCnt->IsInDocBody()) ||
7543 (bLineInFly && pCnt->IsInFly())) ) ) &&
7544 pCnt->getFrameArea().Top() <= rRect.Bottom() &&
7545 pCnt->getFrameArea().Bottom() >= rRect.Top() )
7547 static_cast<const SwTextFrame*>(pCnt)->PaintExtraData( rRect );
7549 if ( bLineInFly && pCnt->GetDrawObjs() )
7550 for (SwAnchoredObject* pAnchoredObj : *pCnt->GetDrawObjs())
7552 if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
7554 if ( pFly->IsFlyInContentFrame() &&
7555 pFly->getFrameArea().Top() <= rRect.Bottom() &&
7556 pFly->getFrameArea().Bottom() >= rRect.Top() )
7557 pFly->RefreshExtraData( rRect );
7560 pCnt = pCnt->GetNextContentFrame();
7565 * For #102450#
7566 * Determine the color, that is respectively will be drawn as background
7567 * for the page frame.
7568 * Using existing method SwFrame::GetBackgroundBrush to determine the color
7569 * that is set at the page frame respectively is parent. If none is found
7570 * return the global retouche color
7572 * @return Color
7574 Color SwPageFrame::GetDrawBackgroundColor() const
7576 const SvxBrushItem* pBrushItem;
7577 std::optional<Color> xDummyColor;
7578 SwRect aDummyRect;
7579 drawinglayer::attribute::SdrAllFillAttributesHelperPtr aFillAttributes;
7581 if ( GetBackgroundBrush( aFillAttributes, pBrushItem, xDummyColor, aDummyRect, true, /*bConsiderTextBox=*/false) )
7583 if(aFillAttributes && aFillAttributes->isUsed())
7585 // let SdrAllFillAttributesHelper do the average color calculation
7586 return Color(aFillAttributes->getAverageColor(aGlobalRetoucheColor.getBColor()));
7588 else if(pBrushItem)
7590 OUString referer;
7591 SwViewShell * sh1 = getRootFrame()->GetCurrShell();
7592 if (sh1 != nullptr) {
7593 SfxObjectShell * sh2 = sh1->GetDoc()->GetPersist();
7594 if (sh2 != nullptr && sh2->HasName()) {
7595 referer = sh2->GetMedium()->GetName();
7598 const Graphic* pGraphic = pBrushItem->GetGraphic(referer);
7600 if(pGraphic)
7602 // #29105# when a graphic is set, it may be possible to calculate a single
7603 // color which looks good in all places of the graphic. Since it is
7604 // planned to have text edit on the overlay one day and the fallback
7605 // to aGlobalRetoucheColor returns something useful, just use that
7606 // for now.
7608 else
7610 // not a graphic, use (hopefully) initialized color
7611 return pBrushItem->GetColor();
7616 return aGlobalRetoucheColor;
7619 /// create/return font used to paint the "empty page" string
7620 const vcl::Font& SwPageFrame::GetEmptyPageFont()
7622 static vcl::Font aEmptyPgFont = []()
7624 vcl::Font tmp;
7625 tmp.SetFontSize( Size( 0, 80 * 20 )); // == 80 pt
7626 tmp.SetWeight( WEIGHT_BOLD );
7627 tmp.SetStyleName(OUString());
7628 tmp.SetFamilyName("Helvetica");
7629 tmp.SetFamily( FAMILY_SWISS );
7630 tmp.SetTransparent( true );
7631 tmp.SetColor( COL_GRAY );
7632 return tmp;
7633 }();
7635 return aEmptyPgFont;
7639 * Retouch for a section
7641 * Retouch will only be done, if the Frame is the last one in his chain.
7642 * The whole area of the upper which is located below the Frame will be
7643 * cleared using PaintSwFrameBackground.
7645 void SwFrame::Retouch( const SwPageFrame * pPage, const SwRect &rRect ) const
7647 if ( gProp.bSFlyMetafile )
7648 return;
7650 OSL_ENSURE( GetUpper(), "Retouche try without Upper." );
7651 OSL_ENSURE( getRootFrame()->GetCurrShell() && gProp.pSGlobalShell->GetWin(), "Retouche on a printer?" );
7653 SwRect aRetouche( GetUpper()->GetPaintArea() );
7654 aRetouche.Top( getFrameArea().Top() + getFrameArea().Height() );
7655 aRetouche.Intersection( gProp.pSGlobalShell->VisArea() );
7657 if ( aRetouche.HasArea() )
7659 //Omit the passed Rect. To do this, we unfortunately need a region to
7660 //cut out.
7661 SwRegionRects aRegion( aRetouche );
7662 aRegion -= rRect;
7663 SwViewShell *pSh = getRootFrame()->GetCurrShell();
7665 // #i16816# tagged pdf support
7666 SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, nullptr, *pSh->GetOut() );
7668 for ( size_t i = 0; i < aRegion.size(); ++i )
7670 const SwRect &rRetouche = aRegion[i];
7672 GetUpper()->PaintBaBo( rRetouche, pPage );
7674 //Hell and Heaven need to be refreshed too.
7675 //To avoid recursion my retouch flag needs to be reset first!
7676 ResetRetouche();
7677 if ( rRetouche.HasArea() )
7679 const Color aPageBackgrdColor(pPage->GetDrawBackgroundColor());
7680 const IDocumentDrawModelAccess& rIDDMA = pSh->getIDocumentDrawModelAccess();
7681 // --> OD #i76669#
7682 SwViewObjectContactRedirector aSwRedirector( *pSh );
7683 // <--
7685 pSh->Imp()->PaintLayer( rIDDMA.GetHellId(), nullptr,
7686 *pPage, rRetouche, &aPageBackgrdColor,
7687 pPage->IsRightToLeft(),
7688 &aSwRedirector );
7689 pSh->Imp()->PaintLayer( rIDDMA.GetHeavenId(), nullptr,
7690 *pPage, rRetouche, &aPageBackgrdColor,
7691 pPage->IsRightToLeft(),
7692 &aSwRedirector );
7695 SetRetouche();
7697 //Because we leave all paint areas, we need to refresh the
7698 //subsidiary lines.
7699 pPage->RefreshSubsidiary( rRetouche );
7702 if ( SwViewShell::IsLstEndAction() )
7703 ResetRetouche();
7707 * Determine the background brush for the frame:
7708 * the background brush is taken from it-self or from its parent (anchor/upper).
7709 * Normally, the background brush is taken, which has no transparent color or
7710 * which has a background graphic. But there are some special cases:
7711 * (1) No background brush is taken from a page frame, if view option "IsPageBack"
7712 * isn't set.
7713 * (2) Background brush from an index section is taken under special conditions.
7714 * In this case parameter <rpCol> is set to the index shading color.
7715 * (3) New (OD 20.08.2002) - Background brush is taken, if on background drawing
7716 * of the frame transparency is considered and its color is not "no fill"/"auto fill"
7718 * Old description in German:
7719 * Returns the Backgroundbrush for the area of the Frame.
7720 * The Brush is defined by the Frame or by an upper, the first Brush is
7721 * used. If no Brush is defined for a Frame, false is returned.
7723 * @param rpBrush
7724 * output parameter - constant reference pointer the found background brush
7726 * @param rpFillStyle
7727 * output parameter - constant reference pointer the found background fill style
7729 * @param rpFillGradient
7730 * output parameter - constant reference pointer the found background fill gradient
7732 * @param rpCol
7733 * output parameter - constant reference pointer to the color of the index shading
7734 * set under special conditions, if background brush is taken from an index section.
7736 * @param rOrigRect
7737 * in-/output parameter - reference to the rectangle the background brush is
7738 * considered for - adjusted to the frame, from which the background brush is
7739 * taken.
7741 * @param bLowerMode
7742 * input parameter - boolean indicating, if background brush should *not* be
7743 * taken from parent.
7745 * @param bConsiderTextBox
7746 * consider the TextBox of this fly frame (if there is any) when determining
7747 * the background color, useful for automatic font color.
7749 * @return true, if a background brush for the frame is found
7751 bool SwFrame::GetBackgroundBrush(
7752 drawinglayer::attribute::SdrAllFillAttributesHelperPtr& rFillAttributes,
7753 const SvxBrushItem* & rpBrush,
7754 std::optional<Color>& rxCol,
7755 SwRect &rOrigRect,
7756 bool bLowerMode,
7757 bool bConsiderTextBox ) const
7759 const SwFrame *pFrame = this;
7760 SwViewShell *pSh = getRootFrame()->GetCurrShell();
7761 const SwViewOption *pOpt = pSh->GetViewOptions();
7762 rpBrush = nullptr;
7763 rxCol.reset();
7766 if ( pFrame->IsPageFrame() && !pOpt->IsPageBack() )
7767 return false;
7769 if (pFrame->supportsFullDrawingLayerFillAttributeSet())
7771 bool bHandledTextBox = false;
7772 if (pFrame->IsFlyFrame() && bConsiderTextBox)
7774 const SwFlyFrame* pFlyFrame = static_cast<const SwFlyFrame*>(pFrame);
7775 SwFrameFormat* pShape
7776 = SwTextBoxHelper::getOtherTextBoxFormat(pFlyFrame->GetFormat(), RES_FLYFRMFMT);
7777 if (pShape)
7779 SdrObject* pObject = pShape->FindRealSdrObject();
7780 if (pObject)
7782 // Work with the fill attributes of the shape of the fly frame.
7783 rFillAttributes =
7784 std::make_shared<drawinglayer::attribute::SdrAllFillAttributesHelper>(
7785 pObject->GetMergedItemSet());
7786 bHandledTextBox = true;
7791 if (!bHandledTextBox)
7792 rFillAttributes = pFrame->getSdrAllFillAttributesHelper();
7794 const SvxBrushItem &rBack = pFrame->GetAttrSet()->GetBackground();
7796 if( pFrame->IsSctFrame() )
7798 const SwSection* pSection = static_cast<const SwSectionFrame*>(pFrame)->GetSection();
7799 // Note: If frame <pFrame> is a section of the index and
7800 // it its background color is "no fill"/"auto fill" and
7801 // it has no background graphic and
7802 // we are not in the page preview and
7803 // we are not in read-only mode and
7804 // option "index shadings" is set and
7805 // the output is not the printer
7806 // then set <rpCol> to the color of the index shading
7807 if( pSection && ( SectionType::ToxHeader == pSection->GetType() ||
7808 SectionType::ToxContent == pSection->GetType() ) &&
7809 (rBack.GetColor() == COL_TRANSPARENT) &&
7810 rBack.GetGraphicPos() == GPOS_NONE &&
7811 !pOpt->IsPagePreview() &&
7812 !pOpt->IsReadonly() &&
7813 // #114856# Form view
7814 !pOpt->IsFormView() &&
7815 pOpt->IsIndexShadings() &&
7816 !pOpt->IsPDFExport() &&
7817 pSh->GetOut()->GetOutDevType() != OUTDEV_PRINTER )
7819 rxCol = pOpt->GetIndexShadingsColor();
7823 // determine, if background draw of frame <pFrame> considers transparency
7824 // Status Quo: background transparency have to be
7825 // considered for fly frames
7826 const bool bConsiderBackgroundTransparency = pFrame->IsFlyFrame();
7828 // #i125189# Do not base the decision for using the parent's fill style for this
7829 // frame when the new DrawingLayer FillAttributes are used on the SdrAllFillAttributesHelper
7830 // information. There the data is already optimized to no fill in the case that the
7831 // transparence is at 100% while no fill is the criteria for derivation
7832 bool bNewDrawingLayerFillStyleIsUsedAndNotNoFill(false);
7834 if(rFillAttributes)
7836 // the new DrawingLayer FillStyle is used
7837 if(rFillAttributes->isUsed())
7839 // it's not drawing::FillStyle_NONE
7840 bNewDrawingLayerFillStyleIsUsedAndNotNoFill = true;
7842 else
7844 // maybe optimized already when 100% transparency is used somewhere, need to test
7845 // XFillStyleItem directly from the model data
7846 const drawing::FillStyle eFillStyle(pFrame->GetAttrSet()->Get(XATTR_FILLSTYLE).GetValue());
7848 if(drawing::FillStyle_NONE != eFillStyle)
7850 bNewDrawingLayerFillStyleIsUsedAndNotNoFill = true;
7855 // add condition:
7856 // If <bConsiderBackgroundTransparency> is set - see above -,
7857 // return brush of frame <pFrame>, if its color is *not* "no fill"/"auto fill"
7858 if (
7859 // #i125189# Done when the new DrawingLayer FillAttributes are used and
7860 // not drawing::FillStyle_NONE (see above)
7861 bNewDrawingLayerFillStyleIsUsedAndNotNoFill ||
7863 // done when SvxBrushItem is used
7864 rBack.GetColor().GetAlpha() == 255 || rBack.GetGraphicPos() != GPOS_NONE ||
7866 // done when direct color is forced
7867 rxCol ||
7869 // done when consider BG transparency and color is not completely transparent
7870 (bConsiderBackgroundTransparency && (rBack.GetColor() != COL_TRANSPARENT))
7873 rpBrush = &rBack;
7874 if ( pFrame->IsPageFrame() && pSh->GetViewOptions()->getBrowseMode() )
7876 rOrigRect = pFrame->getFrameArea();
7877 ::SwAlignRect(rOrigRect, pSh, pSh->GetOut());
7879 else
7881 if (pFrame->IsPageFrame()
7882 && pFrame->GetAttrSet()->GetItem<SfxBoolItem>(RES_BACKGROUND_FULL_SIZE)->GetValue())
7884 rOrigRect = pFrame->getFrameArea();
7886 else if (pFrame->getFrameArea().SSize() != pFrame->getFramePrintArea().SSize())
7888 SwBorderAttrAccess aAccess( SwFrame::GetCache(), pFrame );
7889 const SwBorderAttrs &rAttrs = *aAccess.Get();
7890 ::lcl_CalcBorderRect( rOrigRect, pFrame, rAttrs, false, gProp );
7892 else
7894 rOrigRect = pFrame->getFramePrintArea();
7895 rOrigRect += pFrame->getFrameArea().Pos();
7899 return true;
7902 if ( bLowerMode )
7904 // Do not try to get background brush from parent (anchor/upper)
7905 return false;
7908 // get parent frame - anchor or upper - for next loop
7909 if ( pFrame->IsFlyFrame() )
7911 pFrame = static_cast<const SwFlyFrame*>(pFrame)->GetAnchorFrame();
7913 else
7915 pFrame = pFrame->GetUpper();
7917 } while ( pFrame );
7919 return false;
7922 void SetOutDevAndWin( SwViewShell *pSh, OutputDevice *pO,
7923 vcl::Window *pW, sal_uInt16 nZoom )
7925 pSh->mpOut = pO;
7926 pSh->mpWin = pW;
7927 pSh->mpOpt->SetZoom( nZoom );
7930 Graphic SwFrameFormat::MakeGraphic( ImageMap*, const sal_uInt32 /*nMaximumQuadraticPixels*/, const std::optional<Size>& /*rTargetDPI*/ )
7932 return Graphic();
7935 Graphic SwFlyFrameFormat::MakeGraphic( ImageMap* pMap, const sal_uInt32 /*nMaximumQuadraticPixels*/, const std::optional<Size>& /*rTargetDPI*/ )
7937 Graphic aRet;
7938 //search any Fly!
7939 SwIterator<SwFrame,SwFormat> aIter( *this );
7940 SwFrame *pFirst = aIter.First();
7941 SwViewShell *const pSh =
7942 pFirst ? pFirst->getRootFrame()->GetCurrShell() : nullptr;
7943 if (nullptr != pSh)
7945 SwViewShell *pOldGlobal = gProp.pSGlobalShell;
7946 gProp.pSGlobalShell = pSh;
7948 bool bNoteURL = pMap &&
7949 SfxItemState::SET != GetAttrSet().GetItemState( RES_URL );
7950 if( bNoteURL )
7952 OSL_ENSURE( !pNoteURL, "MakeGraphic: pNoteURL already used? " );
7953 pNoteURL = new SwNoteURL;
7955 SwFlyFrame *pFly = static_cast<SwFlyFrame*>(pFirst);
7957 OutputDevice *pOld = pSh->GetOut();
7958 ScopedVclPtrInstance< VirtualDevice > pDev( *pOld );
7959 pDev->EnableOutput( false );
7961 GDIMetaFile aMet;
7962 MapMode aMap( pOld->GetMapMode().GetMapUnit() );
7963 pDev->SetMapMode( aMap );
7964 aMet.SetPrefMapMode( aMap );
7966 ::SwCalcPixStatics( pSh->GetOut() );
7967 aMet.SetPrefSize( pFly->getFrameArea().SSize() );
7969 aMet.Record( pDev.get() );
7970 pDev->SetLineColor();
7971 pDev->SetFillColor();
7972 pDev->SetFont( pOld->GetFont() );
7974 //Enlarge the rectangle if needed, so the border is painted too.
7975 SwRect aOut( pFly->getFrameArea() );
7976 SwBorderAttrAccess aAccess( SwFrame::GetCache(), pFly );
7977 const SwBorderAttrs &rAttrs = *aAccess.Get();
7978 if ( rAttrs.CalcRightLine() )
7979 aOut.AddWidth(2*gProp.nSPixelSzW );
7980 if ( rAttrs.CalcBottomLine() )
7981 aOut.AddHeight(2*gProp.nSPixelSzH );
7983 // #i92711# start Pre/PostPaint encapsulation before pOut is changed to the buffering VDev
7984 const vcl::Region aRepaintRegion(aOut.SVRect());
7985 pSh->DLPrePaint2(aRepaintRegion);
7987 vcl::Window *pWin = pSh->GetWin();
7988 sal_uInt16 nZoom = pSh->GetViewOptions()->GetZoom();
7989 ::SetOutDevAndWin( pSh, pDev, nullptr, 100 );
7990 gProp.bSFlyMetafile = true;
7991 gProp.pSFlyMetafileOut = pWin->GetOutDev();
7993 SwViewShellImp *pImp = pSh->Imp();
7994 gProp.pSFlyOnlyDraw = pFly;
7995 gProp.pSLines.reset(new SwLineRects);
7997 // determine page, fly frame is on
7998 const SwPageFrame* pFlyPage = pFly->FindPageFrame();
7999 const Color aPageBackgrdColor(pFlyPage->GetDrawBackgroundColor());
8000 const IDocumentDrawModelAccess& rIDDMA = pSh->getIDocumentDrawModelAccess();
8001 // --> OD #i76669#
8002 SwViewObjectContactRedirector aSwRedirector( *pSh );
8003 // <--
8004 pImp->PaintLayer( rIDDMA.GetHellId(), nullptr,
8005 *pFlyPage, aOut, &aPageBackgrdColor,
8006 pFlyPage->IsRightToLeft(),
8007 &aSwRedirector );
8008 gProp.pSLines->PaintLines( pDev, gProp );
8009 if ( pFly->IsFlyInContentFrame() )
8010 pFly->PaintSwFrame( *pDev, aOut );
8011 gProp.pSLines->PaintLines( pDev, gProp );
8012 pImp->PaintLayer( rIDDMA.GetHeavenId(), nullptr,
8013 *pFlyPage, aOut, &aPageBackgrdColor,
8014 pFlyPage->IsRightToLeft(),
8015 &aSwRedirector );
8016 gProp.pSLines->PaintLines( pDev, gProp );
8017 gProp.pSLines.reset();
8018 gProp.pSFlyOnlyDraw = nullptr;
8020 gProp.pSFlyMetafileOut = nullptr;
8021 gProp.bSFlyMetafile = false;
8022 ::SetOutDevAndWin( pSh, pOld, pWin, nZoom );
8024 // #i92711# end Pre/PostPaint encapsulation when pOut is back and content is painted
8025 pSh->DLPostPaint2(true);
8027 aMet.Stop();
8028 aMet.Move( -pFly->getFrameArea().Left(), -pFly->getFrameArea().Top() );
8029 aRet = Graphic( aMet );
8031 if( bNoteURL )
8033 OSL_ENSURE( pNoteURL, "MakeGraphic: Good Bye, NoteURL." );
8034 delete pNoteURL;
8035 pNoteURL = nullptr;
8037 gProp.pSGlobalShell = pOldGlobal;
8039 return aRet;
8042 Graphic SwDrawFrameFormat::MakeGraphic( ImageMap*, const sal_uInt32 nMaximumQuadraticPixels, const std::optional<Size>& rTargetDPI )
8044 Graphic aRet;
8045 SwDrawModel* pMod = getIDocumentDrawModelAccess().GetDrawModel();
8046 if ( pMod )
8048 SdrObject *pObj = FindSdrObject();
8049 SdrView aView( *pMod );
8050 SdrPageView *pPgView = aView.ShowSdrPage(aView.GetModel().GetPage(0));
8051 aView.MarkObj( pObj, pPgView );
8052 aRet = aView.GetMarkedObjBitmapEx(/*bNoVDevIfOneBmpMarked=*/false, nMaximumQuadraticPixels, rTargetDPI);
8053 aView.HideSdrPage();
8055 return aRet;
8058 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */