LibreOffice Module sw (master)  1
paintfrm.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <vcl/lazydelete.hxx>
21 #include <sfx2/docfile.hxx>
22 #include <sfx2/printer.hxx>
23 #include <sfx2/progress.hxx>
24 #include <editeng/brushitem.hxx>
25 #include <editeng/prntitem.hxx>
26 #include <editeng/boxitem.hxx>
27 #include <editeng/shaditem.hxx>
28 #include <svx/framelink.hxx>
29 #include <drawdoc.hxx>
30 #include <tgrditem.hxx>
31 #include <calbck.hxx>
32 #include <fmtsrnd.hxx>
33 #include <fmtclds.hxx>
34 #include <strings.hrc>
35 #include <swmodule.hxx>
36 #include <rootfrm.hxx>
37 #include <pagefrm.hxx>
38 #include <section.hxx>
39 #include <sectfrm.hxx>
40 #include <viewimp.hxx>
41 #include <dflyobj.hxx>
42 #include <flyfrm.hxx>
43 #include <frmatr.hxx>
44 #include <frmtool.hxx>
45 #include <viewopt.hxx>
46 #include <dview.hxx>
47 #include <dcontact.hxx>
48 #include <txtfrm.hxx>
49 #include <ftnfrm.hxx>
50 #include <tabfrm.hxx>
51 #include <rowfrm.hxx>
52 #include <cellfrm.hxx>
53 #include <notxtfrm.hxx>
54 #include <layact.hxx>
55 #include <pagedesc.hxx>
56 #include <ptqueue.hxx>
57 #include <noteurl.hxx>
58 #include "virtoutp.hxx"
59 #include <lineinfo.hxx>
60 #include <dbg_lay.hxx>
61 #include <docsh.hxx>
62 #include <svx/svdogrp.hxx>
63 #include <sortedobjs.hxx>
65 #include <bodyfrm.hxx>
66 #include <hffrm.hxx>
67 #include <colfrm.hxx>
68 #include <sw_primitivetypes2d.hxx>
69 #include <swfont.hxx>
70 
78 
79 #include <ndole.hxx>
80 #include <PostItMgr.hxx>
81 #include <FrameControlsManager.hxx>
82 #include <vcl/settings.hxx>
83 
86 
87 #include <svtools/borderhelper.hxx>
88 
89 #include <bitmaps.hlst>
98 #include <svx/unoapi.hxx>
99 #include <svx/svdpagv.hxx>
100 #include <svx/xfillit0.hxx>
104 #include <sal/log.hxx>
105 
106 #include <memory>
107 #include <vector>
108 #include <algorithm>
109 #include <wrtsh.hxx>
110 #include <edtwin.hxx>
111 #include <view.hxx>
112 #include <paintfrm.hxx>
113 #include <textboxhelper.hxx>
114 #include <o3tl/typed_flags_set.hxx>
115 
116 #include <vcl/BitmapTools.hxx>
117 #include <comphelper/lok.hxx>
119 
120 #define COL_NOTES_SIDEPANE Color(230,230,230)
121 #define COL_NOTES_SIDEPANE_BORDER Color(200,200,200)
122 #define COL_NOTES_SIDEPANE_SCROLLAREA Color(230,230,220)
123 
124 using namespace ::editeng;
125 using namespace ::com::sun::star;
126 
127 namespace {
128 
129 struct SwPaintProperties;
130 
131 //Class declaration; here because they are only used in this file
132 enum class SubColFlags {
133  Page = 0x01, //Helplines of the page
134  Tab = 0x08, //Helplines inside tables
135  Fly = 0x10, //Helplines inside fly frames
136  Sect = 0x20, //Helplines inside sections
137 };
138 
139 }
140 
141 namespace o3tl {
142  template<> struct typed_flags<SubColFlags> : is_typed_flags<SubColFlags, 0x39> {};
143 }
144 
145 namespace {
146 
147 // Classes collecting the border lines and help lines
148 class SwLineRect : public SwRect
149 {
150  Color m_aColor;
151  SvxBorderLineStyle m_nStyle;
152  const SwTabFrame* m_pTabFrame;
153  SubColFlags m_nSubColor; //colorize subsidiary lines
154  bool m_bPainted; //already painted?
155  sal_uInt8 m_nLock; //To distinguish the line and the hell layer.
156 public:
157  SwLineRect( const SwRect &rRect, const Color *pCol, const SvxBorderLineStyle nStyle,
158  const SwTabFrame *pT , const SubColFlags nSCol );
159 
160  const Color& GetColor() const { return m_aColor; }
161  SvxBorderLineStyle GetStyle() const { return m_nStyle; }
162  const SwTabFrame* GetTab() const { return m_pTabFrame; }
163  void SetPainted() { m_bPainted = true; }
164  void Lock(bool bLock)
165  {
166  if (bLock)
167  ++m_nLock;
168  else if (m_nLock)
169  --m_nLock;
170  }
171  bool IsPainted() const { return m_bPainted; }
172  bool IsLocked() const { return m_nLock != 0; }
173  SubColFlags GetSubColor() const { return m_nSubColor; }
174 
175  bool MakeUnion(const SwRect& rRect, SwPaintProperties const& properties);
176 };
177 
178 }
179 
180 #ifdef IOS
181 static void dummy_function()
182 {
183  pid_t pid = getpid();
184  (void) pid;
185 }
186 #endif
187 
188 namespace {
189 
190 class SwLineRects
191 {
192 public:
193  std::vector<SwLineRect> m_aLineRects;
194  typedef std::vector< SwLineRect >::const_iterator const_iterator;
195  typedef std::vector< SwLineRect >::iterator iterator;
196  typedef std::vector< SwLineRect >::reverse_iterator reverse_iterator;
197  typedef std::vector< SwLineRect >::size_type size_type;
198  size_t m_nLastCount; //avoid unnecessary cycles in PaintLines
199  SwLineRects()
200  : m_nLastCount(0)
201  {
202 #ifdef IOS
203  // Work around what is either a compiler bug in Xcode 5.1.1,
204  // or some unknown problem in this file. If I ifdef out this
205  // call, I get a crash in SwSubsRects::PaintSubsidiary: the
206  // address of the rLi reference variable is claimed to be
207  // 0x4000000!
208  dummy_function();
209 #endif
210  }
211  void AddLineRect( const SwRect& rRect, const Color *pColor, const SvxBorderLineStyle nStyle,
212  const SwTabFrame *pTab, const SubColFlags nSCol, SwPaintProperties const &properties );
213  void ConnectEdges( OutputDevice const *pOut, SwPaintProperties const &properties );
214  void PaintLines ( OutputDevice *pOut, SwPaintProperties const &properties );
215  void LockLines( bool bLock );
216 
217  //Limit lines to 100
218  bool isFull() const { return m_aLineRects.size() > 100; }
219 };
220 
221 class SwSubsRects : public SwLineRects
222 {
223  void RemoveSuperfluousSubsidiaryLines( const SwLineRects &rRects, SwPaintProperties const &properties );
224 public:
225  void PaintSubsidiary( OutputDevice *pOut, const SwLineRects *pRects, SwPaintProperties const &properties );
226 };
227 
228 class BorderLines
229 {
231 public:
232  void AddBorderLines(const drawinglayer::primitive2d::Primitive2DContainer& rContainer);
234  {
236  lines.swap(m_Lines);
237  return lines;
238  }
239 };
240 
241 }
242 
243 // Default zoom factor
244 const double aEdgeScale = 0.5;
245 
246 //To optimize the expensive RetouchColor determination
248 
249 namespace sw
250 {
252 {
253  return &aGlobalRetoucheColor;
254 }
255 }
256 
257 namespace {
258 
262 struct SwPaintProperties {
263  // Only repaint the Fly content as well as the background of the Fly content if
264  // a metafile is taken of the Fly.
265  bool bSFlyMetafile;
266  VclPtr<OutputDevice> pSFlyMetafileOut;
267  SwViewShell *pSGlobalShell;
268 
269  // Retouch for transparent Flys is done by the background of the Flys.
270  // The Fly itself should certainly not be spared out. See PaintSwFrameBackground and
271  // lcl_SubtractFlys()
272  SwFlyFrame *pSRetoucheFly;
273  SwFlyFrame *pSRetoucheFly2;
274  SwFlyFrame *pSFlyOnlyDraw;
275 
276  // The borders will be collected in pSLines during the Paint and later
277  // possibly merge them.
278  // The help lines will be collected and merged in gProp.pSSubsLines. These will
279  // be compared with pSLines before the work in order to avoid help lines
280  // to hide borders.
281  std::unique_ptr<BorderLines> pBLines;
282  std::unique_ptr<SwLineRects> pSLines;
283  std::unique_ptr<SwSubsRects> pSSubsLines;
284 
285  // global variable for sub-lines of body, header, footer, section and footnote frames.
286  std::unique_ptr<SwSubsRects> pSSpecSubsLines;
287  SfxProgress *pSProgress;
288 
289  // Sizes of a pixel and the corresponding halves. Will be reset when
290  // entering SwRootFrame::PaintSwFrame
291  tools::Long nSPixelSzW;
292  tools::Long nSPixelSzH;
293  tools::Long nSHalfPixelSzW;
294  tools::Long nSHalfPixelSzH;
295  tools::Long nSMinDistPixelW;
296  tools::Long nSMinDistPixelH;
297 
298  Color aSGlobalRetoucheColor;
299 
300  // Current zoom factor
301  double aSScaleX;
302  double aSScaleY;
303 
304  SwPaintProperties()
305  : bSFlyMetafile(false)
306  , pSFlyMetafileOut(nullptr)
307  , pSGlobalShell(nullptr)
308  , pSRetoucheFly(nullptr)
309  , pSRetoucheFly2(nullptr)
310  , pSFlyOnlyDraw(nullptr)
311  , pSProgress(nullptr)
312  , nSPixelSzW(0)
313  , nSPixelSzH(0)
314  , nSHalfPixelSzW(0)
315  , nSHalfPixelSzH(0)
316  , nSMinDistPixelW(0)
317  , nSMinDistPixelH(0)
318  , aSScaleX(1)
319  , aSScaleY(1)
320  {
321  }
322 
323 };
324 
325 }
326 
327 static SwPaintProperties gProp;
328 
330 {
331  return !gProp.pSGlobalShell->GetViewOptions()->IsPagePreview() &&
332  !gProp.pSGlobalShell->GetViewOptions()->IsReadonly() &&
333  !gProp.pSGlobalShell->GetViewOptions()->IsFormView() &&
335 }
336 //other subsidiary lines enabled?
338 {
339  return !gProp.pSGlobalShell->GetViewOptions()->IsPagePreview() &&
340  !gProp.pSGlobalShell->GetViewOptions()->IsReadonly() &&
341  !gProp.pSGlobalShell->GetViewOptions()->IsFormView() &&
342  !gProp.pSGlobalShell->GetViewOptions()->IsWhitespaceHidden() &&
344 }
345 //subsidiary lines for sections
347 {
348  return !gProp.pSGlobalShell->GetViewOptions()->IsPagePreview() &&
349  !gProp.pSGlobalShell->GetViewOptions()->IsReadonly() &&
350  !gProp.pSGlobalShell->GetViewOptions()->IsFormView() &&
352 }
353 
354 
355 namespace {
356 
357 bool isTableBoundariesEnabled()
358 {
359  if (!gProp.pSGlobalShell->GetViewOptions()->IsTable())
360  return false;
361 
362  if (gProp.pSGlobalShell->GetViewOptions()->IsPagePreview())
363  return false;
364 
365  if (gProp.pSGlobalShell->GetViewOptions()->IsReadonly())
366  return false;
367 
368  if (gProp.pSGlobalShell->GetViewOptions()->IsFormView())
369  return false;
370 
372 }
373 
374 }
375 
383 {
384  // determine 'small' twip-to-pixel relation
385  bool bSmallTwipToPxRelW = false;
386  bool bSmallTwipToPxRelH = false;
387  {
388  Size aCheckTwipToPxRelSz( pOut->PixelToLogic( Size( 100, 100 )) );
389  if ( (aCheckTwipToPxRelSz.Width()/100.0) < 2.0 )
390  {
391  bSmallTwipToPxRelW = true;
392  }
393  if ( (aCheckTwipToPxRelSz.Height()/100.0) < 2.0 )
394  {
395  bSmallTwipToPxRelH = true;
396  }
397  }
398 
399  Size aSz( pOut->PixelToLogic( Size( 1,1 )) );
400 
401  gProp.nSPixelSzW = aSz.Width();
402  if( !gProp.nSPixelSzW )
403  gProp.nSPixelSzW = 1;
404  gProp.nSPixelSzH = aSz.Height();
405  if( !gProp.nSPixelSzH )
406  gProp.nSPixelSzH = 1;
407 
408  // consider 'small' twip-to-pixel relations
409  if ( !bSmallTwipToPxRelW )
410  {
411  gProp.nSHalfPixelSzW = gProp.nSPixelSzW / 2 + 1;
412  }
413  else
414  {
415  gProp.nSHalfPixelSzW = 0;
416  }
417  // consider 'small' twip-to-pixel relations
418  if ( !bSmallTwipToPxRelH )
419  {
420  gProp.nSHalfPixelSzH = gProp.nSPixelSzH / 2 + 1;
421  }
422  else
423  {
424  gProp.nSHalfPixelSzH = 0;
425  }
426 
427  gProp.nSMinDistPixelW = gProp.nSPixelSzW * 2 + 1;
428  gProp.nSMinDistPixelH = gProp.nSPixelSzH * 2 + 1;
429 
430  const MapMode &rMap = pOut->GetMapMode();
431  gProp.aSScaleX = double(rMap.GetScaleX());
432  gProp.aSScaleY = double(rMap.GetScaleY());
433 }
434 
435 namespace {
436 
440 class SwSavePaintStatics : public SwPaintProperties
441 {
442 public:
443  SwSavePaintStatics();
444  ~SwSavePaintStatics();
445 };
446 
447 }
448 
449 SwSavePaintStatics::SwSavePaintStatics()
450 {
451  // Saving globales
452  bSFlyMetafile = gProp.bSFlyMetafile;
453  pSGlobalShell = gProp.pSGlobalShell;
454  pSFlyMetafileOut = gProp.pSFlyMetafileOut;
455  pSRetoucheFly = gProp.pSRetoucheFly;
456  pSRetoucheFly2 = gProp.pSRetoucheFly2;
457  pSFlyOnlyDraw = gProp.pSFlyOnlyDraw;
458  pBLines = std::move(gProp.pBLines);
459  pSLines = std::move(gProp.pSLines);
460  pSSubsLines = std::move(gProp.pSSubsLines);
461  pSSpecSubsLines = std::move(gProp.pSSpecSubsLines);
462  pSProgress = gProp.pSProgress;
463  nSPixelSzW = gProp.nSPixelSzW;
464  nSPixelSzH = gProp.nSPixelSzH;
465  nSHalfPixelSzW = gProp.nSHalfPixelSzW;
466  nSHalfPixelSzH = gProp.nSHalfPixelSzH;
467  nSMinDistPixelW = gProp.nSMinDistPixelW;
468  nSMinDistPixelH = gProp.nSMinDistPixelH ;
469  aSGlobalRetoucheColor = aGlobalRetoucheColor;
470  aSScaleX = gProp.aSScaleX;
471  aSScaleY = gProp.aSScaleY;
472 
473  // Restoring globales to default
474  gProp.bSFlyMetafile = false;
475  gProp.pSFlyMetafileOut = nullptr;
476  gProp.pSRetoucheFly = nullptr;
477  gProp.pSRetoucheFly2 = nullptr;
478  gProp.nSPixelSzW = gProp.nSPixelSzH =
479  gProp.nSHalfPixelSzW = gProp.nSHalfPixelSzH =
480  gProp.nSMinDistPixelW = gProp.nSMinDistPixelH = 0;
481  gProp.aSScaleX = gProp.aSScaleY = 1.0;
482  gProp.pSProgress = nullptr;
483 }
484 
485 SwSavePaintStatics::~SwSavePaintStatics()
486 {
487  // Restoring globales to saved one
488  gProp.pSGlobalShell = pSGlobalShell;
489  gProp.bSFlyMetafile = bSFlyMetafile;
490  gProp.pSFlyMetafileOut = pSFlyMetafileOut;
491  gProp.pSRetoucheFly = pSRetoucheFly;
492  gProp.pSRetoucheFly2 = pSRetoucheFly2;
493  gProp.pSFlyOnlyDraw = pSFlyOnlyDraw;
494  gProp.pBLines = std::move(pBLines);
495  gProp.pSLines = std::move(pSLines);
496  gProp.pSSubsLines = std::move(pSSubsLines);
497  gProp.pSSpecSubsLines = std::move(pSSpecSubsLines);
498  gProp.pSProgress = pSProgress;
499  gProp.nSPixelSzW = nSPixelSzW;
500  gProp.nSPixelSzH = nSPixelSzH;
501  gProp.nSHalfPixelSzW = nSHalfPixelSzW;
502  gProp.nSHalfPixelSzH = nSHalfPixelSzH;
503  gProp.nSMinDistPixelW = nSMinDistPixelW;
504  gProp.nSMinDistPixelH = nSMinDistPixelH;
505  aGlobalRetoucheColor = aSGlobalRetoucheColor;
506  gProp.aSScaleX = aSScaleX;
507  gProp.aSScaleY = aSScaleY;
508 }
509 
510 void BorderLines::AddBorderLines(const drawinglayer::primitive2d::Primitive2DContainer& rContainer)
511 {
512  if(!rContainer.empty())
513  {
514  m_Lines.append(rContainer);
515  }
516 }
517 
518 SwLineRect::SwLineRect(const SwRect& rRect, const Color* pCol, const SvxBorderLineStyle nStyl,
519  const SwTabFrame* pT, const SubColFlags nSCol)
520  : SwRect(rRect)
521  , m_nStyle(nStyl)
522  , m_pTabFrame(pT)
523  , m_nSubColor(nSCol)
524  , m_bPainted(false)
525  , m_nLock(0)
526 {
527  if ( pCol != nullptr )
528  m_aColor = *pCol;
529 }
530 
531 bool SwLineRect::MakeUnion( const SwRect &rRect, SwPaintProperties const & properties)
532 {
533  // It has already been tested outside, whether the rectangles have
534  // the same orientation (horizontal or vertical), color, etc.
535  if ( Height() > Width() ) //Vertical line
536  {
537  if ( Left() == rRect.Left() && Width() == rRect.Width() )
538  {
539  // Merge when there is no gap between the lines
540  const tools::Long nAdd = properties.nSPixelSzW + properties.nSHalfPixelSzW;
541  if ( Bottom() + nAdd >= rRect.Top() &&
542  Top() - nAdd <= rRect.Bottom() )
543  {
544  Bottom( std::max( Bottom(), rRect.Bottom() ) );
545  Top ( std::min( Top(), rRect.Top() ) );
546  return true;
547  }
548  }
549  }
550  else
551  {
552  if ( Top() == rRect.Top() && Height() == rRect.Height() )
553  {
554  // Merge when there is no gap between the lines
555  const tools::Long nAdd = properties.nSPixelSzW + properties.nSHalfPixelSzW;
556  if ( Right() + nAdd >= rRect.Left() &&
557  Left() - nAdd <= rRect.Right() )
558  {
559  Right( std::max( Right(), rRect.Right() ) );
560  Left ( std::min( Left(), rRect.Left() ) );
561  return true;
562  }
563  }
564  }
565  return false;
566 }
567 
568 void SwLineRects::AddLineRect( const SwRect &rRect, const Color *pCol, const SvxBorderLineStyle nStyle,
569  const SwTabFrame *pTab, const SubColFlags nSCol, SwPaintProperties const & properties )
570 {
571  // Loop backwards because lines which can be combined, can usually be painted
572  // in the same context
573  for (reverse_iterator it = m_aLineRects.rbegin(); it != m_aLineRects.rend(); ++it)
574  {
575  SwLineRect &rLRect = *it;
576  // Test for the orientation, color, table
577  if ( rLRect.GetTab() == pTab &&
578  !rLRect.IsPainted() && rLRect.GetSubColor() == nSCol &&
579  (rLRect.Height() > rLRect.Width()) == (rRect.Height() > rRect.Width()) &&
580  (pCol && rLRect.GetColor() == *pCol) )
581  {
582  if ( rLRect.MakeUnion( rRect, properties ) )
583  return;
584  }
585  }
586  m_aLineRects.emplace_back(rRect, pCol, nStyle, pTab, nSCol);
587 }
588 
589 void SwLineRects::ConnectEdges( OutputDevice const *pOut, SwPaintProperties const & properties )
590 {
591  if ( pOut->GetOutDevType() != OUTDEV_PRINTER )
592  {
593  // I'm not doing anything for a too small zoom
594  if ( properties.aSScaleX < aEdgeScale || properties.aSScaleY < aEdgeScale )
595  return;
596  }
597 
598  static const tools::Long nAdd = 20;
599 
600  std::vector<SwLineRect*> aCheck;
601 
602  for (size_t i = 0; i < m_aLineRects.size(); ++i)
603  {
604  SwLineRect& rL1 = m_aLineRects[i];
605  if ( !rL1.GetTab() || rL1.IsPainted() || rL1.IsLocked() )
606  continue;
607 
608  aCheck.clear();
609 
610  const bool bVert = rL1.Height() > rL1.Width();
611  tools::Long nL1a, nL1b, nL1c, nL1d;
612 
613  if ( bVert )
614  {
615  nL1a = rL1.Top(); nL1b = rL1.Left();
616  nL1c = rL1.Right(); nL1d = rL1.Bottom();
617  }
618  else
619  {
620  nL1a = rL1.Left(); nL1b = rL1.Top();
621  nL1c = rL1.Bottom(); nL1d = rL1.Right();
622  }
623 
624  // Collect all lines to possibly link with i1
625  for (iterator it2 = m_aLineRects.begin(); it2 != m_aLineRects.end(); ++it2)
626  {
627  SwLineRect &rL2 = *it2;
628  if ( rL2.GetTab() != rL1.GetTab() ||
629  rL2.IsPainted() ||
630  rL2.IsLocked() ||
631  (bVert == (rL2.Height() > rL2.Width())) )
632  continue;
633 
634  tools::Long nL2a, nL2b, nL2c, nL2d;
635  if ( bVert )
636  {
637  nL2a = rL2.Top(); nL2b = rL2.Left();
638  nL2c = rL2.Right(); nL2d = rL2.Bottom();
639  }
640  else
641  {
642  nL2a = rL2.Left(); nL2b = rL2.Top();
643  nL2c = rL2.Bottom(); nL2d = rL2.Right();
644  }
645 
646  if ( (nL1a - nAdd < nL2d && nL1d + nAdd > nL2a) &&
647  ((nL1b > nL2b && nL1c < nL2c) ||
648  (nL1c >= nL2c && nL1b - nAdd < nL2c) ||
649  (nL1b <= nL2b && nL1c + nAdd > nL2b)) )
650  {
651  aCheck.push_back( &rL2 );
652  }
653  }
654  if ( aCheck.size() < 2 )
655  continue;
656 
657  bool bRemove = false;
658 
659  // For each line test all following ones.
660  for ( size_t k = 0; !bRemove && k < aCheck.size(); ++k )
661  {
662  SwLineRect &rR1 = *aCheck[k];
663 
664  for ( size_t k2 = k+1; !bRemove && k2 < aCheck.size(); ++k2 )
665  {
666  SwLineRect &rR2 = *aCheck[k2];
667  if ( bVert )
668  {
669  SwLineRect *pLA = nullptr;
670  SwLineRect *pLB = nullptr;
671  if ( rR1.Top() < rR2.Top() )
672  {
673  pLA = &rR1; pLB = &rR2;
674  }
675  else if ( rR1.Top() > rR2.Top() )
676  {
677  pLA = &rR2; pLB = &rR1;
678  }
679  // are k1 and k2 describing a double line?
680  if ( pLA && pLA->Bottom() + 60 > pLB->Top() )
681  {
682  if ( rL1.Top() < pLA->Top() )
683  {
684  if ( rL1.Bottom() == pLA->Bottom() )
685  continue; //Small mistake (where?)
686 
687  SwRect aIns( rL1 );
688  aIns.Bottom( pLA->Bottom() );
689  if ( !rL1.IsInside( aIns ) )
690  continue;
691  m_aLineRects.emplace_back(aIns, &rL1.GetColor(),
692  SvxBorderLineStyle::SOLID, rL1.GetTab(),
693  SubColFlags::Tab);
694  if ( isFull() )
695  {
696  --i;
697  k = aCheck.size();
698  break;
699  }
700  }
701 
702  if ( rL1.Bottom() > pLB->Bottom() )
703  rL1.Top( pLB->Top() ); // extend i1 on the top
704  else
705  bRemove = true; //stopping, remove i1
706  }
707  }
708  else
709  {
710  SwLineRect *pLA = nullptr;
711  SwLineRect *pLB = nullptr;
712  if ( rR1.Left() < rR2.Left() )
713  {
714  pLA = &rR1; pLB = &rR2;
715  }
716  else if ( rR1.Left() > rR2.Left() )
717  {
718  pLA = &rR2; pLB = &rR1;
719  }
720  // Is it double line?
721  if ( pLA && pLA->Right() + 60 > pLB->Left() )
722  {
723  if ( rL1.Left() < pLA->Left() )
724  {
725  if ( rL1.Right() == pLA->Right() )
726  continue; //small error
727 
728  SwRect aIns( rL1 );
729  aIns.Right( pLA->Right() );
730  if ( !rL1.IsInside( aIns ) )
731  continue;
732  m_aLineRects.emplace_back(aIns, &rL1.GetColor(),
733  SvxBorderLineStyle::SOLID, rL1.GetTab(),
734  SubColFlags::Tab);
735  if ( isFull() )
736  {
737  --i;
738  k = aCheck.size();
739  break;
740  }
741  }
742  if ( rL1.Right() > pLB->Right() )
743  rL1.Left( pLB->Left() );
744  else
745  bRemove = true;
746  }
747  }
748  }
749  }
750  if ( bRemove )
751  {
752  m_aLineRects.erase(m_aLineRects.begin() + i);
753  --i;
754  }
755  }
756 }
757 
758 void SwSubsRects::RemoveSuperfluousSubsidiaryLines( const SwLineRects &rRects, SwPaintProperties const & properties )
759 {
760  // All help lines that are covered by any border will be removed or split
761  for (size_t i = 0; i < m_aLineRects.size(); ++i)
762  {
763  // get a copy instead of a reference, because an <insert> may destroy
764  // the object due to a necessary array resize.
765  const SwLineRect aSubsLineRect(m_aLineRects[i]);
766 
767  // add condition <aSubsLineRect.IsLocked()> in order to consider only
768  // border lines, which are *not* locked.
769  if ( aSubsLineRect.IsPainted() ||
770  aSubsLineRect.IsLocked() )
771  continue;
772 
773  const bool bVerticalSubs = aSubsLineRect.Height() > aSubsLineRect.Width();
774  SwRect aSubsRect( aSubsLineRect );
775  if ( bVerticalSubs )
776  {
777  aSubsRect.AddLeft ( - (properties.nSPixelSzW+properties.nSHalfPixelSzW) );
778  aSubsRect.AddRight ( properties.nSPixelSzW+properties.nSHalfPixelSzW );
779  }
780  else
781  {
782  aSubsRect.AddTop ( - (properties.nSPixelSzH+properties.nSHalfPixelSzH) );
783  aSubsRect.AddBottom( properties.nSPixelSzH+properties.nSHalfPixelSzH );
784  }
785  for (const_iterator itK = rRects.m_aLineRects.begin(); itK != rRects.m_aLineRects.end();
786  ++itK)
787  {
788  const SwLineRect &rLine = *itK;
789 
790  // do *not* consider painted or locked border lines.
791  // #i1837# - locked border lines have to be considered.
792  if ( rLine.IsLocked () )
793  continue;
794 
795  if ( !bVerticalSubs == ( rLine.Height() > rLine.Width() ) ) //same direction?
796  continue;
797 
798  if ( aSubsRect.IsOver( rLine ) )
799  {
800  if ( bVerticalSubs ) // Vertical?
801  {
802  if ( aSubsRect.Left() <= rLine.Right() &&
803  aSubsRect.Right() >= rLine.Left() )
804  {
805  tools::Long nTmp = rLine.Top()-(properties.nSPixelSzH+1);
806  if ( aSubsLineRect.Top() < nTmp )
807  {
808  SwRect aNewSubsRect( aSubsLineRect );
809  aNewSubsRect.Bottom( nTmp );
810  m_aLineRects.emplace_back(aNewSubsRect, nullptr,
811  aSubsLineRect.GetStyle(), nullptr,
812  aSubsLineRect.GetSubColor());
813  }
814  nTmp = rLine.Bottom()+properties.nSPixelSzH+1;
815  if ( aSubsLineRect.Bottom() > nTmp )
816  {
817  SwRect aNewSubsRect( aSubsLineRect );
818  aNewSubsRect.Top( nTmp );
819  m_aLineRects.emplace_back(aNewSubsRect, nullptr,
820  aSubsLineRect.GetStyle(), nullptr,
821  aSubsLineRect.GetSubColor());
822  }
823  m_aLineRects.erase(m_aLineRects.begin() + i);
824  --i;
825  break;
826  }
827  }
828  else // Horizontal
829  {
830  if ( aSubsRect.Top() <= rLine.Bottom() &&
831  aSubsRect.Bottom() >= rLine.Top() )
832  {
833  tools::Long nTmp = rLine.Left()-(properties.nSPixelSzW+1);
834  if ( aSubsLineRect.Left() < nTmp )
835  {
836  SwRect aNewSubsRect( aSubsLineRect );
837  aNewSubsRect.Right( nTmp );
838  m_aLineRects.emplace_back(aNewSubsRect, nullptr,
839  aSubsLineRect.GetStyle(), nullptr,
840  aSubsLineRect.GetSubColor());
841  }
842  nTmp = rLine.Right()+properties.nSPixelSzW+1;
843  if ( aSubsLineRect.Right() > nTmp )
844  {
845  SwRect aNewSubsRect( aSubsLineRect );
846  aNewSubsRect.Left( nTmp );
847  m_aLineRects.emplace_back(aNewSubsRect, nullptr,
848  aSubsLineRect.GetStyle(), nullptr,
849  aSubsLineRect.GetSubColor());
850  }
851  m_aLineRects.erase(m_aLineRects.begin() + i);
852  --i;
853  break;
854  }
855  }
856  }
857  }
858  }
859 }
860 
861 void SwLineRects::LockLines( bool bLock )
862 {
863  for (SwLineRect& rLRect : m_aLineRects)
864  rLRect.Lock(bLock);
865 }
866 
867 static void lcl_DrawDashedRect( OutputDevice * pOut, SwLineRect const & rLRect )
868 {
869  tools::Long startX = rLRect.Left( ), endX;
870  tools::Long startY = rLRect.Top( ), endY;
871 
872  // Discriminate vertically stretched rect from horizontally stretched
873  // and restrict minimum nHalfLWidth to 1
874  tools::Long nHalfLWidth = std::max( std::min( rLRect.Width( ), rLRect.Height( ) ) / 2, tools::Long(1) );
875 
876  if ( rLRect.Height( ) > rLRect.Width( ) )
877  {
878  startX += nHalfLWidth;
879  endX = startX;
880  endY = startY + rLRect.Height( );
881  }
882  else
883  {
884  startY += nHalfLWidth;
885  endY = startY;
886  endX = startX + rLRect.Width( );
887  }
888 
889  svtools::DrawLine( *pOut, Point( startX, startY ), Point( endX, endY ),
890  sal_uInt32( nHalfLWidth * 2 ), rLRect.GetStyle( ) );
891 }
892 
893 void SwLineRects::PaintLines( OutputDevice *pOut, SwPaintProperties const &properties )
894 {
895  // Paint the borders. Sadly two passes are needed.
896  // Once for the inside and once for the outside edges of tables
897  if (m_aLineRects.size() == m_nLastCount)
898  return;
899 
900  // #i16816# tagged pdf support
901  SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, nullptr, *pOut );
902 
903  pOut->Push( PushFlags::FILLCOLOR|PushFlags::LINECOLOR );
904  pOut->SetFillColor();
905  pOut->SetLineColor();
906  ConnectEdges( pOut, properties );
907  const Color *pLast = nullptr;
908 
909  bool bPaint2nd = false;
910  size_t nMinCount = m_aLineRects.size();
911 
912  for (size_t i = 0; i < m_aLineRects.size(); ++i)
913  {
914  SwLineRect& rLRect = m_aLineRects[i];
915 
916  if ( rLRect.IsPainted() )
917  continue;
918 
919  if ( rLRect.IsLocked() )
920  {
921  nMinCount = std::min( nMinCount, i );
922  continue;
923  }
924 
925  // Paint it now or in the second pass?
926  bool bPaint = true;
927  if ( rLRect.GetTab() )
928  {
929  if ( rLRect.Height() > rLRect.Width() )
930  {
931  // Vertical edge, overlapping with the table edge?
932  SwTwips nLLeft = rLRect.Left() - 30,
933  nLRight = rLRect.Right() + 30,
934  nTLeft = rLRect.GetTab()->getFrameArea().Left() + rLRect.GetTab()->getFramePrintArea().Left(),
935  nTRight = rLRect.GetTab()->getFrameArea().Left() + rLRect.GetTab()->getFramePrintArea().Right();
936  if ( (nTLeft >= nLLeft && nTLeft <= nLRight) ||
937  (nTRight>= nLLeft && nTRight<= nLRight) )
938  bPaint = false;
939  }
940  else
941  {
942  // Horizontal edge, overlapping with the table edge?
943  SwTwips nLTop = rLRect.Top() - 30,
944  nLBottom = rLRect.Bottom() + 30,
945  nTTop = rLRect.GetTab()->getFrameArea().Top() + rLRect.GetTab()->getFramePrintArea().Top(),
946  nTBottom = rLRect.GetTab()->getFrameArea().Top() + rLRect.GetTab()->getFramePrintArea().Bottom();
947  if ( (nTTop >= nLTop && nTTop <= nLBottom) ||
948  (nTBottom >= nLTop && nTBottom <= nLBottom) )
949  bPaint = false;
950  }
951  }
952  if ( bPaint )
953  {
954  if ( !pLast || *pLast != rLRect.GetColor() )
955  {
956  pLast = &rLRect.GetColor();
957 
958  DrawModeFlags nOldDrawMode = pOut->GetDrawMode();
959  if( properties.pSGlobalShell->GetWin() &&
961  pOut->SetDrawMode( DrawModeFlags::Default );
962 
963  pOut->SetLineColor( *pLast );
964  pOut->SetFillColor( *pLast );
965  pOut->SetDrawMode( nOldDrawMode );
966  }
967 
968  if( !rLRect.IsEmpty() )
969  lcl_DrawDashedRect( pOut, rLRect );
970  rLRect.SetPainted();
971  }
972  else
973  bPaint2nd = true;
974  }
975  if ( bPaint2nd )
976  {
977  for (size_t i = 0; i < m_aLineRects.size(); ++i)
978  {
979  SwLineRect& rLRect = m_aLineRects[i];
980  if ( rLRect.IsPainted() )
981  continue;
982 
983  if ( rLRect.IsLocked() )
984  {
985  nMinCount = std::min( nMinCount, i );
986  continue;
987  }
988 
989  if ( !pLast || *pLast != rLRect.GetColor() )
990  {
991  pLast = &rLRect.GetColor();
992 
993  DrawModeFlags nOldDrawMode = pOut->GetDrawMode();
994  if( properties.pSGlobalShell->GetWin() &&
996  {
997  pOut->SetDrawMode( DrawModeFlags::Default );
998  }
999 
1000  pOut->SetFillColor( *pLast );
1001  pOut->SetDrawMode( nOldDrawMode );
1002  }
1003  if( !rLRect.IsEmpty() )
1004  lcl_DrawDashedRect( pOut, rLRect );
1005  rLRect.SetPainted();
1006  }
1007  }
1008  m_nLastCount = nMinCount;
1009  pOut->Pop();
1010 
1011 }
1012 
1013 void SwSubsRects::PaintSubsidiary( OutputDevice *pOut,
1014  const SwLineRects *pRects,
1015  SwPaintProperties const & properties )
1016 {
1017  if (m_aLineRects.empty())
1018  return;
1019 
1020  // #i16816# tagged pdf support
1021  SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, nullptr, *pOut );
1022 
1023  // Remove all help line that are almost covered (tables)
1024  for (size_type i = 0; i != m_aLineRects.size(); ++i)
1025  {
1026  SwLineRect& rLi = m_aLineRects[i];
1027  const bool bVerticalSubs = rLi.Height() > rLi.Width();
1028 
1029  for (size_type k = i + 1; k != m_aLineRects.size(); ++k)
1030  {
1031  SwLineRect& rLk = m_aLineRects[k];
1032  if ( rLi.SSize() == rLk.SSize() )
1033  {
1034  if ( bVerticalSubs == ( rLk.Height() > rLk.Width() ) )
1035  {
1036  if ( bVerticalSubs )
1037  {
1038  tools::Long nLi = rLi.Right();
1039  tools::Long nLk = rLk.Right();
1040  if ( rLi.Top() == rLk.Top() &&
1041  ((nLi < rLk.Left() && nLi+21 > rLk.Left()) ||
1042  (nLk < rLi.Left() && nLk+21 > rLi.Left())))
1043  {
1044  m_aLineRects.erase(m_aLineRects.begin() + i);
1045  // don't continue with inner loop any more:
1046  // the array may shrink!
1047  --i;
1048  break;
1049  }
1050  }
1051  else
1052  {
1053  tools::Long nLi = rLi.Bottom();
1054  tools::Long nLk = rLk.Bottom();
1055  if ( rLi.Left() == rLk.Left() &&
1056  ((nLi < rLk.Top() && nLi+21 > rLk.Top()) ||
1057  (nLk < rLi.Top() && nLk+21 > rLi.Top())))
1058  {
1059  m_aLineRects.erase(m_aLineRects.begin() + i);
1060  // don't continue with inner loop any more:
1061  // the array may shrink!
1062  --i;
1063  break;
1064  }
1065  }
1066  }
1067  }
1068  }
1069  }
1070 
1071  if (pRects && (!pRects->m_aLineRects.empty()))
1072  RemoveSuperfluousSubsidiaryLines( *pRects, properties );
1073 
1074  if (m_aLineRects.empty())
1075  return;
1076 
1077  pOut->Push( PushFlags::FILLCOLOR|PushFlags::LINECOLOR );
1078  pOut->SetLineColor();
1079 
1080  // Reset draw mode in high contrast mode in order to get fill color
1081  // set at output device. Recover draw mode after draw of lines.
1082  // Necessary for the subsidiary lines painted by the fly frames.
1083  DrawModeFlags nOldDrawMode = pOut->GetDrawMode();
1084  if( gProp.pSGlobalShell->GetWin() &&
1086  {
1087  pOut->SetDrawMode( DrawModeFlags::Default );
1088  }
1089 
1090  for (SwLineRect& rLRect : m_aLineRects)
1091  {
1092  // Add condition <!rLRect.IsLocked()> to prevent paint of locked subsidiary lines.
1093  if ( !rLRect.IsPainted() &&
1094  !rLRect.IsLocked() )
1095  {
1096  const Color *pCol = nullptr;
1097  switch ( rLRect.GetSubColor() )
1098  {
1099  case SubColFlags::Page: pCol = &SwViewOption::GetDocBoundariesColor(); break;
1100  case SubColFlags::Fly: pCol = &SwViewOption::GetObjectBoundariesColor(); break;
1101  case SubColFlags::Tab: pCol = &SwViewOption::GetTableBoundariesColor(); break;
1102  case SubColFlags::Sect: pCol = &SwViewOption::GetSectionBoundColor(); break;
1103  }
1104 
1105  if (pCol && pOut->GetFillColor() != *pCol)
1106  pOut->SetFillColor( *pCol );
1107  pOut->DrawRect( rLRect.SVRect() );
1108 
1109  rLRect.SetPainted();
1110  }
1111  }
1112 
1113  pOut->SetDrawMode( nOldDrawMode );
1114 
1115  pOut->Pop();
1116 }
1117 
1118 // Various functions that are use in this file.
1119 
1127 void SwAlignRect( SwRect &rRect, const SwViewShell *pSh, const vcl::RenderContext* pRenderContext )
1128 {
1129  if( !rRect.HasArea() )
1130  return;
1131 
1132  // Make sure that view shell (parameter <pSh>) exists, if the output device
1133  // is taken from this view shell --> no output device, no alignment
1134  // Output device taken from view shell <pSh>, if <gProp.bSFlyMetafile> not set
1135  if ( !gProp.bSFlyMetafile && !pSh )
1136  {
1137  return;
1138  }
1139 
1140  const vcl::RenderContext *pOut = gProp.bSFlyMetafile ?
1141  gProp.pSFlyMetafileOut.get() : pRenderContext;
1142 
1143  // Hold original rectangle in pixel
1144  const tools::Rectangle aOrgPxRect = pOut->LogicToPixel( rRect.SVRect() );
1145  // Determine pixel-center rectangle in twip
1146  const SwRect aPxCenterRect( pOut->PixelToLogic( aOrgPxRect ) );
1147 
1148  // Perform adjustments on pixel level.
1149  SwRect aAlignedPxRect( aOrgPxRect );
1150  if ( rRect.Top() > aPxCenterRect.Top() )
1151  {
1152  // 'leave pixel overlapping on top'
1153  aAlignedPxRect.AddTop( 1 );
1154  }
1155 
1156  if ( rRect.Bottom() < aPxCenterRect.Bottom() )
1157  {
1158  // 'leave pixel overlapping on bottom'
1159  aAlignedPxRect.AddBottom( - 1 );
1160  }
1161 
1162  if ( rRect.Left() > aPxCenterRect.Left() )
1163  {
1164  // 'leave pixel overlapping on left'
1165  aAlignedPxRect.AddLeft( 1 );
1166  }
1167 
1168  if ( rRect.Right() < aPxCenterRect.Right() )
1169  {
1170  // 'leave pixel overlapping on right'
1171  aAlignedPxRect.AddRight( - 1 );
1172  }
1173 
1174  // Consider negative width/height check, if aligned SwRect has negative width/height.
1175  // If Yes, adjust it to width/height = 0 twip.
1176  // NOTE: A SwRect with negative width/height can occur, if the width/height
1177  // of the given SwRect in twip was less than a pixel in twip and that
1178  // the alignment calculates that the aligned SwRect should not contain
1179  // the pixels the width/height is on.
1180  if ( aAlignedPxRect.Width() < 0 )
1181  {
1182  aAlignedPxRect.Width(0);
1183  }
1184  if ( aAlignedPxRect.Height() < 0 )
1185  {
1186  aAlignedPxRect.Height(0);
1187  }
1188  // Consider zero width/height for converting a rectangle from
1189  // pixel to logic it needs a width/height. Thus, set width/height
1190  // to one, if it's zero and correct this on the twip level after the conversion.
1191  bool bZeroWidth = false;
1192  if ( aAlignedPxRect.Width() == 0 )
1193  {
1194  aAlignedPxRect.Width(1);
1195  bZeroWidth = true;
1196  }
1197  bool bZeroHeight = false;
1198  if ( aAlignedPxRect.Height() == 0 )
1199  {
1200  aAlignedPxRect.Height(1);
1201  bZeroHeight = true;
1202  }
1203 
1204  rRect = SwRect(pOut->PixelToLogic( aAlignedPxRect.SVRect() ));
1205 
1206  // Consider zero width/height and adjust calculated aligned twip rectangle.
1207  // Reset width/height to zero; previous negative width/height haven't to be considered.
1208  if ( bZeroWidth )
1209  {
1210  rRect.Width(0);
1211  }
1212  if ( bZeroHeight )
1213  {
1214  rRect.Height(0);
1215  }
1216 }
1217 
1233 void SwAlignGrfRect( SwRect *pGrfRect, const vcl::RenderContext &rOut )
1234 {
1235  tools::Rectangle aPxRect = rOut.LogicToPixel( pGrfRect->SVRect() );
1236  pGrfRect->Pos( rOut.PixelToLogic( aPxRect.TopLeft() ) );
1237  pGrfRect->SSize( rOut.PixelToLogic( aPxRect.GetSize() ) );
1238 }
1239 
1240 static tools::Long lcl_AlignWidth( const tools::Long nWidth, SwPaintProperties const & properties )
1241 {
1242  if ( nWidth )
1243  {
1244  const tools::Long nW = nWidth % properties.nSPixelSzW;
1245 
1246  if ( !nW || nW > properties.nSHalfPixelSzW )
1247  return std::max(tools::Long(1), nWidth - properties.nSHalfPixelSzW);
1248  }
1249  return nWidth;
1250 }
1251 
1252 static tools::Long lcl_AlignHeight( const tools::Long nHeight, SwPaintProperties const & properties )
1253 {
1254  if ( nHeight )
1255  {
1256  const tools::Long nH = nHeight % properties.nSPixelSzH;
1257 
1258  if ( !nH || nH > properties.nSHalfPixelSzH )
1259  return std::max(tools::Long(1), nHeight - properties.nSHalfPixelSzH);
1260  }
1261  return nHeight;
1262 }
1263 
1267 static void lcl_CalcBorderRect( SwRect &rRect, const SwFrame *pFrame,
1268  const SwBorderAttrs &rAttrs,
1269  const bool bShadow,
1270  SwPaintProperties const & properties)
1271 {
1272  // Special handling for cell frames.
1273  // The printing area of a cell frame is completely enclosed in the frame area
1274  // and a cell frame has no shadow. Thus, for cell frames the calculated
1275  // area equals the frame area.
1276  // Notes: Borders of cell frames in R2L text direction will switch its side
1277  // - left border is painted on the right; right border on the left.
1278  // See <lcl_PaintLeftLine> and <lcl_PaintRightLine>.
1279  if( pFrame->IsSctFrame() )
1280  {
1281  rRect = pFrame->getFramePrintArea();
1282  rRect.Pos() += pFrame->getFrameArea().Pos();
1283  }
1284  else if ( pFrame->IsCellFrame() )
1285  rRect = pFrame->getFrameArea();
1286  else
1287  {
1288  rRect = pFrame->getFramePrintArea();
1289  rRect.Pos() += pFrame->getFrameArea().Pos();
1290 
1291  SwRectFn fnRect = pFrame->IsVertical() ? ( pFrame->IsVertLR() ? (pFrame->IsVertLRBT() ? fnRectVertL2RB2T : fnRectVertL2R) : fnRectVert ) : fnRectHori;
1292 
1293  const SvxBoxItem &rBox = rAttrs.GetBox();
1294  const bool bTop = 0 != (pFrame->*fnRect->fnGetTopMargin)();
1295  if ( bTop )
1296  {
1297  SwTwips nDiff = rBox.GetTop() ?
1298  rBox.CalcLineSpace( SvxBoxItemLine::TOP ) :
1299  rBox.GetDistance( SvxBoxItemLine::TOP );
1300  if( nDiff )
1301  (rRect.*fnRect->fnSubTop)( nDiff );
1302  }
1303 
1304  const bool bBottom = 0 != (pFrame->*fnRect->fnGetBottomMargin)();
1305  if ( bBottom )
1306  {
1307  SwTwips nDiff = 0;
1308  // #i29550#
1309  if ( pFrame->IsTabFrame() &&
1310  static_cast<const SwTabFrame*>(pFrame)->IsCollapsingBorders() )
1311  {
1312  // For collapsing borders, we have to add the height of
1313  // the height of the last line
1314  nDiff = static_cast<const SwTabFrame*>(pFrame)->GetBottomLineSize();
1315  }
1316  else
1317  {
1318  nDiff = rBox.GetBottom() ?
1319  rBox.CalcLineSpace( SvxBoxItemLine::BOTTOM ) :
1320  rBox.GetDistance( SvxBoxItemLine::BOTTOM );
1321  }
1322  if( nDiff )
1323  (rRect.*fnRect->fnAddBottom)( nDiff );
1324  }
1325 
1326  if ( rBox.GetLeft() )
1327  (rRect.*fnRect->fnSubLeft)( rBox.CalcLineSpace( SvxBoxItemLine::LEFT ) );
1328  else
1329  (rRect.*fnRect->fnSubLeft)( rBox.GetDistance( SvxBoxItemLine::LEFT ) );
1330 
1331  if ( rBox.GetRight() )
1332  (rRect.*fnRect->fnAddRight)( rBox.CalcLineSpace( SvxBoxItemLine::RIGHT ) );
1333  else
1334  (rRect.*fnRect->fnAddRight)( rBox.GetDistance( SvxBoxItemLine::RIGHT ) );
1335 
1336  if ( bShadow && rAttrs.GetShadow().GetLocation() != SvxShadowLocation::NONE )
1337  {
1338  const SvxShadowItem &rShadow = rAttrs.GetShadow();
1339  if ( bTop )
1340  (rRect.*fnRect->fnSubTop)(rShadow.CalcShadowSpace(SvxShadowItemSide::TOP));
1341  (rRect.*fnRect->fnSubLeft)(rShadow.CalcShadowSpace(SvxShadowItemSide::LEFT));
1342  if ( bBottom )
1343  (rRect.*fnRect->fnAddBottom)
1344  (rShadow.CalcShadowSpace( SvxShadowItemSide::BOTTOM ));
1345  (rRect.*fnRect->fnAddRight)(rShadow.CalcShadowSpace(SvxShadowItemSide::RIGHT));
1346  }
1347  }
1348 
1349  ::SwAlignRect( rRect, properties.pSGlobalShell, properties.pSGlobalShell ? properties.pSGlobalShell->GetOut() : nullptr );
1350 }
1351 
1356 static void lcl_ExtendLeftAndRight( SwRect& _rRect,
1357  const SwFrame& _rFrame,
1358  const SwBorderAttrs& _rAttrs,
1359  const SwRectFn& _rRectFn )
1360 {
1361  if ( _rAttrs.JoinedWithPrev( _rFrame ) )
1362  {
1363  const SwFrame* pPrevFrame = _rFrame.GetPrev();
1364  (_rRect.*_rRectFn->fnSetTop)( (pPrevFrame->*_rRectFn->fnGetPrtBottom)() );
1365  }
1366  if ( _rAttrs.JoinedWithNext( _rFrame ) )
1367  {
1368  const SwFrame* pNextFrame = _rFrame.GetNext();
1369  (_rRect.*_rRectFn->fnSetBottom)( (pNextFrame->*_rRectFn->fnGetPrtTop)() );
1370  }
1371 }
1372 
1376 {
1377  static MapMode aMapMode(MapUnit::MapTwip);
1378  static const Size aSingleUnit = Application::GetDefaultDevice()->PixelToLogic(Size(1, 1), aMapMode);
1379 
1380  double x1 = rRect.Left() + aSingleUnit.getWidth();
1381  double y1 = rRect.Top() + aSingleUnit.getHeight();
1382  double x2 = rRect.Right() - aSingleUnit.getWidth();
1383  double y2 = rRect.Bottom() - aSingleUnit.getHeight();
1384 
1385  return basegfx::B2DRange(x1, y1, x2, y2);
1386 }
1387 
1388 static void lcl_SubtractFlys( const SwFrame *pFrame, const SwPageFrame *pPage,
1389  const SwRect &rRect, SwRegionRects &rRegion, basegfx::utils::B2DClipState& rClipState, SwPaintProperties const & rProperties)
1390 {
1391  const SwSortedObjs& rObjs = *pPage->GetSortedObjs();
1392  const SwFlyFrame* pSelfFly = pFrame->IsInFly() ? pFrame->FindFlyFrame() : gProp.pSRetoucheFly2;
1393  if (!gProp.pSRetoucheFly)
1394  gProp.pSRetoucheFly = gProp.pSRetoucheFly2;
1395 
1396  for (size_t j = 0; (j < rObjs.size()) && !rRegion.empty(); ++j)
1397  {
1398  const SwAnchoredObject* pAnchoredObj = rObjs[j];
1399  const SdrObject* pSdrObj = pAnchoredObj->GetDrawObj();
1400 
1401  // Do not consider invisible objects
1402  if (!pPage->GetFormat()->GetDoc()->getIDocumentDrawModelAccess().IsVisibleLayerId(pSdrObj->GetLayer()))
1403  continue;
1404 
1405  const SwFlyFrame *pFly = dynamic_cast<const SwFlyFrame*>(pAnchoredObj);
1406  if (!pFly)
1407  continue;
1408 
1409  if (pSelfFly == pFly || gProp.pSRetoucheFly == pFly || !rRect.IsOver(pFly->getFrameArea()))
1410  continue;
1411 
1412  if (!pFly->GetFormat()->GetPrint().GetValue() &&
1413  (OUTDEV_PRINTER == gProp.pSGlobalShell->GetOut()->GetOutDevType() ||
1414  gProp.pSGlobalShell->IsPreview()))
1415  continue;
1416 
1417  const bool bLowerOfSelf = pSelfFly && pFly->IsLowerOf( pSelfFly );
1418 
1419  //For character bound Flys only examine those Flys in which it is not
1420  //anchored itself.
1421  //Why only for character bound ones you may ask? It never makes sense to
1422  //subtract frames in which it is anchored itself right?
1423  if (pSelfFly && pSelfFly->IsLowerOf(pFly))
1424  continue;
1425 
1426  //Any why does it not apply for the RetoucheFly too?
1427  if (gProp.pSRetoucheFly && gProp.pSRetoucheFly->IsLowerOf(pFly))
1428  continue;
1429 
1430 #if OSL_DEBUG_LEVEL > 0
1431  //Flys who are anchored inside their own one, must have a bigger OrdNum
1432  //or be character bound.
1433  if (pSelfFly && bLowerOfSelf)
1434  {
1435  OSL_ENSURE( pFly->IsFlyInContentFrame() ||
1436  pSdrObj->GetOrdNumDirect() > pSelfFly->GetVirtDrawObj()->GetOrdNumDirect(),
1437  "Fly with wrong z-Order" );
1438  }
1439 #endif
1440 
1441  bool bStopOnHell = true;
1442  if (pSelfFly)
1443  {
1444  const SdrObject *pTmp = pSelfFly->GetVirtDrawObj();
1445  if (pSdrObj->GetLayer() == pTmp->GetLayer())
1446  {
1447  if (pSdrObj->GetOrdNumDirect() < pTmp->GetOrdNumDirect())
1448  //In the same layer we only observe those that are above.
1449  continue;
1450  }
1451  else
1452  {
1453  if (!bLowerOfSelf && !pFly->GetFormat()->GetOpaque().GetValue())
1454  //From other layers we are only interested in non
1455  //transparent ones or those that are internal
1456  continue;
1457  bStopOnHell = false;
1458  }
1459  }
1460  if (gProp.pSRetoucheFly)
1461  {
1462  const SdrObject *pTmp = gProp.pSRetoucheFly->GetVirtDrawObj();
1463  if ( pSdrObj->GetLayer() == pTmp->GetLayer() )
1464  {
1465  if ( pSdrObj->GetOrdNumDirect() < pTmp->GetOrdNumDirect() )
1466  //In the same layer we only observe those that are above.
1467  continue;
1468  }
1469  else
1470  {
1471  if (!pFly->IsLowerOf( gProp.pSRetoucheFly ) && !pFly->GetFormat()->GetOpaque().GetValue())
1472  //From other layers we are only interested in non
1473  //transparent ones or those that are internal
1474  continue;
1475  bStopOnHell = false;
1476  }
1477  }
1478 
1479  //If the content of the Fly is transparent, we subtract it only if it's
1480  //contained in the hell layer.
1482  bool bHell = pSdrObj->GetLayer() == rIDDMA.GetHellId();
1483  if ( (bStopOnHell && bHell) ||
1487  ( !bHell && pFly->Lower() && pFly->Lower()->IsNoTextFrame() &&
1488  (static_cast<SwNoTextFrame const*>(pFly->Lower())->IsTransparent() ||
1489  static_cast<SwNoTextFrame const*>(pFly->Lower())->HasAnimation() ||
1490  pFly->GetFormat()->GetSurround().IsContour()
1491  )
1492  )
1493  )
1494  continue;
1495 
1496  // Own if-statements for transparent background/shadow of fly frames
1497  // in order to handle special conditions.
1498  if (pFly->IsBackgroundTransparent())
1499  {
1500  // Background <pFly> is transparent drawn. Thus normally, its region
1501  // have not to be subtracted from given region.
1502  // But, if method is called for a fly frame and
1503  // <pFly> is a direct lower of this fly frame and
1504  // <pFly> inherites its transparent background brush from its parent,
1505  // then <pFly> frame area have to be subtracted from given region.
1506  // NOTE: Because in Status Quo transparent backgrounds can only be
1507  // assigned to fly frames, the handle of this special case
1508  // avoids drawing of transparent areas more than once, if
1509  // a fly frame inherites a transparent background from its
1510  // parent fly frame.
1511  if (pFrame->IsFlyFrame() &&
1512  (pFly->GetAnchorFrame()->FindFlyFrame() == pFrame) &&
1514  )
1515  {
1516  SwRect aRect;
1517  SwBorderAttrAccess aAccess( SwFrame::GetCache(), static_cast<SwFrame const *>(pFly) );
1518  const SwBorderAttrs &rAttrs = *aAccess.Get();
1519  ::lcl_CalcBorderRect( aRect, pFly, rAttrs, true, rProperties );
1520  rRegion -= aRect;
1521  rClipState.subtractRange(lcl_ShrinkFly(aRect));
1522  continue;
1523  }
1524  else
1525  {
1526  continue;
1527  }
1528  }
1529 
1530  if (bHell && pFly->GetAnchorFrame()->IsInFly())
1531  {
1532  //So the border won't get dismantled by the background of the other
1533  //Fly.
1534  SwRect aRect;
1535  SwBorderAttrAccess aAccess( SwFrame::GetCache(), static_cast<SwFrame const *>(pFly) );
1536  const SwBorderAttrs &rAttrs = *aAccess.Get();
1537  ::lcl_CalcBorderRect( aRect, pFly, rAttrs, true, rProperties );
1538  rRegion -= aRect;
1539  rClipState.subtractRange(lcl_ShrinkFly(aRect));
1540  }
1541  else
1542  {
1543  SwRect aRect( pFly->getFramePrintArea() );
1544  aRect += pFly->getFrameArea().Pos();
1545  rRegion -= aRect;
1546  rClipState.subtractRange(lcl_ShrinkFly(aRect));
1547  }
1548  }
1549  if (gProp.pSRetoucheFly == gProp.pSRetoucheFly2)
1550  gProp.pSRetoucheFly = nullptr;
1551 }
1552 
1553 static void lcl_implDrawGraphicBackground(const SvxBrushItem& _rBackgrdBrush,
1554  vcl::RenderContext& _rOut,
1555  const SwRect& _rAlignedPaintRect,
1556  const GraphicObject& _rGraphicObj,
1557  SwPaintProperties const & properties)
1558 {
1563  const Color aColor( ( (_rBackgrdBrush.GetColor() != COL_TRANSPARENT) || properties.bSFlyMetafile )
1564  ? _rBackgrdBrush.GetColor()
1566 
1569  sal_Int8 nTransparencyPercent = 0;
1570  bool bDrawTransparent = false;
1571  if ( aColor.IsTransparent() )
1573  {
1574  bDrawTransparent = true;
1575  nTransparencyPercent = ((255 - aColor.GetAlpha())*100 + 0x7F)/0xFF;
1576  }
1577  else if ( (_rGraphicObj.GetAttr().IsTransparent()) &&
1578  (_rBackgrdBrush.GetColor() == COL_TRANSPARENT) )
1581  {
1582  bDrawTransparent = true;
1583  nTransparencyPercent = 100 - (_rGraphicObj.GetAttr().GetAlpha() * 100 + 127) / 255;
1584  }
1585 
1586  if ( bDrawTransparent )
1587  {
1589  if( _rOut.GetFillColor() != aColor.GetRGBColor() )
1590  _rOut.SetFillColor( aColor.GetRGBColor() );
1591  tools::PolyPolygon aPoly( _rAlignedPaintRect.SVRect() );
1592  _rOut.DrawTransparent( aPoly, nTransparencyPercent );
1593  }
1594  else
1595  {
1597  if ( _rOut.GetFillColor() != aColor )
1598  _rOut.SetFillColor( aColor );
1599  _rOut.DrawRect( _rAlignedPaintRect.SVRect() );
1600  }
1601 }
1602 
1635 static void lcl_DrawGraphicBackground( const SvxBrushItem& _rBackgrdBrush,
1636  OutputDevice& _rOut,
1637  const SwRect& _rAlignedPaintRect,
1638  const GraphicObject& _rGraphicObj,
1639  bool _bNumberingGraphic,
1640  SwPaintProperties const & properties,
1641  bool _bBackgrdAlreadyDrawn = false)
1642 {
1643  // draw background with background color, if
1644  // (1) graphic is not used as a numbering AND
1645  // (2) background is not already drawn AND
1646  // (3) intrinsic graphic is transparent OR intrinsic graphic doesn't exists
1647  if ( !_bNumberingGraphic &&
1648  !_bBackgrdAlreadyDrawn &&
1649  ( _rGraphicObj.IsTransparent() || _rGraphicObj.GetType() == GraphicType::NONE )
1650  )
1651  {
1652  lcl_implDrawGraphicBackground( _rBackgrdBrush, _rOut, _rAlignedPaintRect, _rGraphicObj, properties );
1653  }
1654 }
1655 
1671 static void lcl_DrawGraphic( const SvxBrushItem& rBrush, vcl::RenderContext &rOutDev,
1672  SwViewShell &rSh, const SwRect &rGrf, const SwRect &rOut,
1673  bool bGrfNum,
1674  SwPaintProperties const & properties,
1675  bool bBackgrdAlreadyDrawn )
1676  // add parameter <bBackgrdAlreadyDrawn> to indicate
1677  // that the background is already drawn.
1678 {
1679  // Calculate align rectangle from parameter <rGrf> and use aligned
1680  // rectangle <aAlignedGrfRect> in the following code
1681  SwRect aAlignedGrfRect = rGrf;
1682  ::SwAlignRect( aAlignedGrfRect, &rSh, &rOutDev );
1683 
1684  // Change type from <bool> to <bool>.
1685  const bool bNotInside = !rOut.IsInside( aAlignedGrfRect );
1686  if ( bNotInside )
1687  {
1688  rOutDev.Push( PushFlags::CLIPREGION );
1689  rOutDev.IntersectClipRegion( rOut.SVRect() );
1690  }
1691 
1692  GraphicObject *pGrf = const_cast<GraphicObject*>(rBrush.GetGraphicObject());
1693 
1694  // Outsource drawing of background with a background color
1695  ::lcl_DrawGraphicBackground( rBrush, rOutDev, aAlignedGrfRect, *pGrf, bGrfNum, properties, bBackgrdAlreadyDrawn );
1696 
1697  // Because for drawing a graphic left-top-corner and size coordinates are
1698  // used, these coordinates have to be determined on pixel level.
1699  ::SwAlignGrfRect( &aAlignedGrfRect, rOutDev );
1700 
1701  const basegfx::B2DHomMatrix aGraphicTransform(
1703  aAlignedGrfRect.Width(), aAlignedGrfRect.Height(),
1704  aAlignedGrfRect.Left(), aAlignedGrfRect.Top()));
1705 
1707  rOutDev,
1708  *pGrf,
1709  pGrf->GetAttr(),
1710  aGraphicTransform,
1711  OUString(),
1712  OUString(),
1713  OUString());
1714 
1715  if ( bNotInside )
1716  rOutDev.Pop();
1717 }
1718 
1721  const SwRect& rOriginalLayoutRect,
1722  const SwRegionRects& rPaintRegion,
1723  const basegfx::utils::B2DClipState& rClipState,
1724  vcl::RenderContext& rOut)
1725 {
1726  if(rFillAttributes && rFillAttributes->isUsed())
1727  {
1728  basegfx::B2DRange aPaintRange(
1729  rPaintRegion.GetOrigin().Left(),
1730  rPaintRegion.GetOrigin().Top(),
1731  rPaintRegion.GetOrigin().Right(),
1732  rPaintRegion.GetOrigin().Bottom());
1733 
1734  if (!aPaintRange.isEmpty() &&
1735  !rPaintRegion.empty() &&
1736  !basegfx::fTools::equalZero(aPaintRange.getWidth()) &&
1737  !basegfx::fTools::equalZero(aPaintRange.getHeight()))
1738  {
1739  // need to expand for correct AAed and non-AAed visualization as primitive.
1740  // This must probably be removed again when we will be able to get all Writer visualization
1741  // as primitives and Writer prepares all it's stuff in high precision coordinates (also
1742  // needs to avoid moving boundaries around to better show overlapping stuff...)
1744  {
1745  // if AAed in principle expand by 0.5 in all directions. Since painting edges of
1746  // AAed regions does not add to no transparence (0.5 opacity covered by 0.5 opacity
1747  // is not full opacity but 0.75 opacity) we need some overlap here to avoid paint
1748  // artifacts. Checked experimentally - a little bit more in Y is needed, probably
1749  // due to still existing integer alignment and crunching in writer.
1750  static const double fExpandX = 0.55;
1751  static const double fExpandY = 0.70;
1752  const basegfx::B2DVector aSingleUnit(rOut.GetInverseViewTransformation() * basegfx::B2DVector(fExpandX, fExpandY));
1753 
1754  aPaintRange.expand(aPaintRange.getMinimum() - aSingleUnit);
1755  aPaintRange.expand(aPaintRange.getMaximum() + aSingleUnit);
1756  }
1757  else
1758  {
1759  // if not AAed expand by one unit to bottom right due to the missing unit
1760  // from SwRect/Rectangle integer handling
1761  const basegfx::B2DVector aSingleUnit(rOut.GetInverseViewTransformation() * basegfx::B2DVector(1.0, 1.0));
1762 
1763  aPaintRange.expand(aPaintRange.getMaximum() + aSingleUnit);
1764  }
1765 
1766  const basegfx::B2DRange aDefineRange(
1767  rOriginalLayoutRect.Left(),
1768  rOriginalLayoutRect.Top(),
1769  rOriginalLayoutRect.Right(),
1770  rOriginalLayoutRect.Bottom());
1771 
1772  const drawinglayer::primitive2d::Primitive2DContainer& rSequence = rFillAttributes->getPrimitive2DSequence(
1773  aPaintRange,
1774  aDefineRange);
1775 
1776  if(rSequence.size())
1777  {
1779  pPrimitives(&rSequence);
1781  // tdf#86578 the awful lcl_SubtractFlys hack
1782  if (rPaintRegion.size() > 1 || rPaintRegion[0] != rPaintRegion.GetOrigin())
1783  {
1784  basegfx::B2DPolyPolygon const& maskRegion(rClipState.getClipPoly());
1785  primitives.resize(1);
1786  primitives[0] = new drawinglayer::primitive2d::MaskPrimitive2D(
1787  maskRegion, rSequence);
1788  pPrimitives = &primitives;
1789  }
1790  assert(pPrimitives && pPrimitives->size());
1791 
1792  const drawinglayer::geometry::ViewInformation2D aViewInformation2D(
1794  rOut.GetViewTransformation(),
1795  aPaintRange,
1796  nullptr,
1797  0.0,
1798  uno::Sequence< beans::PropertyValue >());
1799  std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor(drawinglayer::processor2d::createProcessor2DFromOutputDevice(
1800  rOut,
1801  aViewInformation2D) );
1802  pProcessor->process(*pPrimitives);
1803  return true;
1804  }
1805  }
1806  }
1807 
1808  return false;
1809 }
1810 
1812  const SvxBrushItem *pBrush,
1813  vcl::RenderContext &rOutDev,
1814  const SwRect &rOrg,
1815  const SwRect &rOut,
1816  const sal_uInt8 nGrfNum,
1817  const bool bConsiderBackgroundTransparency )
1818  // Add 6th parameter to indicate that method should
1819  // consider background transparency, saved in the color of the brush item
1820 {
1821  SwViewShell &rSh = *gProp.pSGlobalShell;
1822  bool bReplaceGrfNum = GRFNUM_REPLACE == nGrfNum;
1823  bool bGrfNum = GRFNUM_NO != nGrfNum;
1824  Size aGrfSize;
1826  if( pBrush && !bReplaceGrfNum )
1827  {
1828  if( rSh.GetViewOptions()->IsGraphic() )
1829  {
1830  OUString referer;
1831  SfxObjectShell * sh = rSh.GetDoc()->GetPersist();
1832  if (sh != nullptr && sh->HasName()) {
1833  referer = sh->GetMedium()->GetName();
1834  }
1835  const Graphic* pGrf = pBrush->GetGraphic(referer);
1836  if( pGrf && GraphicType::NONE != pGrf->GetType() )
1837  {
1838  ePos = pBrush->GetGraphicPos();
1839  if( pGrf->IsSupportedGraphic() )
1840  // don't the use the specific output device! Bug 94802
1841  aGrfSize = ::GetGraphicSizeTwip( *pGrf, nullptr );
1842  }
1843  }
1844  else
1845  bReplaceGrfNum = bGrfNum;
1846  }
1847 
1848  SwRect aGrf;
1849  aGrf.SSize( aGrfSize );
1850  bool bDraw = true;
1851  bool bRetouche = true;
1852  switch ( ePos )
1853  {
1854  case GPOS_LT:
1855  aGrf.Pos() = rOrg.Pos();
1856  break;
1857 
1858  case GPOS_MT:
1859  aGrf.Pos().setY( rOrg.Top() );
1860  aGrf.Pos().setX( rOrg.Left() + rOrg.Width()/2 - aGrfSize.Width()/2 );
1861  break;
1862 
1863  case GPOS_RT:
1864  aGrf.Pos().setY( rOrg.Top() );
1865  aGrf.Pos().setX( rOrg.Right() - aGrfSize.Width() );
1866  break;
1867 
1868  case GPOS_LM:
1869  aGrf.Pos().setY( rOrg.Top() + rOrg.Height()/2 - aGrfSize.Height()/2 );
1870  aGrf.Pos().setX( rOrg.Left() );
1871  break;
1872 
1873  case GPOS_MM:
1874  aGrf.Pos().setY( rOrg.Top() + rOrg.Height()/2 - aGrfSize.Height()/2 );
1875  aGrf.Pos().setX( rOrg.Left() + rOrg.Width()/2 - aGrfSize.Width()/2 );
1876  break;
1877 
1878  case GPOS_RM:
1879  aGrf.Pos().setY( rOrg.Top() + rOrg.Height()/2 - aGrfSize.Height()/2 );
1880  aGrf.Pos().setX( rOrg.Right() - aGrfSize.Width() );
1881  break;
1882 
1883  case GPOS_LB:
1884  aGrf.Pos().setY( rOrg.Bottom() - aGrfSize.Height() );
1885  aGrf.Pos().setX( rOrg.Left() );
1886  break;
1887 
1888  case GPOS_MB:
1889  aGrf.Pos().setY( rOrg.Bottom() - aGrfSize.Height() );
1890  aGrf.Pos().setX( rOrg.Left() + rOrg.Width()/2 - aGrfSize.Width()/2 );
1891  break;
1892 
1893  case GPOS_RB:
1894  aGrf.Pos().setY( rOrg.Bottom() - aGrfSize.Height() );
1895  aGrf.Pos().setX( rOrg.Right() - aGrfSize.Width() );
1896  break;
1897 
1898  case GPOS_AREA:
1899  aGrf = rOrg;
1900  // Despite the fact that the background graphic has to fill the complete
1901  // area, we already checked, whether the graphic will completely fill out
1902  // the region the <rOut> that is to be painted. Thus, nothing has to be
1903  // touched again.
1904  // E.g. this is the case for a Fly Frame without a background
1905  // brush positioned on the border of the page which inherited the background
1906  // brush from the page.
1907  bRetouche = !rOut.IsInside( aGrf );
1908  break;
1909 
1910  case GPOS_TILED:
1911  {
1912  // draw background of tiled graphic before drawing tiled graphic in loop
1913  // determine graphic object
1914  GraphicObject* pGraphicObj = const_cast< GraphicObject* >(pBrush->GetGraphicObject());
1915  // calculate aligned paint rectangle
1916  SwRect aAlignedPaintRect = rOut;
1917  ::SwAlignRect( aAlignedPaintRect, &rSh, &rOutDev );
1918  // draw background color for aligned paint rectangle
1919  lcl_DrawGraphicBackground( *pBrush, rOutDev, aAlignedPaintRect, *pGraphicObj, bGrfNum, gProp );
1920 
1921  // set left-top-corner of background graphic to left-top-corner of the
1922  // area, from which the background brush is determined.
1923  aGrf.Pos() = rOrg.Pos();
1924  // setup clipping at output device
1925  rOutDev.Push( PushFlags::CLIPREGION );
1926  rOutDev.IntersectClipRegion( rOut.SVRect() );
1927  // use new method <GraphicObject::DrawTiled(::)>
1928  {
1929  // calculate paint offset
1930  Point aPaintOffset( aAlignedPaintRect.Pos() - aGrf.Pos() );
1931  // draw background graphic tiled for aligned paint rectangle
1932  // #i42643#
1933  // For PDF export, every draw operation for bitmaps takes a
1934  // noticeable amount of place (~50 characters). Thus, optimize
1935  // between tile bitmap size and number of drawing operations here.
1936 
1937  // A_out
1938  // n_chars = k1 * ---------- + k2 * A_bitmap
1939  // A_bitmap
1940 
1941  // minimum n_chars is obtained for (derive for A_bitmap,
1942  // set to 0, take positive solution):
1943  // k1
1944  // A_bitmap = Sqrt( ---- A_out )
1945  // k2
1946 
1947  // where k1 is the number of chars per draw operation, and
1948  // k2 is the number of chars per bitmap pixel.
1949  // This is approximately 50 and 7 for current PDF writer, respectively.
1950 
1951  const double k1( 50 );
1952  const double k2( 7 );
1953  const Size aSize( aAlignedPaintRect.SSize() );
1954  const double Abitmap( k1/k2 * static_cast<double>(aSize.Width())*aSize.Height() );
1955 
1956  pGraphicObj->DrawTiled( rOutDev,
1957  aAlignedPaintRect.SVRect(),
1958  aGrf.SSize(),
1959  Size( aPaintOffset.X(), aPaintOffset.Y() ),
1960  std::max( 128, static_cast<int>( sqrt(sqrt( Abitmap)) + .5 ) ) );
1961  }
1962  // reset clipping at output device
1963  rOutDev.Pop();
1964  // set <bDraw> and <bRetouche> to false, indicating that background
1965  // graphic and background are already drawn.
1966  bDraw = bRetouche = false;
1967  }
1968  break;
1969 
1970  case GPOS_NONE:
1971  bDraw = false;
1972  break;
1973 
1974  default: OSL_ENSURE( false, "new Graphic position?" );
1975  }
1976 
1979  bool bGrfBackgrdAlreadyDrawn = false;
1980  if ( bRetouche )
1981  {
1982  rOutDev.Push( PushFlags::FILLCOLOR|PushFlags::LINECOLOR );
1983  rOutDev.SetLineColor();
1984 
1985  // check, if an existing background graphic (not filling the complete
1986  // background) is transparent drawn and the background color is
1987  // "no fill" respectively "auto fill", if background transparency
1988  // has to be considered.
1989  // If YES, memorize transparency of background graphic.
1990  // check also, if background graphic bitmap is transparent.
1991  bool bTransparentGrfWithNoFillBackgrd = false;
1992  sal_Int32 nGrfTransparency = 0;
1993  bool bGrfIsTransparent = false;
1994  if ( (ePos != GPOS_NONE) &&
1995  (ePos != GPOS_TILED) && (ePos != GPOS_AREA)
1996  )
1997  {
1998  GraphicObject *pGrf = const_cast<GraphicObject*>(pBrush->GetGraphicObject());
1999  if ( bConsiderBackgroundTransparency )
2000  {
2001  GraphicAttr aGrfAttr = pGrf->GetAttr();
2002  if ( (aGrfAttr.IsTransparent()) &&
2003  (pBrush->GetColor() == COL_TRANSPARENT)
2004  )
2005  {
2006  bTransparentGrfWithNoFillBackgrd = true;
2007  nGrfTransparency = 255 - aGrfAttr.GetAlpha();
2008  }
2009  }
2010  if ( pGrf->IsTransparent() )
2011  {
2012  bGrfIsTransparent = true;
2013  }
2014  }
2015 
2016  // to get color of brush, check background color against COL_TRANSPARENT ("no fill"/"auto fill")
2017  // instead of checking, if transparency is not set.
2018  const Color aColor( pBrush &&
2019  ( (pBrush->GetColor() != COL_TRANSPARENT) ||
2020  gProp.bSFlyMetafile )
2021  ? pBrush->GetColor()
2023 
2024  // determine, if background region have to be
2025  // drawn transparent.
2026  // background region has to be drawn transparent, if
2027  // background transparency have to be considered
2028  // AND
2029  // ( background color is transparent OR
2030  // background graphic is transparent and background color is "no fill"
2031  // )
2032 
2033  enum DrawStyle {
2034  Default,
2035  Transparent,
2036  } eDrawStyle = Default;
2037 
2038  if (bConsiderBackgroundTransparency &&
2039  ( ( aColor.IsTransparent()) ||
2040  bTransparentGrfWithNoFillBackgrd ) )
2041  {
2042  eDrawStyle = Transparent;
2043  }
2044 
2045  // #i75614# reset draw mode in high contrast mode in order to get fill color set
2046  const DrawModeFlags nOldDrawMode = rOutDev.GetDrawMode();
2047  if ( gProp.pSGlobalShell->GetWin() &&
2049  {
2050  rOutDev.SetDrawMode( DrawModeFlags::Default );
2051  }
2052 
2053  // If background region has to be drawn transparent, set only the RGB values of the background color as
2054  // the fill color for the output device.
2055  switch (eDrawStyle)
2056  {
2057  case Transparent:
2058  {
2059  if( rOutDev.GetFillColor() != aColor.GetRGBColor() )
2060  rOutDev.SetFillColor( aColor.GetRGBColor() );
2061  break;
2062  }
2063  default:
2064  {
2065  if( rOutDev.GetFillColor() != aColor )
2066  rOutDev.SetFillColor( aColor );
2067  break;
2068  }
2069  }
2070 
2071  // #i75614#
2072  // restore draw mode
2073  rOutDev.SetDrawMode( nOldDrawMode );
2074 
2075  switch (eDrawStyle)
2076  {
2077  case Transparent:
2078  {
2079  // background region have to be drawn transparent.
2080  // Thus, create a poly-polygon from the region and draw it with
2081  // the corresponding transparency percent.
2082  tools::PolyPolygon aDrawPoly( rOut.SVRect() );
2083  if ( aGrf.HasArea() )
2084  {
2085  if ( !bGrfIsTransparent )
2086  {
2087  // subtract area of background graphic from draw area
2088  // Consider only that part of the graphic area that is overlapping with draw area.
2089  SwRect aTmpGrf = aGrf;
2090  aTmpGrf.Intersection( rOut );
2091  if ( aTmpGrf.HasArea() )
2092  {
2093  tools::Polygon aGrfPoly( aTmpGrf.SVRect() );
2094  aDrawPoly.Insert( aGrfPoly );
2095  }
2096  }
2097  else
2098  bGrfBackgrdAlreadyDrawn = true;
2099  }
2100  // calculate transparency percent:
2101  // ( <transparency value[0x01..0xFF]>*100 + 0x7F ) / 0xFF
2102  // If there is a background graphic with a background color "no fill"/"auto fill",
2103  // the transparency value is taken from the background graphic,
2104  // otherwise take the transparency value from the color.
2105  sal_Int8 nTransparencyPercent = static_cast<sal_Int8>(
2106  (( bTransparentGrfWithNoFillBackgrd ? nGrfTransparency : (255 - aColor.GetAlpha())
2107  )*100 + 0x7F)/0xFF);
2108  // draw poly-polygon transparent
2109  rOutDev.DrawTransparent( aDrawPoly, nTransparencyPercent );
2110 
2111  break;
2112  }
2113  case Default:
2114  default:
2115  {
2116  SwRegionRects aRegion( rOut, 4 );
2117  if ( !bGrfIsTransparent )
2118  aRegion -= aGrf;
2119  else
2120  bGrfBackgrdAlreadyDrawn = true;
2121  // loop rectangles of background region, which has to be drawn
2122  for( size_t i = 0; i < aRegion.size(); ++i )
2123  {
2124  rOutDev.DrawRect( aRegion[i].SVRect() );
2125  }
2126  }
2127  }
2128  rOutDev.Pop();
2129  }
2130 
2131  if( bDraw && aGrf.IsOver( rOut ) )
2132  lcl_DrawGraphic( *pBrush, rOutDev, rSh, aGrf, rOut, bGrfNum, gProp,
2133  bGrfBackgrdAlreadyDrawn );
2134 
2135  if( bReplaceGrfNum )
2136  {
2137  const BitmapEx& rBmp = rSh.GetReplacementBitmap(false);
2138  vcl::Font aTmp( rOutDev.GetFont() );
2139  Graphic::DrawEx(rOutDev, OUString(), aTmp, rBmp, rOrg.Pos(), rOrg.SSize());
2140  }
2141 }
2142 
2151 static void lcl_AdjustRectToPixelSize( SwRect& io_aSwRect, const vcl::RenderContext &aOut )
2152 {
2153  // local constant object of class <Size> to determine number of Twips
2154  // representing a pixel.
2155  const Size aTwipToPxSize( aOut.PixelToLogic( Size( 1,1 )) );
2156 
2157  // local object of class <Rectangle> in Twip coordinates
2158  // calculated from given rectangle aligned to pixel centers.
2159  const tools::Rectangle aPxCenterRect = aOut.PixelToLogic(
2160  aOut.LogicToPixel( io_aSwRect.SVRect() ) );
2161 
2162  // local constant object of class <Rectangle> representing given rectangle
2163  // in pixel.
2164  const tools::Rectangle aOrgPxRect = aOut.LogicToPixel( io_aSwRect.SVRect() );
2165 
2166  // calculate adjusted rectangle from pixel centered rectangle.
2167  // Due to rounding differences <aPxCenterRect> doesn't exactly represents
2168  // the Twip-centers. Thus, adjust borders by half of pixel width/height plus 1.
2169  // Afterwards, adjust calculated Twip-positions of the all borders.
2170  tools::Rectangle aSizedRect = aPxCenterRect;
2171  aSizedRect.AdjustLeft( -(aTwipToPxSize.Width()/2 + 1) );
2172  aSizedRect.AdjustRight( aTwipToPxSize.Width()/2 + 1 );
2173  aSizedRect.AdjustTop( -(aTwipToPxSize.Height()/2 + 1) );
2174  aSizedRect.AdjustBottom(aTwipToPxSize.Height()/2 + 1);
2175 
2176  // adjust left()
2177  while ( aOut.LogicToPixel(aSizedRect).Left() < aOrgPxRect.Left() )
2178  {
2179  aSizedRect.AdjustLeft( 1 );
2180  }
2181  // adjust right()
2182  while ( aOut.LogicToPixel(aSizedRect).Right() > aOrgPxRect.Right() )
2183  {
2184  aSizedRect.AdjustRight( -1 );
2185  }
2186  // adjust top()
2187  while ( aOut.LogicToPixel(aSizedRect).Top() < aOrgPxRect.Top() )
2188  {
2189  aSizedRect.AdjustTop( 1 );
2190  }
2191  // adjust bottom()
2192  while ( aOut.LogicToPixel(aSizedRect).Bottom() > aOrgPxRect.Bottom() )
2193  {
2194  aSizedRect.AdjustBottom( -1 );
2195  }
2196 
2197  io_aSwRect = SwRect( aSizedRect );
2198 
2199 #if OSL_DEBUG_LEVEL > 0
2200  tools::Rectangle aTestOrgPxRect = aOut.LogicToPixel( io_aSwRect.SVRect() );
2201  tools::Rectangle aTestNewPxRect = aOut.LogicToPixel( aSizedRect );
2202  OSL_ENSURE( aTestOrgPxRect == aTestNewPxRect,
2203  "Error in lcl_AlignRectToPixelSize(..): Adjusted rectangle has incorrect position or size");
2204  // check Left()
2205  aSizedRect.AdjustLeft( -1 );
2206  aTestNewPxRect = aOut.LogicToPixel( aSizedRect );
2207  OSL_ENSURE( aTestOrgPxRect.Left() >= (aTestNewPxRect.Left()+1),
2208  "Error in lcl_AlignRectToPixelSize(..): Left() not correct adjusted");
2209  aSizedRect.AdjustLeft( 1 );
2210  // check Right()
2211  aSizedRect.AdjustRight( 1 );
2212  aTestNewPxRect = aOut.LogicToPixel( aSizedRect );
2213  OSL_ENSURE( aTestOrgPxRect.Right() <= (aTestNewPxRect.Right()-1),
2214  "Error in lcl_AlignRectToPixelSize(..): Right() not correct adjusted");
2215  aSizedRect.AdjustRight( -1 );
2216  // check Top()
2217  aSizedRect.AdjustTop( -1 );
2218  aTestNewPxRect = aOut.LogicToPixel( aSizedRect );
2219  OSL_ENSURE( aTestOrgPxRect.Top() >= (aTestNewPxRect.Top()+1),
2220  "Error in lcl_AlignRectToPixelSize(..): Top() not correct adjusted");
2221  aSizedRect.AdjustTop( 1 );
2222  // check Bottom()
2223  aSizedRect.AdjustBottom( 1 );
2224  aTestNewPxRect = aOut.LogicToPixel( aSizedRect );
2225  OSL_ENSURE( aTestOrgPxRect.Bottom() <= (aTestNewPxRect.Bottom()-1),
2226  "Error in lcl_AlignRectToPixelSize(..): Bottom() not correct adjusted");
2227  aSizedRect.AdjustBottom( -1 );
2228 #endif
2229 }
2230 
2231 // FUNCTIONS USED FOR COLLAPSING TABLE BORDER LINES START
2232 
2233 namespace {
2234 
2235 struct SwLineEntry
2236 {
2237  SwTwips mnKey;
2238  SwTwips mnStartPos;
2239  SwTwips mnEndPos;
2240  SwTwips mnLimitedEndPos;
2241 
2242  svx::frame::Style maAttribute;
2243 
2244  enum OverlapType { NO_OVERLAP, OVERLAP1, OVERLAP2, OVERLAP3 };
2245 
2246  enum class VerticalType { LEFT, RIGHT };
2247 
2248 public:
2249  SwLineEntry( SwTwips nKey,
2250  SwTwips nStartPos,
2251  SwTwips nEndPos,
2252  const svx::frame::Style& rAttribute );
2253 
2254  OverlapType Overlaps( const SwLineEntry& rComp ) const;
2255 
2260  void LimitVerticalEndPos(const SwFrame& rFrame, VerticalType eType);
2261 };
2262 
2263 }
2264 
2265 SwLineEntry::SwLineEntry( SwTwips nKey,
2266  SwTwips nStartPos,
2267  SwTwips nEndPos,
2268  const svx::frame::Style& rAttribute )
2269  : mnKey( nKey ),
2270  mnStartPos( nStartPos ),
2271  mnEndPos( nEndPos ),
2272  mnLimitedEndPos(0),
2273  maAttribute( rAttribute )
2274 {
2275 }
2276 
2277 /*
2278 
2279  1. ---------- rOld
2280  ---------- rNew
2281 
2282  2. ---------- rOld
2283  ------------- rNew
2284 
2285  3. ------- rOld
2286  ------------- rNew
2287 
2288  4. ------------- rOld
2289  ---------- rNew
2290 
2291  5. ---------- rOld
2292  ---- rNew
2293 
2294  6. ---------- rOld
2295  ---------- rNew
2296 
2297  7. ------------- rOld
2298  ---------- rNew
2299 
2300  8. ---------- rOld
2301  ------------- rNew
2302 
2303  9. ---------- rOld
2304  ---------- rNew
2305 */
2306 
2307 SwLineEntry::OverlapType SwLineEntry::Overlaps( const SwLineEntry& rNew ) const
2308 {
2309  SwLineEntry::OverlapType eRet = OVERLAP3;
2310 
2311  if ( mnStartPos >= rNew.mnEndPos || mnEndPos <= rNew.mnStartPos )
2312  eRet = NO_OVERLAP;
2313 
2314  // 1, 2, 3
2315  else if ( mnEndPos < rNew.mnEndPos )
2316  eRet = OVERLAP1;
2317 
2318  // 4, 5, 6, 7
2319  else if (mnStartPos <= rNew.mnStartPos)
2320  eRet = OVERLAP2;
2321 
2322  // 8, 9
2323  return eRet;
2324 }
2325 
2326 void SwLineEntry::LimitVerticalEndPos(const SwFrame& rFrame, VerticalType eType)
2327 {
2328  if (!rFrame.IsCellFrame())
2329  {
2330  return;
2331  }
2332 
2333  const auto& rCellFrame = static_cast<const SwCellFrame&>(rFrame);
2334  std::vector<const SwCellFrame*> aCoveredCells = rCellFrame.GetCoveredCells();
2335  // Iterate in reverse order, so we can stop at the first cell that has a border. This can
2336  // determine what is the minimal end position that is safe to use as a limit.
2337  for (auto it = aCoveredCells.rbegin(); it != aCoveredCells.rend(); ++it)
2338  {
2339  const SwCellFrame* pCoveredCell = *it;
2340  SwBorderAttrAccess aAccess( SwFrame::GetCache(), pCoveredCell );
2341  const SwBorderAttrs& rAttrs = *aAccess.Get();
2342  const SvxBoxItem& rBox = rAttrs.GetBox();
2343  if (eType == VerticalType::LEFT && rBox.GetLeft())
2344  {
2345  break;
2346  }
2347 
2348  if (eType == VerticalType::RIGHT && rBox.GetRight())
2349  {
2350  break;
2351  }
2352 
2353  mnLimitedEndPos = pCoveredCell->getFrameArea().Top();
2354  }
2355 }
2356 
2357 namespace {
2358 
2359 struct lt_SwLineEntry
2360 {
2361  bool operator()( const SwLineEntry& e1, const SwLineEntry& e2 ) const
2362  {
2363  return e1.mnStartPos < e2.mnStartPos;
2364  }
2365 };
2366 
2367 }
2368 
2369 typedef std::set< SwLineEntry, lt_SwLineEntry > SwLineEntrySet;
2370 typedef std::map< SwTwips, SwLineEntrySet > SwLineEntryMap;
2371 
2372 namespace {
2373 
2374 class SwTabFramePainter
2375 {
2376  SwLineEntryMap maVertLines;
2377  SwLineEntryMap maHoriLines;
2378  const SwTabFrame& mrTabFrame;
2379 
2380  void Insert( SwLineEntry&, bool bHori );
2381  void Insert(const SwFrame& rFrame, const SvxBoxItem& rBoxItem, const SwRect &rPaintArea);
2382  void HandleFrame(const SwLayoutFrame& rFrame, const SwRect& rPaintArea);
2383  void FindStylesForLine( const Point&,
2384  const Point&,
2386  bool bHori ) const;
2387 
2388 public:
2389  explicit SwTabFramePainter( const SwTabFrame& rTabFrame );
2390 
2391  void PaintLines( OutputDevice& rDev, const SwRect& rRect ) const;
2392 };
2393 
2394 }
2395 
2396 SwTabFramePainter::SwTabFramePainter( const SwTabFrame& rTabFrame )
2397  : mrTabFrame( rTabFrame )
2398 {
2399  SwRect aPaintArea = rTabFrame.GetUpper()->GetPaintArea();
2400  HandleFrame(rTabFrame, aPaintArea);
2401 }
2402 
2403 void SwTabFramePainter::HandleFrame(const SwLayoutFrame& rLayoutFrame, const SwRect& rPaintArea)
2404 {
2405  // Add border lines of cell frames. Skip covered cells. Skip cells
2406  // in special row span row, which do not have a negative row span:
2407  if ( rLayoutFrame.IsCellFrame() && !rLayoutFrame.IsCoveredCell() )
2408  {
2409  const SwCellFrame* pThisCell = static_cast<const SwCellFrame*>(&rLayoutFrame);
2410  const SwRowFrame* pRowFrame = static_cast<const SwRowFrame*>(pThisCell->GetUpper());
2411  const tools::Long nRowSpan = pThisCell->GetTabBox()->getRowSpan();
2412  if ( !pRowFrame->IsRowSpanLine() || nRowSpan > 1 || nRowSpan < -1 )
2413  {
2414  SwBorderAttrAccess aAccess( SwFrame::GetCache(), &rLayoutFrame );
2415  const SwBorderAttrs& rAttrs = *aAccess.Get();
2416  const SvxBoxItem& rBox = rAttrs.GetBox();
2417  Insert(rLayoutFrame, rBox, rPaintArea);
2418  }
2419  }
2420 
2421  // Recurse into lower layout frames, but do not recurse into lower tabframes.
2422  const SwFrame* pLower = rLayoutFrame.Lower();
2423  while ( pLower )
2424  {
2425  if (pLower->IsLayoutFrame() && !pLower->IsTabFrame())
2426  {
2427  const SwLayoutFrame* pLowerLayFrame = static_cast<const SwLayoutFrame*>(pLower);
2428  HandleFrame(*pLowerLayFrame, rPaintArea);
2429  }
2430  pLower = pLower->GetNext();
2431  }
2432 }
2433 
2434 void SwTabFramePainter::PaintLines(OutputDevice& rDev, const SwRect& rRect) const
2435 {
2436  // #i16816# tagged pdf support
2437  SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, nullptr, rDev );
2438 
2439  SwLineEntryMap::const_iterator aIter = maHoriLines.begin();
2440  bool bHori = true;
2441 
2442  // color for subsidiary lines:
2444 
2445  // high contrast mode:
2446  // overrides the color of non-subsidiary lines.
2447  const Color* pHCColor = nullptr;
2448  DrawModeFlags nOldDrawMode = rDev.GetDrawMode();
2449  if( gProp.pSGlobalShell->GetWin() &&
2451  {
2452  pHCColor = &SwViewOption::GetFontColor();
2453  rDev.SetDrawMode( DrawModeFlags::Default );
2454  }
2455 
2456  const SwFrame* pUpper = mrTabFrame.GetUpper();
2457  SwRect aUpper( pUpper->getFramePrintArea() );
2458  aUpper.Pos() += pUpper->getFrameArea().Pos();
2459  SwRect aUpperAligned( aUpper );
2460  ::SwAlignRect( aUpperAligned, gProp.pSGlobalShell, &rDev );
2461 
2462  // prepare SdrFrameBorderDataVector
2463  std::shared_ptr<drawinglayer::primitive2d::SdrFrameBorderDataVector> aData(
2464  std::make_shared<drawinglayer::primitive2d::SdrFrameBorderDataVector>());
2465 
2466  while ( true )
2467  {
2468  if ( bHori && aIter == maHoriLines.end() )
2469  {
2470  aIter = maVertLines.begin();
2471  bHori = false;
2472  }
2473 
2474  if ( !bHori && aIter == maVertLines.end() )
2475  break;
2476 
2477  const SwLineEntrySet& rEntrySet = (*aIter).second;
2478  for (const SwLineEntry& rEntry : rEntrySet)
2479  {
2480  const svx::frame::Style& rEntryStyle( rEntry.maAttribute );
2481 
2482  Point aStart, aEnd;
2483  if ( bHori )
2484  {
2485  aStart.setX( rEntry.mnStartPos );
2486  aStart.setY( rEntry.mnKey );
2487  aEnd.setX( rEntry.mnEndPos );
2488  aEnd.setY( rEntry.mnKey );
2489  }
2490  else
2491  {
2492  aStart.setX( rEntry.mnKey );
2493  aStart.setY( rEntry.mnStartPos );
2494  aEnd.setX( rEntry.mnKey );
2495  aEnd.setY( rEntry.mnEndPos );
2496  }
2497 
2498  svx::frame::Style aStyles[ 7 ];
2499  aStyles[ 0 ] = rEntryStyle;
2500  FindStylesForLine( aStart, aEnd, aStyles, bHori );
2501 
2502  if (!bHori && rEntry.mnLimitedEndPos)
2503  {
2504  aEnd.setY(rEntry.mnLimitedEndPos);
2505  }
2506 
2507  SwRect aRepaintRect( aStart, aEnd );
2508 
2509  // the repaint rectangle has to be moved a bit for the centered lines:
2510  SwTwips nRepaintRectSize = !rEntryStyle.GetWidth() ? 1 : rEntryStyle.GetWidth();
2511  if ( bHori )
2512  {
2513  aRepaintRect.Height( 2 * nRepaintRectSize );
2514  aRepaintRect.Pos().AdjustY( -nRepaintRectSize );
2515 
2516  // To decide on visibility it is also necessary to expand the RepaintRect
2517  // to left/right according existing BorderLine overlap matchings, else there
2518  // will be repaint errors when scrolling in e.t TripleLine BorderLines.
2519  // aStyles[1] == aLFromT, aStyles[3] == aLFromB, aStyles[4] == aRFromT, aStyles[6] == aRFromB
2520  if(aStyles[1].IsUsed() || aStyles[3].IsUsed() || aStyles[4].IsUsed() || aStyles[6].IsUsed())
2521  {
2522  const double fLineWidthMaxLeft(std::max(aStyles[1].GetWidth(), aStyles[3].GetWidth()));
2523  const double fLineWidthMaxRight(std::max(aStyles[4].GetWidth(), aStyles[6].GetWidth()));
2524  aRepaintRect.Width(aRepaintRect.Width() + (fLineWidthMaxLeft + fLineWidthMaxRight));
2525  aRepaintRect.Pos().AdjustX( -fLineWidthMaxLeft );
2526  }
2527  }
2528  else
2529  {
2530  aRepaintRect.Width( 2 * nRepaintRectSize );
2531  aRepaintRect.Pos().AdjustX( -nRepaintRectSize );
2532 
2533  // Accordingly to horizontal case, but for top/bottom
2534  // aStyles[3] == aTFromR, aStyles[1] == aTFromL, aStyles[6] == aBFromR, aStyles[4] == aBFromL
2535  if(aStyles[3].IsUsed() || aStyles[1].IsUsed() || aStyles[6].IsUsed() || aStyles[4].IsUsed())
2536  {
2537  const double fLineWidthMaxTop(std::max(aStyles[3].GetWidth(), aStyles[1].GetWidth()));
2538  const double fLineWidthMaxBottom(std::max(aStyles[6].GetWidth(), aStyles[4].GetWidth()));
2539  aRepaintRect.Height(aRepaintRect.Height() + (fLineWidthMaxTop + fLineWidthMaxBottom));
2540  aRepaintRect.Pos().AdjustY( -fLineWidthMaxTop );
2541  }
2542  }
2543 
2544  if (!rRect.IsOver(aRepaintRect))
2545  {
2546  continue;
2547  }
2548 
2549  // subsidiary lines
2550  const Color* pTmpColor = nullptr;
2551  if (0 == aStyles[ 0 ].GetWidth())
2552  {
2553  if (isTableBoundariesEnabled() && gProp.pSGlobalShell->GetWin())
2554  aStyles[ 0 ].Set( rCol, rCol, rCol, false, 1, 0, 0 );
2555  else
2556  aStyles[0].SetType(SvxBorderLineStyle::NONE);
2557  }
2558  else
2559  pTmpColor = pHCColor;
2560 
2561  // The (twip) positions will be adjusted to meet these requirements:
2562  // 1. The y coordinates are located in the middle of the pixel grid
2563  // 2. The x coordinated are located at the beginning of the pixel grid
2564  // This is done, because the horizontal lines are painted "at
2565  // beginning", whereas the vertical lines are painted "centered".
2566  // By making the line sizes a multiple of one pixel size, we can
2567  // assure that all lines having the same twip size have the same
2568  // pixel size, independent of their position on the screen.
2569  Point aPaintStart = rDev.PixelToLogic( rDev.LogicToPixel(aStart) );
2570  Point aPaintEnd = rDev.PixelToLogic( rDev.LogicToPixel(aEnd) );
2571 
2572  if (gProp.pSGlobalShell->GetWin())
2573  {
2574  // The table borders do not use SwAlignRect, but all the other frames do.
2575  // Therefore we tweak the outer borders a bit to achieve that the outer
2576  // borders match the subsidiary lines of the upper:
2577  if (aStart.X() == aUpper.Left())
2578  aPaintStart.setX( aUpperAligned.Left() );
2579  else if (aStart.X() == aUpper.Right_())
2580  aPaintStart.setX( aUpperAligned.Right_() );
2581  if (aStart.Y() == aUpper.Top())
2582  aPaintStart.setY( aUpperAligned.Top() );
2583  else if (aStart.Y() == aUpper.Bottom_())
2584  aPaintStart.setY( aUpperAligned.Bottom_() );
2585 
2586  if (aEnd.X() == aUpper.Left())
2587  aPaintEnd.setX( aUpperAligned.Left() );
2588  else if (aEnd.X() == aUpper.Right_())
2589  aPaintEnd.setX( aUpperAligned.Right_() );
2590  if (aEnd.Y() == aUpper.Top())
2591  aPaintEnd.setY( aUpperAligned.Top() );
2592  else if (aEnd.Y() == aUpper.Bottom_())
2593  aPaintEnd.setY( aUpperAligned.Bottom_() );
2594  }
2595 
2596  if(aStyles[0].IsUsed())
2597  {
2598  if (bHori)
2599  {
2600  const basegfx::B2DPoint aOrigin(aPaintStart.X(), aPaintStart.Y());
2601  const basegfx::B2DVector aX(basegfx::B2DPoint(aPaintEnd.X(), aPaintEnd.Y()) - aOrigin);
2602 
2603  if(!aX.equalZero())
2604  {
2606  aData->emplace_back(
2607  aOrigin,
2608  aX,
2609  aStyles[0],
2610  pTmpColor);
2612 
2613  rInstance.addSdrConnectStyleData(true, aStyles[1], -aY, true); // aLFromT
2614  rInstance.addSdrConnectStyleData(true, aStyles[2], -aX, true); // aLFromL
2615  rInstance.addSdrConnectStyleData(true, aStyles[3], aY, false); // aLFromB
2616 
2617  rInstance.addSdrConnectStyleData(false, aStyles[4], -aY, true); // aRFromT
2618  rInstance.addSdrConnectStyleData(false, aStyles[5], aX, false); // aRFromR
2619  rInstance.addSdrConnectStyleData(false, aStyles[6], aY, false); // aRFromB
2620  }
2621  }
2622  else // vertical
2623  {
2624  const basegfx::B2DPoint aOrigin(aPaintStart.X(), aPaintStart.Y());
2625  const basegfx::B2DVector aX(basegfx::B2DPoint(aPaintEnd.X(), aPaintEnd.Y()) - aOrigin);
2626 
2627  if(!aX.equalZero())
2628  {
2630  aData->emplace_back(
2631  aOrigin,
2632  aX,
2633  aStyles[0],
2634  pTmpColor);
2636 
2637  rInstance.addSdrConnectStyleData(true, aStyles[3], -aY, false); // aTFromR
2638  rInstance.addSdrConnectStyleData(true, aStyles[2], -aX, true); // aTFromT
2639  rInstance.addSdrConnectStyleData(true, aStyles[1], aY, true); // aTFromL
2640 
2641  rInstance.addSdrConnectStyleData(false, aStyles[6], -aY, false); // aBFromR
2642  rInstance.addSdrConnectStyleData(false, aStyles[5], aX, false); // aBFromB
2643  rInstance.addSdrConnectStyleData(false, aStyles[4], aY, true); // aBFromL
2644  }
2645  }
2646  }
2647  }
2648  ++aIter;
2649  }
2650 
2651  // create instance of SdrFrameBorderPrimitive2D if
2652  // SdrFrameBorderDataVector is used
2653  if(!aData->empty())
2654  {
2656  aSequence.append(
2659  aData,
2660  true))); // force visualization to minimal one discrete unit (pixel)
2661  // paint
2662  mrTabFrame.ProcessPrimitives(aSequence);
2663  }
2664 
2665  // restore output device:
2666  rDev.SetDrawMode( nOldDrawMode );
2667 }
2668 
2674 void SwTabFramePainter::FindStylesForLine( const Point& rStartPoint,
2675  const Point& rEndPoint,
2676  svx::frame::Style* pStyles,
2677  bool bHori ) const
2678 {
2679  // For example, aLFromB means: this vertical line intersects my horizontal line at its left end,
2680  // from bottom.
2681  // pStyles[ 1 ] = bHori ? aLFromT : TFromL
2682  // pStyles[ 2 ] = bHori ? aLFromL : TFromT,
2683  // pStyles[ 3 ] = bHori ? aLFromB : TFromR,
2684  // pStyles[ 4 ] = bHori ? aRFromT : BFromL,
2685  // pStyles[ 5 ] = bHori ? aRFromR : BFromB,
2686  // pStyles[ 6 ] = bHori ? aRFromB : BFromR,
2687 
2688  SwLineEntryMap::const_iterator aMapIter = maVertLines.find( rStartPoint.X() );
2689  OSL_ENSURE( aMapIter != maVertLines.end(), "FindStylesForLine: Error" );
2690  const SwLineEntrySet& rVertSet = (*aMapIter).second;
2691 
2692  for ( const SwLineEntry& rEntry : rVertSet )
2693  {
2694  if ( bHori )
2695  {
2696  if ( rStartPoint.Y() == rEntry.mnStartPos )
2697  pStyles[ 3 ] = rEntry.maAttribute;
2698  else if ( rStartPoint.Y() == rEntry.mnEndPos )
2699  pStyles[ 1 ] = rEntry.maAttribute;
2700  }
2701  else
2702  {
2703  if ( rStartPoint.Y() == rEntry.mnEndPos )
2704  pStyles[ 2 ] = rEntry.maAttribute;
2705  else if ( rEndPoint.Y() == rEntry.mnStartPos )
2706  pStyles[ 5 ] = rEntry.maAttribute;
2707  }
2708  }
2709 
2710  aMapIter = maHoriLines.find( rStartPoint.Y() );
2711  OSL_ENSURE( aMapIter != maHoriLines.end(), "FindStylesForLine: Error" );
2712  const SwLineEntrySet& rHoriSet = (*aMapIter).second;
2713 
2714  for ( const SwLineEntry& rEntry : rHoriSet )
2715  {
2716  if ( bHori )
2717  {
2718  if ( rStartPoint.X() == rEntry.mnEndPos )
2719  pStyles[ 2 ] = rEntry.maAttribute;
2720  else if ( rEndPoint.X() == rEntry.mnStartPos )
2721  pStyles[ 5 ] = rEntry.maAttribute;
2722  }
2723  else
2724  {
2725  if ( rStartPoint.X() == rEntry.mnEndPos )
2726  pStyles[ 1 ] = rEntry.maAttribute;
2727  else if ( rStartPoint.X() == rEntry.mnStartPos )
2728  pStyles[ 3 ] = rEntry.maAttribute;
2729  }
2730  }
2731 
2732  if ( bHori )
2733  {
2734  aMapIter = maVertLines.find( rEndPoint.X() );
2735  OSL_ENSURE( aMapIter != maVertLines.end(), "FindStylesForLine: Error" );
2736  const SwLineEntrySet& rVertSet2 = (*aMapIter).second;
2737 
2738  for ( const SwLineEntry& rEntry : rVertSet2 )
2739  {
2740  if ( rEndPoint.Y() == rEntry.mnStartPos )
2741  pStyles[ 6 ] = rEntry.maAttribute;
2742  else if ( rEndPoint.Y() == rEntry.mnEndPos )
2743  pStyles[ 4 ] = rEntry.maAttribute;
2744  }
2745  }
2746  else
2747  {
2748  aMapIter = maHoriLines.find( rEndPoint.Y() );
2749  OSL_ENSURE( aMapIter != maHoriLines.end(), "FindStylesForLine: Error" );
2750  const SwLineEntrySet& rHoriSet2 = (*aMapIter).second;
2751 
2752  for ( const SwLineEntry& rEntry : rHoriSet2 )
2753  {
2754  if ( rEndPoint.X() == rEntry.mnEndPos )
2755  pStyles[ 4 ] = rEntry.maAttribute;
2756  else if ( rEndPoint.X() == rEntry.mnStartPos )
2757  pStyles[ 6 ] = rEntry.maAttribute;
2758  }
2759  }
2760 }
2761 
2767  SwTabFrame const& rTabFrame, SwFrame const& rFrame, SvxBoxItem const& rBoxItem)
2768 {
2769  SwRowFrame const*const pThisRowFrame =
2770  dynamic_cast<const SwRowFrame*>(rFrame.GetUpper());
2771  return (pThisRowFrame
2772  && (pThisRowFrame->GetUpper() == &rTabFrame)
2773  && rTabFrame.IsFollow()
2774  && !rTabFrame.GetTable()->GetRowsToRepeat()
2775  && ( !pThisRowFrame->GetPrev()
2776  || static_cast<const SwRowFrame*>(pThisRowFrame->GetPrev())
2777  ->IsRowSpanLine())
2778  && !rBoxItem.GetTop()
2779  && rBoxItem.GetBottom());
2780 }
2781 
2782 void SwTabFramePainter::Insert(const SwFrame& rFrame, const SvxBoxItem& rBoxItem, const SwRect& rPaintArea)
2783 {
2784  // build 4 line entries for the 4 borders:
2785  SwRect aBorderRect = rFrame.getFrameArea();
2786 
2787  aBorderRect.Intersection(rPaintArea);
2788 
2790  mrTabFrame, rFrame, rBoxItem));
2791  bool const bVert = mrTabFrame.IsVertical();
2792  bool const bR2L = mrTabFrame.IsRightToLeft();
2793 
2794  bool bWordTableCell = false;
2795  SwViewShell* pShell = rFrame.getRootFrame()->GetCurrShell();
2796  if (pShell)
2797  {
2798  const IDocumentSettingAccess& rIDSA = pShell->GetDoc()->getIDocumentSettingAccess();
2799  bWordTableCell = rIDSA.get(DocumentSettingId::TABLE_ROW_KEEP);
2800  }
2801 
2802  // no scaling needed, it's all in the primitives and the target device
2803  svx::frame::Style aL(rBoxItem.GetLeft(), 1.0);
2804  aL.SetWordTableCell(bWordTableCell);
2805  svx::frame::Style aR(rBoxItem.GetRight(), 1.0);
2806  aR.SetWordTableCell(bWordTableCell);
2807  svx::frame::Style aT(rBoxItem.GetTop(), 1.0);
2808  aT.SetWordTableCell(bWordTableCell);
2809  svx::frame::Style aB(rBoxItem.GetBottom(), 1.0);
2810  aB.SetWordTableCell(bWordTableCell);
2811 
2812  aR.MirrorSelf();
2813  aB.MirrorSelf();
2814 
2815  const SwTwips nLeft = aBorderRect.Left_();
2816  const SwTwips nRight = aBorderRect.Right_();
2817  const SwTwips nTop = aBorderRect.Top_();
2818  const SwTwips nBottom = aBorderRect.Bottom_();
2819 
2820  aL.SetRefMode( svx::frame::RefMode::Centered );
2821  aR.SetRefMode( svx::frame::RefMode::Centered );
2822  aT.SetRefMode( !bVert ? svx::frame::RefMode::Begin : svx::frame::RefMode::End );
2823  aB.SetRefMode( !bVert ? svx::frame::RefMode::Begin : svx::frame::RefMode::End );
2824 
2825  SwLineEntry aLeft (nLeft, nTop, nBottom,
2826  bVert ? aB : (bR2L ? aR : aL));
2827  if (bWordTableCell && rBoxItem.GetLeft())
2828  {
2829  aLeft.LimitVerticalEndPos(rFrame, SwLineEntry::VerticalType::LEFT);
2830  }
2831 
2832  SwLineEntry aRight (nRight, nTop, nBottom,
2833  bVert ? (bBottomAsTop ? aB : aT) : (bR2L ? aL : aR));
2834  if (bWordTableCell && rBoxItem.GetRight())
2835  {
2836  aRight.LimitVerticalEndPos(rFrame, SwLineEntry::VerticalType::RIGHT);
2837  }
2838  SwLineEntry aTop (nTop, nLeft, nRight,
2839  bVert ? aL : (bBottomAsTop ? aB : aT));
2840  SwLineEntry aBottom(nBottom, nLeft, nRight,
2841  bVert ? aR : aB);
2842 
2843  Insert( aLeft, false );
2844  Insert( aRight, false );
2845  Insert( aTop, true );
2846  Insert( aBottom, true );
2847 }
2848 
2849 void SwTabFramePainter::Insert( SwLineEntry& rNew, bool bHori )
2850 {
2851  // get all lines from structure, that have key entry of pLE
2852  SwLineEntryMap* pLine2 = bHori ? &maHoriLines : &maVertLines;
2853  const SwTwips nKey = rNew.mnKey;
2854  SwLineEntryMap::iterator aMapIter = pLine2->find( nKey );
2855 
2856  SwLineEntrySet* pLineSet = aMapIter != pLine2->end() ? &((*aMapIter).second) : nullptr;
2857  if ( !pLineSet )
2858  {
2859  SwLineEntrySet aNewSet;
2860  (*pLine2)[ nKey ] = aNewSet;
2861  pLineSet = &(*pLine2)[ nKey ];
2862  }
2863  SwLineEntrySet::iterator aIter = pLineSet->begin();
2864 
2865  while ( aIter != pLineSet->end() && rNew.mnStartPos < rNew.mnEndPos )
2866  {
2867  const SwLineEntry& rOld = *aIter;
2868 
2869  if (rOld.mnLimitedEndPos)
2870  {
2871  // Don't merge with this line entry as it ends sooner than mnEndPos.
2872  ++aIter;
2873  continue;
2874  }
2875 
2876  const SwLineEntry::OverlapType nOverlapType = rOld.Overlaps( rNew );
2877 
2878  const svx::frame::Style& rOldAttr = rOld.maAttribute;
2879  const svx::frame::Style& rNewAttr = rNew.maAttribute;
2880  const svx::frame::Style& rCmpAttr = std::max(rNewAttr, rOldAttr);
2881 
2882  if ( SwLineEntry::OVERLAP1 == nOverlapType )
2883  {
2884  OSL_ENSURE( rNew.mnStartPos >= rOld.mnStartPos, "Overlap type 3? How this?" );
2885 
2886  // new left segment
2887  const SwLineEntry aLeft( nKey, rOld.mnStartPos, rNew.mnStartPos, rOldAttr );
2888 
2889  // new middle segment
2890  const SwLineEntry aMiddle( nKey, rNew.mnStartPos, rOld.mnEndPos, rCmpAttr );
2891 
2892  // new right segment
2893  rNew.mnStartPos = rOld.mnEndPos;
2894 
2895  // update current lines set
2896  pLineSet->erase( aIter );
2897  if ( aLeft.mnStartPos < aLeft.mnEndPos ) pLineSet->insert( aLeft );
2898  if ( aMiddle.mnStartPos < aMiddle.mnEndPos ) pLineSet->insert( aMiddle );
2899 
2900  aIter = pLineSet->begin();
2901 
2902  continue; // start over
2903  }
2904  else if ( SwLineEntry::OVERLAP2 == nOverlapType )
2905  {
2906  // new left segment
2907  const SwLineEntry aLeft( nKey, rOld.mnStartPos, rNew.mnStartPos, rOldAttr );
2908 
2909  // new middle segment
2910  const SwLineEntry aMiddle( nKey, rNew.mnStartPos, rNew.mnEndPos, rCmpAttr );
2911 
2912  // new right segment
2913  const SwLineEntry aRight( nKey, rNew.mnEndPos, rOld.mnEndPos, rOldAttr );
2914 
2915  // update current lines set
2916  pLineSet->erase( aIter );
2917  if ( aLeft.mnStartPos < aLeft.mnEndPos ) pLineSet->insert( aLeft );
2918  if ( aMiddle.mnStartPos < aMiddle.mnEndPos ) pLineSet->insert( aMiddle );
2919  if ( aRight.mnStartPos < aRight.mnEndPos ) pLineSet->insert( aRight );
2920 
2921  rNew.mnStartPos = rNew.mnEndPos; // rNew should not be inserted!
2922 
2923  break; // we are finished
2924  }
2925  else if ( SwLineEntry::OVERLAP3 == nOverlapType )
2926  {
2927  // new left segment
2928  const SwLineEntry aLeft( nKey, rNew.mnStartPos, rOld.mnStartPos, rNewAttr );
2929 
2930  // new middle segment
2931  const SwLineEntry aMiddle( nKey, rOld.mnStartPos, rNew.mnEndPos, rCmpAttr );
2932 
2933  // new right segment
2934  const SwLineEntry aRight( nKey, rNew.mnEndPos, rOld.mnEndPos, rOldAttr );
2935 
2936  // update current lines set
2937  pLineSet->erase( aIter );
2938  if ( aLeft.mnStartPos < aLeft.mnEndPos ) pLineSet->insert( aLeft );
2939  if ( aMiddle.mnStartPos < aMiddle.mnEndPos ) pLineSet->insert( aMiddle );
2940  if ( aRight.mnStartPos < aRight.mnEndPos ) pLineSet->insert( aRight );
2941 
2942  rNew.mnStartPos = rNew.mnEndPos; // rNew should not be inserted!
2943 
2944  break; // we are finished
2945  }
2946 
2947  ++aIter;
2948  }
2949 
2950  if ( rNew.mnStartPos < rNew.mnEndPos ) // insert rest
2951  pLineSet->insert( rNew );
2952 }
2953 
2958 namespace
2959 {
2960  class SwViewObjectContactRedirector : public sdr::contact::ViewObjectContactRedirector
2961  {
2962  private:
2963  const SwViewShell& mrViewShell;
2964 
2965  public:
2966  explicit SwViewObjectContactRedirector( const SwViewShell& rSh )
2967  : mrViewShell( rSh )
2968  {};
2969 
2971  const sdr::contact::ViewObjectContact& rOriginal,
2972  const sdr::contact::DisplayInfo& rDisplayInfo) override
2973  {
2974  bool bPaint( true );
2975 
2976  SdrObject* pObj = rOriginal.GetViewContact().TryToGetSdrObject();
2977  if ( pObj )
2978  {
2979  bPaint = SwFlyFrame::IsPaint( pObj, &mrViewShell );
2980  }
2981 
2982  if ( !bPaint )
2983  {
2985  }
2986 
2988  rOriginal, rDisplayInfo );
2989  }
2990  };
2991 
2992 } // end of anonymous namespace
2993 // <--
2994 
3004 void SwRootFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const& rRect, SwPrintData const*const pPrintData) const
3005 {
3006  OSL_ENSURE( Lower() && Lower()->IsPageFrame(), "Lower of root is no page." );
3007 
3008  PROTOCOL( this, PROT::FileInit, DbgAction::NONE, nullptr)
3009 
3010  bool bResetRootPaint = false;
3011  SwViewShell *pSh = mpCurrShell;
3012 
3013  if ( pSh->GetWin() )
3014  {
3015  if ( pSh->GetOut() == pSh->GetWin()->GetOutDev() && !pSh->GetWin()->IsVisible() )
3016  {
3017  return;
3018  }
3020  {
3021  SwPaintQueue::Add( pSh, rRect );
3022  return;
3023  }
3024  }
3025  else
3026  SwRootFrame::s_isInPaint = bResetRootPaint = true;
3027 
3028  std::unique_ptr<SwSavePaintStatics> pStatics;
3029  if ( gProp.pSGlobalShell )
3030  pStatics.reset(new SwSavePaintStatics());
3031  gProp.pSGlobalShell = pSh;
3032 
3033  if( !pSh->GetWin() )
3034  gProp.pSProgress = SfxProgress::GetActiveProgress( static_cast<SfxObjectShell*>(pSh->GetDoc()->GetDocShell()) );
3035 
3036  ::SwCalcPixStatics( pSh->GetOut() );
3037  aGlobalRetoucheColor = pSh->Imp()->GetRetoucheColor();
3038 
3039  // Copy rRect; for one, rRect could become dangling during the below action, and for another it
3040  // needs to be copied to aRect anyway as that is modified further down below:
3041  SwRect aRect( rRect );
3042 
3043  //Trigger an action to clear things up if needed.
3044  //Using this trick we can ensure that all values are valid in all paints -
3045  //no problems, no special case(s).
3046  // #i92745#
3047  // Extend check on certain states of the 'current' <SwViewShell> instance to
3048  // all existing <SwViewShell> instances.
3049  bool bPerformLayoutAction( true );
3050  {
3051  for(SwViewShell& rTmpViewShell : pSh->GetRingContainer())
3052  {
3053  if ( rTmpViewShell.IsInEndAction() ||
3054  rTmpViewShell.IsPaintInProgress() ||
3055  ( rTmpViewShell.Imp()->IsAction() &&
3056  rTmpViewShell.Imp()->GetLayAction().IsActionInProgress() ) )
3057  {
3058  bPerformLayoutAction = false;
3059  }
3060 
3061  if(!bPerformLayoutAction)
3062  break;
3063  }
3064  }
3065  if ( bPerformLayoutAction )
3066  {
3067  const_cast<SwRootFrame*>(this)->ResetTurbo();
3068  SwLayAction aAction( const_cast<SwRootFrame*>(this), pSh->Imp() );
3069  aAction.SetPaint( false );
3070  aAction.SetComplete( false );
3071  aAction.SetReschedule( gProp.pSProgress != nullptr );
3072  aAction.Action(&rRenderContext);
3073  ResetTurboFlag();
3074  if ( !pSh->ActionPend() )
3075  pSh->Imp()->DelRegion();
3076  }
3077 
3078  aRect.Intersection( pSh->VisArea() );
3079 
3080  const bool bExtraData = ::IsExtraData( GetFormat()->GetDoc() );
3081 
3082  gProp.pSLines.reset(new SwLineRects); // Container for borders.
3083 
3084  // #104289#. During painting, something (OLE) can
3085  // load the linguistic, which in turn can cause a reformat
3086  // of the document. Dangerous! We better set this flag to
3087  // avoid the reformat.
3088  const bool bOldAction = IsCallbackActionEnabled();
3089  const_cast<SwRootFrame*>(this)->SetCallbackActionEnabled( false );
3090 
3091  const SwPageFrame *pPage = pSh->Imp()->GetFirstVisPage(&rRenderContext);
3092 
3093  // #126222. The positions of headers and footers of the previous
3094  // pages have to be updated, else these headers and footers could
3095  // get visible at a wrong position.
3096  const SwPageFrame *pPageDeco = static_cast<const SwPageFrame*>(pPage->GetPrev());
3097  while (pPageDeco)
3098  {
3099  pPageDeco->PaintDecorators();
3100  OSL_ENSURE(!pPageDeco->GetPrev() || pPageDeco->GetPrev()->IsPageFrame(),
3101  "Neighbour of page is not a page.");
3102  pPageDeco = static_cast<const SwPageFrame*>(pPageDeco->GetPrev());
3103  }
3104 
3105  const bool bBookMode = gProp.pSGlobalShell->GetViewOptions()->IsViewLayoutBookMode();
3106  if ( bBookMode && pPage->GetPrev() && static_cast<const SwPageFrame*>(pPage->GetPrev())->IsEmptyPage() )
3107  pPage = static_cast<const SwPageFrame*>(pPage->GetPrev());
3108 
3109  // #i68597#
3110  const bool bGridPainting(pSh->GetWin() && pSh->Imp()->HasDrawView() && pSh->Imp()->GetDrawView()->IsGridVisible());
3111 
3112  // Hide all page break controls before showing them again
3113  SwWrtShell* pWrtSh = dynamic_cast< SwWrtShell* >( gProp.pSGlobalShell );
3114  if ( pWrtSh )
3115  {
3116  SwEditWin& rEditWin = pWrtSh->GetView().GetEditWin();
3117  SwFrameControlsManager& rMngr = rEditWin.GetFrameControlsManager();
3118  const SwPageFrame* pHiddenPage = pPage;
3119  while ( pHiddenPage->GetPrev() != nullptr )
3120  {
3121  pHiddenPage = static_cast< const SwPageFrame* >( pHiddenPage->GetPrev() );
3122  SwFrameControlPtr pControl = rMngr.GetControl( FrameControlType::PageBreak, pHiddenPage );
3123  if ( pControl )
3124  pControl->ShowAll( false );
3125  }
3126  }
3127 
3128  // #i76669#
3129  SwViewObjectContactRedirector aSwRedirector( *pSh );
3130 
3131  while ( pPage )
3132  {
3133  const bool bPaintRightShadow = pPage->IsRightShadowNeeded();
3134  const bool bPaintLeftShadow = pPage->IsLeftShadowNeeded();
3135  const bool bRightSidebar = pPage->SidebarPosition() == sw::sidebarwindows::SidebarPosition::RIGHT;
3136 
3137  if ( !pPage->IsEmptyPage() )
3138  {
3139  SwRect aPaintRect;
3140  SwPageFrame::GetBorderAndShadowBoundRect( pPage->getFrameArea(), pSh, &rRenderContext, aPaintRect,
3141  bPaintLeftShadow, bPaintRightShadow, bRightSidebar );
3142 
3143  if ( aRect.IsOver( aPaintRect ) )
3144  {
3145  if ( pSh->GetWin() )
3146  {
3147  gProp.pSSubsLines.reset(new SwSubsRects);
3148  gProp.pSSpecSubsLines.reset(new SwSubsRects);
3149  }
3150  gProp.pBLines.reset(new BorderLines);
3151 
3152  aPaintRect.Intersection_( aRect );
3153 
3154  if ( bExtraData &&
3155  pSh->GetWin() && pSh->IsInEndAction() )
3156  {
3157  // enlarge paint rectangle to complete page width, subtract
3158  // current paint area and invalidate the resulting region.
3159  SwRectFnSet aRectFnSet(pPage);
3160  SwRect aPageRectTemp( aPaintRect );
3161  aRectFnSet.SetLeftAndWidth( aPageRectTemp,
3162  aRectFnSet.GetLeft(pPage->getFrameArea()),
3163  aRectFnSet.GetWidth(pPage->getFrameArea()) );
3164  aPageRectTemp.Intersection_( pSh->VisArea() );
3165  vcl::Region aPageRectRegion( aPageRectTemp.SVRect() );
3166  aPageRectRegion.Exclude( aPaintRect.SVRect() );
3167  pSh->GetWin()->Invalidate( aPageRectRegion, InvalidateFlags::Children );
3168  }
3169 
3170  // #i80793#
3171  // enlarge paint rectangle for objects overlapping the same pixel
3172  // in all cases and before the DrawingLayer overlay is initialized.
3173  lcl_AdjustRectToPixelSize( aPaintRect, *(pSh->GetOut()) );
3174 
3175  // #i68597#
3176  // moved paint pre-process for DrawingLayer overlay here since the above
3177  // code dependent from bExtraData may expand the PaintRect
3178  {
3179  // #i75172# if called from SwViewShell::ImplEndAction it should no longer
3180  // really be used but handled by SwViewShell::ImplEndAction already
3181  const vcl::Region aDLRegion(aPaintRect.SVRect());
3182  pSh->DLPrePaint2(aDLRegion);
3183  }
3184 
3185  if(OUTDEV_WINDOW == gProp.pSGlobalShell->GetOut()->GetOutDevType())
3186  {
3187  // changed method SwLayVout::Enter(..)
3188  // 2nd parameter is no longer <const> and will be set to the
3189  // rectangle the virtual output device is calculated from <aPaintRect>,
3190  // if the virtual output is used.
3191  s_pVout->Enter(pSh, aPaintRect, !s_isNoVirDev);
3192 
3193  // Adjust paint rectangle to pixel size
3194  // Thus, all objects overlapping on pixel level with the unadjusted
3195  // paint rectangle will be considered in the paint.
3196  lcl_AdjustRectToPixelSize( aPaintRect, *(pSh->GetOut()) );
3197  }
3198 
3199  // maybe this can be put in the above scope. Since we are not sure, just leave it ATM
3200  s_pVout->SetOrgRect( aPaintRect );
3201 
3202  // determine background color of page for <PaintLayer> method
3203  // calls, paint <hell> or <heaven>
3204  const Color aPageBackgrdColor(pPage->GetDrawBackgroundColor());
3205 
3206  pPage->PaintBaBo( aPaintRect, pPage );
3207 
3208  if ( pSh->Imp()->HasDrawView() )
3209  {
3210  gProp.pSLines->LockLines( true );
3212  pSh->Imp()->PaintLayer( rIDDMA.GetHellId(),
3213  pPrintData,
3214  *pPage, pPage->getFrameArea(),
3215  &aPageBackgrdColor,
3216  pPage->IsRightToLeft(),
3217  &aSwRedirector );
3218  gProp.pSLines->PaintLines( pSh->GetOut(), gProp );
3219  gProp.pSLines->LockLines( false );
3220  }
3221 
3223  pPage->PaintBaBo( aPaintRect, pPage, /*bOnlyTextBackground=*/true );
3224 
3225  if( pSh->GetWin() )
3226  {
3227  // collect sub-lines
3228  pPage->RefreshSubsidiary( aPaintRect );
3229  // paint special sub-lines
3230  gProp.pSSpecSubsLines->PaintSubsidiary( pSh->GetOut(), nullptr, gProp );
3231  }
3232 
3233  pPage->PaintSwFrame( rRenderContext, aPaintRect );
3234 
3235  // no paint of page border and shadow, if writer is in place mode.
3236  if( pSh->GetWin() && pSh->GetDoc()->GetDocShell() &&
3237  !pSh->GetDoc()->GetDocShell()->IsInPlaceActive() )
3238  {
3239  SwPageFrame::PaintBorderAndShadow( pPage->getFrameArea(), pSh, bPaintLeftShadow, bPaintRightShadow, bRightSidebar );
3240  SwPageFrame::PaintNotesSidebar( pPage->getFrameArea(), pSh, pPage->GetPhyPageNum(), bRightSidebar);
3241  }
3242 
3243  gProp.pSLines->PaintLines( pSh->GetOut(), gProp );
3244  if ( pSh->GetWin() )
3245  {
3246  gProp.pSSubsLines->PaintSubsidiary( pSh->GetOut(), gProp.pSLines.get(), gProp );
3247  gProp.pSSubsLines.reset();
3248  gProp.pSSpecSubsLines.reset();
3249  }
3250  // fdo#42750: delay painting these until after subsidiary lines
3251  // fdo#45562: delay painting these until after hell layer
3252  // fdo#47717: but do it before heaven layer
3253  ProcessPrimitives(gProp.pBLines->GetBorderLines_Clear());
3254 
3255  if ( pSh->Imp()->HasDrawView() )
3256  {
3258  pPrintData,
3259  *pPage, pPage->getFrameArea(),
3260  &aPageBackgrdColor,
3261  pPage->IsRightToLeft(),
3262  &aSwRedirector );
3263  }
3264 
3265  if ( bExtraData )
3266  pPage->RefreshExtraData( aPaintRect );
3267 
3268  gProp.pBLines.reset();
3269  s_pVout->Leave();
3270 
3271  // #i68597#
3272  // needed to move grid painting inside Begin/EndDrawLayer bounds and to change
3273  // output rect for it accordingly
3274  if(bGridPainting)
3275  {
3276  SdrPaintView* pPaintView = pSh->Imp()->GetDrawView();
3277  SdrPageView* pPageView = pPaintView->GetSdrPageView();
3278  pPageView->DrawPageViewGrid(*pSh->GetOut(), aPaintRect.SVRect(), SwViewOption::GetTextGridColor() );
3279  }
3280 
3281  // #i68597#
3282  // moved paint post-process for DrawingLayer overlay here, see above
3283  {
3284  pSh->DLPostPaint2(true);
3285  }
3286  }
3287 
3288  pPage->PaintDecorators( );
3289  pPage->PaintBreak();
3290  }
3291  else if ( bBookMode && pSh->GetWin() && !pSh->GetDoc()->GetDocShell()->IsInPlaceActive() )
3292  {
3293  // paint empty page
3294  SwRect aPaintRect;
3295  SwRect aEmptyPageRect( pPage->getFrameArea() );
3296 
3297  // code from vprint.cxx
3298  const SwPageFrame& rFormatPage = pPage->GetFormatPage();
3299  aEmptyPageRect.SSize( rFormatPage.getFrameArea().SSize() );
3300 
3301  SwPageFrame::GetBorderAndShadowBoundRect( aEmptyPageRect, pSh, &rRenderContext, aPaintRect,
3302  bPaintLeftShadow, bPaintRightShadow, bRightSidebar );
3303  aPaintRect.Intersection_( aRect );
3304 
3305  if ( aRect.IsOver( aEmptyPageRect ) )
3306  {
3307  // #i75172# if called from SwViewShell::ImplEndAction it should no longer
3308  // really be used but handled by SwViewShell::ImplEndAction already
3309  {
3310  const vcl::Region aDLRegion(aPaintRect.SVRect());
3311  pSh->DLPrePaint2(aDLRegion);
3312  }
3313 
3314  if( pSh->GetOut()->GetFillColor() != aGlobalRetoucheColor )
3315  pSh->GetOut()->SetFillColor( aGlobalRetoucheColor );
3316  // No line color
3317  pSh->GetOut()->SetLineColor();
3318  // Use aligned page rectangle
3319  {
3320  SwRect aTmpPageRect( aEmptyPageRect );
3321  ::SwAlignRect( aTmpPageRect, pSh, &rRenderContext );
3322  aEmptyPageRect = aTmpPageRect;
3323  }
3324 
3325  pSh->GetOut()->DrawRect( aEmptyPageRect.SVRect() );
3326 
3327  // paint empty page text
3328  const vcl::Font& rEmptyPageFont = SwPageFrame::GetEmptyPageFont();
3329  const vcl::Font aOldFont( pSh->GetOut()->GetFont() );
3330 
3331  pSh->GetOut()->SetFont( rEmptyPageFont );
3332  pSh->GetOut()->DrawText( aEmptyPageRect.SVRect(), SwResId( STR_EMPTYPAGE ),
3333  DrawTextFlags::VCenter |
3334  DrawTextFlags::Center |
3335  DrawTextFlags::Clip );
3336 
3337  pSh->GetOut()->SetFont( aOldFont );
3338  // paint shadow and border for empty page
3339  SwPageFrame::PaintBorderAndShadow( aEmptyPageRect, pSh, bPaintLeftShadow, bPaintRightShadow, bRightSidebar );
3340  SwPageFrame::PaintNotesSidebar( aEmptyPageRect, pSh, pPage->GetPhyPageNum(), bRightSidebar);
3341 
3342  {
3343  pSh->DLPostPaint2(true);
3344  }
3345  }
3346  }
3347 
3348  OSL_ENSURE( !pPage->GetNext() || pPage->GetNext()->IsPageFrame(),
3349  "Neighbour of page is not a page." );
3350  pPage = static_cast<const SwPageFrame*>(pPage->GetNext());
3351  }
3352 
3353  gProp.pSLines.reset();
3354 
3355  if ( bResetRootPaint )
3356  SwRootFrame::s_isInPaint = false;
3357  if ( pStatics )
3358  pStatics.reset();
3359  else
3360  {
3361  gProp.pSProgress = nullptr;
3362  gProp.pSGlobalShell = nullptr;
3363  }
3364 
3365  const_cast<SwRootFrame*>(this)->SetCallbackActionEnabled( bOldAction );
3366 }
3367 
3369 {
3370  vcl::RenderContext* pRenderContext = pCont->getRootFrame()->GetCurrShell()->GetOut();
3371 
3372  //It's possible that the Cont will get destroyed.
3373  SwContentFrame *pCnt = pCont->ContainsContent();
3374  while ( pCnt && pCnt->IsInFootnote() )
3375  {
3376  pCnt->Calc(pRenderContext);
3377  pCnt = pCnt->GetNextContentFrame();
3378  }
3379 }
3380 
3381 namespace {
3382 
3383 class SwShortCut
3384 {
3385  SwRectDist m_fnCheck;
3386  tools::Long m_nLimit;
3387 
3388 public:
3389  SwShortCut( const SwFrame& rFrame, const SwRect& rRect );
3390  bool Stop(const SwRect& rRect) const { return (rRect.*m_fnCheck)(m_nLimit) > 0; }
3391 };
3392 
3393 }
3394 
3395 SwShortCut::SwShortCut( const SwFrame& rFrame, const SwRect& rRect )
3396 {
3397  bool bVert = rFrame.IsVertical();
3398  bool bR2L = rFrame.IsRightToLeft();
3399  if( rFrame.IsNeighbourFrame() && bVert == bR2L )
3400  {
3401  if( bVert )
3402  {
3403  m_fnCheck = &SwRect::GetBottomDistance;
3404  m_nLimit = rRect.Top();
3405  }
3406  else
3407  {
3408  m_fnCheck = &SwRect::GetLeftDistance;
3409  m_nLimit = rRect.Left() + rRect.Width();
3410  }
3411  }
3412  else if( bVert == rFrame.IsNeighbourFrame() )
3413  {
3414  m_fnCheck = &SwRect::GetTopDistance;
3415  m_nLimit = rRect.Top() + rRect.Height();
3416  }
3417  else
3418  {
3419  if ( rFrame.IsVertLR() )
3420  {
3421  m_fnCheck = &SwRect::GetLeftDistance;
3422  m_nLimit = rRect.Right();
3423  }
3424  else
3425  {
3426  m_fnCheck = &SwRect::GetRightDistance;
3427  m_nLimit = rRect.Left();
3428  }
3429  }
3430 }
3431 
3432 void SwLayoutFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const& rRect, SwPrintData const*const) const
3433 {
3434  // #i16816# tagged pdf support
3435  Frame_Info aFrameInfo( *this );
3436  SwTaggedPDFHelper aTaggedPDFHelper( nullptr, &aFrameInfo, nullptr, rRenderContext );
3437 
3438  const SwFrame *pFrame = Lower();
3439  if ( !pFrame )
3440  return;
3441 
3442  SwFrameDeleteGuard g(const_cast<SwLayoutFrame*>(this)); // lock because Calc() and recursion
3443  SwShortCut aShortCut( *pFrame, rRect );
3444  bool bCnt = pFrame->IsContentFrame();
3445  if ( bCnt )
3446  pFrame->Calc(&rRenderContext);
3447 
3448  if ( pFrame->IsFootnoteContFrame() )
3449  {
3450  ::lcl_EmergencyFormatFootnoteCont( const_cast<SwFootnoteContFrame*>(static_cast<const SwFootnoteContFrame*>(pFrame)) );
3451  pFrame = Lower();
3452  }
3453 
3454  const SwPageFrame *pPage = nullptr;
3455  bool bWin = gProp.pSGlobalShell->GetWin() != nullptr;
3457  // Tiled rendering is similar to printing in this case: painting transparently multiple
3458  // times will result in darker colors: avoid that.
3459  bWin = false;
3460 
3461  while ( IsAnLower( pFrame ) )
3462  {
3463  SwRect aPaintRect( pFrame->GetPaintArea() );
3464  if( aShortCut.Stop( aPaintRect ) )
3465  break;
3466  if ( bCnt && gProp.pSProgress )
3468 
3469  //We need to retouch if a frame explicitly requests it.
3470  //First do the retouch, because this could flatten the borders.
3471  if ( pFrame->IsRetouche() )
3472  {
3473  if ( pFrame->IsRetoucheFrame() && bWin && !pFrame->GetNext() )
3474  {
3475  if ( !pPage )
3476  pPage = FindPageFrame();
3477  pFrame->Retouch( pPage, rRect );
3478  }
3479  pFrame->ResetRetouche();
3480  }
3481 
3482  if ( rRect.IsOver( aPaintRect ) )
3483  {
3484  if ( bCnt && pFrame->IsCompletePaint() &&
3485  !rRect.IsInside( aPaintRect ) && Application::AnyInput( VclInputFlags::KEYBOARD ) )
3486  {
3487  //fix(8104): It may happen, that the processing wasn't complete
3488  //but some parts of the paragraph were still repainted.
3489  //This could lead to the situation, that other parts of the
3490  //paragraph won't be repainted at all. The only solution seems
3491  //to be an invalidation of the window.
3492  //To not make it too severe the rectangle is limited by
3493  //painting the desired part and only invalidating the
3494  //remaining paragraph parts.
3495  if ( aPaintRect.Left() == rRect.Left() &&
3496  aPaintRect.Right() == rRect.Right() )
3497  {
3498  aPaintRect.Bottom( rRect.Top() - 1 );
3499  if ( aPaintRect.Height() > 0 )
3500  gProp.pSGlobalShell->InvalidateWindows(aPaintRect);
3501  aPaintRect.Top( rRect.Bottom() + 1 );
3502  aPaintRect.Bottom( pFrame->getFrameArea().Bottom() );
3503  if ( aPaintRect.Height() > 0 )
3504  gProp.pSGlobalShell->InvalidateWindows(aPaintRect);
3505  aPaintRect.Top( pFrame->getFrameArea().Top() );
3506  aPaintRect.Bottom( pFrame->getFrameArea().Bottom() );
3507  }
3508  else
3509  {
3510  gProp.pSGlobalShell->InvalidateWindows( aPaintRect );
3511  pFrame = pFrame->GetNext();
3512  if ( pFrame )
3513  {
3514  bCnt = pFrame->IsContentFrame();
3515  if ( bCnt )
3516  pFrame->Calc(&rRenderContext);
3517  }
3518  continue;
3519  }
3520  }
3521  pFrame->ResetCompletePaint();
3522  aPaintRect.Intersection_( rRect );
3523 
3524  pFrame->PaintSwFrame( rRenderContext, aPaintRect );
3525 
3526  if ( Lower() && Lower()->IsColumnFrame() )
3527  {
3528  //Paint the column separator line if needed. The page is
3529  //responsible for the page frame - not the upper.
3530  const SwFrameFormat *pFormat = GetUpper() && GetUpper()->IsPageFrame()
3531  ? GetUpper()->GetFormat()
3532  : GetFormat();
3533  const SwFormatCol &rCol = pFormat->GetCol();
3534  if ( rCol.GetLineAdj() != COLADJ_NONE )
3535  {
3536  if ( !pPage )
3537  pPage = pFrame->FindPageFrame();
3538 
3539  PaintColLines( aPaintRect, rCol, pPage );
3540  }
3541  }
3542  }
3543  if ( !bCnt && pFrame->GetNext() && pFrame->GetNext()->IsFootnoteContFrame() )
3544  ::lcl_EmergencyFormatFootnoteCont( const_cast<SwFootnoteContFrame*>(static_cast<const SwFootnoteContFrame*>(pFrame->GetNext())) );
3545 
3546  pFrame = pFrame->GetNext();
3547 
3548  if ( pFrame )
3549  {
3550  bCnt = pFrame->IsContentFrame();
3551  if ( bCnt )
3552  pFrame->Calc(&rRenderContext);
3553  }
3554  }
3555 }
3556 
3558  const basegfx::B2DPoint& rStart, const basegfx::B2DPoint& rEnd,
3559  basegfx::BColor aColor )
3560 {
3562 
3563  std::vector< double > aStrokePattern;
3564  basegfx::B2DPolygon aLinePolygon;
3565  aLinePolygon.append(rStart);
3566  aLinePolygon.append(rEnd);
3567 
3569  if ( rSettings.GetHighContrastMode( ) )
3570  {
3571  // Only a solid line in high contrast mode
3572  aColor = rSettings.GetDialogTextColor().getBColor();
3573  }
3574  else
3575  {
3576  // Get a color for the contrast
3577  basegfx::BColor aHslLine = basegfx::utils::rgb2hsl( aColor );
3578  double nLuminance = aHslLine.getZ() * 2.5;
3579  if ( nLuminance == 0 )
3580  nLuminance = 0.5;
3581  else if ( nLuminance >= 1.0 )
3582  nLuminance = aHslLine.getZ() * 0.4;
3583  aHslLine.setZ( nLuminance );
3584  const basegfx::BColor aOtherColor = basegfx::utils::hsl2rgb( aHslLine );
3585 
3586  // Compute the plain line
3587  aSeq[0] =
3589  aLinePolygon, aOtherColor );
3590 
3591  // Dashed line in twips
3592  aStrokePattern.push_back( 40 );
3593  aStrokePattern.push_back( 40 );
3594 
3595  aSeq.resize( 2 );
3596  }
3597 
3598  // Compute the dashed line primitive
3599  aSeq[ aSeq.size( ) - 1 ] =
3601  basegfx::B2DPolyPolygon( aLinePolygon ),
3603  drawinglayer::attribute::StrokeAttribute( aStrokePattern ) );
3604 
3605 
3606  return aSeq;
3607 }
3608 
3610 {
3611  if ( gProp.pSGlobalShell->GetOut()->GetOutDevType() == OUTDEV_PRINTER ||
3612  gProp.pSGlobalShell->GetViewOptions()->IsPDFExport() ||
3613  gProp.pSGlobalShell->GetViewOptions()->IsReadonly() ||
3614  gProp.pSGlobalShell->IsPreview() )
3615  return;
3616 
3617  const SwFrame* pBodyFrame = Lower();
3618  while ( pBodyFrame && !pBodyFrame->IsBodyFrame() )
3619  pBodyFrame = pBodyFrame->GetNext();
3620 
3621  if ( pBodyFrame )
3622  {
3623  const SwLayoutFrame* pLayBody = static_cast< const SwLayoutFrame* >( pBodyFrame );
3624  const SwFlowFrame *pFlowFrame = pLayBody->ContainsContent();
3625 
3626  // Test if the first node is a table
3627  const SwFrame* pFirstFrame = pLayBody->Lower();
3628  if ( pFirstFrame && pFirstFrame->IsTabFrame() )
3629  pFlowFrame = static_cast< const SwTabFrame* >( pFirstFrame );
3630 
3631  SwWrtShell* pWrtSh = dynamic_cast< SwWrtShell* >( gProp.pSGlobalShell );
3632  if ( pWrtSh )
3633  {
3634  SwEditWin& rEditWin = pWrtSh->GetView().GetEditWin();
3635  SwFrameControlsManager& rMngr = rEditWin.GetFrameControlsManager();
3636 
3637  if ( pFlowFrame && pFlowFrame->IsPageBreak( true ) )
3638  rMngr.SetPageBreakControl( this );
3639  else
3641  }
3642  }
3644 }
3645 
3647 {
3648  if ( gProp.pSGlobalShell->GetOut()->GetOutDevType() == OUTDEV_PRINTER ||
3649  gProp.pSGlobalShell->GetViewOptions()->IsPDFExport() ||
3650  gProp.pSGlobalShell->GetViewOptions()->IsReadonly() ||
3651  gProp.pSGlobalShell->IsPreview() )
3652  return;
3653 
3654  const SwFrame* pBodyFrame = Lower();
3655  while ( pBodyFrame && !pBodyFrame->IsBodyFrame() )
3656  pBodyFrame = pBodyFrame->GetNext();
3657 
3658  if ( !pBodyFrame )
3659  return;
3660 
3661  const SwContentFrame *pCnt = static_cast< const SwLayoutFrame* >( pBodyFrame )->ContainsContent();
3662  if ( !(pCnt && pCnt->IsColBreak( true )) )
3663  return;
3664 
3665  // Paint the break only if:
3666  // * Not in header footer edition, to avoid conflicts with the
3667  // header/footer marker
3668  // * Non-printing characters are shown, as this is more consistent
3669  // with other formatting marks
3670  if ( !(!gProp.pSGlobalShell->IsShowHeaderFooterSeparator( FrameControlType::Header ) &&
3671  !gProp.pSGlobalShell->IsShowHeaderFooterSeparator( FrameControlType::Footer ) &&
3672  gProp.pSGlobalShell->GetViewOptions()->IsLineBreak()) )
3673  return;
3674 
3675  SwRect aRect( pCnt->getFramePrintArea() );
3676  aRect.Pos() += pCnt->getFrameArea().Pos();
3677 
3678  // Draw the line
3679  basegfx::B2DPoint aStart( double( aRect.Left() ), aRect.Top() );
3680  basegfx::B2DPoint aEnd( double( aRect.Right() ), aRect.Top() );
3681  double nWidth = aRect.Width();
3682  if ( IsVertical( ) )
3683  {
3684  aStart = basegfx::B2DPoint( double( aRect.Right() ), double( aRect.Top() ) );
3685  aEnd = basegfx::B2DPoint( double( aRect.Right() ), double( aRect.Bottom() ) );
3686  nWidth = aRect.Height();
3687  }
3688 
3690 
3692  lcl_CreateDashedIndicatorPrimitive( aStart, aEnd, aLineColor );
3693 
3694  // Add the text above
3695  OUString aBreakText = SwResId(STR_COLUMN_BREAK);
3696 
3697  basegfx::B2DVector aFontSize;
3698  OutputDevice* pOut = gProp.pSGlobalShell->GetOut();
3699  vcl::Font aFont = pOut->GetSettings().GetStyleSettings().GetToolFont();
3700  aFont.SetFontHeight( 8 * 20 );
3701  pOut->SetFont( aFont );
3703  aFontSize, aFont, IsRightToLeft(), false );
3704 
3705  tools::Rectangle aTextRect;
3706  pOut->GetTextBoundRect( aTextRect, aBreakText );
3707  tools::Long nTextOff = ( nWidth - aTextRect.GetWidth() ) / 2;
3708 
3710  aFontSize.getX(), aFontSize.getY(),
3711  aRect.Left() + nTextOff, aRect.Top() ) );
3712  if ( IsVertical() )
3713  {
3715  aFontSize.getX(), aFontSize.getY(), 0.0, M_PI_2,
3716  aRect.Right(), aRect.Top() + nTextOff );
3717  }
3718 
3719  aSeq.push_back(
3721  aTextMatrix,
3722  aBreakText, 0, aBreakText.getLength(),
3723  std::vector< double >(),
3724  aFontAttr,
3725  lang::Locale(),
3726  aLineColor ) );
3727 
3728  ProcessPrimitives( aSeq );
3729 }
3730 
3732 {
3733  const SwFrame* pFrame = Lower();
3734  while ( pFrame )
3735  {
3736  if ( pFrame->IsLayoutFrame() )
3737  static_cast< const SwLayoutFrame*>( pFrame )->PaintBreak( );
3738  pFrame = pFrame->GetNext();
3739  }
3740 }
3741 
3743 {
3744  SwWrtShell* pWrtSh = dynamic_cast< SwWrtShell* >( gProp.pSGlobalShell );
3745  if ( !pWrtSh )
3746  return;
3747 
3748  SwEditWin& rEditWin = pWrtSh->GetView().GetEditWin();
3749 
3750  const SwLayoutFrame* pBody = FindBodyCont();
3751  if ( !pBody )
3752  return;
3753 
3754  SwRect aBodyRect( pBody->getFrameArea() );
3755 
3756  if ( !(gProp.pSGlobalShell->GetOut()->GetOutDevType() != OUTDEV_PRINTER &&
3757  !gProp.pSGlobalShell->GetViewOptions()->IsPDFExport() &&
3758  !gProp.pSGlobalShell->IsPreview() &&
3759  !gProp.pSGlobalShell->GetViewOptions()->IsReadonly() &&
3760  !gProp.pSGlobalShell->GetViewOptions()->getBrowseMode() &&
3761  ( gProp.pSGlobalShell->IsShowHeaderFooterSeparator( FrameControlType::Header ) ||
3762  gProp.pSGlobalShell->IsShowHeaderFooterSeparator( FrameControlType::Footer ) )) )
3763  return;
3764 
3765  bool bRtl = AllSettings::GetLayoutRTL();
3766  const SwRect& rVisArea = gProp.pSGlobalShell->VisArea();
3767  tools::Long nXOff = std::min( aBodyRect.Right(), rVisArea.Right() );
3768  if ( bRtl )
3769  nXOff = std::max( aBodyRect.Left(), rVisArea.Left() );
3770 
3771  // Header
3772  if ( gProp.pSGlobalShell->IsShowHeaderFooterSeparator( FrameControlType::Header ) )
3773  {
3774  const SwFrame* pHeaderFrame = Lower();
3775  if ( !pHeaderFrame->IsHeaderFrame() )
3776  pHeaderFrame = nullptr;
3777 
3778  tools::Long nHeaderYOff = aBodyRect.Top();
3779  Point nOutputOff = rEditWin.LogicToPixel( Point( nXOff, nHeaderYOff ) );
3781  }
3782 
3783  // Footer
3784  if ( !gProp.pSGlobalShell->IsShowHeaderFooterSeparator( FrameControlType::Footer ) )
3785  return;
3786 
3787  const SwFrame* pFootnoteContFrame = Lower();
3788  while ( pFootnoteContFrame )
3789  {
3790  if ( pFootnoteContFrame->IsFootnoteContFrame() )
3791  aBodyRect.AddBottom( pFootnoteContFrame->getFrameArea().Bottom() - aBodyRect.Bottom() );
3792  pFootnoteContFrame = pFootnoteContFrame->GetNext();
3793  }
3794 
3795  tools::Long nFooterYOff = aBodyRect.Bottom();
3796  Point nOutputOff = rEditWin.LogicToPixel( Point( nXOff, nFooterYOff ) );
3798 }
3799 
3814 {
3815  bool bBackgroundTransparent = GetFormat()->IsBackgroundTransparent();
3816  if ( !bBackgroundTransparent &&
3817  GetFormat()->IsBackgroundBrushInherited() )
3818  {
3819  const SvxBrushItem* pBackgroundBrush = nullptr;
3820  std::optional<Color> xSectionTOXColor;
3821  SwRect aDummyRect;
3823 
3824  if ( GetBackgroundBrush( aFillAttributes, pBackgroundBrush, xSectionTOXColor, aDummyRect, false, /*bConsiderTextBox=*/false) )
3825  {
3826  if ( xSectionTOXColor &&
3827  (xSectionTOXColor->IsTransparent()) &&
3828  (xSectionTOXColor != COL_TRANSPARENT) )
3829  {
3830  bBackgroundTransparent = true;
3831  }
3832  else if(aFillAttributes && aFillAttributes->isUsed())
3833  {
3834  bBackgroundTransparent = aFillAttributes->isTransparent();
3835  }
3836  else if ( pBackgroundBrush )
3837  {
3838  if ( (pBackgroundBrush->GetColor().IsTransparent()) &&
3839  (pBackgroundBrush->GetColor() != COL_TRANSPARENT) )
3840  {
3841  bBackgroundTransparent = true;
3842  }
3843  else
3844  {
3845  const GraphicObject *pTmpGrf =
3846  pBackgroundBrush->GetGraphicObject();
3847  if ( pTmpGrf &&
3848  (pTmpGrf->GetAttr().IsTransparent())
3849  )
3850  {
3851  bBackgroundTransparent = true;
3852  }
3853  }
3854  }
3855  }
3856  }
3857 
3858  return bBackgroundTransparent;
3859 };
3860 
3861 bool SwFlyFrame::IsPaint( SdrObject *pObj, const SwViewShell *pSh )
3862 {
3863  SdrObjUserCall *pUserCall = GetUserCall(pObj);
3864 
3865  if ( nullptr == pUserCall )
3866  return true;
3867 
3868  //Attribute dependent, don't paint for printer or Preview
3869  bool bPaint = gProp.pSFlyOnlyDraw ||
3870  static_cast<SwContact*>(pUserCall)->GetFormat()->GetPrint().GetValue();
3871  if ( !bPaint )
3872  bPaint = pSh->GetWin() && !pSh->IsPreview();
3873 
3874  if ( bPaint )
3875  {
3876  //The paint may be prevented by the superior Flys.
3877  SwFrame *pAnch = nullptr;
3878  if ( dynamic_cast< const SwFlyDrawObj *>( pObj ) != nullptr ) // i#117962#
3879  {
3880  bPaint = false;
3881  }
3882  if ( auto pFlyDraw = dynamic_cast<SwVirtFlyDrawObj *>( pObj ) )
3883  {
3884  SwFlyFrame *pFly = pFlyDraw->GetFlyFrame();
3885  if ( gProp.pSFlyOnlyDraw && gProp.pSFlyOnlyDraw == pFly )
3886  return true;
3887 
3888  //Try to avoid displaying the intermediate stage, Flys which don't
3889  //overlap with the page on which they are anchored won't be
3890  //painted.
3891  //HACK: exception: printing of frames in tables, those can overlap
3892  //a page once in a while when dealing with oversized tables (HTML).
3893  SwPageFrame *pPage = pFly->FindPageFrame();
3894  if ( pPage && pPage->getFrameArea().IsOver( pFly->getFrameArea() ) )
3895  {
3896  pAnch = pFly->AnchorFrame();
3897  }
3898 
3899  }
3900  else
3901  {
3902  // Consider 'virtual' drawing objects
3903  SwDrawContact* pDrawContact = dynamic_cast<SwDrawContact*>(pUserCall);
3904  pAnch = pDrawContact ? pDrawContact->GetAnchorFrame(pObj) : nullptr;
3905  if ( pAnch )
3906  {
3907  if ( !pAnch->isFrameAreaPositionValid() )
3908  pAnch = nullptr;
3909  else if ( pSh->GetOut() == pSh->getIDocumentDeviceAccess().getPrinter( false ))
3910  {
3911  //HACK: we have to omit some of the objects for printing,
3912  //otherwise they would be printed twice.
3913  //The objects should get printed if the TableHack is active
3914  //right now. Afterwards they must not be printed if the
3915  //page over which they float position wise gets printed.
3916  const SwPageFrame *pPage = pAnch->FindPageFrame();
3917  if ( !pPage->getFrameArea().IsOver( SwRect(pObj->GetCurrentBoundRect()) ) )
3918  pAnch = nullptr;
3919  }
3920  }
3921  else
3922  {
3923  if ( dynamic_cast< const SdrObjGroup *>( pObj ) == nullptr )
3924  {
3925  OSL_FAIL( "<SwFlyFrame::IsPaint(..)> - paint of drawing object without anchor frame!?" );
3926  }
3927  }
3928  }
3929  if ( pAnch )
3930  {
3931  if ( pAnch->IsInFly() )
3932  bPaint = SwFlyFrame::IsPaint( pAnch->FindFlyFrame()->GetVirtDrawObj(),
3933  pSh );
3934  else if ( gProp.pSFlyOnlyDraw )
3935  bPaint = false;
3936  }
3937  else
3938  bPaint = false;
3939  }
3940  return bPaint;
3941 }
3942 
3943 void SwCellFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const& rRect, SwPrintData const*const) const
3944 {
3945  if ( GetLayoutRowSpan() >= 1 )
3946  SwLayoutFrame::PaintSwFrame( rRenderContext, rRect );
3947 }
3948 
3949 namespace {
3950 
3951 struct BorderLinesGuard
3952 {
3953  explicit BorderLinesGuard() : m_pBorderLines(std::move(gProp.pBLines))
3954  {
3955  gProp.pBLines.reset(new BorderLines);
3956  }
3957  ~BorderLinesGuard()
3958  {
3959  gProp.pBLines = std::move(m_pBorderLines);
3960  }
3961 private:
3962  std::unique_ptr<BorderLines> m_pBorderLines;
3963 };
3964 
3965 }
3966 
3967 // set strikethrough for deleted objects anchored to character
3968 void SwFrame::SetDrawObjsAsDeleted( bool bDeleted )
3969 {
3970  if ( SwSortedObjs *pObjs = GetDrawObjs() )
3971  {
3972  for (SwAnchoredObject* pAnchoredObj : *pObjs)
3973  {
3974  if ( auto pFly = dynamic_cast<SwFlyFrame *>( pAnchoredObj ) )
3975  {
3976  pFly->SetDeleted(bDeleted);
3977  }
3978  }
3979  }
3980 }
3981 
3982 void SwFlyFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const& rRect, SwPrintData const*const) const
3983 {
3984  //optimize thumbnail generation and store procedure to improve odt saving performance, #i120030#
3985  SwViewShell *pShell = getRootFrame()->GetCurrShell();
3986  if (pShell && pShell->GetDoc() && pShell->GetDoc()->GetDocShell())
3987  {
3988  bool bInGenerateThumbnail = pShell->GetDoc()->GetDocShell()->IsInGenerateAndStoreThumbnail();
3989  if (bInGenerateThumbnail)
3990  {
3991  const SwRect& aVisRect = pShell->VisArea();
3992  if (!aVisRect.IsOver(getFrameArea()))
3993  return;
3994  }
3995  }
3996 
3997  //because of the overlapping of frames and drawing objects the flys have to
3998  //paint their borders (and those of the internal ones) directly.
3999  //e.g. #33066#
4000  gProp.pSLines->LockLines(true);
4001  BorderLinesGuard blg; // this should not paint borders added from PaintBaBo
4002 
4003  SwRect aRect( rRect );
4004  aRect.Intersection_( getFrameArea() );
4005 
4006  rRenderContext.Push( PushFlags::CLIPREGION );
4007  rRenderContext.SetClipRegion();
4008  const SwPageFrame* pPage = FindPageFrame();
4009 
4010  const SwNoTextFrame *pNoText = Lower() && Lower()->IsNoTextFrame()
4011  ? static_cast<const SwNoTextFrame*>(Lower()) : nullptr;
4012 
4013  bool bIsChart = false; //#i102950# don't paint additional borders for charts
4014  //check whether we have a chart
4015  if(pNoText)
4016  {
4017  const SwNoTextNode* pNoTNd = dynamic_cast<const SwNoTextNode*>(pNoText->GetNode());
4018  if( pNoTNd )
4019  {
4020  SwOLENode* pOLENd = const_cast<SwOLENode*>(pNoTNd->GetOLENode());
4021  if( pOLENd && pOLENd->GetOLEObj().GetObject().IsChart() )
4022  bIsChart = true;
4023  }
4024  }
4025 
4026  {
4027  bool bContour = GetFormat()->GetSurround().IsContour();
4028  tools::PolyPolygon aPoly;
4029  if ( bContour )
4030  {
4031  // add 2nd parameter with value <true>
4032  // to indicate that method is called for paint in order to avoid
4033  // load of the intrinsic graphic.
4034  bContour = GetContour( aPoly, true );
4035  }
4036 
4037  // #i47804# - distinguish complete background paint
4038  // and margin paint.
4039  // paint complete background for Writer text fly frames
4040  bool bPaintCompleteBack( !pNoText );
4041  // paint complete background for transparent graphic and contour,
4042  // if own background color exists.
4043  const bool bIsGraphicTransparent = pNoText && pNoText->IsTransparent();
4044  if ( !bPaintCompleteBack &&
4045  ( bIsGraphicTransparent|| bContour ) )
4046  {
4047  const SwFlyFrameFormat* pSwFrameFormat = GetFormat();
4048 
4049  if (pSwFrameFormat && pSwFrameFormat->supportsFullDrawingLayerFillAttributeSet())
4050  {
4051  // check for transparency
4053 
4054  // check if the new fill attributes are used
4055  if(aFillAttributes && aFillAttributes->isUsed())
4056  {
4057  bPaintCompleteBack = true;
4058  }
4059  }
4060  else
4061  {
4062  std::unique_ptr<SvxBrushItem> aBack = GetFormat()->makeBackgroundBrushItem();
4063  // to determine, if background has to be painted, by checking, if
4064  // background color is not COL_TRANSPARENT ("no fill"/"auto fill")
4065  // or a background graphic exists.
4066  bPaintCompleteBack = aBack &&
4067  ((aBack->GetColor() != COL_TRANSPARENT) ||
4068  aBack->GetGraphicPos() != GPOS_NONE);
4069  }
4070  }
4071  // paint of margin needed.
4072  const bool bPaintMarginOnly( !bPaintCompleteBack &&
4074 
4075  // #i47804# - paint background of parent fly frame
4076  // for transparent graphics in layer Hell, if parent fly frame isn't
4077  // in layer Hell. It's only painted the intersection between the
4078  // parent fly frame area and the paint area <aRect>
4080 
4081  if (bIsGraphicTransparent &&
4082  GetFormat()->GetDoc()->getIDocumentSettingAccess().get(DocumentSettingId::SUBTRACT_FLYS) &&
4083  GetVirtDrawObj()->GetLayer() == rIDDMA.GetHellId() &&
4085  {
4086  const SwFlyFrame* pParentFlyFrame = GetAnchorFrame()->FindFlyFrame();
4087  if ( pParentFlyFrame->GetDrawObj()->GetLayer() !=
4088  rIDDMA.GetHellId() )
4089  {
4090  SwFlyFrame* pOldRet = gProp.pSRetoucheFly2;
4091  gProp.pSRetoucheFly2 = const_cast<SwFlyFrame*>(this);
4092 
4093  SwBorderAttrAccess aAccess( SwFrame::GetCache(), pParentFlyFrame );
4094  const SwBorderAttrs &rAttrs = *aAccess.Get();
4095  SwRect aPaintRect( aRect );
4096  aPaintRect.Intersection_( pParentFlyFrame->getFrameArea() );
4097  pParentFlyFrame->PaintSwFrameBackground( aPaintRect, pPage, rAttrs );
4098 
4099  gProp.pSRetoucheFly2 = pOldRet;
4100  }
4101  }
4102 
4103  if ( bPaintCompleteBack || bPaintMarginOnly )
4104  {
4105  //#24926# JP 01.02.96, PaintBaBo is here partially so PaintSwFrameShadowAndBorder
4106  //receives the original Rect but PaintSwFrameBackground only the limited
4107  //one.
4108 
4109  rRenderContext.Push( PushFlags::FILLCOLOR|PushFlags::LINECOLOR );
4110  rRenderContext.SetLineColor();
4111 
4112  pPage = FindPageFrame();
4113 
4114  SwBorderAttrAccess aAccess( SwFrame::GetCache(), static_cast<SwFrame const *>(this) );
4115  const SwBorderAttrs &rAttrs = *aAccess.Get();
4116 
4117  // paint background
4118  {
4119  SwRegionRects aRegion( aRect );
4120  // #i80822#
4121  // suppress painting of background in printing area for
4122  // non-transparent graphics.
4123  if ( bPaintMarginOnly ||
4124  ( pNoText && !bIsGraphicTransparent ) )
4125  {
4126  //What we actually want to paint is the small stripe between
4127  //PrtArea and outer border.
4128  SwRect aTmp( getFramePrintArea() ); aTmp += getFrameArea().Pos();
4129  aRegion -= aTmp;
4130  }
4131  if ( bContour )
4132  {
4133  rRenderContext.Push();
4134  // #i80822#
4135  // apply clip region under the same conditions, which are
4136  // used in <SwNoTextFrame::PaintSwFrame(..)> to set the clip region
4137  // for painting the graphic/OLE. Thus, the clip region is
4138  // also applied for the PDF export.
4140 
4141  if ( !rRenderContext.GetConnectMetaFile() || !pSh || !pSh->GetWin() )
4142  {
4143  rRenderContext.SetClipRegion(vcl::Region(aPoly));
4144  }
4145 
4146  for ( size_t i = 0; i < aRegion.size(); ++i )
4147  {
4148  PaintSwFrameBackground( aRegion[i], pPage, rAttrs, false, true );
4149  }
4150 
4151  rRenderContext.Pop();
4152  }
4153  else
4154  {
4155  for ( size_t i = 0; i < aRegion.size(); ++i )
4156  {
4157  PaintSwFrameBackground( aRegion[i], pPage, rAttrs, false, true );
4158  }
4159  }
4160  }
4161 
4162  // paint border before painting background
4163  PaintSwFrameShadowAndBorder(rRect, pPage, rAttrs);
4164 
4165  rRenderContext.Pop();
4166  }
4167  }
4168 
4169  // fly frame will paint it's subsidiary lines and
4170  // the subsidiary lines of its lowers on its own, due to overlapping with
4171  // other fly frames or other objects.
4172  if( gProp.pSGlobalShell->GetWin()
4173  && !bIsChart ) //#i102950# don't paint additional borders for charts
4174  {
4175  bool bSubsLineRectsCreated;
4176  if ( gProp.pSSubsLines )
4177  {
4178  // Lock already existing subsidiary lines
4179  gProp.pSSubsLines->LockLines( true );
4180  bSubsLineRectsCreated = false;
4181  }
4182  else
4183  {
4184  // create new subsidiary lines
4185  gProp.pSSubsLines.reset(new SwSubsRects);
4186  bSubsLineRectsCreated = true;
4187  }
4188 
4189  bool bSpecSubsLineRectsCreated;
4190  if ( gProp.pSSpecSubsLines )
4191  {
4192  // Lock already existing special subsidiary lines
4193  gProp.pSSpecSubsLines->LockLines( true );
4194  bSpecSubsLineRectsCreated = false;
4195  }
4196  else
4197  {
4198  // create new special subsidiary lines
4199  gProp.pSSpecSubsLines.reset(new SwSubsRects);
4200  bSpecSubsLineRectsCreated = true;
4201  }
4202  // Add subsidiary lines of fly frame and its lowers
4203  RefreshLaySubsidiary( pPage, aRect );
4204  // paint subsidiary lines of fly frame and its lowers
4205  gProp.pSSpecSubsLines->PaintSubsidiary( &rRenderContext, nullptr, gProp );
4206  gProp.pSSubsLines->PaintSubsidiary(&rRenderContext, gProp.pSLines.get(), gProp);
4207  if ( !bSubsLineRectsCreated )
4208  // unlock subsidiary lines
4209  gProp.pSSubsLines->LockLines( false );
4210  else
4211  {
4212  // delete created subsidiary lines container
4213  gProp.pSSubsLines.reset();
4214  }
4215 
4216  if ( !bSpecSubsLineRectsCreated )
4217  // unlock special subsidiary lines
4218  gProp.pSSpecSubsLines->LockLines( false );
4219  else
4220  {
4221  // delete created special subsidiary lines container
4222  gProp.pSSpecSubsLines.reset();
4223  }
4224  }
4225 
4226  SwLayoutFrame::PaintSwFrame( rRenderContext, aRect );
4227 
4228  Validate();
4229 
4230  // first paint lines added by fly frame paint
4231  // and then unlock other lines.
4232  gProp.pSLines->PaintLines( &rRenderContext, gProp );
4233  gProp.pSLines->LockLines( false );
4234  // have to paint frame borders added in heaven layer here...
4235  ProcessPrimitives(gProp.pBLines->GetBorderLines_Clear());
4236 
4237  PaintDecorators();
4238 
4239  // crossing out for tracked deletion
4240  if ( GetAuthor() != std::string::npos && IsDeleted() )
4241  {
4242  tools::Long startX = aRect.Left( ), endX = aRect.Right();
4243  tools::Long startY = aRect.Top( ), endY = aRect.Bottom();
4244  rRenderContext.SetLineColor( SwPostItMgr::GetColorAnchor(GetAuthor()) );
4245  rRenderContext.DrawLine(Point(startX, startY), Point(endX, endY));
4246  rRenderContext.DrawLine(Point(startX, endY), Point(endX, startY));
4247  }
4248 
4249  rRenderContext.Pop();
4250 
4251  if ( gProp.pSProgress && pNoText )
4253 }
4254 
4256 {
4257  // Show the un-float button
4258  SwWrtShell* pWrtSh = dynamic_cast< SwWrtShell* >( gProp.pSGlobalShell );
4259  if ( pWrtSh )
4260  {
4261  UpdateUnfloatButton(pWrtSh, IsShowUnfloatButton(pWrtSh));
4262  }
4263 }
4264 
4266 {
4267  SwWrtShell* pWrtSh = dynamic_cast<SwWrtShell*>(gProp.pSGlobalShell);
4268  if (pWrtSh && pWrtSh->GetViewOptions()->IsShowOutlineContentVisibilityButton())
4270 }
4271 
4272 
4273 void SwTabFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const& rRect, SwPrintData const*const) const
4274 {
4275  const SwViewOption* pViewOption = gProp.pSGlobalShell->GetViewOptions();
4276  if (pViewOption->IsTable())
4277  {
4278  // #i29550#
4279  if ( IsCollapsingBorders() )
4280  {
4281  SwBorderAttrAccess aAccess( SwFrame::GetCache(), static_cast<SwFrame const *>(this) );
4282  const SwBorderAttrs &rAttrs = *aAccess.Get();
4283 
4284  // paint shadow
4285  if ( rAttrs.GetShadow().GetLocation() != SvxShadowLocation::NONE )
4286  {
4287  SwRect aRect;
4288  ::lcl_CalcBorderRect( aRect, this, rAttrs, true, gProp );
4289  PaintShadow( rRect, aRect, rAttrs );
4290  }
4291 
4292  SwTabFramePainter aHelper(*this);
4293  aHelper.PaintLines(rRenderContext, rRect);
4294  }
4295 
4296  SwLayoutFrame::PaintSwFrame( rRenderContext, rRect );
4297  }
4298  // #i6467# - no light grey rectangle for page preview
4299  else if ( gProp.pSGlobalShell->GetWin() && !gProp.pSGlobalShell->IsPreview() )
4300  {
4301  // #i6467# - intersect output rectangle with table frame
4302  SwRect aTabRect( getFramePrintArea() );
4303  aTabRect.Pos() += getFrameArea().Pos();
4304  SwRect aTabOutRect( rRect );
4305  aTabOutRect.Intersection( aTabRect );
4306  SwViewOption::DrawRect( &rRenderContext, aTabOutRect, COL_LIGHTGRAY );
4307  }
4308  const_cast<SwTabFrame*>(this)->ResetComplete();
4309 }
4310 
4324 static void lcl_PaintShadow( const SwRect& rRect, SwRect& rOutRect,
4325  const SvxShadowItem& rShadow, const bool bDrawFullShadowRectangle,
4326  const bool bTop, const bool bBottom,
4327  const bool bLeft, const bool bRight,
4328  SwPaintProperties const & properties)
4329 {
4330  const tools::Long nWidth = ::lcl_AlignWidth ( rShadow.GetWidth(), properties );
4331  const tools::Long nHeight = ::lcl_AlignHeight( rShadow.GetWidth(), properties );
4332 
4333  SwRects aRegion;
4334  SwRect aOut( rOutRect );
4335 
4336  switch ( rShadow.GetLocation() )
4337  {
4338  case SvxShadowLocation::BottomRight:
4339  {
4340  if ( bDrawFullShadowRectangle )
4341  {
4342  // draw full shadow rectangle
4343  aOut.Top( rOutRect.Top() + nHeight );
4344  aOut.Left( rOutRect.Left() + nWidth );
4345  aRegion.push_back( aOut );
4346  }
4347  else
4348  {
4349  if( bBottom )
4350  {
4351  aOut.Top( rOutRect.Bottom() - nHeight );
4352  if( bLeft )
4353  aOut.Left( rOutRect.Left() + nWidth );
4354  aRegion.push_back( aOut );
4355  }
4356  if( bRight )
4357  {
4358  aOut.Left( rOutRect.Right() - nWidth );
4359  if( bTop )
4360  aOut.Top( rOutRect.Top() + nHeight );
4361  else
4362  aOut.Top( rOutRect.Top() );
4363  if( bBottom )
4364  aOut.Bottom( rOutRect.Bottom() - nHeight );
4365  aRegion.push_back( aOut );
4366  }
4367  }
4368 
4369  if( bRight )
4370  rOutRect.AddRight(- nWidth );
4371  if( bBottom )
4372  rOutRect.AddBottom(- nHeight );
4373  }
4374  break;
4375  case SvxShadowLocation::TopLeft:
4376  {
4377  if ( bDrawFullShadowRectangle )
4378  {
4379  // draw full shadow rectangle
4380  aOut.Bottom( rOutRect.Bottom() - nHeight );
4381  aOut.Right( rOutRect.Right() - nWidth );
4382  aRegion.push_back( aOut );
4383  }
4384  else
4385  {
4386  if( bTop )
4387  {
4388  aOut.Bottom( rOutRect.Top() + nHeight );
4389  if( bRight )
4390  aOut.Right( rOutRect.Right() - nWidth );
4391  aRegion.push_back( aOut );
4392  }
4393  if( bLeft )
4394  {
4395  aOut.Right( rOutRect.Left() + nWidth );
4396  if( bBottom )
4397  aOut.Bottom( rOutRect.Bottom() - nHeight );
4398  else
4399  aOut.Bottom( rOutRect.Bottom() );
4400  if( bTop )
4401  aOut.Top( rOutRect.Top() + nHeight );
4402  aRegion.push_back( aOut );
4403  }
4404  }
4405 
4406  if( bLeft )
4407  rOutRect.AddLeft( nWidth );
4408  if( bTop )
4409  rOutRect.AddTop( nHeight );
4410  }
4411  break;
4412  case SvxShadowLocation::TopRight:
4413  {
4414  if ( bDrawFullShadowRectangle )
4415  {
4416  // draw full shadow rectangle
4417  aOut.Bottom( rOutRect.Bottom() - nHeight);
4418  aOut.Left( rOutRect.Left() + nWidth );
4419  aRegion.push_back( aOut );
4420  }
4421  else
4422  {
4423  if( bTop )
4424  {
4425  aOut.Bottom( rOutRect.Top() + nHeight );
4426  if( bLeft )
4427  aOut.Left( rOutRect.Left() + nWidth );
4428  aRegion.push_back( aOut );
4429  }
4430  if( bRight )
4431  {
4432  aOut.Left( rOutRect.Right() - nWidth );
4433  if( bBottom )
4434  aOut.Bottom( rOutRect.Bottom() - nHeight );
4435  else
4436  aOut.Bottom( rOutRect.Bottom() );
4437  if( bTop )
4438  aOut.Top( rOutRect.Top() + nHeight );
4439  aRegion.push_back( aOut );
4440  }
4441  }
4442 
4443  if( bRight )
4444  rOutRect.AddRight( - nWidth );
4445  if( bTop )
4446  rOutRect.AddTop( nHeight );
4447  }
4448  break;
4449  case SvxShadowLocation::BottomLeft:
4450  {
4451  if ( bDrawFullShadowRectangle )
4452  {
4453  // draw full shadow rectangle
4454  aOut.Top( rOutRect.Top() + nHeight );
4455  aOut.Right( rOutRect.Right() - nWidth );
4456  aRegion.push_back( aOut );
4457  }
4458  else
4459  {
4460  if( bBottom )
4461  {
4462  aOut.Top( rOutRect.Bottom()- nHeight );
4463  if( bRight )
4464  aOut.Right( rOutRect.Right() - nWidth );
4465  aRegion.push_back( aOut );
4466  }
4467  if( bLeft )
4468  {
4469  aOut.Right( rOutRect.Left() + nWidth );
4470  if( bTop )
4471  aOut.Top( rOutRect.Top() + nHeight );
4472  else
4473  aOut.Top( rOutRect.Top() );
4474  if( bBottom )
4475  aOut.Bottom( rOutRect.Bottom() - nHeight );
4476  aRegion.push_back( aOut );
4477  }
4478  }
4479 
4480  if( bLeft )
4481  rOutRect.AddLeft( nWidth );
4482  if( bBottom )
4483  rOutRect.AddBottom( - nHeight );
4484  }
4485  break;
4486  default:
4487  assert(false);
4488  break;
4489  }
4490 
4491  vcl::RenderContext *pOut = properties.pSGlobalShell->GetOut();
4492 
4493  DrawModeFlags nOldDrawMode = pOut->GetDrawMode();
4494  Color aShadowColor( rShadow.GetColor().GetRGBColor() );
4495  if( !aRegion.empty() && properties.pSGlobalShell->GetWin() &&
4497  {
4498  // In high contrast mode, the output device has already set the
4499  // DrawModeFlags::SettingsFill flag. This causes the SetFillColor function
4500  // to ignore the setting of a new color. Therefore we have to reset
4501  // the drawing mode
4502  pOut->SetDrawMode( DrawModeFlags::Default );
4503  aShadowColor = SwViewOption::GetFontColor();
4504  }
4505 
4506  if ( pOut->GetFillColor() != aShadowColor )
4507  pOut->SetFillColor( aShadowColor );
4508 
4509  pOut->SetLineColor();
4510 
4511  pOut->SetDrawMode( nOldDrawMode );
4512 
4513  for (const SwRect & rOut : aRegion)
4514  {
4515  aOut = rOut;
4516  if ( rRect.IsOver( aOut ) && aOut.Height() > 0 && aOut.Width() > 0 )
4517  {
4518  aOut.Intersection_( rRect );
4519  pOut->DrawRect( aOut.SVRect() );
4520  }
4521  }
4522 }
4523 
4533 void SwFrame::PaintShadow( const SwRect& rRect, SwRect& rOutRect,
4534  const SwBorderAttrs &rAttrs ) const
4535 {
4536  SvxShadowItem rShadow = rAttrs.GetShadow();
4537 
4538  const bool bCnt = IsContentFrame();
4539  const bool bTop = !bCnt || rAttrs.GetTopLine ( *(this) );
4540  const bool bBottom = !bCnt || rAttrs.GetBottomLine( *(this) );
4541 
4542  if( IsVertical() )
4543  {
4544  switch( rShadow.GetLocation() )
4545  {
4546  case SvxShadowLocation::BottomRight: rShadow.SetLocation(SvxShadowLocation::BottomLeft); break;
4547  case SvxShadowLocation::TopLeft: rShadow.SetLocation(SvxShadowLocation::TopRight); break;
4548  case SvxShadowLocation::TopRight: rShadow.SetLocation(SvxShadowLocation::BottomRight); break;
4549  case SvxShadowLocation::BottomLeft: rShadow.SetLocation(SvxShadowLocation::TopLeft); break;
4550  default: break;
4551  }
4552  }
4553 
4554  // determine, if full shadow rectangle have to be drawn or only two shadow rectangles beside the frame.
4555  // draw full shadow rectangle, if frame background is drawn transparent.
4556  // Status Quo:
4557  // SwLayoutFrame can have transparent drawn backgrounds. Thus,
4558  // "asked" their frame format.
4559  const bool bDrawFullShadowRectangle =
4560  ( IsLayoutFrame() &&
4561  static_cast<const SwLayoutFrame*>(this)->GetFormat()->IsBackgroundTransparent()
4562  );
4563 
4564  SwRectFnSet aRectFnSet(this);
4565  ::lcl_ExtendLeftAndRight( rOutRect, *(this), rAttrs, aRectFnSet.FnRect() );
4566 
4567  lcl_PaintShadow(rRect, rOutRect, rShadow, bDrawFullShadowRectangle, bTop, bBottom, true, true, gProp);
4568 }
4569 
4571  const SwRect& rOutRect,
4572  const SwPageFrame * pPage,
4573  const Color *pColor,
4574  const SvxBorderLineStyle nStyle ) const
4575 {
4576  if ( !rOutRect.IsOver( rRect ) )
4577  return;
4578 
4579  SwRect aOut( rOutRect );
4580  aOut.Intersection_( rRect );
4581 
4582  const SwTabFrame *pTab = IsCellFrame() ? FindTabFrame() : nullptr;
4583  SubColFlags nSubCol = ( IsCellFrame() || IsRowFrame() )
4584  ? SubColFlags::Tab
4585  : ( IsInSct()
4586  ? SubColFlags::Sect
4587  : ( IsInFly() ? SubColFlags::Fly : SubColFlags::Page ) );
4588  if( pColor && gProp.pSGlobalShell->GetWin() &&
4590  {
4591  pColor = &SwViewOption::GetFontColor();
4592  }
4593 
4594  if (pPage->GetSortedObjs() &&
4596  {
4597  SwRegionRects aRegion( aOut, 4 );
4598  basegfx::utils::B2DClipState aClipState;
4599  ::lcl_SubtractFlys( this, pPage, aOut, aRegion, aClipState, gProp );
4600  for ( size_t i = 0; i < aRegion.size(); ++i )
4601  gProp.pSLines->AddLineRect( aRegion[i], pColor, nStyle, pTab, nSubCol, gProp );
4602  }
4603  else
4604  gProp.pSLines->AddLineRect( aOut, pColor, nStyle, pTab, nSubCol, gProp );
4605 }
4606 
4607 namespace drawinglayer::primitive2d
4608 {
4609  namespace {
4610 
4611  class SwBorderRectanglePrimitive2D : public BufferedDecompositionPrimitive2D
4612  {
4613  private:
4616 
4622 
4623  protected:
4625  virtual void create2DDecomposition(
4626  Primitive2DContainer& rContainer,
4627  const geometry::ViewInformation2D& rViewInformation) const override;
4628 
4629  public:
4631  SwBorderRectanglePrimitive2D(
4632  const basegfx::B2DHomMatrix& rB2DHomMatrix,
4633  const svx::frame::Style& rStyleTop,
4634  const svx::frame::Style& rStyleRight,
4635  const svx::frame::Style& rStyleBottom,
4636  const svx::frame::Style& rStyleLeft);
4637 
4639  const basegfx::B2DHomMatrix& getB2DHomMatrix() const { return maB2DHomMatrix; }
4640  const svx::frame::Style& getStyleTop() const { return maStyleTop; }
4641  const svx::frame::Style& getStyleRight() const { return maStyleRight; }
4642  const svx::frame::Style& getStyleBottom() const { return maStyleBottom; }
4643  const svx::frame::Style& getStyleLeft() const { return maStyleLeft; }
4644 
4646  virtual bool operator==(const BasePrimitive2D& rPrimitive) const override;
4647 
4649  virtual basegfx::B2DRange getB2DRange(const geometry::ViewInformation2D& rViewInformation) const override;
4650 
4652  virtual sal_uInt32 getPrimitive2DID() const override;
4653  };
4654 
4655  }
4656 
4657  void SwBorderRectanglePrimitive2D::create2DDecomposition(
4658  Primitive2DContainer& rContainer,
4659  const geometry::ViewInformation2D& /*rViewInformation*/) const
4660  {
4661  basegfx::B2DPoint aTopLeft(getB2DHomMatrix() * basegfx::B2DPoint(0.0, 0.0));
4662  basegfx::B2DPoint aTopRight(getB2DHomMatrix() * basegfx::B2DPoint(1.0, 0.0));
4663  basegfx::B2DPoint aBottomLeft(getB2DHomMatrix() * basegfx::B2DPoint(0.0, 1.0));
4664  basegfx::B2DPoint aBottomRight(getB2DHomMatrix() * basegfx::B2DPoint(1.0, 1.0));
4665 
4666  // prepare SdrFrameBorderDataVector
4667  std::shared_ptr<drawinglayer::primitive2d::SdrFrameBorderDataVector> aData(
4668  std::make_shared<drawinglayer::primitive2d::SdrFrameBorderDataVector>());
4669 
4670  if(getStyleTop().IsUsed())
4671  {
4672  // move top left/right inwards half border width
4673  basegfx::B2DVector aDown(getB2DHomMatrix() * basegfx::B2DVector(0.0, 1.0));
4674  aDown.setLength(getStyleTop().GetWidth() * 0.5);
4675  aTopLeft += aDown;
4676  aTopRight += aDown;
4677  }
4678 
4679  if(getStyleBottom().IsUsed())
4680  {
4681  // move bottom left/right inwards half border width
4682  basegfx::B2DVector aUp(getB2DHomMatrix() * basegfx::B2DVector(0.0, -1.0));
4683  aUp.setLength(getStyleBottom().GetWidth() * 0.5);
4684  aBottomLeft += aUp;
4685  aBottomRight += aUp;
4686  }
4687 
4688  if(getStyleLeft().IsUsed())
4689  {
4690  // move left top/bottom inwards half border width
4691  basegfx::B2DVector aRight(getB2DHomMatrix() * basegfx::B2DVector(1.0, 0.0));
4692  aRight.setLength(getStyleLeft().GetWidth() * 0.5);
4693  aTopLeft += aRight;
4694  aBottomLeft += aRight;
4695  }
4696 
4697  if(getStyleRight().IsUsed())
4698  {
4699  // move right top/bottom inwards half border width
4700  basegfx::B2DVector aLeft(getB2DHomMatrix() * basegfx::B2DVector(-1.0, 0.0));
4701  aLeft.setLength(getStyleRight().GetWidth() * 0.5);
4702  aTopRight += aLeft;
4703  aBottomRight += aLeft;
4704  }
4705 
4706  // go round-robin, from TopLeft to TopRight, down, left and back up. That
4707  // way, the borders will not need to be mirrored in any way
4708  if(getStyleTop().IsUsed())
4709  {
4710  // create BorderPrimitive(s) for top border
4711  const basegfx::B2DVector aVector(aTopRight - aTopLeft);
4712  aData->emplace_back(
4713  aTopLeft,
4714  aVector,
4715  getStyleTop(),
4716  nullptr);
4718 
4719  if(getStyleLeft().IsUsed())
4720  {
4721  rInstance.addSdrConnectStyleData(true, getStyleLeft(), basegfx::B2DVector(aBottomLeft - aTopLeft), false);
4722  }
4723 
4724  if(getStyleRight().IsUsed())
4725  {
4726  rInstance.addSdrConnectStyleData(false, getStyleRight(), basegfx::B2DVector(aBottomRight - aTopRight), false);
4727  }
4728  }
4729 
4730  if(getStyleRight().IsUsed())
4731  {
4732  // create BorderPrimitive(s) for right border
4733  const basegfx::B2DVector aVector(aBottomRight - aTopRight);
4734  aData->emplace_back(
4735  aTopRight,
4736  aVector,
4737  getStyleRight(),
4738  nullptr);
4740 
4741  if(getStyleTop().IsUsed())
4742  {
4743  rInstance.addSdrConnectStyleData(true, getStyleTop(), basegfx::B2DVector(aTopLeft - aTopRight), false);
4744  }
4745 
4746  if(getStyleBottom().IsUsed())
4747  {
4748  rInstance.addSdrConnectStyleData(false, getStyleBottom(), basegfx::B2DVector(aBottomLeft - aBottomRight), false);
4749  }
4750  }
4751 
4752  if(getStyleBottom().IsUsed())
4753  {
4754  // create BorderPrimitive(s) for bottom border
4755  const basegfx::B2DVector aVector(aBottomLeft - aBottomRight);
4756  aData->emplace_back(
4757  aBottomRight,
4758  aVector,
4759  getStyleBottom(),
4760  nullptr);
4762 
4763  if(getStyleRight().IsUsed())
4764  {
4765  rInstance.addSdrConnectStyleData(true, getStyleRight(), basegfx::B2DVector(aTopRight - aBottomRight), false);
4766  }
4767 
4768  if(getStyleLeft().IsUsed())
4769  {
4770  rInstance.addSdrConnectStyleData(false, getStyleLeft(), basegfx::B2DVector(aTopLeft - aBottomLeft), false);
4771  }
4772  }
4773 
4774  if(getStyleLeft().IsUsed())
4775  {
4776  // create BorderPrimitive(s) for left border
4777  const basegfx::B2DVector aVector(aTopLeft - aBottomLeft);
4778  aData->emplace_back(
4779  aBottomLeft,
4780  aVector,
4781  getStyleLeft(),
4782  nullptr);
4784 
4785  if(getStyleBottom().IsUsed())
4786  {
4787  rInstance.addSdrConnectStyleData(true, getStyleBottom(), basegfx::B2DVector(aBottomRight - aBottomLeft), false);
4788  }
4789 
4790  if(getStyleTop().IsUsed())
4791  {
4792  rInstance.addSdrConnectStyleData(false, getStyleTop(), basegfx::B2DVector(aTopRight - aTopLeft), false);
4793  }
4794  }
4795 
4796  // create instance of SdrFrameBorderPrimitive2D if
4797  // SdrFrameBorderDataVector is used
4798  if(!aData->empty())
4799  {
4800  rContainer.append(
4803  aData,
4804  true))); // force visualization to minimal one discrete unit (pixel)
4805  }
4806  }
4807 
4808  SwBorderRectanglePrimitive2D::SwBorderRectanglePrimitive2D(
4809  const basegfx::B2DHomMatrix& rB2DHomMatrix,
4810  const svx::frame::Style& rStyleTop,
4811  const svx::frame::Style& rStyleRight,
4812  const svx::frame::Style& rStyleBottom,
4813  const svx::frame::Style& rStyleLeft)
4814  : BufferedDecompositionPrimitive2D(),
4815  maB2DHomMatrix(rB2DHomMatrix),
4816  maStyleTop(rStyleTop),
4817  maStyleRight(rStyleRight),
4818  maStyleBottom(rStyleBottom),
4819  maStyleLeft(rStyleLeft)
4820  {
4821  }
4822 
4823  bool SwBorderRectanglePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
4824  {
4825  if(BasePrimitive2D::operator==(rPrimitive))
4826  {
4827  const SwBorderRectanglePrimitive2D& rCompare = static_cast<const SwBorderRectanglePrimitive2D&>(rPrimitive);
4828 
4829  return (getB2DHomMatrix() == rCompare.getB2DHomMatrix() &&
4830  getStyleTop() == rCompare.getStyleTop() &&
4831  getStyleRight() == rCompare.getStyleRight() &&
4832  getStyleBottom() == rCompare.getStyleBottom() &&
4833  getStyleLeft() == rCompare.getStyleLeft());
4834  }
4835 
4836  return false;
4837  }
4838 
4839  basegfx::B2DRange SwBorderRectanglePrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
4840  {
4841  basegfx::B2DRange aRetval(0.0, 0.0, 1.0, 1.0);
4842 
4843  aRetval.transform(getB2DHomMatrix());
4844  return aRetval;
4845  }
4846 
4847  // provide unique ID
4848  sal_uInt32 SwBorderRectanglePrimitive2D::getPrimitive2DID() const
4849  {
4851  }
4852 
4853 } // end of namespace drawinglayer::primitive2d
4854 
4855 namespace {
4856 
4857 editeng::SvxBorderLine const * get_ptr(std::optional<editeng::SvxBorderLine> const & opt) {
4858  return opt ? &*opt : nullptr;
4859 }
4860 
4861 }
4862 
4864  const SwFont& rFont,
4865  const SwRect& rPaintArea,
4866  const bool bVerticalLayout,
4867  const bool bVerticalLayoutLRBT,
4868  const bool bJoinWithPrev,
4869  const bool bJoinWithNext )
4870 {
4871  SwRect aAlignedRect(rPaintArea);
4872  SwAlignRect(aAlignedRect, gProp.pSGlobalShell, gProp.pSGlobalShell->GetOut());
4873 
4874  bool bTop = true;
4875  bool bBottom = true;
4876  bool bLeft = true;
4877  bool bRight = true;
4878 
4879  switch (rFont.GetOrientation(bVerticalLayout, bVerticalLayoutLRBT).get())
4880  {
4881  case 0 :
4882  bLeft = !bJoinWithPrev;
4883  bRight = !bJoinWithNext;
4884  break;
4885  case 900 :
4886  bBottom = !bJoinWithPrev;
4887  bTop = !bJoinWithNext;
4888  break;
4889  case 1800 :
4890  bRight = !bJoinWithPrev;
4891  bLeft = !bJoinWithNext;
4892  break;
4893  case 2700 :
4894  bTop = !bJoinWithPrev;
4895  bBottom = !bJoinWithNext;
4896  break;
4897  }
4898 
4899  // Paint shadow (reduce painting rect)
4900  {
4901  const SvxShadowItem aShadow(
4902  0, &rFont.GetShadowColor(), rFont.GetShadowWidth(),
4903  rFont.GetAbsShadowLocation(bVerticalLayout, bVerticalLayoutLRBT));
4904 
4905  if( aShadow.GetLocation() != SvxShadowLocation::NONE )
4906  {
4907  lcl_PaintShadow( rPaintArea, aAlignedRect, aShadow,
4908  false, bTop, bBottom, bLeft, bRight, gProp);
4909  }
4910  }
4911 
4912  const basegfx::B2DHomMatrix aBorderTransform(
4914  aAlignedRect.Width(), aAlignedRect.Height(),
4915  aAlignedRect.Left(), aAlignedRect.Top()));
4916  const svx::frame::Style aStyleTop(
4917  bTop ? get_ptr(rFont.GetAbsTopBorder(bVerticalLayout, bVerticalLayoutLRBT)) : nullptr,
4918  1.0);
4919  const svx::frame::Style aStyleRight(
4920  bRight ? get_ptr(rFont.GetAbsRightBorder(bVerticalLayout, bVerticalLayoutLRBT)) : nullptr,
4921  1.0);
4922  const svx::frame::Style aStyleBottom(
4923  bBottom ? get_ptr(rFont.GetAbsBottomBorder(bVerticalLayout, bVerticalLayoutLRBT))
4924  : nullptr,
4925  1.0);
4926  const svx::frame::Style aStyleLeft(
4927  bLeft ? get_ptr(rFont.GetAbsLeftBorder(bVerticalLayout, bVerticalLayoutLRBT)) : nullptr,
4928  1.0);
4930 
4931  aBorderLineTarget.append(
4933  new drawinglayer::primitive2d::SwBorderRectanglePrimitive2D(
4934  aBorderTransform,
4935  aStyleTop,
4936  aStyleRight,
4937  aStyleBottom,
4938  aStyleLeft)));
4939  gProp.pBLines->AddBorderLines(aBorderLineTarget);
4940 }
4941 
4943 static const SwFrame* lcl_HasNextCell( const SwFrame& rFrame )
4944 {
4945  OSL_ENSURE( rFrame.IsCellFrame(),
4946  "lcl_HasNextCell( const SwFrame& rFrame ) should be called with SwCellFrame" );
4947 
4948  const SwFrame* pTmpFrame = &rFrame;
4949  do
4950  {
4951  if ( pTmpFrame->GetNext() )
4952  return pTmpFrame->GetNext();
4953 
4954  pTmpFrame = pTmpFrame->GetUpper()->GetUpper();
4955  }
4956  while ( pTmpFrame->IsCellFrame() );
4957 
4958  return nullptr;
4959 }
4960 
4982 static const SwFrame* lcl_GetCellFrameForBorderAttrs( const SwFrame* _pCellFrame,
4983  const SwBorderAttrs& _rCellBorderAttrs,
4984  const bool _bTop )
4985 {
4986  OSL_ENSURE( _pCellFrame, "No cell frame available, dying soon" );
4987 
4988  // determine, if cell frame is at bottom/top border of a table frame and
4989  // the table frame has/is a follow.
4990  const SwFrame* pTmpFrame = _pCellFrame;
4991  bool bCellAtBorder = true;
4992  bool bCellAtLeftBorder = !_pCellFrame->GetPrev();
4993  bool bCellAtRightBorder = !_pCellFrame->GetNext();
4994  while( !pTmpFrame->IsRowFrame() || !pTmpFrame->GetUpper()->IsTabFrame() )
4995  {
4996  pTmpFrame = pTmpFrame->GetUpper();
4997  if ( pTmpFrame->IsRowFrame() &&
4998  (_bTop ? pTmpFrame->GetPrev() : pTmpFrame->GetNext())
4999  )
5000  {
5001  bCellAtBorder = false;
5002  }
5003  if ( pTmpFrame->IsCellFrame() )
5004  {
5005  if ( pTmpFrame->GetPrev() )
5006  {
5007  bCellAtLeftBorder = false;
5008  }
5009  if ( pTmpFrame->GetNext() )
5010  {
5011  bCellAtRightBorder = false;
5012  }
5013  }
5014  }
5015  OSL_ENSURE( pTmpFrame && pTmpFrame->IsRowFrame(), "No RowFrame available" );
5016 
5017  const SwLayoutFrame* pParentRowFrame = static_cast<const SwLayoutFrame*>(pTmpFrame);
5018  const SwTabFrame* pParentTabFrame =
5019  static_cast<const SwTabFrame*>(pParentRowFrame->GetUpper());
5020 
5021  const bool bCellNeedsAttribute = bCellAtBorder &&
5022  ( _bTop ?
5023  // bCellInFirstRowWithMaster
5024  ( !pParentRowFrame->GetPrev() &&
5025  pParentTabFrame->IsFollow() &&
5026  0 == pParentTabFrame->GetTable()->GetRowsToRepeat() ) :
5027  // bCellInLastRowWithFollow
5028  ( !pParentRowFrame->GetNext() &&
5029  pParentTabFrame->GetFollow() )
5030  );
5031 
5032  const SwFrame* pRet = _pCellFrame;
5033  if ( bCellNeedsAttribute )
5034  {
5035  // determine, if cell frame has no borders inside the table.
5036  const SwFrame* pNextCell = nullptr;
5037  bool bNoBordersInside = false;
5038 
5039  if ( bCellAtLeftBorder && ( nullptr != ( pNextCell = lcl_HasNextCell( *_pCellFrame ) ) ) )
5040  {
5041  SwBorderAttrAccess aAccess( SwFrame::GetCache(), pNextCell );
5042  const SwBorderAttrs &rBorderAttrs = *aAccess.Get();
5043  const SvxBoxItem& rBorderBox = rBorderAttrs.GetBox();
5044  bCellAtRightBorder = !lcl_HasNextCell( *pNextCell );
5045  bNoBordersInside =
5046  ( !rBorderBox.GetTop() || !pParentRowFrame->GetPrev() ) &&
5047  !rBorderBox.GetLeft() &&
5048  ( !rBorderBox.GetRight() || bCellAtRightBorder ) &&
5049  ( !rBorderBox.GetBottom() || !pParentRowFrame->GetNext() );
5050  }
5051  else
5052  {
5053  const SvxBoxItem& rBorderBox = _rCellBorderAttrs.GetBox();
5054  bNoBordersInside =
5055  ( !rBorderBox.GetTop() || !pParentRowFrame->GetPrev() ) &&
5056  ( !rBorderBox.GetLeft() || bCellAtLeftBorder ) &&
5057  ( !rBorderBox.GetRight() || bCellAtRightBorder ) &&
5058  ( !rBorderBox.GetBottom() || !pParentRowFrame->GetNext() );
5059  }
5060 
5061  if ( bNoBordersInside )
5062  {
5063  if ( _bTop && !_rCellBorderAttrs.GetBox().GetTop() )
5064  {
5065  //-hack
5066  // Cell frame has no top border and no border inside the table, but
5067  // it is at the top border of a table frame, which is a follow.
5068  // Thus, use border attributes of cell frame in first row of complete table.
5069  // First, determine first table frame of complete table.
5070  SwTabFrame* pMasterTabFrame = pParentTabFrame->FindMaster( true );
5071  // determine first row of complete table.
5072  const SwFrame* pFirstRow = pMasterTabFrame->GetLower();
5073  // return first cell in first row
5074  SwFrame* pLowerCell = const_cast<SwFrame*>(pFirstRow->GetLower());
5075  while ( !pLowerCell->IsCellFrame() ||
5076  ( pLowerCell->GetLower() && pLowerCell->GetLower()->IsRowFrame() )
5077  )
5078  {
5079  pLowerCell = pLowerCell->GetLower();
5080  }
5081  OSL_ENSURE( pLowerCell && pLowerCell->IsCellFrame(), "No CellFrame available" );
5082  pRet = pLowerCell;
5083  }
5084  else if ( !_bTop && !_rCellBorderAttrs.GetBox().GetBottom() )
5085  {
5086  //-hack
5087  // Cell frame has no bottom border and no border inside the table,
5088  // but it is at the bottom border of a table frame, which has a follow.
5089  // Thus, use border attributes of cell frame in last row of complete table.
5090  // First, determine last table frame of complete table.
5091  SwTabFrame* pLastTabFrame = const_cast<SwTabFrame*>(pParentTabFrame->GetFollow());
5092  while ( pLastTabFrame->GetFollow() )
5093  {
5094  pLastTabFrame = pLastTabFrame->GetFollow();
5095  }
5096  // determine last row of complete table.
5097  SwFrame* pLastRow = pLastTabFrame->GetLastLower();
5098  // return first bottom border cell in last row
5099  SwFrame* pLowerCell = pLastRow->GetLower();
5100  while ( !pLowerCell->IsCellFrame() ||
5101  ( pLowerCell->GetLower() && pLowerCell->GetLower()->IsRowFrame() )
5102  )
5103  {
5104  if ( pLowerCell->IsRowFrame() )
5105  {
5106  while ( pLowerCell->GetNext() )
5107  {
5108  pLowerCell = pLowerCell->GetNext();
5109  }
5110  }
5111  pLowerCell = pLowerCell->GetLower();
5112  }
5113  OSL_ENSURE( pLowerCell && pLowerCell->IsCellFrame(), "No CellFrame available" );
5114  pRet = pLowerCell;
5115  }
5116  }
5117  }
5118 
5119  return pRet;
5120 }
5121 
5122 std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> SwFrame::CreateProcessor2D( ) const
5123 {
5124  basegfx::B2DRange aViewRange;
5125 
5126  SdrPage *pDrawPage = getRootFrame()->GetCurrShell()->Imp()->GetPageView()->GetPage();
5127  const drawinglayer::geometry::ViewInformation2D aNewViewInfos(
5129  getRootFrame()->GetCurrShell()->GetOut()->GetViewTransformation(),
5130  aViewRange,
5131  GetXDrawPageForSdrPage( pDrawPage ),
5132  0.0,
5133  uno::Sequence< beans::PropertyValue >() );
5134 
5136  *getRootFrame()->GetCurrShell()->GetOut(),
5137  aNewViewInfos );
5138 }
5139 
5141 {
5142  std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor2D = CreateProcessor2D();
5143  if ( pProcessor2D )
5144  {
5145  pProcessor2D->process( rSequence );
5146  }
5147 }
5148 
5151  const SwRect& rRect,
5152  const SwPageFrame* /*pPage*/,
5153  const SwBorderAttrs& rAttrs) const
5154 {
5155  // There's nothing (Row,Body,Footnote,Root,Column,NoText) need to do here
5157  return;
5158 
5159  if (IsCellFrame() && !gProp.pSGlobalShell->GetViewOptions()->IsTable())
5160  return;
5161 
5162  // #i29550#
5163  if ( IsTabFrame() || IsCellFrame() || IsRowFrame() )
5164  {
5165  const SwTabFrame* pTabFrame = FindTabFrame();
5166  if ( pTabFrame->IsCollapsingBorders() )
5167  return;
5168 
5169  if ( pTabFrame->GetTable()->IsNewModel() && ( !IsCellFrame() || IsCoveredCell() ) )
5170  return;
5171  }
5172 
5173  const bool bLine = rAttrs.IsLine();
5174  const bool bShadow = rAttrs.GetShadow().GetLocation() != SvxShadowLocation::NONE;
5175 
5176  // - flag to control,
5177  //-hack has to be used.
5178  const bool bb4779636HackActive = true;
5179 
5180  const SwFrame* pCellFrameForBottomBorderAttrs = nullptr;
5181  const SwFrame* pCellFrameForTopBorderAttrs = nullptr;
5182  bool bFoundCellForTopOrBorderAttrs = false;
5183  if ( bb4779636HackActive && IsCellFrame() )
5184  {
5185  pCellFrameForBottomBorderAttrs = lcl_GetCellFrameForBorderAttrs( this, rAttrs, false );
5186  if ( pCellFrameForBottomBorderAttrs != this )
5187  bFoundCellForTopOrBorderAttrs = true;
5188  pCellFrameForTopBorderAttrs = lcl_GetCellFrameForBorderAttrs( this, rAttrs, true );
5189  if ( pCellFrameForTopBorderAttrs != this )
5190  bFoundCellForTopOrBorderAttrs = true;
5191  }
5192 
5193  // - add condition <bFoundCellForTopOrBorderAttrs>
5194  //-hack
5195  if ( !(bLine || bShadow || bFoundCellForTopOrBorderAttrs) )
5196  return;
5197 
5198  //If the rectangle is completely inside the PrtArea, no border needs to
5199  //be painted.
5200  //For the PrtArea the aligned value needs to be used, otherwise it could
5201  //happen, that some parts won't be processed.
5202  SwRect aRect( getFramePrintArea() );
5203  aRect += getFrameArea().Pos();
5204  ::SwAlignRect( aRect, gProp.pSGlobalShell, gProp.pSGlobalShell->GetOut() );
5205  // new local boolean variable in order to
5206  // suspend border paint under special cases - see below.
5207  // NOTE: This is a fix for the implementation of feature #99657#.
5208  bool bDrawOnlyShadowForTransparentFrame = false;
5209  if ( aRect.IsInside( rRect ) )
5210  {
5211  // paint shadow, if background is transparent.
5212  // Because of introduced transparent background for fly frame #99657#,
5213  // the shadow have to be drawn if the background is transparent,
5214  // in spite the fact that the paint rectangle <rRect> lies fully
5215  // in the printing area.
5216  // NOTE to chosen solution:
5217  // On transparent background, continue processing, but suspend
5218  // drawing of border by setting <bDrawOnlyShadowForTransparentFrame>
5219  // to true.
5220  if ( IsLayoutFrame() &&
5221  static_cast<const SwLayoutFrame*>(this)->GetFormat()->IsBackgroundTransparent() )
5222  {
5223  bDrawOnlyShadowForTransparentFrame = true;
5224  }
5225  else
5226  {
5227  return;
5228  }
5229  }
5230 
5231  ::lcl_CalcBorderRect( aRect, this, rAttrs, true, gProp );
5232  rAttrs.SetGetCacheLine( true );
5233 
5234  if(bShadow)
5235  {
5236  PaintShadow(rRect, aRect, rAttrs);
5237  }
5238 
5239  // suspend drawing of border
5240  // add condition < NOT bDrawOnlyShadowForTransparentFrame > - see above
5241  // - add condition <bFoundCellForTopOrBorderAttrs>
5242  //-hack.
5243  if((bLine || bFoundCellForTopOrBorderAttrs) && !bDrawOnlyShadowForTransparentFrame)
5244  {
5245  // define SvxBorderLine(s) to use
5246  const SvxBoxItem& rBox(rAttrs.GetBox());
5247  const SvxBorderLine* pLeftBorder(rBox.GetLeft());
5248  const SvxBorderLine* pRightBorder(rBox.GetRight());
5249  const SvxBorderLine* pTopBorder(rBox.GetTop());
5250  const SvxBorderLine* pBottomBorder(rBox.GetBottom());
5251 
5252  // if R2L, exchange Right/Left
5253  const bool bR2L(IsCellFrame() && IsRightToLeft());
5254 
5255  if(bR2L)
5256  {
5257  std::swap(pLeftBorder, pRightBorder);
5258  }
5259 
5260  // if ContentFrame and joined Prev/Next, reset top/bottom as needed
5261  if(IsContentFrame())
5262  {
5263  const SwFrame* pDirRefFrame(IsCellFrame() ? FindTabFrame() : this);
5264  const SwRectFnSet aRectFnSet(pDirRefFrame);
5265  const SwRectFn& _rRectFn(aRectFnSet.FnRect());
5266 
5267  if(rAttrs.JoinedWithPrev(*this))
5268  {
5269  // tdf#115296 re-add adaptation of vert distance to close the evtl.
5270  // existing gap to previous frame
5271  const SwFrame* pPrevFrame(GetPrev());
5272  (aRect.*_rRectFn->fnSetTop)( (pPrevFrame->*_rRectFn->fnGetPrtBottom)() );
5273 
5274  // ...and disable top border paint/creation
5275  pTopBorder = nullptr;
5276  }
5277 
5278  if(rAttrs.JoinedWithNext(*this))
5279  {
5280  // tdf#115296 re-add adaptation of vert distance to close the evtl.
5281  // existing gap to next frame
5282  const SwFrame* pNextFrame(GetNext());
5283  (aRect.*_rRectFn->fnSetBottom)( (pNextFrame->*_rRectFn->fnGetPrtTop)() );
5284 
5285  // ...and disable bottom border paint/creation
5286  pBottomBorder = nullptr;
5287  }
5288  }
5289 
5290  // necessary to replace TopBorder?
5291  if((!IsContentFrame() || rAttrs.GetTopLine(*this)) && IsCellFrame() && pCellFrameForTopBorderAttrs != this)
5292  {
5293  SwBorderAttrAccess aAccess(SwFrame::GetCache(), pCellFrameForTopBorderAttrs);
5294  pTopBorder = aAccess.Get()->GetBox().GetTop();
5295  }
5296 
5297  // necessary to replace BottomBorder?
5298  if((!IsContentFrame() || rAttrs.GetBottomLine(*this)) && IsCellFrame() && pCellFrameForBottomBorderAttrs != this)
5299  {
5300  SwBorderAttrAccess aAccess(SwFrame::GetCache(), pCellFrameForBottomBorderAttrs);
5301  pBottomBorder = aAccess.Get()->GetBox().GetBottom();
5302  }
5303 
5304  if(nullptr != pLeftBorder || nullptr != pRightBorder || nullptr != pTopBorder || nullptr != pBottomBorder)
5305  {
5306  // now we have all SvxBorderLine(s) sorted out, create geometry
5307  const basegfx::B2DHomMatrix aBorderTransform(
5309  aRect.Width(), aRect.Height(),
5310  aRect.Left(), aRect.Top()));
5311  const svx::frame::Style aStyleTop(pTopBorder, 1.0);
5312  const svx::frame::Style aStyleRight(pRightBorder, 1.0);
5313  const svx::frame::Style aStyleBottom(pBottomBorder, 1.0);
5314  const svx::frame::Style aStyleLeft(pLeftBorder, 1.0);
5316 
5317  aBorderLineTarget.append(
5319  new drawinglayer::primitive2d::SwBorderRectanglePrimitive2D(
5320  aBorderTransform,
5321  aStyleTop,
5322  aStyleRight,
5323  aStyleBottom,
5324  aStyleLeft)));
5325  gProp.pBLines->AddBorderLines(aBorderLineTarget);
5326  }
5327  }
5328 
5329  rAttrs.SetGetCacheLine( false );
5330 }
5331 
5339  const SwRect& rRect,
5340  const SwPageFrame* pPage,
5341  const SwBorderAttrs&) const
5342 {
5343  //If the rectangle is completely inside the PrtArea, no border needs to
5344  //be painted.
5345  SwRect aRect( getFramePrintArea() );
5346  aRect.Pos() += getFrameArea().Pos();
5347  if ( !aRect.IsInside( rRect ) )
5348  PaintLine( rRect, pPage );
5349 }
5350 
5353  const SwPageFrame *pPage ) const
5354 {
5355  //The length of the line is derived from the percentual indication on the
5356  //PageDesc. The position is also stated on the PageDesc.
5357  //The pen can directly be taken from the PageDesc.
5358 
5359  if ( !pPage )
5360  pPage = FindPageFrame();
5361  const SwPageFootnoteInfo &rInf = pPage->GetPageDesc()->GetFootnoteInfo();
5362 
5363  SwRectFnSet aRectFnSet(this);
5364  SwTwips nPrtWidth = aRectFnSet.GetWidth(getFramePrintArea());
5365  Fraction aFract( nPrtWidth, 1 );
5366  aFract *= rInf.GetWidth();
5367  const SwTwips nWidth = static_cast<tools::Long>(aFract);
5368 
5369  SwTwips nX = aRectFnSet.GetPrtLeft(*this);
5370  switch ( rInf.GetAdj() )
5371  {
5372  case css::text::HorizontalAdjust_CENTER:
5373  nX += nPrtWidth/2 - nWidth/2; break;
5374  case css::text::HorizontalAdjust_RIGHT:
5375  nX += nPrtWidth - nWidth; break;
5376  case css::text::HorizontalAdjust_LEFT:
5377  /* do nothing */; break;
5378  default:
5379  SAL_WARN("sw.core", "New adjustment for footnote lines?");
5380  assert(false);
5381  }
5382  SwTwips nLineWidth = rInf.GetLineWidth();
5383  const SwRect aLineRect = aRectFnSet.IsVert() ?
5384  SwRect( Point(getFrameArea().Left()+getFrameArea().Width()-rInf.GetTopDist()-nLineWidth,
5385  nX), Size( nLineWidth, nWidth ) )
5386  : SwRect( Point( nX, getFrameArea().Pos().Y() + rInf.GetTopDist() ),
5387  Size( nWidth, rInf.GetLineWidth()));
5388  if ( aLineRect.HasArea() && rInf.GetLineStyle() != SvxBorderLineStyle::NONE)
5389  PaintBorderLine( rRect, aLineRect , pPage, &rInf.GetLineColor(),
5390  rInf.GetLineStyle() );
5391 }
5392 
5394 void SwLayoutFrame::PaintColLines( const SwRect &rRect, const SwFormatCol &rFormatCol,
5395  const SwPageFrame *pPage ) const
5396 {
5397  const SwFrame *pCol = Lower();
5398  if ( !pCol || !pCol->IsColumnFrame() )
5399  return;
5400 
5401  SwRectFn fnRect = pCol->IsVertical() ? ( pCol->IsVertLR() ? (pCol->IsVertLRBT() ? fnRectVertL2RB2T : fnRectVertL2R) : fnRectVert ) : fnRectHori;
5402 
5403  SwRect aLineRect = getFramePrintArea();
5404  aLineRect += getFrameArea().Pos();
5405 
5406  SwTwips nTop = ((aLineRect.*fnRect->fnGetHeight)()*rFormatCol.GetLineHeight())
5407  / 100 - (aLineRect.*fnRect->fnGetHeight)();
5408  SwTwips nBottom = 0;
5409 
5410  switch ( rFormatCol.GetLineAdj() )
5411  {
5412  case COLADJ_CENTER:
5413  nBottom = nTop / 2; nTop -= nBottom; break;
5414  case COLADJ_TOP:
5415  nBottom = nTop; nTop = 0; break;
5416  case COLADJ_BOTTOM:
5417  break;
5418  default:
5419  OSL_ENSURE( false, "New adjustment for column lines?" );
5420  }
5421 
5422  if( nTop )
5423  (aLineRect.*fnRect->fnSubTop)( nTop );
5424  if( nBottom )
5425  (aLineRect.*fnRect->fnAddBottom)( nBottom );
5426 
5427  SwTwips nPenHalf = rFormatCol.GetLineWidth();
5428  (aLineRect.*fnRect->fnSetWidth)( nPenHalf );
5429  nPenHalf /= 2;
5430 
5431  //We need to be a bit generous here, to not lose something.
5432  SwRect aRect( rRect );
5433  (aRect.*fnRect->fnSubLeft)( nPenHalf + gProp.nSPixelSzW );
5434  (aRect.*fnRect->fnAddRight)( nPenHalf + gProp.nSPixelSzW );
5435  SwRectGet fnGetX = IsRightToLeft() ? fnRect->fnGetLeft : fnRect->fnGetRight;
5436  while ( pCol->GetNext() )
5437  {
5438  (aLineRect.*fnRect->fnSetPosX)
5439  ( (pCol->getFrameArea().*fnGetX)() - nPenHalf );
5440  if ( aRect.IsOver( aLineRect ) )
5441  PaintBorderLine( aRect, aLineRect , pPage, &rFormatCol.GetLineColor(),
5442  rFormatCol.GetLineStyle() );
5443  pCol = pCol->GetNext();
5444  }
5445 }
5446 
5447 void SwPageFrame::PaintGrid( OutputDevice const * pOut, SwRect const &rRect ) const
5448 {
5449  if( !m_bHasGrid || gProp.pSRetoucheFly || gProp.pSRetoucheFly2 )
5450  return;
5451  SwTextGridItem const*const pGrid(GetGridItem(this));
5452  if( !(pGrid && ( OUTDEV_PRINTER != pOut->GetOutDevType() ?
5453  pGrid->GetDisplayGrid() : pGrid->GetPrintGrid() )) )
5454  return;
5455 
5456  const SwLayoutFrame* pBody = FindBodyCont();
5457  if( !pBody )
5458  return;
5459 
5460  SwRect aGrid( pBody->getFramePrintArea() );
5461  aGrid += pBody->getFrameArea().Pos();
5462 
5463  SwRect aInter( aGrid );
5464  aInter.Intersection( rRect );
5465  if( !aInter.HasArea() )
5466  return;
5467 
5468  bool bGrid = pGrid->GetRubyTextBelow();
5469  bool bCell = GRID_LINES_CHARS == pGrid->GetGridType();
5470  tools::Long nGrid = pGrid->GetBaseHeight();
5471  const SwDoc* pDoc = GetFormat()->GetDoc();
5472  tools::Long nGridWidth = GetGridWidth(*pGrid, *pDoc);
5473  tools::Long nRuby = pGrid->GetRubyHeight();
5474  tools::Long nSum = nGrid + nRuby;
5475  const Color *pCol = &pGrid->GetColor();
5476 
5477  SwTwips nRight = aInter.Left() + aInter.Width();
5478  SwTwips nBottom = aInter.Top() + aInter.Height();
5479  if( IsVertical() )
5480  {
5481  SwTwips nOrig = aGrid.Left() + aGrid.Width();
5482  SwTwips nY = nOrig + nSum *
5483  ( ( nOrig - aInter.Left() ) / nSum );
5484  SwRect aTmp( Point( nY, aInter.Top() ),
5485  Size( 1, aInter.Height() ) );
5486  SwTwips nX = aGrid.Top() + nGrid *
5487  ( ( aInter.Top() - aGrid.Top() )/ nGrid );
5488  if( nX < aInter.Top() )
5489  nX += nGrid;
5490  SwTwips nGridBottom = aGrid.Top() + aGrid.Height();
5491  bool bLeft = aGrid.Top() >= aInter.Top();
5492  bool bRight = nGridBottom <= nBottom;
5493  bool bBorder = bLeft || bRight;
5494  while( nY > nRight )
5495  {
5496  aTmp.Pos().setX( nY );
5497  if( bGrid )
5498  {
5499  nY -= nGrid;
5500  SwTwips nPosY = std::max( SwTwips(aInter.Left()), nY );
5501  SwTwips nHeight = std::min(nRight, SwTwips(aTmp.Pos().X()))-nPosY;
5502  if( nHeight > 0 )
5503  {
5504  if( bCell )
5505  {
5506  SwRect aVert( Point( nPosY, nX ),
5507  Size( nHeight, 1 ) );
5508  while( aVert.Top() <= nBottom )
5509  {
5510  PaintBorderLine(rRect,aVert,this,pCol);
5511  aVert.Pos().AdjustY(nGrid );
5512  }
5513  }
5514  else if( bBorder )
5515  {
5516  SwRect aVert( Point( nPosY, aGrid.Top() ),
5517  Size( nHeight, 1 ) );
5518  if( bLeft )
5519  PaintBorderLine(rRect,aVert,this,pCol);
5520  if( bRight )