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