LibreOffice Module sw (master)  1
paintfrm.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  * Licensed to the Apache Software Foundation (ASF) under one or more
12  * contributor license agreements. See the NOTICE file distributed
13  * with this work for additional information regarding copyright
14  * ownership. The ASF licenses this file to you under the Apache
15  * License, Version 2.0 (the "License"); you may not use this file
16  * except in compliance with the License. You may obtain a copy of
17  * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <vcl/lazydelete.hxx>
21 #include <sfx2/docfile.hxx>
22 #include <sfx2/printer.hxx>
23 #include <sfx2/progress.hxx>
24 #include <editeng/brushitem.hxx>
25 #include <editeng/prntitem.hxx>
26 #include <editeng/boxitem.hxx>
27 #include <editeng/shaditem.hxx>
28 #include <svx/framelink.hxx>
29 #include <drawdoc.hxx>
30 #include <tgrditem.hxx>
31 #include <calbck.hxx>
32 #include <fmtsrnd.hxx>
33 #include <fmtclds.hxx>
34 #include <strings.hrc>
35 #include <swmodule.hxx>
36 #include <rootfrm.hxx>
37 #include <pagefrm.hxx>
38 #include <section.hxx>
39 #include <sectfrm.hxx>
40 #include <viewimp.hxx>
41 #include <dflyobj.hxx>
42 #include <flyfrm.hxx>
43 #include <frmatr.hxx>
44 #include <frmtool.hxx>
45 #include <viewopt.hxx>
46 #include <dview.hxx>
47 #include <dcontact.hxx>
48 #include <txtfrm.hxx>
49 #include <ftnfrm.hxx>
50 #include <tabfrm.hxx>
51 #include <rowfrm.hxx>
52 #include <cellfrm.hxx>
53 #include <notxtfrm.hxx>
54 #include <layact.hxx>
55 #include <pagedesc.hxx>
56 #include <ptqueue.hxx>
57 #include <noteurl.hxx>
58 #include "virtoutp.hxx"
59 #include <lineinfo.hxx>
60 #include <dbg_lay.hxx>
61 #include <docsh.hxx>
62 #include <svx/svdogrp.hxx>
63 #include <sortedobjs.hxx>
65 #include <bodyfrm.hxx>
66 #include <hffrm.hxx>
67 #include <colfrm.hxx>
68 #include <sw_primitivetypes2d.hxx>
69 #include <swfont.hxx>
70 
78 
79 #include <ndole.hxx>
80 #include <PostItMgr.hxx>
81 #include <FrameControlsManager.hxx>
82 #include <vcl/settings.hxx>
83 
86 
87 #include <svtools/borderhelper.hxx>
88 
89 #include <bitmaps.hlst>
98 #include <svx/unoapi.hxx>
99 #include <svx/svdpagv.hxx>
100 #include <svx/xfillit0.hxx>
104 #include <sal/log.hxx>
105 
106 #include <memory>
107 #include <vector>
108 #include <algorithm>
109 #include <wrtsh.hxx>
110 #include <edtwin.hxx>
111 #include <view.hxx>
112 #include <paintfrm.hxx>
113 #include <textboxhelper.hxx>
114 #include <o3tl/typed_flags_set.hxx>
115 
116 #include <vcl/BitmapTools.hxx>
117 #include <comphelper/lok.hxx>
119 #include <vcl/GraphicLoader.hxx>
120 
121 using namespace ::editeng;
122 using namespace ::com::sun::star;
123 
124 namespace {
125 
126 struct SwPaintProperties;
127 
128 //Class declaration; here because they are only used in this file
129 enum class SubColFlags {
130  Page = 0x01, //Helplines of the page
131  Tab = 0x08, //Helplines inside tables
132  Fly = 0x10, //Helplines inside fly frames
133  Sect = 0x20, //Helplines inside sections
134 };
135 
136 }
137 
138 namespace o3tl {
139  template<> struct typed_flags<SubColFlags> : is_typed_flags<SubColFlags, 0x39> {};
140 }
141 
142 namespace {
143 
144 // Classes collecting the border lines and help lines
145 class SwLineRect : public SwRect
146 {
147  Color m_aColor;
148  SvxBorderLineStyle m_nStyle;
149  const SwTabFrame* m_pTabFrame;
150  SubColFlags m_nSubColor; //colorize subsidiary lines
151  bool m_bPainted; //already painted?
152  sal_uInt8 m_nLock; //To distinguish the line and the hell layer.
153 public:
154  SwLineRect( const SwRect &rRect, const Color *pCol, const SvxBorderLineStyle nStyle,
155  const SwTabFrame *pT , const SubColFlags nSCol );
156 
157  const Color& GetColor() const { return m_aColor; }
158  SvxBorderLineStyle GetStyle() const { return m_nStyle; }
159  const SwTabFrame* GetTab() const { return m_pTabFrame; }
160  void SetPainted() { m_bPainted = true; }
161  void Lock(bool bLock)
162  {
163  if (bLock)
164  ++m_nLock;
165  else if (m_nLock)
166  --m_nLock;
167  }
168  bool IsPainted() const { return m_bPainted; }
169  bool IsLocked() const { return m_nLock != 0; }
170  SubColFlags GetSubColor() const { return m_nSubColor; }
171 
172  bool MakeUnion(const SwRect& rRect, SwPaintProperties const& properties);
173 };
174 
175 }
176 
177 #ifdef IOS
178 static void dummy_function()
179 {
180  pid_t pid = getpid();
181  (void) pid;
182 }
183 #endif
184 
185 namespace {
186 
187 class SwLineRects
188 {
189 public:
190  std::vector<SwLineRect> m_aLineRects;
191  typedef std::vector< SwLineRect >::const_iterator const_iterator;
192  typedef std::vector< SwLineRect >::iterator iterator;
193  typedef std::vector< SwLineRect >::reverse_iterator reverse_iterator;
194  typedef std::vector< SwLineRect >::size_type size_type;
195  size_t m_nLastCount; //avoid unnecessary cycles in PaintLines
196  SwLineRects()
197  : m_nLastCount(0)
198  {
199 #ifdef IOS
200  // Work around what is either a compiler bug in Xcode 5.1.1,
201  // or some unknown problem in this file. If I ifdef out this
202  // call, I get a crash in SwSubsRects::PaintSubsidiary: the
203  // address of the rLi reference variable is claimed to be
204  // 0x4000000!
205  dummy_function();
206 #endif
207  }
208  void AddLineRect( const SwRect& rRect, const Color *pColor, const SvxBorderLineStyle nStyle,
209  const SwTabFrame *pTab, const SubColFlags nSCol, SwPaintProperties const &properties );
210  void ConnectEdges( OutputDevice const *pOut, SwPaintProperties const &properties );
211  void PaintLines ( OutputDevice *pOut, SwPaintProperties const &properties );
212  void LockLines( bool bLock );
213 
214  //Limit lines to 100
215  bool isFull() const { return m_aLineRects.size() > 100; }
216 };
217 
218 class SwSubsRects : public SwLineRects
219 {
220  void RemoveSuperfluousSubsidiaryLines( const SwLineRects &rRects, SwPaintProperties const &properties );
221 public:
222  void PaintSubsidiary( OutputDevice *pOut, const SwLineRects *pRects, SwPaintProperties const &properties );
223 };
224 
225 class BorderLines
226 {
228 public:
229  void AddBorderLines(drawinglayer::primitive2d::Primitive2DContainer&& rContainer);
231  {
233  lines.swap(m_Lines);
234  return lines;
235  }
236 };
237 
238 }
239 
240 // Default zoom factor
241 const double aEdgeScale = 0.5;
242 
243 //To optimize the expensive RetouchColor determination
245 
246 namespace sw
247 {
249 {
250  return &aGlobalRetoucheColor;
251 }
252 }
253 
254 namespace {
255 
259 struct SwPaintProperties {
260  // Only repaint the Fly content as well as the background of the Fly content if
261  // a metafile is taken of the Fly.
262  bool bSFlyMetafile;
263  VclPtr<OutputDevice> pSFlyMetafileOut;
264  SwViewShell *pSGlobalShell;
265 
266  // Retouch for transparent Flys is done by the background of the Flys.
267  // The Fly itself should certainly not be spared out. See PaintSwFrameBackground and
268  // lcl_SubtractFlys()
269  SwFlyFrame *pSRetoucheFly;
270  SwFlyFrame *pSRetoucheFly2;
271  SwFlyFrame *pSFlyOnlyDraw;
272 
273  // The borders will be collected in pSLines during the Paint and later
274  // possibly merge them.
275  // The help lines will be collected and merged in gProp.pSSubsLines. These will
276  // be compared with pSLines before the work in order to avoid help lines
277  // to hide borders.
278  std::unique_ptr<BorderLines> pBLines;
279  std::unique_ptr<SwLineRects> pSLines;
280  std::unique_ptr<SwSubsRects> pSSubsLines;
281 
282  // global variable for sub-lines of body, header, footer, section and footnote frames.
283  std::unique_ptr<SwSubsRects> pSSpecSubsLines;
284  SfxProgress *pSProgress;
285 
286  // Sizes of a pixel and the corresponding halves. Will be reset when
287  // entering SwRootFrame::PaintSwFrame
288  tools::Long nSPixelSzW;
289  tools::Long nSPixelSzH;
290  tools::Long nSHalfPixelSzW;
291  tools::Long nSHalfPixelSzH;
292  tools::Long nSMinDistPixelW;
293  tools::Long nSMinDistPixelH;
294 
295  Color aSGlobalRetoucheColor;
296 
297  // Current zoom factor
298  double aSScaleX;
299  double aSScaleY;
300 
301  SwPaintProperties()
302  : bSFlyMetafile(false)
303  , pSFlyMetafileOut(nullptr)
304  , pSGlobalShell(nullptr)
305  , pSRetoucheFly(nullptr)
306  , pSRetoucheFly2(nullptr)
307  , pSFlyOnlyDraw(nullptr)
308  , pSProgress(nullptr)
309  , nSPixelSzW(0)
310  , nSPixelSzH(0)
311  , nSHalfPixelSzW(0)
312  , nSHalfPixelSzH(0)
313  , nSMinDistPixelW(0)
314  , nSMinDistPixelH(0)
315  , aSScaleX(1)
316  , aSScaleY(1)
317  {
318  }
319 
320 };
321 
322 }
323 
324 static SwPaintProperties gProp;
325 
327 {
328  return !gProp.pSGlobalShell->GetViewOptions()->IsPagePreview() &&
329  !gProp.pSGlobalShell->GetViewOptions()->IsReadonly() &&
330  !gProp.pSGlobalShell->GetViewOptions()->IsFormView() &&
332 }
333 //other subsidiary lines enabled?
335 {
336  return !gProp.pSGlobalShell->GetViewOptions()->IsPagePreview() &&
337  !gProp.pSGlobalShell->GetViewOptions()->IsReadonly() &&
338  !gProp.pSGlobalShell->GetViewOptions()->IsFormView() &&
339  !gProp.pSGlobalShell->GetViewOptions()->IsWhitespaceHidden() &&
341 }
342 //subsidiary lines for sections
344 {
345  return !gProp.pSGlobalShell->GetViewOptions()->IsPagePreview() &&
346  !gProp.pSGlobalShell->GetViewOptions()->IsReadonly() &&
347  !gProp.pSGlobalShell->GetViewOptions()->IsFormView() &&
349 }
350 
351 
352 namespace {
353 
354 bool isTableBoundariesEnabled()
355 {
356  if (!gProp.pSGlobalShell->GetViewOptions()->IsTable())
357  return false;
358 
359  if (gProp.pSGlobalShell->GetViewOptions()->IsPagePreview())
360  return false;
361 
362  if (gProp.pSGlobalShell->GetViewOptions()->IsReadonly())
363  return false;
364 
365  if (gProp.pSGlobalShell->GetViewOptions()->IsFormView())
366  return false;
367 
369 }
370 
371 }
372 
380 {
381  // determine 'small' twip-to-pixel relation
382  bool bSmallTwipToPxRelW = false;
383  bool bSmallTwipToPxRelH = false;
384  {
385  Size aCheckTwipToPxRelSz( pOut->PixelToLogic( Size( 100, 100 )) );
386  if ( (aCheckTwipToPxRelSz.Width()/100.0) < 2.0 )
387  {
388  bSmallTwipToPxRelW = true;
389  }
390  if ( (aCheckTwipToPxRelSz.Height()/100.0) < 2.0 )
391  {
392  bSmallTwipToPxRelH = true;
393  }
394  }
395 
396  Size aSz( pOut->PixelToLogic( Size( 1,1 )) );
397 
398  gProp.nSPixelSzW = aSz.Width();
399  if( !gProp.nSPixelSzW )
400  gProp.nSPixelSzW = 1;
401  gProp.nSPixelSzH = aSz.Height();
402  if( !gProp.nSPixelSzH )
403  gProp.nSPixelSzH = 1;
404 
405  // consider 'small' twip-to-pixel relations
406  if ( !bSmallTwipToPxRelW )
407  {
408  gProp.nSHalfPixelSzW = gProp.nSPixelSzW / 2 + 1;
409  }
410  else
411  {
412  gProp.nSHalfPixelSzW = 0;
413  }
414  // consider 'small' twip-to-pixel relations
415  if ( !bSmallTwipToPxRelH )
416  {
417  gProp.nSHalfPixelSzH = gProp.nSPixelSzH / 2 + 1;
418  }
419  else
420  {
421  gProp.nSHalfPixelSzH = 0;
422  }
423 
424  gProp.nSMinDistPixelW = gProp.nSPixelSzW * 2 + 1;
425  gProp.nSMinDistPixelH = gProp.nSPixelSzH * 2 + 1;
426 
427  const MapMode &rMap = pOut->GetMapMode();
428  gProp.aSScaleX = double(rMap.GetScaleX());
429  gProp.aSScaleY = double(rMap.GetScaleY());
430 }
431 
432 namespace {
433 
437 class SwSavePaintStatics : public SwPaintProperties
438 {
439 public:
440  SwSavePaintStatics();
441  ~SwSavePaintStatics();
442 };
443 
444 }
445 
446 SwSavePaintStatics::SwSavePaintStatics()
447 {
448  // Saving globales
449  bSFlyMetafile = gProp.bSFlyMetafile;
450  pSGlobalShell = gProp.pSGlobalShell;
451  pSFlyMetafileOut = gProp.pSFlyMetafileOut;
452  pSRetoucheFly = gProp.pSRetoucheFly;
453  pSRetoucheFly2 = gProp.pSRetoucheFly2;
454  pSFlyOnlyDraw = gProp.pSFlyOnlyDraw;
455  pBLines = std::move(gProp.pBLines);
456  pSLines = std::move(gProp.pSLines);
457  pSSubsLines = std::move(gProp.pSSubsLines);
458  pSSpecSubsLines = std::move(gProp.pSSpecSubsLines);
459  pSProgress = gProp.pSProgress;
460  nSPixelSzW = gProp.nSPixelSzW;
461  nSPixelSzH = gProp.nSPixelSzH;
462  nSHalfPixelSzW = gProp.nSHalfPixelSzW;
463  nSHalfPixelSzH = gProp.nSHalfPixelSzH;
464  nSMinDistPixelW = gProp.nSMinDistPixelW;
465  nSMinDistPixelH = gProp.nSMinDistPixelH ;
466  aSGlobalRetoucheColor = aGlobalRetoucheColor;
467  aSScaleX = gProp.aSScaleX;
468  aSScaleY = gProp.aSScaleY;
469 
470  // Restoring globales to default
471  gProp.bSFlyMetafile = false;
472  gProp.pSFlyMetafileOut = nullptr;
473  gProp.pSRetoucheFly = nullptr;
474  gProp.pSRetoucheFly2 = nullptr;
475  gProp.nSPixelSzW = gProp.nSPixelSzH =
476  gProp.nSHalfPixelSzW = gProp.nSHalfPixelSzH =
477  gProp.nSMinDistPixelW = gProp.nSMinDistPixelH = 0;
478  gProp.aSScaleX = gProp.aSScaleY = 1.0;
479  gProp.pSProgress = nullptr;
480 }
481 
482 SwSavePaintStatics::~SwSavePaintStatics()
483 {
484  // Restoring globales to saved one
485  gProp.pSGlobalShell = pSGlobalShell;
486  gProp.bSFlyMetafile = bSFlyMetafile;
487  gProp.pSFlyMetafileOut = pSFlyMetafileOut;
488  gProp.pSRetoucheFly = pSRetoucheFly;
489  gProp.pSRetoucheFly2 = pSRetoucheFly2;
490  gProp.pSFlyOnlyDraw = pSFlyOnlyDraw;
491  gProp.pBLines = std::move(pBLines);
492  gProp.pSLines = std::move(pSLines);
493  gProp.pSSubsLines = std::move(pSSubsLines);
494  gProp.pSSpecSubsLines = std::move(pSSpecSubsLines);
495  gProp.pSProgress = pSProgress;
496  gProp.nSPixelSzW = nSPixelSzW;
497  gProp.nSPixelSzH = nSPixelSzH;
498  gProp.nSHalfPixelSzW = nSHalfPixelSzW;
499  gProp.nSHalfPixelSzH = nSHalfPixelSzH;
500  gProp.nSMinDistPixelW = nSMinDistPixelW;
501  gProp.nSMinDistPixelH = nSMinDistPixelH;
502  aGlobalRetoucheColor = aSGlobalRetoucheColor;
503  gProp.aSScaleX = aSScaleX;
504  gProp.aSScaleY = aSScaleY;
505 }
506 
507 void BorderLines::AddBorderLines(drawinglayer::primitive2d::Primitive2DContainer&& rContainer)
508 {
509  if(!rContainer.empty())
510  {
511  m_Lines.append(std::move(rContainer));
512  }
513 }
514 
515 SwLineRect::SwLineRect(const SwRect& rRect, const Color* pCol, const SvxBorderLineStyle nStyl,
516  const SwTabFrame* pT, const SubColFlags nSCol)
517  : SwRect(rRect)
518  , m_nStyle(nStyl)
519  , m_pTabFrame(pT)
520  , m_nSubColor(nSCol)
521  , m_bPainted(false)
522  , m_nLock(0)
523 {
524  if ( pCol != nullptr )
525  m_aColor = *pCol;
526 }
527 
528 bool SwLineRect::MakeUnion( const SwRect &rRect, SwPaintProperties const & properties)
529 {
530  // It has already been tested outside, whether the rectangles have
531  // the same orientation (horizontal or vertical), color, etc.
532  if ( Height() > Width() ) //Vertical line
533  {
534  if ( Left() == rRect.Left() && Width() == rRect.Width() )
535  {
536  // Merge when there is no gap between the lines
537  const tools::Long nAdd = properties.nSPixelSzW + properties.nSHalfPixelSzW;
538  if ( Bottom() + nAdd >= rRect.Top() &&
539  Top() - nAdd <= rRect.Bottom() )
540  {
541  Bottom( std::max( Bottom(), rRect.Bottom() ) );
542  Top ( std::min( Top(), rRect.Top() ) );
543  return true;
544  }
545  }
546  }
547  else
548  {
549  if ( Top() == rRect.Top() && Height() == rRect.Height() )
550  {
551  // Merge when there is no gap between the lines
552  const tools::Long nAdd = properties.nSPixelSzW + properties.nSHalfPixelSzW;
553  if ( Right() + nAdd >= rRect.Left() &&
554  Left() - nAdd <= rRect.Right() )
555  {
556  Right( std::max( Right(), rRect.Right() ) );
557  Left ( std::min( Left(), rRect.Left() ) );
558  return true;
559  }
560  }
561  }
562  return false;
563 }
564 
565 void SwLineRects::AddLineRect( const SwRect &rRect, const Color *pCol, const SvxBorderLineStyle nStyle,
566  const SwTabFrame *pTab, const SubColFlags nSCol, SwPaintProperties const & properties )
567 {
568  // Loop backwards because lines which can be combined, can usually be painted
569  // in the same context
570  for (reverse_iterator it = m_aLineRects.rbegin(); it != m_aLineRects.rend(); ++it)
571  {
572  SwLineRect &rLRect = *it;
573  // Test for the orientation, color, table
574  if ( rLRect.GetTab() == pTab &&
575  !rLRect.IsPainted() && rLRect.GetSubColor() == nSCol &&
576  (rLRect.Height() > rLRect.Width()) == (rRect.Height() > rRect.Width()) &&
577  (pCol && rLRect.GetColor() == *pCol) )
578  {
579  if ( rLRect.MakeUnion( rRect, properties ) )
580  return;
581  }
582  }
583  m_aLineRects.emplace_back(rRect, pCol, nStyle, pTab, nSCol);
584 }
585 
586 void SwLineRects::ConnectEdges( OutputDevice const *pOut, SwPaintProperties const & properties )
587 {
588  if ( pOut->GetOutDevType() != OUTDEV_PRINTER )
589  {
590  // I'm not doing anything for a too small zoom
591  if ( properties.aSScaleX < aEdgeScale || properties.aSScaleY < aEdgeScale )
592  return;
593  }
594 
595  static const tools::Long nAdd = 20;
596 
597  std::vector<SwLineRect*> aCheck;
598 
599  for (size_t i = 0; i < m_aLineRects.size(); ++i)
600  {
601  SwLineRect& rL1 = m_aLineRects[i];
602  if ( !rL1.GetTab() || rL1.IsPainted() || rL1.IsLocked() )
603  continue;
604 
605  aCheck.clear();
606 
607  const bool bVert = rL1.Height() > rL1.Width();
608  tools::Long nL1a, nL1b, nL1c, nL1d;
609 
610  if ( bVert )
611  {
612  nL1a = rL1.Top(); nL1b = rL1.Left();
613  nL1c = rL1.Right(); nL1d = rL1.Bottom();
614  }
615  else
616  {
617  nL1a = rL1.Left(); nL1b = rL1.Top();
618  nL1c = rL1.Bottom(); nL1d = rL1.Right();
619  }
620 
621  // Collect all lines to possibly link with i1
622  for (iterator it2 = m_aLineRects.begin(); it2 != m_aLineRects.end(); ++it2)
623  {
624  SwLineRect &rL2 = *it2;
625  if ( rL2.GetTab() != rL1.GetTab() ||
626  rL2.IsPainted() ||
627  rL2.IsLocked() ||
628  (bVert == (rL2.Height() > rL2.Width())) )
629  continue;
630 
631  tools::Long nL2a, nL2b, nL2c, nL2d;
632  if ( bVert )
633  {
634  nL2a = rL2.Top(); nL2b = rL2.Left();
635  nL2c = rL2.Right(); nL2d = rL2.Bottom();
636  }
637  else
638  {
639  nL2a = rL2.Left(); nL2b = rL2.Top();
640  nL2c = rL2.Bottom(); nL2d = rL2.Right();
641  }
642 
643  if ( (nL1a - nAdd < nL2d && nL1d + nAdd > nL2a) &&
644  ((nL1b > nL2b && nL1c < nL2c) ||
645  (nL1c >= nL2c && nL1b - nAdd < nL2c) ||
646  (nL1b <= nL2b && nL1c + nAdd > nL2b)) )
647  {
648  aCheck.push_back( &rL2 );
649  }
650  }
651  if ( aCheck.size() < 2 )
652  continue;
653 
654  bool bRemove = false;
655 
656  // For each line test all following ones.
657  for ( size_t k = 0; !bRemove && k < aCheck.size(); ++k )
658  {
659  SwLineRect &rR1 = *aCheck[k];
660 
661  for ( size_t k2 = k+1; !bRemove && k2 < aCheck.size(); ++k2 )
662  {
663  SwLineRect &rR2 = *aCheck[k2];
664  if ( bVert )
665  {
666  SwLineRect *pLA = nullptr;
667  SwLineRect *pLB = nullptr;
668  if ( rR1.Top() < rR2.Top() )
669  {
670  pLA = &rR1; pLB = &rR2;
671  }
672  else if ( rR1.Top() > rR2.Top() )
673  {
674  pLA = &rR2; pLB = &rR1;
675  }
676  // are k1 and k2 describing a double line?
677  if ( pLA && pLA->Bottom() + 60 > pLB->Top() )
678  {
679  if ( rL1.Top() < pLA->Top() )
680  {
681  if ( rL1.Bottom() == pLA->Bottom() )
682  continue; //Small mistake (where?)
683 
684  SwRect aIns( rL1 );
685  aIns.Bottom( pLA->Bottom() );
686  if ( !rL1.Contains( aIns ) )
687  continue;
688  m_aLineRects.emplace_back(aIns, &rL1.GetColor(),
689  SvxBorderLineStyle::SOLID, rL1.GetTab(),
690  SubColFlags::Tab);
691  if ( isFull() )
692  {
693  --i;
694  k = aCheck.size();
695  break;
696  }
697  }
698 
699  if ( rL1.Bottom() > pLB->Bottom() )
700  rL1.Top( pLB->Top() ); // extend i1 on the top
701  else
702  bRemove = true; //stopping, remove i1
703  }
704  }
705  else
706  {
707  SwLineRect *pLA = nullptr;
708  SwLineRect *pLB = nullptr;
709  if ( rR1.Left() < rR2.Left() )
710  {
711  pLA = &rR1; pLB = &rR2;
712  }
713  else if ( rR1.Left() > rR2.Left() )
714  {
715  pLA = &rR2; pLB = &rR1;
716  }
717  // Is it double line?
718  if ( pLA && pLA->Right() + 60 > pLB->Left() )
719  {
720  if ( rL1.Left() < pLA->Left() )
721  {
722  if ( rL1.Right() == pLA->Right() )
723  continue; //small error
724 
725  SwRect aIns( rL1 );
726  aIns.Right( pLA->Right() );
727  if ( !rL1.Contains( aIns ) )
728  continue;
729  m_aLineRects.emplace_back(aIns, &rL1.GetColor(),
730  SvxBorderLineStyle::SOLID, rL1.GetTab(),
731  SubColFlags::Tab);
732  if ( isFull() )
733  {
734  --i;
735  k = aCheck.size();
736  break;
737  }
738  }
739  if ( rL1.Right() > pLB->Right() )
740  rL1.Left( pLB->Left() );
741  else
742  bRemove = true;
743  }
744  }
745  }
746  }
747  if ( bRemove )
748  {
749  m_aLineRects.erase(m_aLineRects.begin() + i);
750  --i;
751  }
752  }
753 }
754 
755 void SwSubsRects::RemoveSuperfluousSubsidiaryLines( const SwLineRects &rRects, SwPaintProperties const & properties )
756 {
757  // All help lines that are covered by any border will be removed or split
758  for (size_t i = 0; i < m_aLineRects.size(); ++i)
759  {
760  // get a copy instead of a reference, because an <insert> may destroy
761  // the object due to a necessary array resize.
762  const SwLineRect aSubsLineRect(m_aLineRects[i]);
763 
764  // add condition <aSubsLineRect.IsLocked()> in order to consider only
765  // border lines, which are *not* locked.
766  if ( aSubsLineRect.IsPainted() ||
767  aSubsLineRect.IsLocked() )
768  continue;
769 
770  const bool bVerticalSubs = aSubsLineRect.Height() > aSubsLineRect.Width();
771  SwRect aSubsRect( aSubsLineRect );
772  if ( bVerticalSubs )
773  {
774  aSubsRect.AddLeft ( - (properties.nSPixelSzW+properties.nSHalfPixelSzW) );
775  aSubsRect.AddRight ( properties.nSPixelSzW+properties.nSHalfPixelSzW );
776  }
777  else
778  {
779  aSubsRect.AddTop ( - (properties.nSPixelSzH+properties.nSHalfPixelSzH) );
780  aSubsRect.AddBottom( properties.nSPixelSzH+properties.nSHalfPixelSzH );
781  }
782  for (const_iterator itK = rRects.m_aLineRects.begin(); itK != rRects.m_aLineRects.end();
783  ++itK)
784  {
785  const SwLineRect &rLine = *itK;
786 
787  // do *not* consider painted or locked border lines.
788  // #i1837# - locked border lines have to be considered.
789  if ( rLine.IsLocked () )
790  continue;
791 
792  if ( !bVerticalSubs == ( rLine.Height() > rLine.Width() ) ) //same direction?
793  continue;
794 
795  if ( aSubsRect.Overlaps( rLine ) )
796  {
797  if ( bVerticalSubs ) // Vertical?
798  {
799  if ( aSubsRect.Left() <= rLine.Right() &&
800  aSubsRect.Right() >= rLine.Left() )
801  {
802  tools::Long nTmp = rLine.Top()-(properties.nSPixelSzH+1);
803  if ( aSubsLineRect.Top() < nTmp )
804  {
805  SwRect aNewSubsRect( aSubsLineRect );
806  aNewSubsRect.Bottom( nTmp );
807  m_aLineRects.emplace_back(aNewSubsRect, nullptr,
808  aSubsLineRect.GetStyle(), nullptr,
809  aSubsLineRect.GetSubColor());
810  }
811  nTmp = rLine.Bottom()+properties.nSPixelSzH+1;
812  if ( aSubsLineRect.Bottom() > nTmp )
813  {
814  SwRect aNewSubsRect( aSubsLineRect );
815  aNewSubsRect.Top( nTmp );
816  m_aLineRects.emplace_back(aNewSubsRect, nullptr,
817  aSubsLineRect.GetStyle(), nullptr,
818  aSubsLineRect.GetSubColor());
819  }
820  m_aLineRects.erase(m_aLineRects.begin() + i);
821  --i;
822  break;
823  }
824  }
825  else // Horizontal
826  {
827  if ( aSubsRect.Top() <= rLine.Bottom() &&
828  aSubsRect.Bottom() >= rLine.Top() )
829  {
830  tools::Long nTmp = rLine.Left()-(properties.nSPixelSzW+1);
831  if ( aSubsLineRect.Left() < nTmp )
832  {
833  SwRect aNewSubsRect( aSubsLineRect );
834  aNewSubsRect.Right( nTmp );
835  m_aLineRects.emplace_back(aNewSubsRect, nullptr,
836  aSubsLineRect.GetStyle(), nullptr,
837  aSubsLineRect.GetSubColor());
838  }
839  nTmp = rLine.Right()+properties.nSPixelSzW+1;
840  if ( aSubsLineRect.Right() > nTmp )
841  {
842  SwRect aNewSubsRect( aSubsLineRect );
843  aNewSubsRect.Left( nTmp );
844  m_aLineRects.emplace_back(aNewSubsRect, nullptr,
845  aSubsLineRect.GetStyle(), nullptr,
846  aSubsLineRect.GetSubColor());
847  }
848  m_aLineRects.erase(m_aLineRects.begin() + i);
849  --i;
850  break;
851  }
852  }
853  }
854  }
855  }
856 }
857 
858 void SwLineRects::LockLines( bool bLock )
859 {
860  for (SwLineRect& rLRect : m_aLineRects)
861  rLRect.Lock(bLock);
862 }
863 
864 static void lcl_DrawDashedRect( OutputDevice * pOut, SwLineRect const & rLRect )
865 {
866  tools::Long startX = rLRect.Left( ), endX;
867  tools::Long startY = rLRect.Top( ), endY;
868 
869  // Discriminate vertically stretched rect from horizontally stretched
870  // and restrict minimum nHalfLWidth to 1
871  tools::Long nHalfLWidth = std::max( std::min( rLRect.Width( ), rLRect.Height( ) ) / 2, tools::Long(1) );
872 
873  if ( rLRect.Height( ) > rLRect.Width( ) )
874  {
875  startX += nHalfLWidth;
876  endX = startX;
877  endY = startY + rLRect.Height( );
878  }
879  else
880  {
881  startY += nHalfLWidth;
882  endY = startY;
883  endX = startX + rLRect.Width( );
884  }
885 
886  svtools::DrawLine( *pOut, Point( startX, startY ), Point( endX, endY ),
887  sal_uInt32( nHalfLWidth * 2 ), rLRect.GetStyle( ) );
888 }
889 
890 void SwLineRects::PaintLines( OutputDevice *pOut, SwPaintProperties const &properties )
891 {
892  // Paint the borders. Sadly two passes are needed.
893  // Once for the inside and once for the outside edges of tables
894  if (m_aLineRects.size() == m_nLastCount)
895  return;
896 
897  // #i16816# tagged pdf support
898  SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, nullptr, *pOut );
899 
901  pOut->SetFillColor();
902  pOut->SetLineColor();
903  ConnectEdges( pOut, properties );
904  const Color *pLast = nullptr;
905 
906  bool bPaint2nd = false;
907  size_t nMinCount = m_aLineRects.size();
908 
909  for (size_t i = 0; i < m_aLineRects.size(); ++i)
910  {
911  SwLineRect& rLRect = m_aLineRects[i];
912 
913  if ( rLRect.IsPainted() )
914  continue;
915 
916  if ( rLRect.IsLocked() )
917  {
918  nMinCount = std::min( nMinCount, i );
919  continue;
920  }
921 
922  // Paint it now or in the second pass?
923  bool bPaint = true;
924  if ( rLRect.GetTab() )
925  {
926  if ( rLRect.Height() > rLRect.Width() )
927  {
928  // Vertical edge, overlapping with the table edge?
929  SwTwips nLLeft = rLRect.Left() - 30,
930  nLRight = rLRect.Right() + 30,
931  nTLeft = rLRect.GetTab()->getFrameArea().Left() + rLRect.GetTab()->getFramePrintArea().Left(),
932  nTRight = rLRect.GetTab()->getFrameArea().Left() + rLRect.GetTab()->getFramePrintArea().Right();
933  if ( (nTLeft >= nLLeft && nTLeft <= nLRight) ||
934  (nTRight>= nLLeft && nTRight<= nLRight) )
935  bPaint = false;
936  }
937  else
938  {
939  // Horizontal edge, overlapping with the table edge?
940  SwTwips nLTop = rLRect.Top() - 30,
941  nLBottom = rLRect.Bottom() + 30,
942  nTTop = rLRect.GetTab()->getFrameArea().Top() + rLRect.GetTab()->getFramePrintArea().Top(),
943  nTBottom = rLRect.GetTab()->getFrameArea().Top() + rLRect.GetTab()->getFramePrintArea().Bottom();
944  if ( (nTTop >= nLTop && nTTop <= nLBottom) ||
945  (nTBottom >= nLTop && nTBottom <= nLBottom) )
946  bPaint = false;
947  }
948  }
949  if ( bPaint )
950  {
951  if ( !pLast || *pLast != rLRect.GetColor() )
952  {
953  pLast = &rLRect.GetColor();
954 
955  DrawModeFlags nOldDrawMode = pOut->GetDrawMode();
956  if( properties.pSGlobalShell->GetWin() &&
958  pOut->SetDrawMode( DrawModeFlags::Default );
959 
960  pOut->SetLineColor( *pLast );
961  pOut->SetFillColor( *pLast );
962  pOut->SetDrawMode( nOldDrawMode );
963  }
964 
965  if( !rLRect.IsEmpty() )
966  lcl_DrawDashedRect( pOut, rLRect );
967  rLRect.SetPainted();
968  }
969  else
970  bPaint2nd = true;
971  }
972  if ( bPaint2nd )
973  {
974  for (size_t i = 0; i < m_aLineRects.size(); ++i)
975  {
976  SwLineRect& rLRect = m_aLineRects[i];
977  if ( rLRect.IsPainted() )
978  continue;
979 
980  if ( rLRect.IsLocked() )
981  {
982  nMinCount = std::min( nMinCount, i );
983  continue;
984  }
985 
986  if ( !pLast || *pLast != rLRect.GetColor() )
987  {
988  pLast = &rLRect.GetColor();
989 
990  DrawModeFlags nOldDrawMode = pOut->GetDrawMode();
991  if( properties.pSGlobalShell->GetWin() &&
993  {
994  pOut->SetDrawMode( DrawModeFlags::Default );
995  }
996 
997  pOut->SetFillColor( *pLast );
998  pOut->SetDrawMode( nOldDrawMode );
999  }
1000  if( !rLRect.IsEmpty() )
1001  lcl_DrawDashedRect( pOut, rLRect );
1002  rLRect.SetPainted();
1003  }
1004  }
1005  m_nLastCount = nMinCount;
1006  pOut->Pop();
1007 
1008 }
1009 
1010 void SwSubsRects::PaintSubsidiary( OutputDevice *pOut,
1011  const SwLineRects *pRects,
1012  SwPaintProperties const & properties )
1013 {
1014  if (m_aLineRects.empty())
1015  return;
1016 
1017  // #i16816# tagged pdf support
1018  SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, nullptr, *pOut );
1019 
1020  // Remove all help line that are almost covered (tables)
1021  for (size_type i = 0; i != m_aLineRects.size(); ++i)
1022  {
1023  SwLineRect& rLi = m_aLineRects[i];
1024  const bool bVerticalSubs = rLi.Height() > rLi.Width();
1025 
1026  for (size_type k = i + 1; k != m_aLineRects.size(); ++k)
1027  {
1028  SwLineRect& rLk = m_aLineRects[k];
1029  if ( rLi.SSize() == rLk.SSize() )
1030  {
1031  if ( bVerticalSubs == ( rLk.Height() > rLk.Width() ) )
1032  {
1033  if ( bVerticalSubs )
1034  {
1035  tools::Long nLi = rLi.Right();
1036  tools::Long nLk = rLk.Right();
1037  if ( rLi.Top() == rLk.Top() &&
1038  ((nLi < rLk.Left() && nLi+21 > rLk.Left()) ||
1039  (nLk < rLi.Left() && nLk+21 > rLi.Left())))
1040  {
1041  m_aLineRects.erase(m_aLineRects.begin() + i);
1042  // don't continue with inner loop any more:
1043  // the array may shrink!
1044  --i;
1045  break;
1046  }
1047  }
1048  else
1049  {
1050  tools::Long nLi = rLi.Bottom();
1051  tools::Long nLk = rLk.Bottom();
1052  if ( rLi.Left() == rLk.Left() &&
1053  ((nLi < rLk.Top() && nLi+21 > rLk.Top()) ||
1054  (nLk < rLi.Top() && nLk+21 > rLi.Top())))
1055  {
1056  m_aLineRects.erase(m_aLineRects.begin() + i);
1057  // don't continue with inner loop any more:
1058  // the array may shrink!
1059  --i;
1060  break;
1061  }
1062  }
1063  }
1064  }
1065  }
1066  }
1067 
1068  if (pRects && (!pRects->m_aLineRects.empty()))
1069  RemoveSuperfluousSubsidiaryLines( *pRects, properties );
1070 
1071  if (m_aLineRects.empty())
1072  return;
1073 
1075  pOut->SetLineColor();
1076 
1077  // Reset draw mode in high contrast mode in order to get fill color
1078  // set at output device. Recover draw mode after draw of lines.
1079  // Necessary for the subsidiary lines painted by the fly frames.
1080  DrawModeFlags nOldDrawMode = pOut->GetDrawMode();
1081  if( gProp.pSGlobalShell->GetWin() &&
1083  {
1084  pOut->SetDrawMode( DrawModeFlags::Default );
1085  }
1086 
1087  for (SwLineRect& rLRect : m_aLineRects)
1088  {
1089  // Add condition <!rLRect.IsLocked()> to prevent paint of locked subsidiary lines.
1090  if ( !rLRect.IsPainted() &&
1091  !rLRect.IsLocked() )
1092  {
1093  const Color *pCol = nullptr;
1094  switch ( rLRect.GetSubColor() )
1095  {
1096  case SubColFlags::Page: pCol = &SwViewOption::GetDocBoundariesColor(); break;
1097  case SubColFlags::Fly: pCol = &SwViewOption::GetObjectBoundariesColor(); break;
1098  case SubColFlags::Tab: pCol = &SwViewOption::GetTableBoundariesColor(); break;
1099  case SubColFlags::Sect: pCol = &SwViewOption::GetSectionBoundColor(); break;
1100  }
1101 
1102  if (pCol && pOut->GetFillColor() != *pCol)
1103  pOut->SetFillColor( *pCol );
1104  pOut->DrawRect( rLRect.SVRect() );
1105 
1106  rLRect.SetPainted();
1107  }
1108  }
1109 
1110  pOut->SetDrawMode( nOldDrawMode );
1111 
1112  pOut->Pop();
1113 }
1114 
1115 // Various functions that are use in this file.
1116 
1124 void SwAlignRect( SwRect &rRect, const SwViewShell *pSh, const vcl::RenderContext* pRenderContext )
1125 {
1126  if( !rRect.HasArea() )
1127  return;
1128 
1129  // Make sure that view shell (parameter <pSh>) exists, if the output device
1130  // is taken from this view shell --> no output device, no alignment
1131  // Output device taken from view shell <pSh>, if <gProp.bSFlyMetafile> not set
1132  if ( !gProp.bSFlyMetafile && !pSh )
1133  {
1134  return;
1135  }
1136 
1137  const vcl::RenderContext *pOut = gProp.bSFlyMetafile ?
1138  gProp.pSFlyMetafileOut.get() : pRenderContext;
1139 
1140  // Hold original rectangle in pixel
1141  const tools::Rectangle aOrgPxRect = pOut->LogicToPixel( rRect.SVRect() );
1142  // Determine pixel-center rectangle in twip
1143  const SwRect aPxCenterRect( pOut->PixelToLogic( aOrgPxRect ) );
1144 
1145  // Perform adjustments on pixel level.
1146  SwRect aAlignedPxRect( aOrgPxRect );
1147  if ( rRect.Top() > aPxCenterRect.Top() )
1148  {
1149  // 'leave pixel overlapping on top'
1150  aAlignedPxRect.AddTop( 1 );
1151  }
1152 
1153  if ( rRect.Bottom() < aPxCenterRect.Bottom() )
1154  {
1155  // 'leave pixel overlapping on bottom'
1156  aAlignedPxRect.AddBottom( - 1 );
1157  }
1158 
1159  if ( rRect.Left() > aPxCenterRect.Left() )
1160  {
1161  // 'leave pixel overlapping on left'
1162  aAlignedPxRect.AddLeft( 1 );
1163  }
1164 
1165  if ( rRect.Right() < aPxCenterRect.Right() )
1166  {
1167  // 'leave pixel overlapping on right'
1168  aAlignedPxRect.AddRight( - 1 );
1169  }
1170 
1171  // Consider negative width/height check, if aligned SwRect has negative width/height.
1172  // If Yes, adjust it to width/height = 0 twip.
1173  // NOTE: A SwRect with negative width/height can occur, if the width/height
1174  // of the given SwRect in twip was less than a pixel in twip and that
1175  // the alignment calculates that the aligned SwRect should not contain
1176  // the pixels the width/height is on.
1177  if ( aAlignedPxRect.Width() < 0 )
1178  {
1179  aAlignedPxRect.Width(0);
1180  }
1181  if ( aAlignedPxRect.Height() < 0 )
1182  {
1183  aAlignedPxRect.Height(0);
1184  }
1185  // Consider zero width/height for converting a rectangle from
1186  // pixel to logic it needs a width/height. Thus, set width/height
1187  // to one, if it's zero and correct this on the twip level after the conversion.
1188  bool bZeroWidth = false;
1189  if ( aAlignedPxRect.Width() == 0 )
1190  {
1191  aAlignedPxRect.Width(1);
1192  bZeroWidth = true;
1193  }
1194  bool bZeroHeight = false;
1195  if ( aAlignedPxRect.Height() == 0 )
1196  {
1197  aAlignedPxRect.Height(1);
1198  bZeroHeight = true;
1199  }
1200 
1201  rRect = SwRect(pOut->PixelToLogic( aAlignedPxRect.SVRect() ));
1202 
1203  // Consider zero width/height and adjust calculated aligned twip rectangle.
1204  // Reset width/height to zero; previous negative width/height haven't to be considered.
1205  if ( bZeroWidth )
1206  {
1207  rRect.Width(0);
1208  }
1209  if ( bZeroHeight )
1210  {
1211  rRect.Height(0);
1212  }
1213 }
1214 
1230 void SwAlignGrfRect( SwRect *pGrfRect, const vcl::RenderContext &rOut )
1231 {
1232  tools::Rectangle aPxRect = rOut.LogicToPixel( pGrfRect->SVRect() );
1233  pGrfRect->Pos( rOut.PixelToLogic( aPxRect.TopLeft() ) );
1234  pGrfRect->SSize( rOut.PixelToLogic( aPxRect.GetSize() ) );
1235 }
1236 
1237 static tools::Long lcl_AlignWidth( const tools::Long nWidth, SwPaintProperties const & properties )
1238 {
1239  if ( nWidth )
1240  {
1241  const tools::Long nW = nWidth % properties.nSPixelSzW;
1242 
1243  if ( !nW || nW > properties.nSHalfPixelSzW )
1244  return std::max(tools::Long(1), nWidth - properties.nSHalfPixelSzW);
1245  }
1246  return nWidth;
1247 }
1248 
1249 static tools::Long lcl_AlignHeight( const tools::Long nHeight, SwPaintProperties const & properties )
1250 {
1251  if ( nHeight )
1252  {
1253  const tools::Long nH = nHeight % properties.nSPixelSzH;
1254 
1255  if ( !nH || nH > properties.nSHalfPixelSzH )
1256  return std::max(tools::Long(1), nHeight - properties.nSHalfPixelSzH);
1257  }
1258  return nHeight;
1259 }
1260 
1264 static void lcl_CalcBorderRect( SwRect &rRect, const SwFrame *pFrame,
1265  const SwBorderAttrs &rAttrs,
1266  const bool bShadow,
1267  SwPaintProperties const & properties)
1268 {
1269  // Special handling for cell frames.
1270  // The printing area of a cell frame is completely enclosed in the frame area
1271  // and a cell frame has no shadow. Thus, for cell frames the calculated
1272  // area equals the frame area.
1273  // Notes: Borders of cell frames in R2L text direction will switch its side
1274  // - left border is painted on the right; right border on the left.
1275  // See <lcl_PaintLeftLine> and <lcl_PaintRightLine>.
1276  if( pFrame->IsSctFrame() )
1277  {
1278  rRect = pFrame->getFramePrintArea();
1279  rRect.Pos() += pFrame->getFrameArea().Pos();
1280  }
1281  else if ( pFrame->IsCellFrame() )
1282  rRect = pFrame->getFrameArea();
1283  else
1284  {
1285  rRect = pFrame->getFramePrintArea();
1286  rRect.Pos() += pFrame->getFrameArea().Pos();
1287 
1288  SwRectFn fnRect = pFrame->IsVertical() ? ( pFrame->IsVertLR() ? (pFrame->IsVertLRBT() ? fnRectVertL2RB2T : fnRectVertL2R) : fnRectVert ) : fnRectHori;
1289 
1290  const SvxBoxItem &rBox = rAttrs.GetBox();
1291  const bool bTop = 0 != (pFrame->*fnRect->fnGetTopMargin)();
1292  if ( bTop )
1293  {
1294  SwTwips nDiff = rBox.GetTop() ?
1295  rBox.CalcLineSpace( SvxBoxItemLine::TOP ) :
1296  rBox.GetDistance( SvxBoxItemLine::TOP );
1297  if( nDiff )
1298  (rRect.*fnRect->fnSubTop)( nDiff );
1299  }
1300 
1301  const bool bBottom = 0 != (pFrame->*fnRect->fnGetBottomMargin)();
1302  if ( bBottom )
1303  {
1304  SwTwips nDiff = 0;
1305  // #i29550#
1306  if ( pFrame->IsTabFrame() &&
1307  static_cast<const SwTabFrame*>(pFrame)->IsCollapsingBorders() )
1308  {
1309  // For collapsing borders, we have to add the height of
1310  // the height of the last line
1311  nDiff = static_cast<const SwTabFrame*>(pFrame)->GetBottomLineSize();
1312  }
1313  else
1314  {
1315  nDiff = rBox.GetBottom() ?
1316  rBox.CalcLineSpace( SvxBoxItemLine::BOTTOM ) :
1317  rBox.GetDistance( SvxBoxItemLine::BOTTOM );
1318  }
1319  if( nDiff )
1320  (rRect.*fnRect->fnAddBottom)( nDiff );
1321  }
1322 
1323  if ( rBox.GetLeft() )
1324  (rRect.*fnRect->fnSubLeft)( rBox.CalcLineSpace( SvxBoxItemLine::LEFT ) );
1325  else
1326  (rRect.*fnRect->fnSubLeft)( rBox.GetDistance( SvxBoxItemLine::LEFT ) );
1327 
1328  if ( rBox.GetRight() )
1329  (rRect.*fnRect->fnAddRight)( rBox.CalcLineSpace( SvxBoxItemLine::RIGHT ) );
1330  else
1331  (rRect.*fnRect->fnAddRight)( rBox.GetDistance( SvxBoxItemLine::RIGHT ) );
1332 
1333  if ( bShadow && rAttrs.GetShadow().GetLocation() != SvxShadowLocation::NONE )
1334  {
1335  const SvxShadowItem &rShadow = rAttrs.GetShadow();
1336  if ( bTop )
1337  (rRect.*fnRect->fnSubTop)(rShadow.CalcShadowSpace(SvxShadowItemSide::TOP));
1338  (rRect.*fnRect->fnSubLeft)(rShadow.CalcShadowSpace(SvxShadowItemSide::LEFT));
1339  if ( bBottom )
1340  (rRect.*fnRect->fnAddBottom)
1341  (rShadow.CalcShadowSpace( SvxShadowItemSide::BOTTOM ));
1342  (rRect.*fnRect->fnAddRight)(rShadow.CalcShadowSpace(SvxShadowItemSide::RIGHT));
1343  }
1344  }
1345 
1346  ::SwAlignRect( rRect, properties.pSGlobalShell, properties.pSGlobalShell ? properties.pSGlobalShell->GetOut() : nullptr );
1347 }
1348 
1353 static void lcl_ExtendLeftAndRight( SwRect& _rRect,
1354  const SwFrame& _rFrame,
1355  const SwBorderAttrs& _rAttrs,
1356  const SwRectFn& _rRectFn )
1357 {
1358  if ( _rAttrs.JoinedWithPrev( _rFrame ) )
1359  {
1360  const SwFrame* pPrevFrame = _rFrame.GetPrev();
1361  (_rRect.*_rRectFn->fnSetTop)( (pPrevFrame->*_rRectFn->fnGetPrtBottom)() );
1362  }
1363  if ( _rAttrs.JoinedWithNext( _rFrame ) )
1364  {
1365  const SwFrame* pNextFrame = _rFrame.GetNext();
1366  (_rRect.*_rRectFn->fnSetBottom)( (pNextFrame->*_rRectFn->fnGetPrtTop)() );
1367  }
1368 }
1369 
1373 {
1374  static MapMode aMapMode(MapUnit::MapTwip);
1375  static const Size aSingleUnit = Application::GetDefaultDevice()->PixelToLogic(Size(1, 1), aMapMode);
1376 
1377  double x1 = rRect.Left() + aSingleUnit.getWidth();
1378  double y1 = rRect.Top() + aSingleUnit.getHeight();
1379  double x2 = rRect.Right() - aSingleUnit.getWidth();
1380  double y2 = rRect.Bottom() - aSingleUnit.getHeight();
1381 
1382  return basegfx::B2DRange(x1, y1, x2, y2);
1383 }
1384 
1385 static void lcl_SubtractFlys( const SwFrame *pFrame, const SwPageFrame *pPage,
1386  const SwRect &rRect, SwRegionRects &rRegion, basegfx::utils::B2DClipState& rClipState, SwPaintProperties const & rProperties)
1387 {
1388  const SwSortedObjs& rObjs = *pPage->GetSortedObjs();
1389  const SwFlyFrame* pSelfFly = pFrame->IsInFly() ? pFrame->FindFlyFrame() : gProp.pSRetoucheFly2;
1390  if (!gProp.pSRetoucheFly)
1391  gProp.pSRetoucheFly = gProp.pSRetoucheFly2;
1392 
1393  for (size_t j = 0; (j < rObjs.size()) && !rRegion.empty(); ++j)
1394  {
1395  const SwAnchoredObject* pAnchoredObj = rObjs[j];
1396  const SdrObject* pSdrObj = pAnchoredObj->GetDrawObj();
1397 
1398  // Do not consider invisible objects
1399  if (!pPage->GetFormat()->GetDoc()->getIDocumentDrawModelAccess().IsVisibleLayerId(pSdrObj->GetLayer()))
1400  continue;
1401 
1402  const SwFlyFrame *pFly = pAnchoredObj->DynCastFlyFrame();
1403  if (!pFly)
1404  continue;
1405 
1406  if (pSelfFly == pFly || gProp.pSRetoucheFly == pFly || !rRect.Overlaps(pFly->getFrameArea()))
1407  continue;
1408 
1409  if (!pFly->GetFormat()->GetPrint().GetValue() &&
1410  (OUTDEV_PRINTER == gProp.pSGlobalShell->GetOut()->GetOutDevType() ||
1411  gProp.pSGlobalShell->IsPreview()))
1412  continue;
1413 
1414  const bool bLowerOfSelf = pSelfFly && pFly->IsLowerOf( pSelfFly );
1415 
1416  //For character bound Flys only examine those Flys in which it is not
1417  //anchored itself.
1418  //Why only for character bound ones you may ask? It never makes sense to
1419  //subtract frames in which it is anchored itself right?
1420  if (pSelfFly && pSelfFly->IsLowerOf(pFly))
1421  continue;
1422 
1423  //Any why does it not apply for the RetoucheFly too?
1424  if (gProp.pSRetoucheFly && gProp.pSRetoucheFly->IsLowerOf(pFly))
1425  continue;
1426 
1427 #if OSL_DEBUG_LEVEL > 0
1428  //Flys who are anchored inside their own one, must have a bigger OrdNum
1429  //or be character bound.
1430  if (pSelfFly && bLowerOfSelf)
1431  {
1432  OSL_ENSURE( pFly->IsFlyInContentFrame() ||
1433  pSdrObj->GetOrdNumDirect() > pSelfFly->GetVirtDrawObj()->GetOrdNumDirect(),
1434  "Fly with wrong z-Order" );
1435  }
1436 #endif
1437 
1438  bool bStopOnHell = true;
1439  if (pSelfFly)
1440  {
1441  const SdrObject *pTmp = pSelfFly->GetVirtDrawObj();
1442  if (pSdrObj->GetLayer() == pTmp->GetLayer())
1443  {
1444  if (pSdrObj->GetOrdNumDirect() < pTmp->GetOrdNumDirect())
1445  //In the same layer we only observe those that are above.
1446  continue;
1447  }
1448  else
1449  {
1450  if (!bLowerOfSelf && !pFly->GetFormat()->GetOpaque().GetValue())
1451  //From other layers we are only interested in non
1452  //transparent ones or those that are internal
1453  continue;
1454  bStopOnHell = false;
1455  }
1456  }
1457  if (gProp.pSRetoucheFly)
1458  {
1459  const SdrObject *pTmp = gProp.pSRetoucheFly->GetVirtDrawObj();
1460  if ( pSdrObj->GetLayer() == pTmp->GetLayer() )
1461  {
1462  if ( pSdrObj->GetOrdNumDirect() < pTmp->GetOrdNumDirect() )
1463  //In the same layer we only observe those that are above.
1464  continue;
1465  }
1466  else
1467  {
1468  if (!pFly->IsLowerOf( gProp.pSRetoucheFly ) && !pFly->GetFormat()->GetOpaque().GetValue())
1469  //From other layers we are only interested in non
1470  //transparent ones or those that are internal
1471  continue;
1472  bStopOnHell = false;
1473  }
1474  }
1475 
1476  //If the content of the Fly is transparent, we subtract it only if it's
1477  //contained in the hell layer.
1479  bool bHell = pSdrObj->GetLayer() == rIDDMA.GetHellId();
1480  if ( (bStopOnHell && bHell) ||
1484  ( !bHell && pFly->Lower() && pFly->Lower()->IsNoTextFrame() &&
1485  (static_cast<SwNoTextFrame const*>(pFly->Lower())->IsTransparent() ||
1486  static_cast<SwNoTextFrame const*>(pFly->Lower())->HasAnimation() ||
1487  pFly->GetFormat()->GetSurround().IsContour()
1488  )
1489  )
1490  )
1491  continue;
1492 
1493  // Own if-statements for transparent background/shadow of fly frames
1494  // in order to handle special conditions.
1495  if (pFly->IsBackgroundTransparent())
1496  {
1497  // Background <pFly> is transparent drawn. Thus normally, its region
1498  // have not to be subtracted from given region.
1499  // But, if method is called for a fly frame and
1500  // <pFly> is a direct lower of this fly frame and
1501  // <pFly> inherites its transparent background brush from its parent,
1502  // then <pFly> frame area have to be subtracted from given region.
1503  // NOTE: Because in Status Quo transparent backgrounds can only be
1504  // assigned to fly frames, the handle of this special case
1505  // avoids drawing of transparent areas more than once, if
1506  // a fly frame inherites a transparent background from its
1507  // parent fly frame.
1508  if (pFrame->IsFlyFrame() &&
1509  (pFly->GetAnchorFrame()->FindFlyFrame() == pFrame) &&
1511  )
1512  {
1513  SwRect aRect;
1514  SwBorderAttrAccess aAccess( SwFrame::GetCache(), static_cast<SwFrame const *>(pFly) );
1515  const SwBorderAttrs &rAttrs = *aAccess.Get();
1516  ::lcl_CalcBorderRect( aRect, pFly, rAttrs, true, rProperties );
1517  rRegion -= aRect;
1518  rClipState.subtractRange(lcl_ShrinkFly(aRect));
1519  continue;
1520  }
1521  else
1522  {
1523  continue;
1524  }
1525  }
1526 
1527  if (bHell && pFly->GetAnchorFrame()->IsInFly())
1528  {
1529  //So the border won't get dismantled by the background of the other
1530  //Fly.
1531  SwRect aRect;
1532  SwBorderAttrAccess aAccess( SwFrame::GetCache(), static_cast<SwFrame const *>(pFly) );
1533  const SwBorderAttrs &rAttrs = *aAccess.Get();
1534  ::lcl_CalcBorderRect( aRect, pFly, rAttrs, true, rProperties );
1535  rRegion -= aRect;
1536  rClipState.subtractRange(lcl_ShrinkFly(aRect));
1537  }
1538  else
1539  {
1540  SwRect aRect( pFly->getFramePrintArea() );
1541  aRect += pFly->getFrameArea().Pos();
1542  rRegion -= aRect;
1543  rClipState.subtractRange(lcl_ShrinkFly(aRect));
1544  }
1545  }
1546  if (gProp.pSRetoucheFly == gProp.pSRetoucheFly2)
1547  gProp.pSRetoucheFly = nullptr;
1548 }
1549 
1550 static void lcl_implDrawGraphicBackground(const SvxBrushItem& _rBackgrdBrush,
1551  vcl::RenderContext& _rOut,
1552  const SwRect& _rAlignedPaintRect,
1553  const GraphicObject& _rGraphicObj,
1554  SwPaintProperties const & properties)
1555 {
1560  const Color aColor( ( (_rBackgrdBrush.GetColor() != COL_TRANSPARENT) || properties.bSFlyMetafile )
1561  ? _rBackgrdBrush.GetColor()
1563 
1566  sal_Int8 nTransparencyPercent = 0;
1567  bool bDrawTransparent = false;
1568  if ( aColor.IsTransparent() )
1570  {
1571  bDrawTransparent = true;
1572  nTransparencyPercent = ((255 - aColor.GetAlpha())*100 + 0x7F)/0xFF;
1573  }
1574  else if ( (_rGraphicObj.GetAttr().IsTransparent()) &&
1575  (_rBackgrdBrush.GetColor() == COL_TRANSPARENT) )
1578  {
1579  bDrawTransparent = true;
1580  nTransparencyPercent = 100 - (_rGraphicObj.GetAttr().GetAlpha() * 100 + 127) / 255;
1581  }
1582 
1583  if ( bDrawTransparent )
1584  {
1586  if( _rOut.GetFillColor() != aColor.GetRGBColor() )
1587  _rOut.SetFillColor( aColor.GetRGBColor() );
1588  tools::PolyPolygon aPoly( _rAlignedPaintRect.SVRect() );
1589  _rOut.DrawTransparent( aPoly, nTransparencyPercent );
1590  }
1591  else
1592  {
1594  if ( _rOut.GetFillColor() != aColor )
1595  _rOut.SetFillColor( aColor );
1596  _rOut.DrawRect( _rAlignedPaintRect.SVRect() );
1597  }
1598 }
1599 
1632 static void lcl_DrawGraphicBackground( const SvxBrushItem& _rBackgrdBrush,
1633  OutputDevice& _rOut,
1634  const SwRect& _rAlignedPaintRect,
1635  const GraphicObject& _rGraphicObj,
1636  bool _bNumberingGraphic,
1637  SwPaintProperties const & properties,
1638  bool _bBackgrdAlreadyDrawn = false)
1639 {
1640  // draw background with background color, if
1641  // (1) graphic is not used as a numbering AND
1642  // (2) background is not already drawn AND
1643  // (3) intrinsic graphic is transparent OR intrinsic graphic doesn't exists
1644  if ( !_bNumberingGraphic &&
1645  !_bBackgrdAlreadyDrawn &&
1646  ( _rGraphicObj.IsTransparent() || _rGraphicObj.GetType() == GraphicType::NONE )
1647  )
1648  {
1649  lcl_implDrawGraphicBackground( _rBackgrdBrush, _rOut, _rAlignedPaintRect, _rGraphicObj, properties );
1650  }
1651 }
1652 
1668 static void lcl_DrawGraphic( const SvxBrushItem& rBrush, vcl::RenderContext &rOutDev,
1669  const SwViewShell &rSh, const SwRect &rGrf, const SwRect &rOut,
1670  bool bGrfNum,
1671  SwPaintProperties const & properties,
1672  bool bBackgrdAlreadyDrawn )
1673  // add parameter <bBackgrdAlreadyDrawn> to indicate
1674  // that the background is already drawn.
1675 {
1676  // Calculate align rectangle from parameter <rGrf> and use aligned
1677  // rectangle <aAlignedGrfRect> in the following code
1678  SwRect aAlignedGrfRect = rGrf;
1679  ::SwAlignRect( aAlignedGrfRect, &rSh, &rOutDev );
1680 
1681  // Change type from <bool> to <bool>.
1682  const bool bNotInside = !rOut.Contains( aAlignedGrfRect );
1683  if ( bNotInside )
1684  {
1685  rOutDev.Push( vcl::PushFlags::CLIPREGION );
1686  rOutDev.IntersectClipRegion( rOut.SVRect() );
1687  }
1688 
1689  GraphicObject *pGrf = const_cast<GraphicObject*>(rBrush.GetGraphicObject());
1690 
1691  OUString aOriginURL = pGrf->GetGraphic().getOriginURL();
1692  if (pGrf->GetGraphic().GetType() == GraphicType::Default && !aOriginURL.isEmpty())
1693  {
1694  Graphic aGraphic = vcl::graphic::loadFromURL(aOriginURL);
1695  pGrf->SetGraphic(aGraphic);
1696  }
1697 
1698  // Outsource drawing of background with a background color
1699  ::lcl_DrawGraphicBackground( rBrush, rOutDev, aAlignedGrfRect, *pGrf, bGrfNum, properties, bBackgrdAlreadyDrawn );
1700 
1701  // Because for drawing a graphic left-top-corner and size coordinates are
1702  // used, these coordinates have to be determined on pixel level.
1703  ::SwAlignGrfRect( &aAlignedGrfRect, rOutDev );
1704 
1705  const basegfx::B2DHomMatrix aGraphicTransform(
1707  aAlignedGrfRect.Width(), aAlignedGrfRect.Height(),
1708  aAlignedGrfRect.Left(), aAlignedGrfRect.Top()));
1709 
1711  rOutDev,
1712  *pGrf,
1713  pGrf->GetAttr(),
1714  aGraphicTransform,
1715  OUString(),
1716  OUString(),
1717  OUString());
1718 
1719  if ( bNotInside )
1720  rOutDev.Pop();
1721 }
1722 
1725  const SwRect& rOriginalLayoutRect,
1726  const SwRegionRects& rPaintRegion,
1727  const basegfx::utils::B2DClipState& rClipState,
1728  vcl::RenderContext& rOut)
1729 {
1730  if(rFillAttributes && rFillAttributes->isUsed())
1731  {
1732  basegfx::B2DRange aPaintRange(
1733  rPaintRegion.GetOrigin().Left(),
1734  rPaintRegion.GetOrigin().Top(),
1735  rPaintRegion.GetOrigin().Right(),
1736  rPaintRegion.GetOrigin().Bottom());
1737 
1738  if (!aPaintRange.isEmpty() &&
1739  !rPaintRegion.empty() &&
1740  !basegfx::fTools::equalZero(aPaintRange.getWidth()) &&
1741  !basegfx::fTools::equalZero(aPaintRange.getHeight()))
1742  {
1743  // need to expand for correct AAed and non-AAed visualization as primitive.
1744  // This must probably be removed again when we will be able to get all Writer visualization
1745  // as primitives and Writer prepares all it's stuff in high precision coordinates (also
1746  // needs to avoid moving boundaries around to better show overlapping stuff...)
1748  {
1749  // if AAed in principle expand by 0.5 in all directions. Since painting edges of
1750  // AAed regions does not add to no transparence (0.5 opacity covered by 0.5 opacity
1751  // is not full opacity but 0.75 opacity) we need some overlap here to avoid paint
1752  // artifacts. Checked experimentally - a little bit more in Y is needed, probably
1753  // due to still existing integer alignment and crunching in writer.
1754  static const double fExpandX = 0.55;
1755  static const double fExpandY = 0.70;
1756  const basegfx::B2DVector aSingleUnit(rOut.GetInverseViewTransformation() * basegfx::B2DVector(fExpandX, fExpandY));
1757 
1758  aPaintRange.expand(aPaintRange.getMinimum() - aSingleUnit);
1759  aPaintRange.expand(aPaintRange.getMaximum() + aSingleUnit);
1760  }
1761  else
1762  {
1763  // if not AAed expand by one unit to bottom right due to the missing unit
1764  // from SwRect/Rectangle integer handling
1765  const basegfx::B2DVector aSingleUnit(rOut.GetInverseViewTransformation() * basegfx::B2DVector(1.0, 1.0));
1766 
1767  aPaintRange.expand(aPaintRange.getMaximum() + aSingleUnit);
1768  }
1769 
1770  const basegfx::B2DRange aDefineRange(
1771  rOriginalLayoutRect.Left(),
1772  rOriginalLayoutRect.Top(),
1773  rOriginalLayoutRect.Right(),
1774  rOriginalLayoutRect.Bottom());
1775 
1776  const drawinglayer::primitive2d::Primitive2DContainer& rSequence = rFillAttributes->getPrimitive2DSequence(
1777  aPaintRange,
1778  aDefineRange);
1779 
1780  if(rSequence.size())
1781  {
1783  pPrimitives(&rSequence);
1785  // tdf#86578 the awful lcl_SubtractFlys hack
1786  if (rPaintRegion.size() > 1 || rPaintRegion[0] != rPaintRegion.GetOrigin())
1787  {
1788  basegfx::B2DPolyPolygon const& maskRegion(rClipState.getClipPoly());
1789  primitives.resize(1);
1790  primitives[0] = new drawinglayer::primitive2d::MaskPrimitive2D(
1791  maskRegion, drawinglayer::primitive2d::Primitive2DContainer(rSequence));
1792  pPrimitives = &primitives;
1793  }
1794  assert(pPrimitives && pPrimitives->size());
1795 
1796  const drawinglayer::geometry::ViewInformation2D aViewInformation2D(
1798  rOut.GetViewTransformation(),
1799  aPaintRange,
1800  nullptr,
1801  0.0);
1802  std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor(drawinglayer::processor2d::createProcessor2DFromOutputDevice(
1803  rOut,
1804  aViewInformation2D) );
1805  pProcessor->process(*pPrimitives);
1806  return true;
1807  }
1808  }
1809  }
1810 
1811  return false;
1812 }
1813 
1815  const SvxBrushItem *pBrush,
1816  vcl::RenderContext &rOutDev,
1817  const SwRect &rOrg,
1818  const SwRect &rOut,
1819  const sal_uInt8 nGrfNum,
1820  const bool bConsiderBackgroundTransparency )
1821  // Add 6th parameter to indicate that method should
1822  // consider background transparency, saved in the color of the brush item
1823 {
1824  SwViewShell &rSh = *gProp.pSGlobalShell;
1825  bool bReplaceGrfNum = GRFNUM_REPLACE == nGrfNum;
1826  bool bGrfNum = GRFNUM_NO != nGrfNum;
1827  Size aGrfSize;
1829  if( pBrush && !bReplaceGrfNum )
1830  {
1831  if( rSh.GetViewOptions()->IsGraphic() )
1832  {
1833  OUString referer;
1834  SfxObjectShell * sh = rSh.GetDoc()->GetPersist();
1835  if (sh != nullptr && sh->HasName()) {
1836  referer = sh->GetMedium()->GetName();
1837  }
1838  const Graphic* pGrf = pBrush->GetGraphic(referer);
1839  if( pGrf && GraphicType::NONE != pGrf->GetType() )
1840  {
1841  ePos = pBrush->GetGraphicPos();
1842  if( pGrf->IsSupportedGraphic() )
1843  // don't the use the specific output device! Bug 94802
1844  aGrfSize = ::GetGraphicSizeTwip( *pGrf, nullptr );
1845  }
1846  }
1847  else
1848  bReplaceGrfNum = bGrfNum;
1849  }
1850 
1851  SwRect aGrf;
1852  aGrf.SSize( aGrfSize );
1853  bool bDraw = true;
1854  bool bRetouche = true;
1855  switch ( ePos )
1856  {
1857  case GPOS_LT:
1858  aGrf.Pos() = rOrg.Pos();
1859  break;
1860 
1861  case GPOS_MT:
1862  aGrf.Pos().setY( rOrg.Top() );
1863  aGrf.Pos().setX( rOrg.Left() + rOrg.Width()/2 - aGrfSize.Width()/2 );
1864  break;
1865 
1866  case GPOS_RT:
1867  aGrf.Pos().setY( rOrg.Top() );
1868  aGrf.Pos().setX( rOrg.Right() - aGrfSize.Width() );
1869  break;
1870 
1871  case GPOS_LM:
1872  aGrf.Pos().setY( rOrg.Top() + rOrg.Height()/2 - aGrfSize.Height()/2 );
1873  aGrf.Pos().setX( rOrg.Left() );
1874  break;
1875 
1876  case GPOS_MM:
1877  aGrf.Pos().setY( rOrg.Top() + rOrg.Height()/2 - aGrfSize.Height()/2 );
1878  aGrf.Pos().setX( rOrg.Left() + rOrg.Width()/2 - aGrfSize.Width()/2 );
1879  break;
1880 
1881  case GPOS_RM:
1882  aGrf.Pos().setY( rOrg.Top() + rOrg.Height()/2 - aGrfSize.Height()/2 );
1883  aGrf.Pos().setX( rOrg.Right() - aGrfSize.Width() );
1884  break;
1885 
1886  case GPOS_LB:
1887  aGrf.Pos().setY( rOrg.Bottom() - aGrfSize.Height() );
1888  aGrf.Pos().setX( rOrg.Left() );
1889  break;
1890 
1891  case GPOS_MB:
1892  aGrf.Pos().setY( rOrg.Bottom() - aGrfSize.Height() );
1893  aGrf.Pos().setX( rOrg.Left() + rOrg.Width()/2 - aGrfSize.Width()/2 );
1894  break;
1895 
1896  case GPOS_RB:
1897  aGrf.Pos().setY( rOrg.Bottom() - aGrfSize.Height() );
1898  aGrf.Pos().setX( rOrg.Right() - aGrfSize.Width() );
1899  break;
1900 
1901  case GPOS_AREA:
1902  aGrf = rOrg;
1903  // Despite the fact that the background graphic has to fill the complete
1904  // area, we already checked, whether the graphic will completely fill out
1905  // the region the <rOut> that is to be painted. Thus, nothing has to be
1906  // touched again.
1907  // E.g. this is the case for a Fly Frame without a background
1908  // brush positioned on the border of the page which inherited the background
1909  // brush from the page.
1910  bRetouche = !rOut.Contains( aGrf );
1911  break;
1912 
1913  case GPOS_TILED:
1914  {
1915  // draw background of tiled graphic before drawing tiled graphic in loop
1916  // determine graphic object
1917  GraphicObject* pGraphicObj = const_cast< GraphicObject* >(pBrush->GetGraphicObject());
1918  // calculate aligned paint rectangle
1919  SwRect aAlignedPaintRect = rOut;
1920  ::SwAlignRect( aAlignedPaintRect, &rSh, &rOutDev );
1921  // draw background color for aligned paint rectangle
1922  lcl_DrawGraphicBackground( *pBrush, rOutDev, aAlignedPaintRect, *pGraphicObj, bGrfNum, gProp );
1923 
1924  // set left-top-corner of background graphic to left-top-corner of the
1925  // area, from which the background brush is determined.
1926  aGrf.Pos() = rOrg.Pos();
1927  // setup clipping at output device
1928  rOutDev.Push( vcl::PushFlags::CLIPREGION );
1929  rOutDev.IntersectClipRegion( rOut.SVRect() );
1930  // use new method <GraphicObject::DrawTiled(::)>
1931  {
1932  // calculate paint offset
1933  Point aPaintOffset( aAlignedPaintRect.Pos() - aGrf.Pos() );
1934  // draw background graphic tiled for aligned paint rectangle
1935  // #i42643#
1936  // For PDF export, every draw operation for bitmaps takes a
1937  // noticeable amount of place (~50 characters). Thus, optimize
1938  // between tile bitmap size and number of drawing operations here.
1939 
1940  // A_out
1941  // n_chars = k1 * ---------- + k2 * A_bitmap
1942  // A_bitmap
1943 
1944  // minimum n_chars is obtained for (derive for A_bitmap,
1945  // set to 0, take positive solution):
1946  // k1
1947  // A_bitmap = Sqrt( ---- A_out )
1948  // k2
1949 
1950  // where k1 is the number of chars per draw operation, and
1951  // k2 is the number of chars per bitmap pixel.
1952  // This is approximately 50 and 7 for current PDF writer, respectively.
1953 
1954  const double k1( 50 );
1955  const double k2( 7 );
1956  const Size aSize( aAlignedPaintRect.SSize() );
1957  const double Abitmap( k1/k2 * static_cast<double>(aSize.Width())*aSize.Height() );
1958 
1959  pGraphicObj->DrawTiled( rOutDev,
1960  aAlignedPaintRect.SVRect(),
1961  aGrf.SSize(),
1962  Size( aPaintOffset.X(), aPaintOffset.Y() ),
1963  std::max( 128, static_cast<int>( sqrt(sqrt( Abitmap)) + .5 ) ) );
1964  }
1965  // reset clipping at output device
1966  rOutDev.Pop();
1967  // set <bDraw> and <bRetouche> to false, indicating that background
1968  // graphic and background are already drawn.
1969  bDraw = bRetouche = false;
1970  }
1971  break;
1972 
1973  case GPOS_NONE:
1974  bDraw = false;
1975  break;
1976 
1977  default: OSL_ENSURE( false, "new Graphic position?" );
1978  }
1979 
1982  bool bGrfBackgrdAlreadyDrawn = false;
1983  if ( bRetouche )
1984  {
1986  rOutDev.SetLineColor();
1987 
1988  // check, if an existing background graphic (not filling the complete
1989  // background) is transparent drawn and the background color is
1990  // "no fill" respectively "auto fill", if background transparency
1991  // has to be considered.
1992  // If YES, memorize transparency of background graphic.
1993  // check also, if background graphic bitmap is transparent.
1994  bool bTransparentGrfWithNoFillBackgrd = false;
1995  sal_Int32 nGrfTransparency = 0;
1996  bool bGrfIsTransparent = false;
1997  if ( (ePos != GPOS_NONE) &&
1998  (ePos != GPOS_TILED) && (ePos != GPOS_AREA)
1999  )
2000  {
2001  GraphicObject *pGrf = const_cast<GraphicObject*>(pBrush->GetGraphicObject());
2002  if ( bConsiderBackgroundTransparency )
2003  {
2004  GraphicAttr aGrfAttr = pGrf->GetAttr();
2005  if ( (aGrfAttr.IsTransparent()) &&
2006  (pBrush->GetColor() == COL_TRANSPARENT)
2007  )
2008  {
2009  bTransparentGrfWithNoFillBackgrd = true;
2010  nGrfTransparency = 255 - aGrfAttr.GetAlpha();
2011  }
2012  }
2013  if ( pGrf->IsTransparent() )
2014  {
2015  bGrfIsTransparent = true;
2016  }
2017  }
2018 
2019  // to get color of brush, check background color against COL_TRANSPARENT ("no fill"/"auto fill")
2020  // instead of checking, if transparency is not set.
2021  const Color aColor( pBrush &&
2022  ( (pBrush->GetColor() != COL_TRANSPARENT) ||
2023  gProp.bSFlyMetafile )
2024  ? pBrush->GetColor()
2026 
2027  // determine, if background region have to be
2028  // drawn transparent.
2029  // background region has to be drawn transparent, if
2030  // background transparency have to be considered
2031  // AND
2032  // ( background color is transparent OR
2033  // background graphic is transparent and background color is "no fill"
2034  // )
2035 
2036  enum DrawStyle {
2037  Default,
2038  Transparent,
2039  } eDrawStyle = Default;
2040 
2041  if (bConsiderBackgroundTransparency &&
2042  ( ( aColor.IsTransparent()) ||
2043  bTransparentGrfWithNoFillBackgrd ) )
2044  {
2045  eDrawStyle = Transparent;
2046  }
2047 
2048  // #i75614# reset draw mode in high contrast mode in order to get fill color set
2049  const DrawModeFlags nOldDrawMode = rOutDev.GetDrawMode();
2050  if ( gProp.pSGlobalShell->GetWin() &&
2052  {
2053  rOutDev.SetDrawMode( DrawModeFlags::Default );
2054  }
2055 
2056  // If background region has to be drawn transparent, set only the RGB values of the background color as
2057  // the fill color for the output device.
2058  switch (eDrawStyle)
2059  {
2060  case Transparent:
2061  {
2062  if( rOutDev.GetFillColor() != aColor.GetRGBColor() )
2063  rOutDev.SetFillColor( aColor.GetRGBColor() );
2064  break;
2065  }
2066  default:
2067  {
2068  if( rOutDev.GetFillColor() != aColor )
2069  rOutDev.SetFillColor( aColor );
2070  break;
2071  }
2072  }
2073 
2074  // #i75614#
2075  // restore draw mode
2076  rOutDev.SetDrawMode( nOldDrawMode );
2077 
2078  switch (eDrawStyle)
2079  {
2080  case Transparent:
2081  {
2082  // background region have to be drawn transparent.
2083  // Thus, create a poly-polygon from the region and draw it with
2084  // the corresponding transparency percent.
2085  tools::PolyPolygon aDrawPoly( rOut.SVRect() );
2086  if ( aGrf.HasArea() )
2087  {
2088  if ( !bGrfIsTransparent )
2089  {
2090  // subtract area of background graphic from draw area
2091  // Consider only that part of the graphic area that is overlapping with draw area.
2092  SwRect aTmpGrf = aGrf;
2093  aTmpGrf.Intersection( rOut );
2094  if ( aTmpGrf.HasArea() )
2095  {
2096  tools::Polygon aGrfPoly( aTmpGrf.SVRect() );
2097  aDrawPoly.Insert( aGrfPoly );
2098  }
2099  }
2100  else
2101  bGrfBackgrdAlreadyDrawn = true;
2102  }
2103  // calculate transparency percent:
2104  // ( <transparency value[0x01..0xFF]>*100 + 0x7F ) / 0xFF
2105  // If there is a background graphic with a background color "no fill"/"auto fill",
2106  // the transparency value is taken from the background graphic,
2107  // otherwise take the transparency value from the color.
2108  sal_Int8 nTransparencyPercent = static_cast<sal_Int8>(
2109  (( bTransparentGrfWithNoFillBackgrd ? nGrfTransparency : (255 - aColor.GetAlpha())
2110  )*100 + 0x7F)/0xFF);
2111  // draw poly-polygon transparent
2112  rOutDev.DrawTransparent( aDrawPoly, nTransparencyPercent );
2113 
2114  break;
2115  }
2116  case Default:
2117  default:
2118  {
2119  SwRegionRects aRegion( rOut, 4 );
2120  if ( !bGrfIsTransparent )
2121  aRegion -= aGrf;
2122  else
2123  bGrfBackgrdAlreadyDrawn = true;
2124  // loop rectangles of background region, which has to be drawn
2125  for( size_t i = 0; i < aRegion.size(); ++i )
2126  {
2127  rOutDev.DrawRect( aRegion[i].SVRect() );
2128  }
2129  }
2130  }
2131  rOutDev.Pop();
2132  }
2133 
2134  if( bDraw && aGrf.Overlaps( rOut ) )
2135  lcl_DrawGraphic( *pBrush, rOutDev, rSh, aGrf, rOut, bGrfNum, gProp,
2136  bGrfBackgrdAlreadyDrawn );
2137 
2138  if( bReplaceGrfNum )
2139  {
2140  const BitmapEx& rBmp = rSh.GetReplacementBitmap(false);
2141  vcl::Font aTmp( rOutDev.GetFont() );
2142  Graphic::DrawEx(rOutDev, OUString(), aTmp, rBmp, rOrg.Pos(), rOrg.SSize());
2143  }
2144 }
2145 
2154 static void lcl_AdjustRectToPixelSize( SwRect& io_aSwRect, const vcl::RenderContext &aOut )
2155 {
2156  // local constant object of class <Size> to determine number of Twips
2157  // representing a pixel.
2158  const Size aTwipToPxSize( aOut.PixelToLogic( Size( 1,1 )) );
2159 
2160  // local object of class <Rectangle> in Twip coordinates
2161  // calculated from given rectangle aligned to pixel centers.
2162  const tools::Rectangle aPxCenterRect = aOut.PixelToLogic(
2163  aOut.LogicToPixel( io_aSwRect.SVRect() ) );
2164 
2165  // local constant object of class <Rectangle> representing given rectangle
2166  // in pixel.
2167  const tools::Rectangle aOrgPxRect = aOut.LogicToPixel( io_aSwRect.SVRect() );
2168 
2169  // calculate adjusted rectangle from pixel centered rectangle.
2170  // Due to rounding differences <aPxCenterRect> doesn't exactly represents
2171  // the Twip-centers. Thus, adjust borders by half of pixel width/height plus 1.
2172  // Afterwards, adjust calculated Twip-positions of the all borders.
2173  tools::Rectangle aSizedRect = aPxCenterRect;
2174  aSizedRect.AdjustLeft( -(aTwipToPxSize.Width()/2 + 1) );
2175  aSizedRect.AdjustRight( aTwipToPxSize.Width()/2 + 1 );
2176  aSizedRect.AdjustTop( -(aTwipToPxSize.Height()/2 + 1) );
2177  aSizedRect.AdjustBottom(aTwipToPxSize.Height()/2 + 1);
2178 
2179  // adjust left()
2180  while ( aOut.LogicToPixel(aSizedRect).Left() < aOrgPxRect.Left() )
2181  {
2182  aSizedRect.AdjustLeft( 1 );
2183  }
2184  // adjust right()
2185  while ( aOut.LogicToPixel(aSizedRect).Right() > aOrgPxRect.Right() )
2186  {
2187  aSizedRect.AdjustRight( -1 );
2188  }
2189  // adjust top()
2190  while ( aOut.LogicToPixel(aSizedRect).Top() < aOrgPxRect.Top() )
2191  {
2192  aSizedRect.AdjustTop( 1 );
2193  }
2194  // adjust bottom()
2195  while ( aOut.LogicToPixel(aSizedRect).Bottom() > aOrgPxRect.Bottom() )
2196  {
2197  aSizedRect.AdjustBottom( -1 );
2198  }
2199 
2200  io_aSwRect = SwRect( aSizedRect );
2201 
2202 #if OSL_DEBUG_LEVEL > 0
2203  tools::Rectangle aTestOrgPxRect = aOut.LogicToPixel( io_aSwRect.SVRect() );
2204  tools::Rectangle aTestNewPxRect = aOut.LogicToPixel( aSizedRect );
2205  OSL_ENSURE( aTestOrgPxRect == aTestNewPxRect,
2206  "Error in lcl_AlignRectToPixelSize(..): Adjusted rectangle has incorrect position or size");
2207  // check Left()
2208  aSizedRect.AdjustLeft( -1 );
2209  aTestNewPxRect = aOut.LogicToPixel( aSizedRect );
2210  OSL_ENSURE( aTestOrgPxRect.Left() >= (aTestNewPxRect.Left()+1),
2211  "Error in lcl_AlignRectToPixelSize(..): Left() not correct adjusted");
2212  aSizedRect.AdjustLeft( 1 );
2213  // check Right()
2214  aSizedRect.AdjustRight( 1 );
2215  aTestNewPxRect = aOut.LogicToPixel( aSizedRect );
2216  OSL_ENSURE( aTestOrgPxRect.Right() <= (aTestNewPxRect.Right()-1),
2217  "Error in lcl_AlignRectToPixelSize(..): Right() not correct adjusted");
2218  aSizedRect.AdjustRight( -1 );
2219  // check Top()
2220  aSizedRect.AdjustTop( -1 );
2221  aTestNewPxRect = aOut.LogicToPixel( aSizedRect );
2222  OSL_ENSURE( aTestOrgPxRect.Top() >= (aTestNewPxRect.Top()+1),
2223  "Error in lcl_AlignRectToPixelSize(..): Top() not correct adjusted");
2224  aSizedRect.AdjustTop( 1 );
2225  // check Bottom()
2226  aSizedRect.AdjustBottom( 1 );
2227  aTestNewPxRect = aOut.LogicToPixel( aSizedRect );
2228  OSL_ENSURE( aTestOrgPxRect.Bottom() <= (aTestNewPxRect.Bottom()-1),
2229  "Error in lcl_AlignRectToPixelSize(..): Bottom() not correct adjusted");
2230  aSizedRect.AdjustBottom( -1 );
2231 #endif
2232 }
2233 
2234 // FUNCTIONS USED FOR COLLAPSING TABLE BORDER LINES START
2235 
2236 namespace {
2237 
2238 struct SwLineEntry
2239 {
2240  SwTwips mnKey;
2241  SwTwips mnStartPos;
2242  SwTwips mnEndPos;
2243  SwTwips mnLimitedEndPos;
2244 
2245  svx::frame::Style maAttribute;
2246 
2247  enum OverlapType { NO_OVERLAP, OVERLAP1, OVERLAP2, OVERLAP3 };
2248 
2249  enum class VerticalType { LEFT, RIGHT };
2250 
2251 public:
2252  SwLineEntry( SwTwips nKey,
2253  SwTwips nStartPos,
2254  SwTwips nEndPos,
2255  const svx::frame::Style& rAttribute );
2256 
2257  OverlapType Overlaps( const SwLineEntry& rComp ) const;
2258 
2263  void LimitVerticalEndPos(const SwFrame& rFrame, VerticalType eType);
2264 };
2265 
2266 }
2267 
2268 SwLineEntry::SwLineEntry( SwTwips nKey,
2269  SwTwips nStartPos,
2270  SwTwips nEndPos,
2271  const svx::frame::Style& rAttribute )
2272  : mnKey( nKey ),
2273  mnStartPos( nStartPos ),
2274  mnEndPos( nEndPos ),
2275  mnLimitedEndPos(0),
2276  maAttribute( rAttribute )
2277 {
2278 }
2279 
2280 /*
2281 
2282  1. ---------- rOld
2283  ---------- rNew
2284 
2285  2. ---------- rOld
2286  ------------- rNew
2287 
2288  3. ------- rOld
2289  ------------- rNew
2290 
2291  4. ------------- rOld
2292  ---------- rNew
2293 
2294  5. ---------- rOld
2295  ---- rNew
2296 
2297  6. ---------- rOld
2298  ---------- rNew
2299 
2300  7. ------------- rOld
2301  ---------- rNew
2302 
2303  8. ---------- rOld
2304  ------------- rNew
2305 
2306  9. ---------- rOld
2307  ---------- rNew
2308 */
2309 
2310 SwLineEntry::OverlapType SwLineEntry::Overlaps( const SwLineEntry& rNew ) const
2311 {
2312  SwLineEntry::OverlapType eRet = OVERLAP3;
2313 
2314  if ( mnStartPos >= rNew.mnEndPos || mnEndPos <= rNew.mnStartPos )
2315  eRet = NO_OVERLAP;
2316 
2317  // 1, 2, 3
2318  else if ( mnEndPos < rNew.mnEndPos )
2319  eRet = OVERLAP1;
2320 
2321  // 4, 5, 6, 7
2322  else if (mnStartPos <= rNew.mnStartPos)
2323  eRet = OVERLAP2;
2324 
2325  // 8, 9
2326  return eRet;
2327 }
2328 
2329 void SwLineEntry::LimitVerticalEndPos(const SwFrame& rFrame, VerticalType eType)
2330 {
2331  if (!rFrame.IsCellFrame())
2332  {
2333  return;
2334  }
2335 
2336  const auto& rCellFrame = static_cast<const SwCellFrame&>(rFrame);
2337  std::vector<const SwCellFrame*> aCoveredCells = rCellFrame.GetCoveredCells();
2338  // Iterate in reverse order, so we can stop at the first cell that has a border. This can
2339  // determine what is the minimal end position that is safe to use as a limit.
2340  for (auto it = aCoveredCells.rbegin(); it != aCoveredCells.rend(); ++it)
2341  {
2342  const SwCellFrame* pCoveredCell = *it;
2343  SwBorderAttrAccess aAccess( SwFrame::GetCache(), pCoveredCell );
2344  const SwBorderAttrs& rAttrs = *aAccess.Get();
2345  const SvxBoxItem& rBox = rAttrs.GetBox();
2346  if (eType == VerticalType::LEFT && rBox.GetLeft())
2347  {
2348  break;
2349  }
2350 
2351  if (eType == VerticalType::RIGHT && rBox.GetRight())
2352  {
2353  break;
2354  }
2355 
2356  mnLimitedEndPos = pCoveredCell->getFrameArea().Top();
2357  }
2358 }
2359 
2360 namespace {
2361 
2362 struct lt_SwLineEntry
2363 {
2364  bool operator()( const SwLineEntry& e1, const SwLineEntry& e2 ) const
2365  {
2366  return e1.mnStartPos < e2.mnStartPos;
2367  }
2368 };
2369 
2370 }
2371 
2372 typedef std::set< SwLineEntry, lt_SwLineEntry > SwLineEntrySet;
2373 typedef std::map< SwTwips, SwLineEntrySet > SwLineEntryMap;
2374 
2375 namespace {
2376 
2377 class SwTabFramePainter
2378 {
2379  SwLineEntryMap maVertLines;
2380  SwLineEntryMap maHoriLines;
2381  const SwTabFrame& mrTabFrame;
2382 
2383  void Insert( SwLineEntry&, bool bHori );
2384  void Insert(const SwFrame& rFrame, const SvxBoxItem& rBoxItem, const SwRect &rPaintArea);
2385  void HandleFrame(const SwLayoutFrame& rFrame, const SwRect& rPaintArea);
2386  void FindStylesForLine( const Point&,
2387  const Point&,
2389  bool bHori ) const;
2390 
2391 public:
2392  explicit SwTabFramePainter( const SwTabFrame& rTabFrame );
2393 
2394  void PaintLines( OutputDevice& rDev, const SwRect& rRect ) const;
2395 };
2396 
2397 }
2398 
2399 SwTabFramePainter::SwTabFramePainter( const SwTabFrame& rTabFrame )
2400  : mrTabFrame( rTabFrame )
2401 {
2402  SwRect aPaintArea = rTabFrame.GetUpper()->GetPaintArea();
2403  HandleFrame(rTabFrame, aPaintArea);
2404 }
2405 
2406 void SwTabFramePainter::HandleFrame(const SwLayoutFrame& rLayoutFrame, const SwRect& rPaintArea)
2407 {
2408  // Add border lines of cell frames. Skip covered cells. Skip cells
2409  // in special row span row, which do not have a negative row span:
2410  if ( rLayoutFrame.IsCellFrame() && !rLayoutFrame.IsCoveredCell() )
2411  {
2412  const SwCellFrame* pThisCell = static_cast<const SwCellFrame*>(&rLayoutFrame);
2413  const SwRowFrame* pRowFrame = static_cast<const SwRowFrame*>(pThisCell->GetUpper());
2414  const tools::Long nRowSpan = pThisCell->GetTabBox()->getRowSpan();
2415  if ( !pRowFrame->IsRowSpanLine() || nRowSpan > 1 || nRowSpan < -1 )
2416  {
2417  SwBorderAttrAccess aAccess( SwFrame::GetCache(), &rLayoutFrame );
2418  const SwBorderAttrs& rAttrs = *aAccess.Get();
2419  const SvxBoxItem& rBox = rAttrs.GetBox();
2420  Insert(rLayoutFrame, rBox, rPaintArea);
2421  }
2422  }
2423 
2424  // Recurse into lower layout frames, but do not recurse into lower tabframes.
2425  const SwFrame* pLower = rLayoutFrame.Lower();
2426  while ( pLower )
2427  {
2428  if (pLower->IsLayoutFrame() && !pLower->IsTabFrame())
2429  {
2430  const SwLayoutFrame* pLowerLayFrame = static_cast<const SwLayoutFrame*>(pLower);
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.Overlaps(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()->GetOutDev() && !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()->DeletePaintRegion();
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.Overlaps( 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->GetDrawBackgroundColor());
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.Overlaps( 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.Overlaps( aPaintRect ) )
3486  {
3487  if ( bCnt && pFrame->IsCompletePaint() &&
3488  !rRect.Contains( 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( std::move(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* pBackgroundBrush = nullptr;
3823  std::optional<Color> xSectionTOXColor;
3824  SwRect aDummyRect;
3826 
3827  if ( GetBackgroundBrush( aFillAttributes, pBackgroundBrush, 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 ( pBackgroundBrush )
3840  {
3841  if ( (pBackgroundBrush->GetColor().IsTransparent()) &&
3842  (pBackgroundBrush->GetColor() != COL_TRANSPARENT) )
3843  {
3844  bBackgroundTransparent = true;
3845  }
3846  else
3847  {
3848  const GraphicObject *pTmpGrf =
3849  pBackgroundBrush->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().Overlaps( 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().Overlaps( SwRect(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 // set strikethrough for deleted objects anchored to character
3971 void SwFrame::SetDrawObjsAsDeleted( bool bDeleted )
3972 {
3973  if ( SwSortedObjs *pObjs = GetDrawObjs() )
3974  {
3975  for (SwAnchoredObject* pAnchoredObj : *pObjs)
3976  {
3977  if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
3978  {
3979  pFly->SetDeleted(bDeleted);
3980  }
3981  }
3982  }
3983 }
3984 
3985 void SwFlyFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const& rRect, SwPrintData const*const) const
3986 {
3987  //optimize thumbnail generation and store procedure to improve odt saving performance, #i120030#
3988  SwViewShell *pShell = getRootFrame()->GetCurrShell();
3989  if (pShell && pShell->GetDoc() && pShell->GetDoc()->GetDocShell())
3990  {
3991  bool bInGenerateThumbnail = pShell->GetDoc()->GetDocShell()->IsInGenerateAndStoreThumbnail();
3992  if (bInGenerateThumbnail)
3993  {
3994  const SwRect& aVisRect = pShell->VisArea();
3995  if (!aVisRect.Overlaps(getFrameArea()))
3996  return;
3997  }
3998  }
3999 
4000  //because of the overlapping of frames and drawing objects the flys have to
4001  //paint their borders (and those of the internal ones) directly.
4002  //e.g. #33066#
4003  gProp.pSLines->LockLines(true);
4004  BorderLinesGuard blg; // this should not paint borders added from PaintBaBo
4005 
4006  SwRect aRect( rRect );
4007  aRect.Intersection_( getFrameArea() );
4008 
4009  rRenderContext.Push( vcl::PushFlags::CLIPREGION );
4010  rRenderContext.SetClipRegion();
4011  const SwPageFrame* pPage = FindPageFrame();
4012 
4013  const SwNoTextFrame *pNoText = Lower() && Lower()->IsNoTextFrame()
4014  ? static_cast<const SwNoTextFrame*>(Lower()) : nullptr;
4015 
4016  bool bIsChart = false; //#i102950# don't paint additional borders for charts
4017  //check whether we have a chart
4018  if(pNoText)
4019  {
4020  const SwNoTextNode* pNoTNd = dynamic_cast<const SwNoTextNode*>(pNoText->GetNode());
4021  if( pNoTNd )
4022  {
4023  SwOLENode* pOLENd = const_cast<SwOLENode*>(pNoTNd->GetOLENode());
4024  if( pOLENd && pOLENd->GetOLEObj().GetObject().IsChart() )
4025  bIsChart = true;
4026  }
4027  }
4028 
4029  {
4030  bool bContour = GetFormat()->GetSurround().IsContour();
4031  tools::PolyPolygon aPoly;
4032  if ( bContour )
4033  {
4034  // add 2nd parameter with value <true>
4035  // to indicate that method is called for paint in order to avoid
4036  // load of the intrinsic graphic.
4037  bContour = GetContour( aPoly, true );
4038  }
4039 
4040  // #i47804# - distinguish complete background paint
4041  // and margin paint.
4042  // paint complete background for Writer text fly frames
4043  bool bPaintCompleteBack( !pNoText );
4044  // paint complete background for transparent graphic and contour,
4045  // if own background color exists.
4046  const bool bIsGraphicTransparent = pNoText && pNoText->IsTransparent();
4047  if ( !bPaintCompleteBack &&
4048  ( bIsGraphicTransparent|| bContour ) )
4049  {
4050  const SwFlyFrameFormat* pSwFrameFormat = GetFormat();
4051 
4052  if (pSwFrameFormat && pSwFrameFormat->supportsFullDrawingLayerFillAttributeSet())
4053  {
4054  // check for transparency
4056 
4057  // check if the new fill attributes are used
4058  if(aFillAttributes && aFillAttributes->isUsed())
4059  {
4060  bPaintCompleteBack = true;
4061  }
4062  }
4063  else
4064  {
4065  std::unique_ptr<SvxBrushItem> aBack = GetFormat()->makeBackgroundBrushItem();
4066  // to determine, if background has to be painted, by checking, if
4067  // background color is not COL_TRANSPARENT ("no fill"/"auto fill")
4068  // or a background graphic exists.
4069  bPaintCompleteBack = aBack &&
4070  ((aBack->GetColor() != COL_TRANSPARENT) ||
4071  aBack->GetGraphicPos() != GPOS_NONE);
4072  }
4073  }
4074  // paint of margin needed.
4075  const bool bPaintMarginOnly( !bPaintCompleteBack &&
4077 
4078  // #i47804# - paint background of parent fly frame
4079  // for transparent graphics in layer Hell, if parent fly frame isn't
4080  // in layer Hell. It's only painted the intersection between the
4081  // parent fly frame area and the paint area <aRect>
4083 
4084  if (bIsGraphicTransparent &&
4085  GetFormat()->GetDoc()->getIDocumentSettingAccess().get(DocumentSettingId::SUBTRACT_FLYS) &&
4086  GetVirtDrawObj()->GetLayer() == rIDDMA.GetHellId() &&
4088  {
4089  const SwFlyFrame* pParentFlyFrame = GetAnchorFrame()->FindFlyFrame();
4090  if ( pParentFlyFrame->GetDrawObj()->GetLayer() !=
4091  rIDDMA.GetHellId() )
4092  {
4093  SwFlyFrame* pOldRet = gProp.pSRetoucheFly2;
4094  gProp.pSRetoucheFly2 = const_cast<SwFlyFrame*>(this);
4095 
4096  SwBorderAttrAccess aAccess( SwFrame::GetCache(), pParentFlyFrame );
4097  const SwBorderAttrs &rAttrs = *aAccess.Get();
4098  SwRect aPaintRect( aRect );
4099  aPaintRect.Intersection_( pParentFlyFrame->getFrameArea() );
4100  pParentFlyFrame->PaintSwFrameBackground( aPaintRect, pPage, rAttrs );
4101 
4102  gProp.pSRetoucheFly2 = pOldRet;
4103  }
4104  }
4105 
4106  if ( bPaintCompleteBack || bPaintMarginOnly )
4107  {
4108  //#24926# JP 01.02.96, PaintBaBo is here partially so PaintSwFrameShadowAndBorder
4109  //receives the original Rect but PaintSwFrameBackground only the limited
4110  //one.
4111 
4113  rRenderContext.SetLineColor();
4114 
4115  pPage = FindPageFrame();
4116 
4117  SwBorderAttrAccess aAccess( SwFrame::GetCache(), static_cast<SwFrame const *>(this) );
4118  const SwBorderAttrs &rAttrs = *aAccess.Get();
4119 
4120  // paint background
4121  {
4122  SwRegionRects aRegion( aRect );
4123  // #i80822#
4124  // suppress painting of background in printing area for
4125  // non-transparent graphics.
4126  if ( bPaintMarginOnly ||
4127  ( pNoText && !bIsGraphicTransparent ) )
4128  {
4129  //What we actually want to paint is the small stripe between
4130  //PrtArea and outer border.
4131  SwRect aTmp( getFramePrintArea() ); aTmp += getFrameArea().Pos();
4132  aRegion -= aTmp;
4133  }
4134  if ( bContour )
4135  {
4136  rRenderContext.Push();
4137  // #i80822#
4138  // apply clip region under the same conditions, which are
4139  // used in <SwNoTextFrame::PaintSwFrame(..)> to set the clip region
4140  // for painting the graphic/OLE. Thus, the clip region is
4141  // also applied for the PDF export.
4143 
4144  if ( !rRenderContext.GetConnectMetaFile() || !pSh || !pSh->GetWin() )
4145  {
4146  rRenderContext.SetClipRegion(vcl::Region(aPoly));
4147  }
4148 
4149  for ( size_t i = 0; i < aRegion.size(); ++i )
4150  {
4151  PaintSwFrameBackground( aRegion[i], pPage, rAttrs, false, true );
4152  }
4153 
4154  rRenderContext.Pop();
4155  }
4156  else
4157  {
4158  for ( size_t i = 0; i < aRegion.size(); ++i )
4159  {
4160  PaintSwFrameBackground( aRegion[i], pPage, rAttrs, false, true );
4161  }
4162  }
4163  }
4164 
4165  // paint border before painting background
4166  PaintSwFrameShadowAndBorder(rRect, pPage, rAttrs);
4167 
4168  rRenderContext.Pop();
4169  }
4170  }
4171 
4172  // fly frame will paint it's subsidiary lines and
4173  // the subsidiary lines of its lowers on its own, due to overlapping with
4174  // other fly frames or other objects.
4175  if( gProp.pSGlobalShell->GetWin()
4176  && !bIsChart ) //#i102950# don't paint additional borders for charts
4177  {
4178  bool bSubsLineRectsCreated;
4179  if ( gProp.pSSubsLines )
4180  {
4181  // Lock already existing subsidiary lines
4182  gProp.pSSubsLines->LockLines( true );
4183  bSubsLineRectsCreated = false;
4184  }
4185  else
4186  {
4187  // create new subsidiary lines
4188  gProp.pSSubsLines.reset(new SwSubsRects);
4189  bSubsLineRectsCreated = true;
4190  }
4191 
4192  bool bSpecSubsLineRectsCreated;
4193  if ( gProp.pSSpecSubsLines )
4194  {
4195  // Lock already existing special subsidiary lines
4196  gProp.pSSpecSubsLines->LockLines( true );
4197  bSpecSubsLineRectsCreated = false;
4198  }
4199  else
4200  {
4201  // create new special subsidiary lines
4202  gProp.pSSpecSubsLines.reset(new SwSubsRects);
4203  bSpecSubsLineRectsCreated = true;
4204  }
4205  // Add subsidiary lines of fly frame and its lowers
4206  RefreshLaySubsidiary( pPage, aRect );
4207  // paint subsidiary lines of fly frame and its lowers
4208  gProp.pSSpecSubsLines->PaintSubsidiary( &rRenderContext, nullptr, gProp );
4209  gProp.pSSubsLines->PaintSubsidiary(&rRenderContext, gProp.pSLines.get(), gProp);
4210  if ( !bSubsLineRectsCreated )
4211  // unlock subsidiary lines
4212  gProp.pSSubsLines->LockLines( false );
4213  else
4214  {
4215  // delete created subsidiary lines container
4216  gProp.pSSubsLines.reset();
4217  }
4218 
4219  if ( !bSpecSubsLineRectsCreated )
4220  // unlock special subsidiary lines
4221  gProp.pSSpecSubsLines->LockLines( false );
4222  else
4223  {
4224  // delete created special subsidiary lines container
4225  gProp.pSSpecSubsLines.reset();
4226  }
4227  }
4228 
4229  SwLayoutFrame::PaintSwFrame( rRenderContext, aRect );
4230 
4231  Validate();
4232 
4233  // first paint lines added by fly frame paint
4234  // and then unlock other lines.
4235  gProp.pSLines->PaintLines( &rRenderContext, gProp );
4236  gProp.pSLines->LockLines( false );
4237  // have to paint frame borders added in heaven layer here...
4238  ProcessPrimitives(gProp.pBLines->GetBorderLines_Clear());
4239 
4240  PaintDecorators();
4241 
4242  // crossing out for tracked deletion
4243  if ( GetAuthor() != std::string::npos && IsDeleted() )
4244  {
4245  tools::Long startX = aRect.Left( ), endX = aRect.Right();
4246  tools::Long startY = aRect.Top( ), endY = aRect.Bottom();
4247  rRenderContext.SetLineColor( SwPostItMgr::GetColorAnchor(GetAuthor()) );
4248  rRenderContext.DrawLine(Point(startX, startY), Point(endX, endY));
4249  rRenderContext.DrawLine(Point(startX, endY), Point(endX, startY));
4250  }
4251 
4252  rRenderContext.Pop();
4253 
4254  if ( gProp.pSProgress && pNoText )
4256 }
4257 
4259 {
4260  // Show the un-float button
4261  SwWrtShell* pWrtSh = dynamic_cast< SwWrtShell* >( gProp.pSGlobalShell );
4262  if ( pWrtSh )
4263  {
4264  UpdateUnfloatButton(pWrtSh, IsShowUnfloatButton(pWrtSh));
4265  }
4266 }
4267 
4269 {
4270  SwWrtShell* pWrtSh = dynamic_cast<SwWrtShell*>(gProp.pSGlobalShell);
4271  if (pWrtSh && pWrtSh->GetViewOptions()->IsShowOutlineContentVisibilityButton())
4273 }
4274 
4275 
4276 void SwTabFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const& rRect, SwPrintData const*const) const
4277 {
4278  const SwViewOption* pViewOption = gProp.pSGlobalShell->GetViewOptions();
4279  if (pViewOption->IsTable())
4280  {
4281  // #i29550#
4282  if ( IsCollapsingBorders() )
4283  {
4284  SwBorderAttrAccess aAccess( SwFrame::GetCache(), static_cast<SwFrame const *>(this) );
4285  const SwBorderAttrs &rAttrs = *aAccess.Get();
4286 
4287  // paint shadow
4288  if ( rAttrs.GetShadow().GetLocation() != SvxShadowLocation::NONE )
4289  {
4290  SwRect aRect;
4291  ::lcl_CalcBorderRect( aRect, this, rAttrs, true, gProp );
4292  PaintShadow( rRect, aRect, rAttrs );
4293  }
4294 
4295  SwTabFramePainter aHelper(*this);
4296  aHelper.PaintLines(rRenderContext, rRect);
4297  }
4298 
4299  SwLayoutFrame::PaintSwFrame( rRenderContext, rRect );
4300  }
4301  // #i6467# - no light grey rectangle for page preview
4302  else if ( gProp.pSGlobalShell->GetWin() && !gProp.pSGlobalShell->IsPreview() )
4303  {
4304  // #i6467# - intersect output rectangle with table frame
4305  SwRect aTabRect( getFramePrintArea() );
4306  aTabRect.Pos() += getFrameArea().Pos();
4307  SwRect aTabOutRect( rRect );
4308  aTabOutRect.Intersection( aTabRect );
4309  SwViewOption::DrawRect( &rRenderContext, aTabOutRect, COL_LIGHTGRAY );
4310  }
4311  const_cast<SwTabFrame*>(this)->ResetComplete();
4312 }
4313 
4327 static void lcl_PaintShadow( const SwRect& rRect, SwRect& rOutRect,
4328  const SvxShadowItem& rShadow, const bool bDrawFullShadowRectangle,
4329  const bool bTop, const bool bBottom,
4330  const bool bLeft, const bool bRight,
4331  SwPaintProperties const & properties)
4332 {
4333  const tools::Long nWidth = ::lcl_AlignWidth ( rShadow.GetWidth(), properties );
4334  const tools::Long nHeight = ::lcl_AlignHeight( rShadow.GetWidth(), properties );
4335 
4336  SwRects aRegion;
4337  SwRect aOut( rOutRect );
4338 
4339  switch ( rShadow.GetLocation() )
4340  {
4341  case SvxShadowLocation::BottomRight:
4342  {
4343  if ( bDrawFullShadowRectangle )
4344  {
4345  // draw full shadow rectangle
4346  aOut.Top( rOutRect.Top() + nHeight );
4347  aOut.Left( rOutRect.Left() + nWidth );
4348  aRegion.push_back( aOut );
4349  }
4350  else
4351  {
4352  if( bBottom )
4353  {
4354  aOut.Top( rOutRect.Bottom() - nHeight );
4355  if( bLeft )
4356  aOut.Left( rOutRect.Left() + nWidth );
4357  aRegion.push_back( aOut );
4358  }
4359  if( bRight )
4360  {
4361  aOut.Left( rOutRect.Right() - nWidth );
4362  if( bTop )
4363  aOut.Top( rOutRect.Top() + nHeight );
4364  else
4365  aOut.Top( rOutRect.Top() );
4366  if( bBottom )
4367  aOut.Bottom( rOutRect.Bottom() - nHeight );
4368  aRegion.push_back( aOut );
4369  }
4370  }
4371 
4372  if( bRight )
4373  rOutRect.AddRight(- nWidth );
4374  if( bBottom )
4375  rOutRect.AddBottom(- nHeight );
4376  }
4377  break;
4378  case SvxShadowLocation::TopLeft:
4379  {
4380  if ( bDrawFullShadowRectangle )
4381  {
4382  // draw full shadow rectangle
4383  aOut.Bottom( rOutRect.Bottom() - nHeight );
4384  aOut.Right( rOutRect.Right() - nWidth );
4385  aRegion.push_back( aOut );
4386  }
4387  else
4388  {
4389  if( bTop )
4390  {
4391  aOut.Bottom( rOutRect.Top() + nHeight );
4392  if( bRight )
4393  aOut.Right( rOutRect.Right() - nWidth );
4394  aRegion.push_back( aOut );
4395  }
4396  if( bLeft )
4397  {
4398  aOut.Right( rOutRect.Left() + nWidth );
4399  if( bBottom )
4400  aOut.Bottom( rOutRect.Bottom() - nHeight );
4401  else
4402  aOut.Bottom( rOutRect.Bottom() );
4403  if( bTop )
4404  aOut.Top( rOutRect.Top() + nHeight );
4405  aRegion.push_back( aOut );
4406  }
4407  }
4408 
4409  if( bLeft )
4410  rOutRect.AddLeft( nWidth );
4411  if( bTop )
4412  rOutRect.AddTop( nHeight );
4413  }
4414  break;
4415  case SvxShadowLocation::TopRight:
4416  {
4417  if ( bDrawFullShadowRectangle )
4418  {
4419  // draw full shadow rectangle
4420  aOut.Bottom( rOutRect.Bottom() - nHeight);
4421  aOut.Left( rOutRect.Left() + nWidth );
4422  aRegion.push_back( aOut );
4423  }
4424  else
4425  {
4426  if( bTop )
4427  {
4428  aOut.Bottom( rOutRect.Top() + nHeight );
4429  if( bLeft )
4430  aOut.Left( rOutRect.Left() + nWidth );
4431  aRegion.push_back( aOut );
4432  }
4433  if( bRight )
4434  {
4435  aOut.Left( rOutRect.Right() - nWidth );
4436  if( bBottom )
4437  aOut.Bottom( rOutRect.Bottom() - nHeight );
4438  else
4439  aOut.Bottom( rOutRect.Bottom() );
4440  if( bTop )
4441  aOut.Top( rOutRect.Top() + nHeight );
4442  aRegion.push_back( aOut );
4443  }
4444  }
4445 
4446  if( bRight )
4447  rOutRect.AddRight( - nWidth );
4448  if( bTop )
4449  rOutRect.AddTop( nHeight );
4450  }
4451  break;
4452  case SvxShadowLocation::BottomLeft:
4453  {
4454  if ( bDrawFullShadowRectangle )
4455  {
4456  // draw full shadow rectangle
4457  aOut.Top( rOutRect.Top() + nHeight );
4458  aOut.Right( rOutRect.Right() - nWidth );
4459  aRegion.push_back( aOut );
4460  }
4461  else
4462  {
4463  if( bBottom )
4464  {
4465  aOut.Top( rOutRect.Bottom()- nHeight );
4466  if( bRight )
4467  aOut.Right( rOutRect.Right() - nWidth );
4468  aRegion.push_back( aOut );
4469  }
4470  if( bLeft )
4471  {
4472  aOut.Right( rOutRect.Left() + nWidth );
4473  if( bTop )
4474  aOut.Top( rOutRect.Top() + nHeight );
4475  else
4476  aOut.Top( rOutRect.Top() );
4477  if( bBottom )
4478  aOut.Bottom( rOutRect.Bottom() - nHeight );
4479  aRegion.push_back( aOut );
4480  }
4481  }
4482 
4483  if( bLeft )
4484  rOutRect.AddLeft( nWidth );
4485  if( bBottom )
4486  rOutRect.AddBottom( - nHeight );
4487  }
4488  break;
4489  default:
4490  assert(false);
4491  break;
4492  }
4493 
4494  vcl::RenderContext *pOut = properties.pSGlobalShell->GetOut();
4495 
4496  DrawModeFlags nOldDrawMode = pOut->GetDrawMode();
4497  Color aShadowColor( rShadow.GetColor().GetRGBColor() );
4498  if( !aRegion.empty() && properties.pSGlobalShell->GetWin() &&
4500  {
4501  // In high contrast mode, the output device has already set the
4502  // DrawModeFlags::SettingsFill flag. This causes the SetFillColor function
4503  // to ignore the setting of a new color. Therefore we have to reset
4504  // the drawing mode
4505  pOut->SetDrawMode( DrawModeFlags::Default );
4506  aShadowColor = SwViewOption::GetFontColor();
4507  }
4508 
4509  if ( pOut->GetFillColor() != aShadowColor )
4510  pOut->SetFillColor( aShadowColor );
4511 
4512  pOut->SetLineColor();
4513 
4514  pOut->SetDrawMode( nOldDrawMode );
4515 
4516  for (const SwRect & rOut : aRegion)
4517  {
4518  aOut = rOut;
4519  if ( rRect.Overlaps( aOut ) && aOut.Height() > 0 && aOut.Width() > 0 )
4520  {
4521  aOut.Intersection_( rRect );
4522  pOut->DrawRect( aOut.SVRect() );
4523  }
4524  }
4525 }
4526 
4536 void SwFrame::PaintShadow( const SwRect& rRect, SwRect& rOutRect,
4537  const SwBorderAttrs &rAttrs ) const
4538 {
4539  SvxShadowItem rShadow = rAttrs.GetShadow();
4540 
4541  const bool bCnt = IsContentFrame();
4542  const bool bTop = !bCnt || rAttrs.GetTopLine ( *(this) );
4543  const bool bBottom = !bCnt || rAttrs.GetBottomLine( *(this) );
4544 
4545  if( IsVertical() )
4546  {
4547  switch( rShadow.GetLocation() )
4548  {
4549  case SvxShadowLocation::BottomRight: rShadow.SetLocation(SvxShadowLocation::BottomLeft); break;
4550  case SvxShadowLocation::TopLeft: rShadow.SetLocation(SvxShadowLocation::TopRight); break;
4551  case SvxShadowLocation::TopRight: rShadow.SetLocation(SvxShadowLocation::BottomRight); break;
4552  case SvxShadowLocation::BottomLeft: rShadow.SetLocation(SvxShadowLocation::TopLeft); break;
4553  default: break;
4554  }
4555  }
4556 
4557  // determine, if full shadow rectangle have to be drawn or only two shadow rectangles beside the frame.
4558  // draw full shadow rectangle, if frame background is drawn transparent.
4559  // Status Quo:
4560  // SwLayoutFrame can have transparent drawn backgrounds. Thus,
4561  // "asked" their frame format.
4562  const bool bDrawFullShadowRectangle =
4563  ( IsLayoutFrame() &&
4564  static_cast<const SwLayoutFrame*>(this)->GetFormat()->IsBackgroundTransparent()
4565  );
4566 
4567  SwRectFnSet aRectFnSet(this);
4568  ::lcl_ExtendLeftAndRight( rOutRect, *(this), rAttrs, aRectFnSet.FnRect() );
4569 
4570  lcl_PaintShadow(rRect, rOutRect, rShadow, bDrawFullShadowRectangle, bTop, bBottom, true, true, gProp);
4571 }
4572 
4574  const SwRect& rOutRect,
4575  const SwPageFrame * pPage,
4576  const Color *pColor,
4577  const SvxBorderLineStyle nStyle ) const
4578 {
4579  if ( !rOutRect.Overlaps( rRect ) )
4580  return;
4581 
4582  SwRect aOut( rOutRect );
4583  aOut.Intersection_( rRect );
4584 
4585  const SwTabFrame *pTab = IsCellFrame() ? FindTabFrame() : nullptr;
4586  SubColFlags nSubCol = ( IsCellFrame() || IsRowFrame() )
4587  ? SubColFlags::Tab
4588  : ( IsInSct()
4589  ? SubColFlags::Sect
4590  : ( IsInFly() ? SubColFlags::Fly : SubColFlags::Page ) );
4591  if( pColor && gProp.pSGlobalShell->GetWin() &&
4593  {
4594  pColor = &SwViewOption::GetFontColor();
4595  }
4596 
4597  if (pPage->GetSortedObjs() &&
4599  {
4600  SwRegionRects aRegion( aOut, 4 );
4601  basegfx::utils::B2DClipState aClipState;
4602  ::lcl_SubtractFlys( this, pPage, aOut, aRegion, aClipState, gProp );
4603  for ( size_t i = 0; i < aRegion.size(); ++i )
4604  gProp.pSLines->AddLineRect( aRegion[i], pColor, nStyle, pTab, nSubCol, gProp );
4605  }
4606  else
4607  gProp.pSLines->AddLineRect( aOut, pColor, nStyle, pTab, nSubCol, gProp );
4608 }
4609 
4610 namespace drawinglayer::primitive2d
4611 {
4612  namespace {
4613 
4614  class SwBorderRectanglePrimitive2D : public BufferedDecompositionPrimitive2D
4615  {
4616  private:
4619 
4625 
4626  protected:
4628  virtual void create2DDecomposition(
4629  Primitive2DContainer& rContainer,
4630  const geometry::ViewInformation2D& rViewInformation) const override;
4631 
4632  public:
4634  SwBorderRectanglePrimitive2D(
4635  const basegfx::B2DHomMatrix& rB2DHomMatrix,
4636  const svx::frame::Style& rStyleTop,
4637  const svx::frame::Style& rStyleRight,
4638  const svx::frame::Style& rStyleBottom,
4639  const svx::frame::Style& rStyleLeft);
4640 
4642  const basegfx::B2DHomMatrix& getB2DHomMatrix() const { return maB2DHomMatrix; }
4643  const svx::frame::Style& getStyleTop() const { return maStyleTop; }
4644  const svx::frame::Style& getStyleRight() const { return maStyleRight; }
4645  const svx::frame::Style& getStyleBottom() const { return maStyleBottom; }
4646  const svx::frame::Style& getStyleLeft() const { return maStyleLeft; }
4647 
4649  virtual bool operator==(const BasePrimitive2D& rPrimitive) const override;
4650 
4652  virtual basegfx::B2DRange getB2DRange(const geometry::ViewInformation2D& rViewInformation) const override;
4653 
4655  virtual sal_uInt32 getPrimitive2DID() const override;
4656  };
4657 
4658  }
4659 
4660  void SwBorderRectanglePrimitive2D::create2DDecomposition(
4661  Primitive2DContainer& rContainer,
4662  const geometry::ViewInformation2D& /*rViewInformation*/) const
4663  {
4664  basegfx::B2DPoint aTopLeft(getB2DHomMatrix() * basegfx::B2DPoint(0.0, 0.0));
4665  basegfx::B2DPoint aTopRight(getB2DHomMatrix() * basegfx::B2DPoint(1.0, 0.0));
4666  basegfx::B2DPoint aBottomLeft(getB2DHomMatrix() * basegfx::B2DPoint(0.0, 1.0));
4667  basegfx::B2DPoint aBottomRight(getB2DHomMatrix() * basegfx::B2DPoint(1.0, 1.0));
4668 
4669  // prepare SdrFrameBorderDataVector
4670  std::shared_ptr<drawinglayer::primitive2d::SdrFrameBorderDataVector> aData(
4671  std::make_shared<drawinglayer::primitive2d::SdrFrameBorderDataVector>());
4672 
4673  if(getStyleTop().IsUsed())
4674  {
4675  // move top left/right inwards half border width
4676  basegfx::B2DVector aDown(getB2DHomMatrix() * basegfx::B2DVector(0.0, 1.0));
4677  aDown.setLength(getStyleTop().GetWidth() * 0.5);
4678  aTopLeft += aDown;
4679  aTopRight += aDown;
4680  }
4681 
4682  if(getStyleBottom().IsUsed())
4683  {
4684  // move bottom left/right inwards half border width
4685  basegfx::B2DVector aUp(getB2DHomMatrix() * basegfx::B2DVector(0.0, -1.0));
4686  aUp.setLength(getStyleBottom().GetWidth() * 0.5);
4687  aBottomLeft += aUp;
4688  aBottomRight += aUp;
4689  }
4690 
4691  if(getStyleLeft().IsUsed())
4692  {
4693  // move left top/bottom inwards half border width
4694  basegfx::B2DVector aRight(getB2DHomMatrix() * basegfx::B2DVector(1.0, 0.0));
4695  aRight.setLength(getStyleLeft().GetWidth() * 0.5);
4696  aTopLeft += aRight;
4697  aBottomLeft += aRight;
4698  }
4699 
4700  if(getStyleRight().IsUsed())
4701  {
4702  // move right top/bottom inwards half border width
4703  basegfx::B2DVector aLeft(getB2DHomMatrix() * basegfx::B2DVector(-1.0, 0.0));
4704  aLeft.setLength(getStyleRight().GetWidth() * 0.5);
4705  aTopRight += aLeft;
4706  aBottomRight += aLeft;
4707  }
4708 
4709  // go round-robin, from TopLeft to TopRight, down, left and back up. That
4710  // way, the borders will not need to be mirrored in any way
4711  if(getStyleTop().IsUsed())
4712  {
4713  // create BorderPrimitive(s) for top border
4714  const basegfx::B2DVector aVector(aTopRight - aTopLeft);
4715  aData->emplace_back(
4716  aTopLeft,
4717  aVector,
4718  getStyleTop(),
4719  nullptr);
4721 
4722  if(getStyleLeft().IsUsed())
4723  {
4724  rInstance.addSdrConnectStyleData(true, getStyleLeft(), basegfx::B2DVector(aBottomLeft - aTopLeft), false);
4725  }
4726 
4727  if(getStyleRight().IsUsed())
4728  {
4729  rInstance.addSdrConnectStyleData(false, getStyleRight(), basegfx::B2DVector(aBottomRight - aTopRight), false);
4730  }
4731  }
4732 
4733  if(getStyleRight().IsUsed())
4734  {
4735  // create BorderPrimitive(s) for right border
4736  const basegfx::B2DVector aVector(aBottomRight - aTopRight);
4737  aData->emplace_back(
4738  aTopRight,
4739  aVector,
4740  getStyleRight(),
4741  nullptr);
4743 
4744  if(getStyleTop().IsUsed())
4745  {
4746  rInstance.addSdrConnectStyleData(true, getStyleTop(), basegfx::B2DVector(aTopLeft - aTopRight), false);
4747  }
4748 
4749  if(getStyleBottom().IsUsed())
4750  {
4751  rInstance.addSdrConnectStyleData(false, getStyleBottom(), basegfx::B2DVector(aBottomLeft - aBottomRight), false);
4752  }
4753  }
4754 
4755  if(getStyleBottom().IsUsed())
4756  {
4757  // create BorderPrimitive(s) for bottom border
4758  const basegfx::B2DVector aVector(aBottomLeft - aBottomRight);
4759  aData->emplace_back(
4760  aBottomRight,
4761  aVector,
4762  getStyleBottom(),
4763  nullptr);
4765 
4766  if(getStyleRight().IsUsed())
4767  {
4768  rInstance.addSdrConnectStyleData(true, getStyleRight(), basegfx::B2DVector(aTopRight - aBottomRight), false);
4769  }
4770 
4771  if(getStyleLeft().IsUsed())
4772  {
4773  rInstance.addSdrConnectStyleData(false, getStyleLeft(), basegfx::B2DVector(aTopLeft - aBottomLeft), false);
4774  }
4775  }
4776 
4777  if(getStyleLeft().IsUsed())
4778  {
4779  // create BorderPrimitive(s) for left border
4780  const basegfx::B2DVector aVector(aTopLeft - aBottomLeft);
4781  aData->emplace_back(
4782  aBottomLeft,
4783  aVector,
4784  getStyleLeft(),
4785  nullptr);
4787 
4788  if(getStyleBottom().IsUsed())
4789  {
4790  rInstance.addSdrConnectStyleData(true, getStyleBottom(), basegfx::B2DVector(aBottomRight - aBottomLeft), false);
4791  }
4792 
4793  if(getStyleTop().IsUsed())
4794  {
4795  rInstance.addSdrConnectStyleData(false, getStyleTop(), basegfx::B2DVector(aTopRight - aTopLeft), false);
4796  }
4797  }
4798 
4799  // create instance of SdrFrameBorderPrimitive2D if
4800  // SdrFrameBorderDataVector is used
4801  if(!aData->empty())
4802  {
4803  rContainer.append(
4806  aData,
4807  true))); // force visualization to minimal one discrete unit (pixel)
4808  }
4809  }
4810 
4811  SwBorderRectanglePrimitive2D::SwBorderRectanglePrimitive2D(
4812  const basegfx::B2DHomMatrix& rB2DHomMatrix,
4813  const svx::frame::Style& rStyleTop,
4814  const svx::frame::Style& rStyleRight,
4815  const svx::frame::Style& rStyleBottom,
4816  const svx::frame::Style& rStyleLeft)
4817  : maB2DHomMatrix(rB2DHomMatrix),
4818  maStyleTop(rStyleTop),
4819  maStyleRight(rStyleRight),
4820  maStyleBottom(rStyleBottom),
4821  maStyleLeft(rStyleLeft)
4822  {
4823  }
4824 
4825  bool SwBorderRectanglePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
4826  {
4827  if(BasePrimitive2D::operator==(rPrimitive))
4828  {
4829  const SwBorderRectanglePrimitive2D& rCompare = static_cast<const SwBorderRectanglePrimitive2D&>(rPrimitive);
4830 
4831  return (getB2DHomMatrix() == rCompare.getB2DHomMatrix() &&
4832  getStyleTop() == rCompare.getStyleTop() &&
4833  getStyleRight() == rCompare.getStyleRight() &&
4834  getStyleBottom() == rCompare.getStyleBottom() &&
4835  getStyleLeft() == rCompare.getStyleLeft());
4836  }
4837 
4838  return false;
4839  }
4840 
4841  basegfx::B2DRange SwBorderRectanglePrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
4842  {
4843  basegfx::B2DRange aRetval(0.0, 0.0, 1.0, 1.0);
4844 
4845  aRetval.transform(getB2DHomMatrix());
4846  return aRetval;
4847  }
4848 
4849  // provide unique ID
4850  sal_uInt32 SwBorderRectanglePrimitive2D::getPrimitive2DID() const
4851  {
4853  }
4854 
4855 } // end of namespace drawinglayer::primitive2d
4856 
4857 namespace {
4858 
4859 editeng::SvxBorderLine const * get_ptr(std::optional<editeng::SvxBorderLine> const & opt) {
4860  return opt ? &*opt : nullptr;
4861 }
4862 
4863 }
4864 
4866  const SwFont& rFont,
4867  const SwRect& rPaintArea,
4868  const bool bVerticalLayout,
4869  const bool bVerticalLayoutLRBT,
4870  const bool bJoinWithPrev,
4871  const bool bJoinWithNext )
4872 {
4873  SwRect aAlignedRect(rPaintArea);
4874  SwAlignRect(aAlignedRect, gProp.pSGlobalShell, gProp.pSGlobalShell->GetOut());
4875 
4876  bool bTop = true;
4877  bool bBottom = true;
4878  bool bLeft = true;
4879  bool bRight = true;
4880 
4881  switch (rFont.GetOrientation(bVerticalLayout, bVerticalLayoutLRBT).get())
4882  {
4883  case 0 :
4884  bLeft = !bJoinWithPrev;
4885  bRight = !bJoinWithNext;
4886  break;
4887  case 900 :
4888  bBottom = !bJoinWithPrev;
4889  bTop = !bJoinWithNext;
4890  break;
4891  case 1800 :
4892  bRight = !bJoinWithPrev;
4893  bLeft = !bJoinWithNext;
4894  break;
4895  case 2700 :
4896  bTop = !bJoinWithPrev;
4897  bBottom = !bJoinWithNext;
4898  break;
4899  }
4900 
4901  // Paint shadow (reduce painting rect)
4902  {
4903  const SvxShadowItem aShadow(
4904  0, &rFont.GetShadowColor(), rFont.GetShadowWidth(),
4905  rFont.GetAbsShadowLocation(bVerticalLayout, bVerticalLayoutLRBT));
4906 
4907  if( aShadow.GetLocation() != SvxShadowLocation::NONE )
4908  {
4909  lcl_PaintShadow( rPaintArea, aAlignedRect, aShadow,
4910  false, bTop, bBottom, bLeft, bRight, gProp);
4911  }
4912  }
4913 
4914  const basegfx::B2DHomMatrix aBorderTransform(
4916  aAlignedRect.Width(), aAlignedRect.Height(),
4917  aAlignedRect.Left(), aAlignedRect.Top()));
4918  const svx::frame::Style aStyleTop(
4919  bTop ? get_ptr(rFont.GetAbsTopBorder(bVerticalLayout, bVerticalLayoutLRBT)) : nullptr,
4920  1.0);
4921  const svx::frame::Style aStyleRight(
4922  bRight ? get_ptr(rFont.GetAbsRightBorder(bVerticalLayout, bVerticalLayoutLRBT)) : nullptr,
4923  1.0);
4924  const svx::frame::Style aStyleBottom(
4925  bBottom ? get_ptr(rFont.GetAbsBottomBorder(bVerticalLayout, bVerticalLayoutLRBT))
4926  : nullptr,
4927  1.0);
4928  const svx::frame::Style aStyleLeft(
4929  bLeft ? get_ptr(rFont.GetAbsLeftBorder(bVerticalLayout, bVerticalLayoutLRBT)) : nullptr,
4930  1.0);
4932 
4933  aBorderLineTarget.append(
4935  new drawinglayer::primitive2d::SwBorderRectanglePrimitive2D(
4936  aBorderTransform,
4937  aStyleTop,
4938  aStyleRight,
4939  aStyleBottom,
4940  aStyleLeft)));
4941  gProp.pBLines->AddBorderLines(std::move(aBorderLineTarget));
4942 }
4943 
4945 static const SwFrame* lcl_HasNextCell( const SwFrame& rFrame )
4946 {
4947  OSL_ENSURE( rFrame.IsCellFrame(),
4948  "lcl_HasNextCell( const SwFrame& rFrame ) should be called with SwCellFrame" );
4949 
4950  const SwFrame* pTmpFrame = &rFrame;
4951  do
4952  {
4953  if ( pTmpFrame->GetNext() )
4954  return pTmpFrame->GetNext();
4955 
4956  pTmpFrame = pTmpFrame->GetUpper()->GetUpper();
4957  }
4958  while ( pTmpFrame->IsCellFrame() );
4959 
4960  return nullptr;
4961 }
4962 
4984 static const SwFrame* lcl_GetCellFrameForBorderAttrs( const SwFrame* _pCellFrame,
4985  const SwBorderAttrs& _rCellBorderAttrs,
4986  const bool _bTop )
4987 {
4988  OSL_ENSURE( _pCellFrame, "No cell frame available, dying soon" );
4989 
4990  // determine, if cell frame is at bottom/top border of a table frame and
4991  // the table frame has/is a follow.
4992  const SwFrame* pTmpFrame = _pCellFrame;
4993  bool bCellAtBorder = true;
4994  bool bCellAtLeftBorder = !_pCellFrame->GetPrev();
4995  bool bCellAtRightBorder = !_pCellFrame->GetNext();
4996  while( !pTmpFrame->IsRowFrame() || !pTmpFrame->GetUpper()->IsTabFrame() )
4997  {
4998  pTmpFrame = pTmpFrame->GetUpper();
4999  if ( pTmpFrame->IsRowFrame() &&
5000  (_bTop ? pTmpFrame->GetPrev() : pTmpFrame->GetNext())
5001  )
5002  {
5003  bCellAtBorder = false;
5004  }
5005  if ( pTmpFrame->IsCellFrame() )
5006  {
5007  if ( pTmpFrame->GetPrev() )
5008  {
5009  bCellAtLeftBorder = false;
5010  }
5011  if ( pTmpFrame->GetNext() )
5012  {
5013  bCellAtRightBorder = false;
5014  }
5015  }
5016  }
5017  OSL_ENSURE( pTmpFrame && pTmpFrame->IsRowFrame(), "No RowFrame available" );
5018 
5019  const SwLayoutFrame* pParentRowFrame = static_cast<const SwLayoutFrame*>(pTmpFrame);
5020  const SwTabFrame* pParentTabFrame =
5021  static_cast<const SwTabFrame*>(pParentRowFrame->GetUpper());
5022 
5023  const bool bCellNeedsAttribute = bCellAtBorder &&
5024  ( _bTop ?
5025  // bCellInFirstRowWithMaster
5026  ( !pParentRowFrame->GetPrev() &&
5027  pParentTabFrame->IsFollow() &&
5028  0 == pParentTabFrame->GetTable()->GetRowsToRepeat() ) :
5029  // bCellInLastRowWithFollow
5030  ( !pParentRowFrame->GetNext() &&
5031  pParentTabFrame->GetFollow() )
5032  );
5033 
5034  const SwFrame* pRet = _pCellFrame;
5035  if ( bCellNeedsAttribute )
5036  {
5037  // determine, if cell frame has no borders inside the table.
5038  const SwFrame* pNextCell = nullptr;
5039  bool bNoBordersInside = false;
5040 
5041  if ( bCellAtLeftBorder && ( nullptr != ( pNextCell = lcl_HasNextCell( *_pCellFrame ) ) ) )
5042  {
5043  SwBorderAttrAccess aAccess( SwFrame::GetCache(), pNextCell );
5044  const SwBorderAttrs &rBorderAttrs = *aAccess.Get();
5045  const SvxBoxItem& rBorderBox = rBorderAttrs.GetBox();
5046  bCellAtRightBorder = !lcl_HasNextCell( *pNextCell );
5047  bNoBordersInside =
5048  ( !rBorderBox.GetTop() || !pParentRowFrame->GetPrev() ) &&
5049  !rBorderBox.GetLeft() &&
5050  ( !rBorderBox.GetRight() || bCellAtRightBorder ) &&
5051  ( !rBorderBox.GetBottom() || !pParentRowFrame->GetNext() );
5052  }
5053  else
5054  {
5055  const SvxBoxItem& rBorderBox = _rCellBorderAttrs.GetBox();
5056  bNoBordersInside =
5057  ( !rBorderBox.GetTop() || !pParentRowFrame->GetPrev() ) &&
5058  ( !rBorderBox.GetLeft() || bCellAtLeftBorder ) &&
5059  ( !rBorderBox.GetRight() || bCellAtRightBorder ) &&
5060  ( !rBorderBox.GetBottom() || !pParentRowFrame->GetNext() );
5061  }
5062 
5063  if ( bNoBordersInside )
5064  {
5065  if ( _bTop && !_rCellBorderAttrs.GetBox().GetTop() )
5066  {
5067  //-hack
5068  // Cell frame has no top border and no border inside the table, but
5069  // it is at the top border of a table frame, which is a follow.
5070  // Thus, use border attributes of cell frame in first row of complete table.
5071  // First, determine first table frame of complete table.
5072  SwTabFrame* pMasterTabFrame = pParentTabFrame->FindMaster( true );
5073  // determine first row of complete table.
5074  const SwFrame* pFirstRow = pMasterTabFrame->GetLower();
5075  // return first cell in first row
5076  SwFrame* pLowerCell = const_cast<SwFrame*>(pFirstRow->GetLower());
5077  while ( !pLowerCell->IsCellFrame() ||
5078  ( pLowerCell->GetLower() && pLowerCell->GetLower()->IsRowFrame() )
5079  )
5080  {
5081  pLowerCell = pLowerCell->GetLower();
5082  }
5083  OSL_ENSURE( pLowerCell && pLowerCell->IsCellFrame(), "No CellFrame available" );
5084  pRet = pLowerCell;
5085  }
5086  else if ( !_bTop && !_rCellBorderAttrs.GetBox().GetBottom() )
5087  {
5088  //-hack
5089  // Cell frame has no bottom border and no border inside the table,
5090  // but it is at the bottom border of a table frame, which has a follow.
5091  // Thus, use border attributes of cell frame in last row of complete table.
5092  // First, determine last table frame of complete table.
5093  SwTabFrame* pLastTabFrame = const_cast<SwTabFrame*>(pParentTabFrame->GetFollow());
5094  while ( pLastTabFrame->GetFollow() )
5095  {
5096  pLastTabFrame = pLastTabFrame->GetFollow();
5097  }
5098  // determine last row of complete table.
5099  SwFrame* pLastRow = pLastTabFrame->GetLastLower();
5100  // return first bottom border cell in last row
5101  SwFrame* pLowerCell = pLastRow->GetLower();
5102  while ( !pLowerCell->IsCellFrame() ||
5103  ( pLowerCell->GetLower() && pLowerCell->GetLower()->IsRowFrame() )
5104  )
5105  {
5106  if ( pLowerCell->IsRowFrame() )
5107  {
5108  while ( pLowerCell->GetNext() )
5109  {
5110  pLowerCell = pLowerCell->GetNext();
5111  }
5112  }
5113  pLowerCell = pLowerCell->GetLower();
5114  }
5115  OSL_ENSURE( pLowerCell && pLowerCell->IsCellFrame(), "No CellFrame available" );
5116  pRet = pLowerCell;
5117  }
5118  }
5119  }
5120 
5121  return pRet;
5122 }
5123 
5124 std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> SwFrame::CreateProcessor2D( ) const
5125 {
5126  basegfx::B2DRange aViewRange;
5127 
5128  SdrPage *pDrawPage = getRootFrame()->GetCurrShell()->Imp()->GetPageView()->GetPage();
5129  const drawinglayer::geometry::ViewInformation2D aNewViewInfos(
5131  getRootFrame()->GetCurrShell()->GetOut()->GetViewTransformation(),
5132  aViewRange,
5133  GetXDrawPageForSdrPage( pDrawPage ),
5134  0.0);
5135 
5137  *getRootFrame()->GetCurrShell()->GetOut(),
5138  aNewViewInfos );
5139 }
5140 
5142 {
5143  std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor2D = CreateProcessor2D();
5144  if ( pProcessor2D )
5145  {
5146  pProcessor2D->process( rSequence );
5147  }
5148 }
5149 
5152  const SwRect& rRect,
5153  const SwPageFrame* /*pPage*/,
5154  const SwBorderAttrs& rAttrs) const
5155 {
5156  // There's nothing (Row,Body,Footnote,Root,Column,NoText) need to do here
5158  return;
5159 
5160  if (IsCellFrame() && !gProp.pSGlobalShell->GetViewOptions()->IsTable())
5161  return;
5162 
5163  // #i29550#
5164  if ( IsTabFrame() || IsCellFrame() || IsRowFrame() )
5165  {
5166  const SwTabFrame* pTabFrame = FindTabFrame();
5167  if ( pTabFrame->IsCollapsingBorders() )
5168  return;
5169 
5170  if ( pTabFrame->GetTable()->IsNewModel() && ( !IsCellFrame() || IsCoveredCell() ) )
5171  return;
5172  }
5173 
5174  const bool bLine = rAttrs.IsLine();
5175  const bool bShadow = rAttrs.GetShadow().GetLocation() != SvxShadowLocation::NONE;
5176 
5177  // - flag to control,
5178  //-hack has to be used.
5179  const bool bb4779636HackActive = true;
5180 
5181  const SwFrame* pCellFrameForBottomBorderAttrs = nullptr;
5182  const SwFrame* pCellFrameForTopBorderAttrs = nullptr;
5183  bool bFoundCellForTopOrBorderAttrs = false;
5184  if ( bb4779636HackActive && IsCellFrame() )
5185  {
5186  pCellFrameForBottomBorderAttrs = lcl_GetCellFrameForBorderAttrs( this, rAttrs, false );
5187  if ( pCellFrameForBottomBorderAttrs != this )
5188  bFoundCellForTopOrBorderAttrs = true;
5189  pCellFrameForTopBorderAttrs = lcl_GetCellFrameForBorderAttrs( this, rAttrs, true );
5190  if ( pCellFrameForTopBorderAttrs != this )
5191  bFoundCellForTopOrBorderAttrs = true;
5192  }
5193 
5194  // - add condition <bFoundCellForTopOrBorderAttrs>
5195  //-hack
5196  if ( !(bLine || bShadow || bFoundCellForTopOrBorderAttrs) )
5197  return;
5198 
5199  //If the rectangle is completely inside the PrtArea, no border needs to
5200  //be painted.
5201  //For the PrtArea the aligned value needs to be used, otherwise it could
5202  //happen, that some parts won't be processed.
5203  SwRect aRect( getFramePrintArea() );
5204  aRect += getFrameArea().Pos();
5205  ::SwAlignRect( aRect, gProp.pSGlobalShell, gProp.pSGlobalShell->GetOut() );
5206  // new local boolean variable in order to
5207  // suspend border paint under special cases - see below.
5208  // NOTE: This is a fix for the implementation of feature #99657#.
5209  bool bDrawOnlyShadowForTransparentFrame = false;
5210  if ( aRect.Contains( rRect ) )
5211  {
5212  // paint shadow, if background is transparent.
5213  // Because of introduced transparent background for fly frame #99657#,
5214  // the shadow have to be drawn if the background is transparent,
5215  // in spite the fact that the paint rectangle <rRect> lies fully
5216  // in the printing area.
5217  // NOTE to chosen solution:
5218  // On transparent background, continue processing, but suspend
5219  // drawing of border by setting <bDrawOnlyShadowForTransparentFrame>
5220  // to true.
5221  if ( IsLayoutFrame() &&
5222  static_cast<const SwLayoutFrame*>(this)->GetFormat()->IsBackgroundTransparent() )
5223  {
5224  bDrawOnlyShadowForTransparentFrame = true;
5225  }
5226  else
5227  {
5228  return;
5229  }
5230  }
5231 
5232  ::lcl_CalcBorderRect( aRect, this, rAttrs, true, gProp );
5233  rAttrs.SetGetCacheLine( true );
5234 
5235  if(bShadow)
5236  {
5237  PaintShadow(rRect, aRect, rAttrs);
5238  }
5239 
5240  // suspend drawing of border
5241  // add condition < NOT bDrawOnlyShadowForTransparentFrame > - see above
5242  // - add condition <bFoundCellForTopOrBorderAttrs>
5243  //-hack.
5244  if((bLine || bFoundCellForTopOrBorderAttrs) && !bDrawOnlyShadowForTransparentFrame)
5245  {
5246  // define SvxBorderLine(s) to use
5247  const SvxBoxItem& rBox(rAttrs.GetBox());
5248  const SvxBorderLine* pLeftBorder(rBox.GetLeft());
5249  const SvxBorderLine* pRightBorder(rBox.GetRight());
5250  const SvxBorderLine* pTopBorder(rBox.GetTop());
5251  const SvxBorderLine* pBottomBorder(rBox.GetBottom());
5252 
5253  // if R2L, exchange Right/Left
5254  const bool bR2L(IsCellFrame() && IsRightToLeft());
5255 
5256  if(bR2L)
5257  {
5258  std::swap(pLeftBorder, pRightBorder);
5259  }
5260 
5261  // if ContentFrame and joined Prev/Next, reset top/bottom as needed
5262  if(IsContentFrame())
5263  {
5264  const SwFrame* pDirRefFrame(IsCellFrame() ? FindTabFrame() : this);
5265  const SwRectFnSet aRectFnSet(pDirRefFrame);
5266  const SwRectFn& _rRectFn(aRectFnSet.FnRect());
5267 
5268  if(rAttrs.JoinedWithPrev(*this))
5269  {
5270  // tdf#115296 re-add adaptation of vert distance to close the evtl.
5271  // existing gap to previous frame
5272  const SwFrame* pPrevFrame(GetPrev());
5273  (aRect.*_rRectFn->fnSetTop)( (pPrevFrame->*_rRectFn->fnGetPrtBottom)() );
5274 
5275  // ...and disable top border paint/creation
5276  pTopBorder = nullptr;
5277  }
5278 
5279  if(rAttrs.JoinedWithNext(*this))
5280  {
5281  // tdf#115296 re-add adaptation of vert distance to close the evtl.
5282  // existing gap to next frame
5283  const SwFrame* pNextFrame(GetNext());
5284  (aRect.*_rRectFn->fnSetBottom)( (pNextFrame->*_rRectFn->fnGetPrtTop)() );
5285 
5286  // ...and disable bottom border paint/creation
5287  pBottomBorder = nullptr;
5288  }
5289  }
5290 
5291  // necessary to replace TopBorder?
5292  if((!IsContentFrame() || rAttrs.GetTopLine(*this)) && IsCellFrame() && pCellFrameForTopBorderAttrs != this)
5293  {
5294  SwBorderAttrAccess aAccess(SwFrame::GetCache(), pCellFrameForTopBorderAttrs);
5295  pTopBorder = aAccess.Get()->GetBox().GetTop();
5296  }
5297 
5298  // necessary to replace BottomBorder?
5299  if((!IsContentFrame() || rAttrs.GetBottomLine(*this)) && IsCellFrame() && pCellFrameForBottomBorderAttrs != this)
5300  {
5301  SwBorderAttrAccess aAccess(SwFrame::GetCache(), pCellFrameForBottomBorderAttrs);
5302  pBottomBorder = aAccess.Get()->GetBox().GetBottom();
5303  }
5304 
5305  if(nullptr != pLeftBorder || nullptr != pRightBorder || nullptr != pTopBorder || nullptr != pBottomBorder)
5306  {
5307  // now we have all SvxBorderLine(s) sorted out, create geometry
5308  const basegfx::B2DHomMatrix aBorderTransform(
5310  aRect.Width(), aRect.Height(),
5311  aRect.Left(), aRect.Top()));
5312  const svx::frame::Style aStyleTop(pTopBorder, 1.0);
5313  const svx::frame::Style aStyleRight(pRightBorder, 1.0);
5314  const svx::frame::Style aStyleBottom(pBottomBorder, 1.0);
5315  const svx::frame::Style aStyleLeft(pLeftBorder, 1.0);
5317 
5318  aBorderLineTarget.append(
5320  new drawinglayer::primitive2d::SwBorderRectanglePrimitive2D(
5321  aBorderTransform,
5322  aStyleTop,
5323  aStyleRight,
5324  aStyleBottom,
5325  aStyleLeft)));
5326  gProp.pBLines->AddBorderLines(std::move(aBorderLineTarget));
5327  }
5328  }
5329 
5330  rAttrs.SetGetCacheLine( false );
5331 }
5332 
5340  const SwRect& rRect,
5341  const SwPageFrame* pPage,
5342  const SwBorderAttrs&) const
5343 {
5344  //If the rectangle is completely inside the PrtArea, no border needs to
5345  //be painted.
5346  SwRect aRect( getFramePrintArea() );
5347  aRect.Pos() += getFrameArea().Pos();
5348  if ( !aRect.Contains( rRect ) )
5349  PaintLine( rRect, pPage );
5350 }
5351 
5354  const SwPageFrame *pPage ) const
5355 {
5356  //The length of the line is derived from the percentual indication on the
5357  //PageDesc. The position is also stated on the PageDesc.
5358  //The pen can directly be taken from the PageDesc.
5359 
5360  if ( !pPage )
5361  pPage = FindPageFrame();
5362  const SwPageFootnoteInfo &rInf = pPage->GetPageDesc()->GetFootnoteInfo();
5363 
5364  SwRectFnSet aRectFnSet(this);
5365  SwTwips nPrtWidth = aRectFnSet.GetWidth(getFramePrintArea());
5366  Fraction aFract( nPrtWidth, 1 );
5367  aFract *= rInf.GetWidth();
5368  const SwTwips nWidth = static_cast<tools::Long>(aFract);
5369 
5370  SwTwips nX = aRectFnSet.GetPrtLeft(*this);
5371  switch ( rInf.GetAdj() )
5372  {
5373  case css::text::HorizontalAdjust_CENTER:
5374  nX += nPrtWidth/2 - nWidth/2; break;
5375  case css::text::HorizontalAdjust_RIGHT:
5376  nX += nPrtWidth - nWidth; break;
5377  case css::text::HorizontalAdjust_LEFT:
5378  /* do nothing */; break;
5379  default:
5380  SAL_WARN("sw.core", "New adjustment for footnote lines?");
5381  assert(false);
5382  }
5383  SwTwips nLineWidth = rInf.GetLineWidth();
5384  const SwRect aLineRect = aRectFnSet.IsVert() ?
5385  SwRect( Point(getFrameArea().Left()+getFrameArea().Width()-rInf.GetTopDist()-nLineWidth,
5386  nX), Size( nLineWidth, nWidth ) )
5387  : SwRect( Point( nX, getFrameArea().Pos().Y() + rInf.GetTopDist() ),
5388  Size( nWidth, rInf.GetLineWidth()));
5389  if ( aLineRect.HasArea() && rInf.GetLineStyle() != SvxBorderLineStyle::NONE)
5390  PaintBorderLine( rRect, aLineRect , pPage, &rInf.GetLineColor(),
5391  rInf.GetLineStyle() );
5392 }
5393 
5395 void SwLayoutFrame::PaintColLines( const SwRect &rRect, const SwFormatCol &rFormatCol,
5396  const SwPageFrame *pPage ) const
5397 {
5398  const SwFrame *pCol = Lower();
5399  if ( !pCol || !pCol->IsColumnFrame() )
5400  return;
5401 
5402  SwRectFn fnRect = pCol->IsVertical() ? ( pCol->IsVertLR() ? (pCol->IsVertLRBT() ? fnRectVertL2RB2T : fnRectVertL2R) : fnRectVert ) : fnRectHori;
5403 
5404  SwRect aLineRect = getFramePrintArea();
5405  aLineRect += getFrameArea().Pos();
5406 
5407  SwTwips nTop = ((aLineRect.*fnRect->fnGetHeight)()*rFormatCol.GetLineHeight())
5408  / 100 - (aLineRect.*fnRect->fnGetHeight)();
5409  SwTwips nBottom = 0;
5410 
5411  switch ( rFormatCol.GetLineAdj() )
5412  {
5413  case COLADJ_CENTER:
5414  nBottom = nTop / 2; nTop -= nBottom; break;
5415  case COLADJ_TOP:
5416  nBottom = nTop; nTop = 0; break;
5417  case COLADJ_BOTTOM:
5418  break;
5419  default:
5420  OSL_ENSURE( false, "New adjustment for column lines?" );
5421  }
5422 
5423  if( nTop )
5424  (aLineRect.*fnRect->fnSubTop)( nTop );
5425  if( nBottom )
5426  (aLineRect.*fnRect->fnAddBottom)( nBottom );
5427 
5428  SwTwips nPenHalf = rFormatCol.GetLineWidth();
5429  (aLineRect.*fnRect->fnSetWidth)( nPenHalf );
5430  nPenHalf /= 2;
5431 
5432  //We need to be a bit generous here, to not lose something.
5433  SwRect aRect( rRect );
5434  (aRect.*fnRect->fnSubLeft)( nPenHalf + gProp.nSPixelSzW );
5435  (aRect.*fnRect->fnAddRight)( nPenHalf + gProp.nSPixelSzW );
5436  SwRectGet fnGetX = IsRightToLeft() ? fnRect->fnGetLeft : fnRect->fnGetRight;
5437  while ( pCol->GetNext() )
5438  {
5439  (aLineRect.*fnRect->fnSetPosX)
5440  ( (pCol->getFrameArea().*fnGetX)() - nPenHalf );
5441  if ( aRect.Overlaps( aLineRect ) )
5442  PaintBorderLine( aRect, aLineRect , pPage, &rFormatCol.GetLineColor(),
5443  rFormatCol.GetLineStyle() );
5444  pCol = pCol->GetNext();
5445  }
5446 }
5447 
5448 void SwPageFrame::PaintGrid( OutputDevice const * pOut, SwRect const &rRect ) const
5449 {
5450  if( !m_bHasGrid || gProp.pSRetoucheFly || gProp.pSRetoucheFly2 )
5451  return;
5452  SwTextGridItem const*const pGrid(GetGridItem(this));
5453  if( !(pGrid && ( OUTDEV_PRINTER != pOut->GetOutDevType() ?
5454  pGrid->GetDisplayGrid() : pGrid->GetPrintGrid() )) )
5455  return;
5456 
5457  const SwLayoutFrame* pBody = FindBodyCont();
5458  if( !pBody )
5459  return;
5460 
5461  SwRect aGrid( pBody->getFramePrintArea() );
5462  aGrid += pBody->getFrameArea().Pos();
5463 
5464  SwRect aInter( aGrid );
5465  aInter.Intersection( rRect );
5466  if( !aInter.HasArea() )
5467  return;
5468 
5469  bool bGrid = pGrid->GetRubyTextBelow();
5470  bool bCell = GRID_LINES_CHARS == pGrid->GetGridType();
5471  tools::Long nGrid = pGrid->GetBaseHeight();
5472  const SwDoc* pDoc = GetFormat()->GetDoc();
5473  tools::Long nGridWidth = GetGridWidth(*pGrid, *pDoc);
5474  tools::Long nRuby = pGrid->GetRubyHeight();
5475  tools::Long nSum = nGrid + nRuby;
5476  const Color *pCol = &pGrid->GetColor();
5477 
5478  SwTwips nRight = aInter.Left() + aInter.Width();
5479  SwTwips nBottom = aInter.Top() + aInter.Height();
5480  if( IsVertical() )
5481  {
5482  SwTwips nOrig = aGrid.Left() + aGrid.Width();
5483  SwTwips nY = nOrig + nSum *
5484  ( ( nOrig - aInter.Left() ) / nSum );
5485  SwRect aTmp( Point( nY, aInter.Top() ),
5486  Size( 1, aInter.Height() ) );
5487  SwTwips nX = aGrid.Top() + nGrid *
5488  ( ( aInter.Top() - aGrid.Top() )/ nGrid );
5489  if( nX < aInter.Top() )
5490  nX += nGrid;
5491  SwTwips nGridBottom = aGrid.Top() + aGrid.Height();
5492  bool bLeft = aGrid.Top() >= aInter.Top();
5493  bool bRight = nGridBottom <= nBottom;
5494  bool bBorder = bLeft || bRight;
5495  while( nY > nRight )
5496  {
5497  aTmp.Pos().setX( nY );
5498  if( bGrid )
5499  {
5500  nY -= nGrid;
5501  SwTwips nPosY = std::max(