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