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 <utility>
21#include <vcl/lazydelete.hxx>
22#include <sfx2/docfile.hxx>
23#include <sfx2/printer.hxx>
24#include <sfx2/progress.hxx>
25#include <editeng/brushitem.hxx>
26#include <editeng/prntitem.hxx>
27#include <editeng/boxitem.hxx>
28#include <editeng/shaditem.hxx>
29#include <svx/ctredlin.hxx>
30#include <svx/framelink.hxx>
31#include <drawdoc.hxx>
32#include <tgrditem.hxx>
33#include <calbck.hxx>
34#include <fmtsrnd.hxx>
35#include <fmtclds.hxx>
36#include <strings.hrc>
37#include <swmodule.hxx>
38#include <rootfrm.hxx>
39#include <pagefrm.hxx>
40#include <section.hxx>
41#include <sectfrm.hxx>
42#include <viewimp.hxx>
43#include <dflyobj.hxx>
44#include <flyfrm.hxx>
45#include <frmatr.hxx>
46#include <frmtool.hxx>
47#include <viewopt.hxx>
48#include <dview.hxx>
49#include <dcontact.hxx>
50#include <txtfrm.hxx>
51#include <ftnfrm.hxx>
52#include <tabfrm.hxx>
53#include <rowfrm.hxx>
54#include <cellfrm.hxx>
55#include <notxtfrm.hxx>
56#include <layact.hxx>
57#include <pagedesc.hxx>
58#include <ptqueue.hxx>
59#include <noteurl.hxx>
60#include "virtoutp.hxx"
61#include <lineinfo.hxx>
62#include <dbg_lay.hxx>
63#include <docsh.hxx>
64#include <svx/svdogrp.hxx>
65#include <sortedobjs.hxx>
67#include <bodyfrm.hxx>
68#include <hffrm.hxx>
69#include <colfrm.hxx>
71#include <swfont.hxx>
72
80
81#include <ndole.hxx>
82#include <PostItMgr.hxx>
84#include <vcl/settings.hxx>
85
87
89
90#include <bitmaps.hlst>
99#include <svx/unoapi.hxx>
100#include <svx/svdpagv.hxx>
101#include <svx/xfillit0.hxx>
105#include <sal/log.hxx>
106
107#include <memory>
108#include <vector>
109#include <algorithm>
110#include <wrtsh.hxx>
111#include <edtwin.hxx>
112#include <view.hxx>
113#include <paintfrm.hxx>
114#include <textboxhelper.hxx>
116
117#include <vcl/BitmapTools.hxx>
118#include <comphelper/lok.hxx>
120#include <vcl/GraphicLoader.hxx>
122
123using namespace ::editeng;
124using namespace ::com::sun::star;
125
126namespace {
127
128struct SwPaintProperties;
129
130//Class declaration; here because they are only used in this file
131enum 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
140namespace o3tl {
141 template<> struct typed_flags<SubColFlags> : is_typed_flags<SubColFlags, 0x39> {};
142}
143
144namespace {
145
146// Classes collecting the border lines and help lines
147class SwLineRect : public SwRect
148{
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.
155public:
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
180static void dummy_function()
181{
182 pid_t pid = getpid();
183 (void) pid;
184}
185#endif
186
187namespace {
188
189class SwLineRects
190{
191public:
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
220class SwSubsRects : public SwLineRects
221{
222 void RemoveSuperfluousSubsidiaryLines( const SwLineRects &rRects, SwPaintProperties const &properties );
223public:
224 void PaintSubsidiary( OutputDevice *pOut, const SwLineRects *pRects, SwPaintProperties const &properties );
225};
226
227class BorderLines
228{
230public:
231 void AddBorderLines(drawinglayer::primitive2d::Primitive2DContainer&& rContainer);
233 {
235 lines.swap(m_Lines);
236 return lines;
237 }
238};
239
240}
241
242// Default zoom factor
243const double aEdgeScale = 0.5;
244
245//To optimize the expensive RetouchColor determination
247
248namespace sw
249{
251{
252 return &aGlobalRetoucheColor;
253}
254}
255
256namespace {
257
261struct 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
326static 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
354namespace {
355
356bool 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
434namespace {
435
439class SwSavePaintStatics : public SwPaintProperties
440{
441public:
442 SwSavePaintStatics();
443 ~SwSavePaintStatics();
444};
445
446}
447
448SwSavePaintStatics::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
484SwSavePaintStatics::~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
509void BorderLines::AddBorderLines(drawinglayer::primitive2d::Primitive2DContainer&& rContainer)
510{
511 if(!rContainer.empty())
512 {
513 m_Lines.append(std::move(rContainer));
514 }
515}
516
517SwLineRect::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
530bool 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
567void 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
588void 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.Contains( 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.Contains( 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
757void 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.Overlaps( 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
860void SwLineRects::LockLines( bool bLock )
861{
862 for (SwLineRect& rLRect : m_aLineRects)
863 rLRect.Lock(bLock);
864}
865
866static 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
892void 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
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
1012void 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
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
1126void 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 = SwRect(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
1232void 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
1239static 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
1251static 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
1266static 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 || rBox.GetTop() )
1295 {
1296 SwTwips nDiff = rBox.GetTop() ?
1297 rBox.CalcLineSpace( SvxBoxItemLine::TOP, /*bEvenIfNoLine=*/false, /*bAllowNegative=*/true ) :
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
1355static 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
1387static 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
1402 continue;
1403
1404 const SwFlyFrame *pFly = pAnchoredObj->DynCastFlyFrame();
1405 if (!pFly)
1406 continue;
1407
1408 if (pSelfFly == pFly || gProp.pSRetoucheFly == pFly || !rRect.Overlaps(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
1552static void lcl_implDrawGraphicBackground(const SvxBrushItem& _rBackgrdBrush,
1553 vcl::RenderContext& _rOut,
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( _rOut.GetFillColor() != aColor.GetRGBColor() )
1589 _rOut.SetFillColor( aColor.GetRGBColor() );
1590 tools::PolyPolygon aPoly( _rAlignedPaintRect.SVRect() );
1591 _rOut.DrawTransparent( aPoly, nTransparencyPercent );
1592 }
1593 else
1594 {
1596 if ( _rOut.GetFillColor() != aColor )
1597 _rOut.SetFillColor( aColor );
1598 _rOut.DrawRect( _rAlignedPaintRect.SVRect() );
1599 }
1600}
1601
1634static void lcl_DrawGraphicBackground( const SvxBrushItem& _rBackgrdBrush,
1635 OutputDevice& _rOut,
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_implDrawGraphicBackground( _rBackgrdBrush, _rOut, _rAlignedPaintRect, _rGraphicObj, properties );
1652 }
1653}
1654
1670static void lcl_DrawGraphic( const SvxBrushItem& rBrush, vcl::RenderContext &rOutDev,
1671 const 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, &rOutDev );
1682
1683 // Change type from <bool> to <bool>.
1684 const bool bNotInside = !rOut.Contains( aAlignedGrfRect );
1685 if ( bNotInside )
1686 {
1688 rOutDev.IntersectClipRegion( rOut.SVRect() );
1689 }
1690
1691 GraphicObject *pGrf = const_cast<GraphicObject*>(rBrush.GetGraphicObject());
1692
1693 OUString aOriginURL = pGrf->GetGraphic().getOriginURL();
1694 if (pGrf->GetGraphic().GetType() == GraphicType::Default && !aOriginURL.isEmpty())
1695 {
1696 Graphic aGraphic = vcl::graphic::loadFromURL(aOriginURL);
1697 pGrf->SetGraphic(aGraphic);
1698 }
1699
1700 // Outsource drawing of background with a background color
1701 ::lcl_DrawGraphicBackground( rBrush, rOutDev, aAlignedGrfRect, *pGrf, bGrfNum, properties, bBackgrdAlreadyDrawn );
1702
1703 // Because for drawing a graphic left-top-corner and size coordinates are
1704 // used, these coordinates have to be determined on pixel level.
1705 ::SwAlignGrfRect( &aAlignedGrfRect, rOutDev );
1706
1707 const basegfx::B2DHomMatrix aGraphicTransform(
1709 aAlignedGrfRect.Width(), aAlignedGrfRect.Height(),
1710 aAlignedGrfRect.Left(), aAlignedGrfRect.Top()));
1711
1713 rOutDev,
1714 *pGrf,
1715 pGrf->GetAttr(),
1716 aGraphicTransform,
1717 OUString(),
1718 OUString(),
1719 OUString());
1720
1721 if ( bNotInside )
1722 rOutDev.Pop();
1723}
1724
1727 const SwRect& rOriginalLayoutRect,
1728 const SwRegionRects& rPaintRegion,
1729 const basegfx::utils::B2DClipState& rClipState,
1730 vcl::RenderContext& rOut)
1731{
1732 if(rFillAttributes && rFillAttributes->isUsed())
1733 {
1734 basegfx::B2DRange aPaintRange(
1735 rPaintRegion.GetOrigin().Left(),
1736 rPaintRegion.GetOrigin().Top(),
1737 rPaintRegion.GetOrigin().Right(),
1738 rPaintRegion.GetOrigin().Bottom());
1739
1740 if (!aPaintRange.isEmpty() &&
1741 !rPaintRegion.empty() &&
1742 !basegfx::fTools::equalZero(aPaintRange.getWidth()) &&
1743 !basegfx::fTools::equalZero(aPaintRange.getHeight()))
1744 {
1745 // need to expand for correct AAed and non-AAed visualization as primitive.
1746 // This must probably be removed again when we will be able to get all Writer visualization
1747 // as primitives and Writer prepares all it's stuff in high precision coordinates (also
1748 // needs to avoid moving boundaries around to better show overlapping stuff...)
1750 {
1751 // if AAed in principle expand by 0.5 in all directions. Since painting edges of
1752 // AAed regions does not add to no transparence (0.5 opacity covered by 0.5 opacity
1753 // is not full opacity but 0.75 opacity) we need some overlap here to avoid paint
1754 // artifacts. Checked experimentally - a little bit more in Y is needed, probably
1755 // due to still existing integer alignment and crunching in writer.
1756 static const double fExpandX = 0.55;
1757 static const double fExpandY = 0.70;
1758 const basegfx::B2DVector aSingleUnit(rOut.GetInverseViewTransformation() * basegfx::B2DVector(fExpandX, fExpandY));
1759
1760 aPaintRange.expand(aPaintRange.getMinimum() - aSingleUnit);
1761 aPaintRange.expand(aPaintRange.getMaximum() + aSingleUnit);
1762 }
1763 else
1764 {
1765 // if not AAed expand by one unit to bottom right due to the missing unit
1766 // from SwRect/Rectangle integer handling
1767 const basegfx::B2DVector aSingleUnit(rOut.GetInverseViewTransformation() * basegfx::B2DVector(1.0, 1.0));
1768
1769 aPaintRange.expand(aPaintRange.getMaximum() + aSingleUnit);
1770 }
1771
1772 const basegfx::B2DRange aDefineRange(
1773 rOriginalLayoutRect.Left(),
1774 rOriginalLayoutRect.Top(),
1775 rOriginalLayoutRect.Right(),
1776 rOriginalLayoutRect.Bottom());
1777
1778 const drawinglayer::primitive2d::Primitive2DContainer& rSequence = rFillAttributes->getPrimitive2DSequence(
1779 aPaintRange,
1780 aDefineRange);
1781
1782 if(rSequence.size())
1783 {
1785 pPrimitives(&rSequence);
1787 // tdf#86578 the awful lcl_SubtractFlys hack
1788 if (rPaintRegion.size() > 1 || rPaintRegion[0] != rPaintRegion.GetOrigin())
1789 {
1790 basegfx::B2DPolyPolygon const& maskRegion(rClipState.getClipPoly());
1791 primitives.resize(1);
1794 pPrimitives = &primitives;
1795 }
1796 assert(pPrimitives && pPrimitives->size());
1797
1799 aViewInformation2D.setViewTransformation(rOut.GetViewTransformation());
1800 aViewInformation2D.setViewport(aPaintRange);
1801
1802 std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor(drawinglayer::processor2d::createProcessor2DFromOutputDevice(
1803 rOut,
1804 aViewInformation2D) );
1805 pProcessor->process(*pPrimitives);
1806 return true;
1807 }
1808 }
1809 }
1810
1811 return false;
1812}
1813
1815 const SvxBrushItem *pBrush,
1816 vcl::RenderContext &rOutDev,
1817 const SwRect &rOrg,
1818 const SwRect &rOut,
1819 const sal_uInt8 nGrfNum,
1820 const bool bConsiderBackgroundTransparency )
1821 // Add 6th parameter to indicate that method should
1822 // consider background transparency, saved in the color of the brush item
1823{
1824 SwViewShell &rSh = *gProp.pSGlobalShell;
1825 bool bReplaceGrfNum = GRFNUM_REPLACE == nGrfNum;
1826 bool bGrfNum = GRFNUM_NO != nGrfNum;
1827 Size aGrfSize;
1829 if( pBrush && !bReplaceGrfNum )
1830 {
1831 if( rSh.GetViewOptions()->IsGraphic() )
1832 {
1833 OUString referer;
1834 SfxObjectShell * sh = rSh.GetDoc()->GetPersist();
1835 if (sh != nullptr && sh->HasName()) {
1836 referer = sh->GetMedium()->GetName();
1837 }
1838 const Graphic* pGrf = pBrush->GetGraphic(referer);
1839 if( pGrf && GraphicType::NONE != pGrf->GetType() )
1840 {
1841 ePos = pBrush->GetGraphicPos();
1842 if( pGrf->IsSupportedGraphic() )
1843 // don't the use the specific output device! Bug 94802
1844 aGrfSize = ::GetGraphicSizeTwip( *pGrf, nullptr );
1845 }
1846 }
1847 else
1848 bReplaceGrfNum = bGrfNum;
1849 }
1850
1851 SwRect aGrf;
1852 aGrf.SSize( aGrfSize );
1853 bool bDraw = true;
1854 bool bRetouche = true;
1855 switch ( ePos )
1856 {
1857 case GPOS_LT:
1858 aGrf.Pos() = rOrg.Pos();
1859 break;
1860
1861 case GPOS_MT:
1862 aGrf.Pos().setY( rOrg.Top() );
1863 aGrf.Pos().setX( rOrg.Left() + rOrg.Width()/2 - aGrfSize.Width()/2 );
1864 break;
1865
1866 case GPOS_RT:
1867 aGrf.Pos().setY( rOrg.Top() );
1868 aGrf.Pos().setX( rOrg.Right() - aGrfSize.Width() );
1869 break;
1870
1871 case GPOS_LM:
1872 aGrf.Pos().setY( rOrg.Top() + rOrg.Height()/2 - aGrfSize.Height()/2 );
1873 aGrf.Pos().setX( rOrg.Left() );
1874 break;
1875
1876 case GPOS_MM:
1877 aGrf.Pos().setY( rOrg.Top() + rOrg.Height()/2 - aGrfSize.Height()/2 );
1878 aGrf.Pos().setX( rOrg.Left() + rOrg.Width()/2 - aGrfSize.Width()/2 );
1879 break;
1880
1881 case GPOS_RM:
1882 aGrf.Pos().setY( rOrg.Top() + rOrg.Height()/2 - aGrfSize.Height()/2 );
1883 aGrf.Pos().setX( rOrg.Right() - aGrfSize.Width() );
1884 break;
1885
1886 case GPOS_LB:
1887 aGrf.Pos().setY( rOrg.Bottom() - aGrfSize.Height() );
1888 aGrf.Pos().setX( rOrg.Left() );
1889 break;
1890
1891 case GPOS_MB:
1892 aGrf.Pos().setY( rOrg.Bottom() - aGrfSize.Height() );
1893 aGrf.Pos().setX( rOrg.Left() + rOrg.Width()/2 - aGrfSize.Width()/2 );
1894 break;
1895
1896 case GPOS_RB:
1897 aGrf.Pos().setY( rOrg.Bottom() - aGrfSize.Height() );
1898 aGrf.Pos().setX( rOrg.Right() - aGrfSize.Width() );
1899 break;
1900
1901 case GPOS_AREA:
1902 aGrf = rOrg;
1903 // Despite the fact that the background graphic has to fill the complete
1904 // area, we already checked, whether the graphic will completely fill out
1905 // the region the <rOut> that is to be painted. Thus, nothing has to be
1906 // touched again.
1907 // E.g. this is the case for a Fly Frame without a background
1908 // brush positioned on the border of the page which inherited the background
1909 // brush from the page.
1910 bRetouche = !rOut.Contains( aGrf );
1911 break;
1912
1913 case GPOS_TILED:
1914 {
1915 // draw background of tiled graphic before drawing tiled graphic in loop
1916 // determine graphic object
1917 GraphicObject* pGraphicObj = const_cast< GraphicObject* >(pBrush->GetGraphicObject());
1918 // calculate aligned paint rectangle
1919 SwRect aAlignedPaintRect = rOut;
1920 ::SwAlignRect( aAlignedPaintRect, &rSh, &rOutDev );
1921 // draw background color for aligned paint rectangle
1922 lcl_DrawGraphicBackground( *pBrush, rOutDev, aAlignedPaintRect, *pGraphicObj, bGrfNum, gProp );
1923
1924 // set left-top-corner of background graphic to left-top-corner of the
1925 // area, from which the background brush is determined.
1926 aGrf.Pos() = rOrg.Pos();
1927 // setup clipping at output device
1929 rOutDev.IntersectClipRegion( rOut.SVRect() );
1930 // use new method <GraphicObject::DrawTiled(::)>
1931 {
1932 // calculate paint offset
1933 Point aPaintOffset( aAlignedPaintRect.Pos() - aGrf.Pos() );
1934 // draw background graphic tiled for aligned paint rectangle
1935 // #i42643#
1936 // For PDF export, every draw operation for bitmaps takes a
1937 // noticeable amount of place (~50 characters). Thus, optimize
1938 // between tile bitmap size and number of drawing operations here.
1939
1940 // A_out
1941 // n_chars = k1 * ---------- + k2 * A_bitmap
1942 // A_bitmap
1943
1944 // minimum n_chars is obtained for (derive for A_bitmap,
1945 // set to 0, take positive solution):
1946 // k1
1947 // A_bitmap = Sqrt( ---- A_out )
1948 // k2
1949
1950 // where k1 is the number of chars per draw operation, and
1951 // k2 is the number of chars per bitmap pixel.
1952 // This is approximately 50 and 7 for current PDF writer, respectively.
1953
1954 const double k1( 50 );
1955 const double k2( 7 );
1956 const Size aSize( aAlignedPaintRect.SSize() );
1957 const double Abitmap( k1/k2 * static_cast<double>(aSize.Width())*aSize.Height() );
1958
1959 pGraphicObj->DrawTiled( rOutDev,
1960 aAlignedPaintRect.SVRect(),
1961 aGrf.SSize(),
1962 Size( aPaintOffset.X(), aPaintOffset.Y() ),
1963 std::max( 128, static_cast<int>( sqrt(sqrt( Abitmap)) + .5 ) ) );
1964 }
1965 // reset clipping at output device
1966 rOutDev.Pop();
1967 // set <bDraw> and <bRetouche> to false, indicating that background
1968 // graphic and background are already drawn.
1969 bDraw = bRetouche = false;
1970 }
1971 break;
1972
1973 case GPOS_NONE:
1974 bDraw = false;
1975 break;
1976
1977 default: OSL_ENSURE( false, "new Graphic position?" );
1978 }
1979
1982 bool bGrfBackgrdAlreadyDrawn = false;
1983 if ( bRetouche )
1984 {
1986 rOutDev.SetLineColor();
1987
1988 // check, if an existing background graphic (not filling the complete
1989 // background) is transparent drawn and the background color is
1990 // "no fill" respectively "auto fill", if background transparency
1991 // has to be considered.
1992 // If YES, memorize transparency of background graphic.
1993 // check also, if background graphic bitmap is transparent.
1994 bool bTransparentGrfWithNoFillBackgrd = false;
1995 sal_Int32 nGrfTransparency = 0;
1996 bool bGrfIsTransparent = false;
1997 if ( (ePos != GPOS_NONE) &&
1998 (ePos != GPOS_TILED) && (ePos != GPOS_AREA)
1999 )
2000 {
2001 GraphicObject *pGrf = const_cast<GraphicObject*>(pBrush->GetGraphicObject());
2002 if ( bConsiderBackgroundTransparency )
2003 {
2004 GraphicAttr aGrfAttr = pGrf->GetAttr();
2005 if ( (aGrfAttr.IsTransparent()) &&
2006 (pBrush->GetColor() == COL_TRANSPARENT)
2007 )
2008 {
2009 bTransparentGrfWithNoFillBackgrd = true;
2010 nGrfTransparency = 255 - aGrfAttr.GetAlpha();
2011 }
2012 }
2013 if ( pGrf->IsTransparent() )
2014 {
2015 bGrfIsTransparent = true;
2016 }
2017 }
2018
2019 // to get color of brush, check background color against COL_TRANSPARENT ("no fill"/"auto fill")
2020 // instead of checking, if transparency is not set.
2021 const Color aColor( pBrush &&
2022 ( (pBrush->GetColor() != COL_TRANSPARENT) ||
2023 gProp.bSFlyMetafile )
2024 ? pBrush->GetColor()
2026
2027 // determine, if background region have to be
2028 // drawn transparent.
2029 // background region has to be drawn transparent, if
2030 // background transparency have to be considered
2031 // AND
2032 // ( background color is transparent OR
2033 // background graphic is transparent and background color is "no fill"
2034 // )
2035
2036 enum DrawStyle {
2037 Default,
2039 } eDrawStyle = Default;
2040
2041 if (bConsiderBackgroundTransparency &&
2042 ( ( aColor.IsTransparent()) ||
2043 bTransparentGrfWithNoFillBackgrd ) )
2044 {
2045 eDrawStyle = Transparent;
2046 }
2047
2048 // #i75614# reset draw mode in high contrast mode in order to get fill color set
2049 const DrawModeFlags nOldDrawMode = rOutDev.GetDrawMode();
2050 if ( gProp.pSGlobalShell->GetWin() &&
2052 {
2053 rOutDev.SetDrawMode( DrawModeFlags::Default );
2054 }
2055
2056 // If background region has to be drawn transparent, set only the RGB values of the background color as
2057 // the fill color for the output device.
2058 switch (eDrawStyle)
2059 {
2060 case Transparent:
2061 {
2062 if( rOutDev.GetFillColor() != aColor.GetRGBColor() )
2063 rOutDev.SetFillColor( aColor.GetRGBColor() );
2064 break;
2065 }
2066 default:
2067 {
2068 if( rOutDev.GetFillColor() != aColor )
2069 rOutDev.SetFillColor( aColor );
2070 break;
2071 }
2072 }
2073
2074 // #i75614#
2075 // restore draw mode
2076 rOutDev.SetDrawMode( nOldDrawMode );
2077
2078 switch (eDrawStyle)
2079 {
2080 case Transparent:
2081 {
2082 // background region have to be drawn transparent.
2083 // Thus, create a poly-polygon from the region and draw it with
2084 // the corresponding transparency percent.
2085 tools::PolyPolygon aDrawPoly( rOut.SVRect() );
2086 if ( aGrf.HasArea() )
2087 {
2088 if ( !bGrfIsTransparent )
2089 {
2090 // subtract area of background graphic from draw area
2091 // Consider only that part of the graphic area that is overlapping with draw area.
2092 SwRect aTmpGrf = aGrf;
2093 aTmpGrf.Intersection( rOut );
2094 if ( aTmpGrf.HasArea() )
2095 {
2096 tools::Polygon aGrfPoly( aTmpGrf.SVRect() );
2097 aDrawPoly.Insert( aGrfPoly );
2098 }
2099 }
2100 else
2101 bGrfBackgrdAlreadyDrawn = true;
2102 }
2103 // calculate transparency percent:
2104 // ( <transparency value[0x01..0xFF]>*100 + 0x7F ) / 0xFF
2105 // If there is a background graphic with a background color "no fill"/"auto fill",
2106 // the transparency value is taken from the background graphic,
2107 // otherwise take the transparency value from the color.
2108 sal_Int8 nTransparencyPercent = static_cast<sal_Int8>(
2109 (( bTransparentGrfWithNoFillBackgrd ? nGrfTransparency : (255 - aColor.GetAlpha())
2110 )*100 + 0x7F)/0xFF);
2111 // draw poly-polygon transparent
2112 rOutDev.DrawTransparent( aDrawPoly, nTransparencyPercent );
2113
2114 break;
2115 }
2116 case Default:
2117 default:
2118 {
2119 SwRegionRects aRegion( rOut, 4 );
2120 if ( !bGrfIsTransparent )
2121 aRegion -= aGrf;
2122 else
2123 bGrfBackgrdAlreadyDrawn = true;
2124 // loop rectangles of background region, which has to be drawn
2125 for( size_t i = 0; i < aRegion.size(); ++i )
2126 {
2127 rOutDev.DrawRect( aRegion[i].SVRect() );
2128 }
2129 }
2130 }
2131 rOutDev.Pop();
2132 }
2133
2134 if( bDraw && aGrf.Overlaps( rOut ) )
2135 lcl_DrawGraphic( *pBrush, rOutDev, rSh, aGrf, rOut, bGrfNum, gProp,
2136 bGrfBackgrdAlreadyDrawn );
2137
2138 if( bReplaceGrfNum )
2139 {
2140 const BitmapEx& rBmp = rSh.GetReplacementBitmap(false);
2141 vcl::Font aTmp( rOutDev.GetFont() );
2142 Graphic::DrawEx(rOutDev, OUString(), aTmp, rBmp, rOrg.Pos(), rOrg.SSize());
2143 }
2144}
2145
2154static void lcl_AdjustRectToPixelSize( SwRect& io_aSwRect, const vcl::RenderContext &aOut )
2155{
2156 // local constant object of class <Size> to determine number of Twips
2157 // representing a pixel.
2158 const Size aTwipToPxSize( aOut.PixelToLogic( Size( 1,1 )) );
2159
2160 // local object of class <Rectangle> in Twip coordinates
2161 // calculated from given rectangle aligned to pixel centers.
2162 const tools::Rectangle aPxCenterRect = aOut.PixelToLogic(
2163 aOut.LogicToPixel( io_aSwRect.SVRect() ) );
2164
2165 // local constant object of class <Rectangle> representing given rectangle
2166 // in pixel.
2167 const tools::Rectangle aOrgPxRect = aOut.LogicToPixel( io_aSwRect.SVRect() );
2168
2169 // calculate adjusted rectangle from pixel centered rectangle.
2170 // Due to rounding differences <aPxCenterRect> doesn't exactly represents
2171 // the Twip-centers. Thus, adjust borders by half of pixel width/height plus 1.
2172 // Afterwards, adjust calculated Twip-positions of the all borders.
2173 tools::Rectangle aSizedRect = aPxCenterRect;
2174 aSizedRect.AdjustLeft( -(aTwipToPxSize.Width()/2 + 1) );
2175 aSizedRect.AdjustRight( aTwipToPxSize.Width()/2 + 1 );
2176 aSizedRect.AdjustTop( -(aTwipToPxSize.Height()/2 + 1) );
2177 aSizedRect.AdjustBottom(aTwipToPxSize.Height()/2 + 1);
2178
2179 // adjust left()
2180 while ( aOut.LogicToPixel(aSizedRect).Left() < aOrgPxRect.Left() )
2181 {
2182 aSizedRect.AdjustLeft( 1 );
2183 }
2184 // adjust right()
2185 while ( aOut.LogicToPixel(aSizedRect).Right() > aOrgPxRect.Right() )
2186 {
2187 aSizedRect.AdjustRight( -1 );
2188 }
2189 // adjust top()
2190 while ( aOut.LogicToPixel(aSizedRect).Top() < aOrgPxRect.Top() )
2191 {
2192 aSizedRect.AdjustTop( 1 );
2193 }
2194 // adjust bottom()
2195 while ( aOut.LogicToPixel(aSizedRect).Bottom() > aOrgPxRect.Bottom() )
2196 {
2197 aSizedRect.AdjustBottom( -1 );
2198 }
2199
2200 io_aSwRect = SwRect( aSizedRect );
2201
2202#if OSL_DEBUG_LEVEL > 0
2203 tools::Rectangle aTestOrgPxRect = aOut.LogicToPixel( io_aSwRect.SVRect() );
2204 tools::Rectangle aTestNewPxRect = aOut.LogicToPixel( aSizedRect );
2205 OSL_ENSURE( aTestOrgPxRect == aTestNewPxRect,
2206 "Error in lcl_AlignRectToPixelSize(..): Adjusted rectangle has incorrect position or size");
2207 // check Left()
2208 aSizedRect.AdjustLeft( -1 );
2209 aTestNewPxRect = aOut.LogicToPixel( aSizedRect );
2210 OSL_ENSURE( aTestOrgPxRect.Left() >= (aTestNewPxRect.Left()+1),
2211 "Error in lcl_AlignRectToPixelSize(..): Left() not correct adjusted");
2212 aSizedRect.AdjustLeft( 1 );
2213 // check Right()
2214 aSizedRect.AdjustRight( 1 );
2215 aTestNewPxRect = aOut.LogicToPixel( aSizedRect );
2216 OSL_ENSURE( aTestOrgPxRect.Right() <= (aTestNewPxRect.Right()-1),
2217 "Error in lcl_AlignRectToPixelSize(..): Right() not correct adjusted");
2218 aSizedRect.AdjustRight( -1 );
2219 // check Top()
2220 aSizedRect.AdjustTop( -1 );
2221 aTestNewPxRect = aOut.LogicToPixel( aSizedRect );
2222 OSL_ENSURE( aTestOrgPxRect.Top() >= (aTestNewPxRect.Top()+1),
2223 "Error in lcl_AlignRectToPixelSize(..): Top() not correct adjusted");
2224 aSizedRect.AdjustTop( 1 );
2225 // check Bottom()
2226 aSizedRect.AdjustBottom( 1 );
2227 aTestNewPxRect = aOut.LogicToPixel( aSizedRect );
2228 OSL_ENSURE( aTestOrgPxRect.Bottom() <= (aTestNewPxRect.Bottom()-1),
2229 "Error in lcl_AlignRectToPixelSize(..): Bottom() not correct adjusted");
2230 aSizedRect.AdjustBottom( -1 );
2231#endif
2232}
2233
2234// FUNCTIONS USED FOR COLLAPSING TABLE BORDER LINES START
2235
2236namespace {
2237
2238struct SwLineEntry
2239{
2240 SwTwips mnKey;
2241 SwTwips mnStartPos;
2242 SwTwips mnEndPos;
2243 SwTwips mnLimitedEndPos;
2244 bool mbOuter;
2245
2246 svx::frame::Style maAttribute;
2247
2248 enum OverlapType { NO_OVERLAP, OVERLAP1, OVERLAP2, OVERLAP3 };
2249
2250 enum class VerticalType { LEFT, RIGHT };
2251
2252public:
2253 SwLineEntry( SwTwips nKey,
2254 SwTwips nStartPos,
2255 SwTwips nEndPos,
2256 bool bOuter,
2257 const svx::frame::Style& rAttribute );
2258
2259 OverlapType Overlaps( const SwLineEntry& rComp ) const;
2260
2265 void LimitVerticalEndPos(const SwFrame& rFrame, VerticalType eType);
2266};
2267
2268}
2269
2270SwLineEntry::SwLineEntry( SwTwips nKey,
2271 SwTwips nStartPos,
2272 SwTwips nEndPos,
2273 bool bOuter,
2274 const svx::frame::Style& rAttribute )
2275 : mnKey( nKey ),
2276 mnStartPos( nStartPos ),
2277 mnEndPos( nEndPos ),
2278 mnLimitedEndPos(0),
2279 mbOuter(bOuter),
2280 maAttribute( rAttribute )
2281{
2282}
2283
2284/*
2285
2286 1. ---------- rOld
2287 ---------- rNew
2288
2289 2. ---------- rOld
2290 ------------- rNew
2291
2292 3. ------- rOld
2293 ------------- rNew
2294
2295 4. ------------- rOld
2296 ---------- rNew
2297
2298 5. ---------- rOld
2299 ---- rNew
2300
2301 6. ---------- rOld
2302 ---------- rNew
2303
2304 7. ------------- rOld
2305 ---------- rNew
2306
2307 8. ---------- rOld
2308 ------------- rNew
2309
2310 9. ---------- rOld
2311 ---------- rNew
2312*/
2313
2314SwLineEntry::OverlapType SwLineEntry::Overlaps( const SwLineEntry& rNew ) const
2315{
2316 SwLineEntry::OverlapType eRet = OVERLAP3;
2317
2318 if ( mnStartPos >= rNew.mnEndPos || mnEndPos <= rNew.mnStartPos )
2319 eRet = NO_OVERLAP;
2320
2321 // 1, 2, 3
2322 else if ( mnEndPos < rNew.mnEndPos )
2323 eRet = OVERLAP1;
2324
2325 // 4, 5, 6, 7
2326 else if (mnStartPos <= rNew.mnStartPos)
2327 eRet = OVERLAP2;
2328
2329 // 8, 9
2330 return eRet;
2331}
2332
2333void SwLineEntry::LimitVerticalEndPos(const SwFrame& rFrame, VerticalType eType)
2334{
2335 if (!rFrame.IsCellFrame())
2336 {
2337 return;
2338 }
2339
2340 const auto& rCellFrame = static_cast<const SwCellFrame&>(rFrame);
2341 std::vector<const SwCellFrame*> aCoveredCells = rCellFrame.GetCoveredCells();
2342 // Iterate in reverse order, so we can stop at the first cell that has a border. This can
2343 // determine what is the minimal end position that is safe to use as a limit.
2344 for (auto it = aCoveredCells.rbegin(); it != aCoveredCells.rend(); ++it)
2345 {
2346 const SwCellFrame* pCoveredCell = *it;
2347 SwBorderAttrAccess aAccess( SwFrame::GetCache(), pCoveredCell );
2348 const SwBorderAttrs& rAttrs = *aAccess.Get();
2349 const SvxBoxItem& rBox = rAttrs.GetBox();
2350 if (eType == VerticalType::LEFT && rBox.GetLeft())
2351 {
2352 break;
2353 }
2354
2355 if (eType == VerticalType::RIGHT && rBox.GetRight())
2356 {
2357 break;
2358 }
2359
2360 mnLimitedEndPos = pCoveredCell->getFrameArea().Top();
2361 }
2362}
2363
2364namespace {
2365
2366struct lt_SwLineEntry
2367{
2368 bool operator()( const SwLineEntry& e1, const SwLineEntry& e2 ) const
2369 {
2370 return e1.mnStartPos < e2.mnStartPos;
2371 }
2372};
2373
2374}
2375
2376typedef std::set< SwLineEntry, lt_SwLineEntry > SwLineEntrySet;
2377typedef std::map< SwTwips, SwLineEntrySet > SwLineEntryMap;
2378
2379namespace {
2380
2381class SwTabFramePainter
2382{
2383 SwLineEntryMap maVertLines;
2384 SwLineEntryMap maHoriLines;
2385 const SwTabFrame& mrTabFrame;
2386
2387 void Insert( SwLineEntry&, bool bHori );
2388 void Insert(const SwFrame& rFrame, const SvxBoxItem& rBoxItem, const SwRect &rPaintArea);
2389 void HandleFrame(const SwLayoutFrame& rFrame, const SwRect& rPaintArea);
2390 void FindStylesForLine( Point&,
2391 Point&,
2393 bool bHori,
2394 bool bOuter ) const;
2395
2396public:
2397 explicit SwTabFramePainter( const SwTabFrame& rTabFrame );
2398
2399 void PaintLines( OutputDevice& rDev, const SwRect& rRect ) const;
2400};
2401
2402}
2403
2404SwTabFramePainter::SwTabFramePainter( const SwTabFrame& rTabFrame )
2405 : mrTabFrame( rTabFrame )
2406{
2407 SwRect aPaintArea = rTabFrame.GetUpper()->GetPaintArea();
2408 HandleFrame(rTabFrame, aPaintArea);
2409}
2410
2411void SwTabFramePainter::HandleFrame(const SwLayoutFrame& rLayoutFrame, const SwRect& rPaintArea)
2412{
2413 // Add border lines of cell frames. Skip covered cells. Skip cells
2414 // in special row span row, which do not have a negative row span:
2415 if ( rLayoutFrame.IsCellFrame() && !rLayoutFrame.IsCoveredCell() )
2416 {
2417 const SwCellFrame* pThisCell = static_cast<const SwCellFrame*>(&rLayoutFrame);
2418 const SwRowFrame* pRowFrame = static_cast<const SwRowFrame*>(pThisCell->GetUpper());
2419 const tools::Long nRowSpan = pThisCell->GetTabBox()->getRowSpan();
2420 if ( !pRowFrame->IsRowSpanLine() || nRowSpan > 1 || nRowSpan < -1 )
2421 {
2422 SwBorderAttrAccess aAccess( SwFrame::GetCache(), &rLayoutFrame );
2423 const SwBorderAttrs& rAttrs = *aAccess.Get();
2424 const SvxBoxItem& rBox = rAttrs.GetBox();
2425 Insert(rLayoutFrame, rBox, rPaintArea);
2426 }
2427 }
2428
2429 // Recurse into lower layout frames, but do not recurse into lower tabframes.
2430 const SwFrame* pLower = rLayoutFrame.Lower();
2431 while ( pLower )
2432 {
2433 if (pLower->IsLayoutFrame() && !pLower->IsTabFrame())
2434 {
2435 const SwLayoutFrame* pLowerLayFrame = static_cast<const SwLayoutFrame*>(pLower);
2436 HandleFrame(*pLowerLayFrame, rPaintArea);
2437 }
2438 pLower = pLower->GetNext();
2439 }
2440}
2441
2442void SwTabFramePainter::PaintLines(OutputDevice& rDev, const SwRect& rRect) const
2443{
2444 // #i16816# tagged pdf support
2445 SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, nullptr, rDev );
2446
2447 SwLineEntryMap::const_iterator aIter = maHoriLines.begin();
2448 bool bHori = true;
2449
2450 // color for subsidiary lines:
2452
2453 // high contrast mode:
2454 // overrides the color of non-subsidiary lines.
2455 const Color* pHCColor = nullptr;
2456 DrawModeFlags nOldDrawMode = rDev.GetDrawMode();
2457 if( gProp.pSGlobalShell->GetWin() &&
2459 {
2460 pHCColor = &SwViewOption::GetFontColor();
2461 rDev.SetDrawMode( DrawModeFlags::Default );
2462 }
2463
2464 const SwFrame* pUpper = mrTabFrame.GetUpper();
2465 SwRect aUpper( pUpper->getFramePrintArea() );
2466 aUpper.Pos() += pUpper->getFrameArea().Pos();
2467 SwRect aUpperAligned( aUpper );
2468 ::SwAlignRect( aUpperAligned, gProp.pSGlobalShell, &rDev );
2469
2470 // prepare SdrFrameBorderDataVector
2471 std::shared_ptr<drawinglayer::primitive2d::SdrFrameBorderDataVector> aData(
2472 std::make_shared<drawinglayer::primitive2d::SdrFrameBorderDataVector>());
2473
2474 while ( true )
2475 {
2476 if ( bHori && aIter == maHoriLines.end() )
2477 {
2478 aIter = maVertLines.begin();
2479 bHori = false;
2480 }
2481
2482 if ( !bHori && aIter == maVertLines.end() )
2483 break;
2484
2485 const SwLineEntrySet& rEntrySet = (*aIter).second;
2486 for (const SwLineEntry& rEntry : rEntrySet)
2487 {
2488 const svx::frame::Style& rEntryStyle( rEntry.maAttribute );
2489
2490 Point aStart, aEnd;
2491 if ( bHori )
2492 {
2493 aStart.setX( rEntry.mnStartPos );
2494 aStart.setY( rEntry.mnKey );
2495 aEnd.setX( rEntry.mnEndPos );
2496 aEnd.setY( rEntry.mnKey );
2497 }
2498 else
2499 {
2500 aStart.setX( rEntry.mnKey );
2501 aStart.setY( rEntry.mnStartPos );
2502 aEnd.setX( rEntry.mnKey );
2503 aEnd.setY( rEntry.mnEndPos );
2504 }
2505
2506 svx::frame::Style aStyles[ 7 ];
2507 aStyles[ 0 ] = rEntryStyle;
2508 FindStylesForLine(aStart, aEnd, aStyles, bHori, rEntry.mbOuter);
2509
2510 if (!bHori && rEntry.mnLimitedEndPos)
2511 {
2512 aEnd.setY(rEntry.mnLimitedEndPos);
2513 }
2514
2515 SwRect aRepaintRect( aStart, aEnd );
2516
2517 // the repaint rectangle has to be moved a bit for the centered lines:
2518 SwTwips nRepaintRectSize = !rEntryStyle.GetWidth() ? 1 : rEntryStyle.GetWidth();
2519 if ( bHori )
2520 {
2521 aRepaintRect.Height( 2 * nRepaintRectSize );
2522 aRepaintRect.Pos().AdjustY( -nRepaintRectSize );
2523
2524 // To decide on visibility it is also necessary to expand the RepaintRect
2525 // to left/right according existing BorderLine overlap matchings, else there
2526 // will be repaint errors when scrolling in e.t TripleLine BorderLines.
2527 // aStyles[1] == aLFromT, aStyles[3] == aLFromB, aStyles[4] == aRFromT, aStyles[6] == aRFromB
2528 if(aStyles[1].IsUsed() || aStyles[3].IsUsed() || aStyles[4].IsUsed() || aStyles[6].IsUsed())
2529 {
2530 const double fLineWidthMaxLeft(std::max(aStyles[1].GetWidth(), aStyles[3].GetWidth()));
2531 const double fLineWidthMaxRight(std::max(aStyles[4].GetWidth(), aStyles[6].GetWidth()));
2532 aRepaintRect.Width(aRepaintRect.Width() + (fLineWidthMaxLeft + fLineWidthMaxRight));
2533 aRepaintRect.Pos().AdjustX( -fLineWidthMaxLeft );
2534 }
2535 }
2536 else
2537 {
2538 aRepaintRect.Width( 2 * nRepaintRectSize );
2539 aRepaintRect.Pos().AdjustX( -nRepaintRectSize );
2540
2541 // Accordingly to horizontal case, but for top/bottom
2542 // aStyles[3] == aTFromR, aStyles[1] == aTFromL, aStyles[6] == aBFromR, aStyles[4] == aBFromL
2543 if(aStyles[3].IsUsed() || aStyles[1].IsUsed() || aStyles[6].IsUsed() || aStyles[4].IsUsed())
2544 {
2545 const double fLineWidthMaxTop(std::max(aStyles[3].GetWidth(), aStyles[1].GetWidth()));
2546 const double fLineWidthMaxBottom(std::max(aStyles[6].GetWidth(), aStyles[4].GetWidth()));
2547 aRepaintRect.Height(aRepaintRect.Height() + (fLineWidthMaxTop + fLineWidthMaxBottom));
2548 aRepaintRect.Pos().AdjustY( -fLineWidthMaxTop );
2549 }
2550 }
2551
2552 if (!rRect.Overlaps(aRepaintRect))
2553 {
2554 continue;
2555 }
2556
2557 // subsidiary lines
2558 const Color* pTmpColor = nullptr;
2559 if (0 == aStyles[ 0 ].GetWidth())
2560 {
2561 if (isTableBoundariesEnabled() && gProp.pSGlobalShell->GetWin())
2562 aStyles[ 0 ].Set( rCol, rCol, rCol, false, 1, 0, 0 );
2563 else
2564 aStyles[0].SetType(SvxBorderLineStyle::NONE);
2565 }
2566 else
2567 pTmpColor = pHCColor;
2568
2569 // The (twip) positions will be adjusted to meet these requirements:
2570 // 1. The y coordinates are located in the middle of the pixel grid
2571 // 2. The x coordinated are located at the beginning of the pixel grid
2572 // This is done, because the horizontal lines are painted "at
2573 // beginning", whereas the vertical lines are painted "centered".
2574 // By making the line sizes a multiple of one pixel size, we can
2575 // assure that all lines having the same twip size have the same
2576 // pixel size, independent of their position on the screen.
2577 Point aPaintStart = rDev.PixelToLogic( rDev.LogicToPixel(aStart) );
2578 Point aPaintEnd = rDev.PixelToLogic( rDev.LogicToPixel(aEnd) );
2579
2580 if (gProp.pSGlobalShell->GetWin())
2581 {
2582 // The table borders do not use SwAlignRect, but all the other frames do.
2583 // Therefore we tweak the outer borders a bit to achieve that the outer
2584 // borders match the subsidiary lines of the upper:
2585 if (aStart.X() == aUpper.Left())
2586 aPaintStart.setX( aUpperAligned.Left() );
2587 else if (aStart.X() == aUpper.Right_())
2588 aPaintStart.setX( aUpperAligned.Right_() );
2589 if (aStart.Y() == aUpper.Top())
2590 aPaintStart.setY( aUpperAligned.Top() );
2591 else if (aStart.Y() == aUpper.Bottom_())
2592 aPaintStart.setY( aUpperAligned.Bottom_() );
2593
2594 if (aEnd.X() == aUpper.Left())
2595 aPaintEnd.setX( aUpperAligned.Left() );
2596 else if (aEnd.X() == aUpper.Right_())
2597 aPaintEnd.setX( aUpperAligned.Right_() );
2598 if (aEnd.Y() == aUpper.Top())
2599 aPaintEnd.setY( aUpperAligned.Top() );
2600 else if (aEnd.Y() == aUpper.Bottom_())
2601 aPaintEnd.setY( aUpperAligned.Bottom_() );
2602 }
2603
2604 if(aStyles[0].IsUsed())
2605 {
2606 if (bHori)
2607 {
2608 const basegfx::B2DPoint aOrigin(aPaintStart.X(), aPaintStart.Y());
2609 const basegfx::B2DVector aX(basegfx::B2DPoint(aPaintEnd.X(), aPaintEnd.Y()) - aOrigin);
2610
2611 if(!aX.equalZero())
2612 {
2614 aData->emplace_back(
2615 aOrigin,
2616 aX,
2617 aStyles[0],
2618 pTmpColor);
2620
2621 rInstance.addSdrConnectStyleData(true, aStyles[1], -aY, true); // aLFromT
2622 rInstance.addSdrConnectStyleData(true, aStyles[2], -aX, true); // aLFromL
2623 rInstance.addSdrConnectStyleData(true, aStyles[3], aY, false); // aLFromB
2624
2625 rInstance.addSdrConnectStyleData(false, aStyles[4], -aY, true); // aRFromT
2626 rInstance.addSdrConnectStyleData(false, aStyles[5], aX, false); // aRFromR
2627 rInstance.addSdrConnectStyleData(false, aStyles[6], aY, false); // aRFromB
2628 }
2629 }
2630 else // vertical
2631 {
2632 const basegfx::B2DPoint aOrigin(aPaintStart.X(), aPaintStart.Y());
2633 const basegfx::B2DVector aX(basegfx::B2DPoint(aPaintEnd.X(), aPaintEnd.Y()) - aOrigin);
2634
2635 if(!aX.equalZero())
2636 {
2638 aData->emplace_back(
2639 aOrigin,
2640 aX,
2641 aStyles[0],
2642 pTmpColor);
2644
2645 rInstance.addSdrConnectStyleData(true, aStyles[3], -aY, false); // aTFromR
2646 rInstance.addSdrConnectStyleData(true, aStyles[2], -aX, true); // aTFromT
2647 rInstance.addSdrConnectStyleData(true, aStyles[1], aY, true); // aTFromL
2648
2649 rInstance.addSdrConnectStyleData(false, aStyles[6], -aY, false); // aBFromR
2650 rInstance.addSdrConnectStyleData(false, aStyles[5], aX, false); // aBFromB
2651 rInstance.addSdrConnectStyleData(false, aStyles[4], aY, true); // aBFromL
2652 }
2653 }
2654 }
2655 }
2656 ++aIter;
2657 }
2658
2659 // create instance of SdrFrameBorderPrimitive2D if
2660 // SdrFrameBorderDataVector is used
2661 if(!aData->empty())
2662 {
2664 aSequence.append(
2667 aData,
2668 true))); // force visualization to minimal one discrete unit (pixel)
2669 // paint
2670 mrTabFrame.ProcessPrimitives(aSequence);
2671 }
2672
2673 // restore output device:
2674 rDev.SetDrawMode( nOldDrawMode );
2675}
2676
2682void SwTabFramePainter::FindStylesForLine( Point& rStartPoint,
2683 Point& rEndPoint,
2684 svx::frame::Style* pStyles,
2685 bool bHori, bool bOuter ) const
2686{
2687 // For example, aLFromB means: this vertical line intersects my horizontal line at its left end,
2688 // from bottom.
2689 // pStyles[ 1 ] = bHori ? aLFromT : TFromL
2690 // pStyles[ 2 ] = bHori ? aLFromL : TFromT,
2691 // pStyles[ 3 ] = bHori ? aLFromB : TFromR,
2692 // pStyles[ 4 ] = bHori ? aRFromT : BFromL,
2693 // pStyles[ 5 ] = bHori ? aRFromR : BFromB,
2694 // pStyles[ 6 ] = bHori ? aRFromB : BFromR,
2695
2696 bool bWordTableCell = false;
2697 SwViewShell* pShell = mrTabFrame.getRootFrame()->GetCurrShell();
2698 if (pShell)
2699 {
2700 const IDocumentSettingAccess& rIDSA = pShell->GetDoc()->getIDocumentSettingAccess();
2701 bWordTableCell = rIDSA.get(DocumentSettingId::TABLE_ROW_KEEP);
2702 }
2703
2704 SwLineEntryMap::const_iterator aMapIter = maVertLines.find( rStartPoint.X() );
2705 OSL_ENSURE( aMapIter != maVertLines.end(), "FindStylesForLine: Error" );
2706 const SwLineEntrySet& rVertSet = (*aMapIter).second;
2707
2708 for ( const SwLineEntry& rEntry : rVertSet )
2709 {
2710 if ( bHori )
2711 {
2712 if ( rStartPoint.Y() == rEntry.mnStartPos )
2713 pStyles[ 3 ] = rEntry.maAttribute;
2714 else if ( rStartPoint.Y() == rEntry.mnEndPos )
2715 pStyles[ 1 ] = rEntry.maAttribute;
2716
2717 if (bWordTableCell && rStartPoint.X() == rEntry.mnKey && !bOuter && rEntry.mbOuter)
2718 {
2719 rStartPoint.AdjustX(rEntry.maAttribute.GetWidth());
2720 }
2721 }
2722 else
2723 {
2724 if ( rStartPoint.Y() == rEntry.mnEndPos )
2725 pStyles[ 2 ] = rEntry.maAttribute;
2726 else if ( rEndPoint.Y() == rEntry.mnStartPos )
2727 pStyles[ 5 ] = rEntry.maAttribute;
2728 }
2729 }
2730
2731 aMapIter = maHoriLines.find( rStartPoint.Y() );
2732 OSL_ENSURE( aMapIter != maHoriLines.end(), "FindStylesForLine: Error" );
2733 const SwLineEntrySet& rHoriSet = (*aMapIter).second;
2734
2735 for ( const SwLineEntry& rEntry : rHoriSet )
2736 {
2737 if ( bHori )
2738 {
2739 if ( rStartPoint.X() == rEntry.mnEndPos )
2740 pStyles[ 2 ] = rEntry.maAttribute;
2741 else if ( rEndPoint.X() == rEntry.mnStartPos )
2742 pStyles[ 5 ] = rEntry.maAttribute;
2743 }
2744 else
2745 {
2746 if ( rStartPoint.X() == rEntry.mnEndPos )
2747 pStyles[ 1 ] = rEntry.maAttribute;
2748 else if ( rStartPoint.X() == rEntry.mnStartPos )
2749 pStyles[ 3 ] = rEntry.maAttribute;
2750
2751 if (bWordTableCell && rStartPoint.Y() == rEntry.mnKey && !bOuter && rEntry.mbOuter)
2752 {
2753 rStartPoint.AdjustY(rEntry.maAttribute.GetWidth());
2754 }
2755 }
2756 }
2757
2758 if ( bHori )
2759 {
2760 aMapIter = maVertLines.find( rEndPoint.X() );
2761 OSL_ENSURE( aMapIter != maVertLines.end(), "FindStylesForLine: Error" );
2762 const SwLineEntrySet& rVertSet2 = (*aMapIter).second;
2763
2764 for ( const SwLineEntry& rEntry : rVertSet2 )
2765 {
2766 if ( rEndPoint.Y() == rEntry.mnStartPos )
2767 pStyles[ 6 ] = rEntry.maAttribute;
2768 else if ( rEndPoint.Y() == rEntry.mnEndPos )
2769 pStyles[ 4 ] = rEntry.maAttribute;
2770
2771 if (bWordTableCell && rEndPoint.X() == rEntry.mnKey && !bOuter && rEntry.mbOuter)
2772 {
2773 rEndPoint.AdjustX(-rEntry.maAttribute.GetWidth());
2774 }
2775 }
2776 }
2777 else
2778 {
2779 aMapIter = maHoriLines.find( rEndPoint.Y() );
2780 OSL_ENSURE( aMapIter != maHoriLines.end(), "FindStylesForLine: Error" );
2781 const SwLineEntrySet& rHoriSet2 = (*aMapIter).second;
2782
2783 for ( const SwLineEntry& rEntry : rHoriSet2 )
2784 {
2785 if ( rEndPoint.X() == rEntry.mnEndPos )
2786 pStyles[ 4 ] = rEntry.maAttribute;
2787 else if ( rEndPoint.X() == rEntry.mnStartPos )
2788 pStyles[ 6 ] = rEntry.maAttribute;
2789
2790 if (bWordTableCell && rEndPoint.Y() == rEntry.mnKey && !bOuter && rEntry.mbOuter)
2791 {
2792 rEndPoint.AdjustY(-rEntry.maAttribute.GetWidth());
2793 }
2794 }
2795 }
2796}
2797
2803 SwTabFrame const& rTabFrame, SwFrame const& rFrame, SvxBoxItem const& rBoxItem)
2804{
2805 SwRowFrame const*const pThisRowFrame =
2806 dynamic_cast<const SwRowFrame*>(rFrame.GetUpper());
2807 return (pThisRowFrame
2808 && (pThisRowFrame->GetUpper() == &rTabFrame)
2809 && rTabFrame.IsFollow()
2810 && !rTabFrame.GetTable()->GetRowsToRepeat()
2811 && ( !pThisRowFrame->GetPrev()
2812 || static_cast<const SwRowFrame*>(pThisRowFrame->GetPrev())
2813 ->IsRowSpanLine())
2814 && !rBoxItem.GetTop()
2815 && rBoxItem.GetBottom());
2816}
2817
2818void SwTabFramePainter::Insert(const SwFrame& rFrame, const SvxBoxItem& rBoxItem, const SwRect& rPaintArea)
2819{
2820 // build 4 line entries for the 4 borders:
2821 SwRect aBorderRect = rFrame.getFrameArea();
2822
2823 aBorderRect.Intersection(rPaintArea);
2824
2826 mrTabFrame, rFrame, rBoxItem));
2827 bool const bVert = mrTabFrame.IsVertical();
2828 bool const bR2L = mrTabFrame.IsRightToLeft();
2829
2830 bool bWordTableCell = false;
2831 SwViewShell* pShell = rFrame.getRootFrame()->GetCurrShell();
2832 if (pShell)
2833 {
2834 const IDocumentSettingAccess& rIDSA = pShell->GetDoc()->getIDocumentSettingAccess();
2835 bWordTableCell = rIDSA.get(DocumentSettingId::TABLE_ROW_KEEP);
2836 }
2837
2838 // no scaling needed, it's all in the primitives and the target device
2840 aL.SetWordTableCell(bWordTableCell);
2842 aR.SetWordTableCell(bWordTableCell);
2844 aT.SetWordTableCell(bWordTableCell);
2846 aB.SetWordTableCell(bWordTableCell);
2847
2848 // First cell in a row.
2849 bool bLeftIsOuter = rFrame.IsCellFrame() && rFrame.GetUpper()->GetLower() == &rFrame;
2850 // Last cell in a row.
2851 bool bRightIsOuter = rFrame.IsCellFrame() && rFrame.GetNext() == nullptr;
2852 // First row in a table.
2853 bool bTopIsOuter = rFrame.IsCellFrame() && rFrame.GetUpper()->GetUpper()->GetLower() == rFrame.GetUpper();
2854 // Last row in a table.
2855 bool bBottomIsOuter = rFrame.IsCellFrame() && rFrame.GetUpper()->GetNext() == nullptr;
2856
2857 aR.MirrorSelf();
2858 if (!bWordTableCell || !bBottomIsOuter)
2859 {
2860 // Outer horizontal lines are never mirrored in Word.
2861 aB.MirrorSelf();
2862 }
2863
2864 const SwTwips nLeft = aBorderRect.Left_();
2865 const SwTwips nRight = aBorderRect.Right_();
2866 const SwTwips nTop = aBorderRect.Top_();
2867 const SwTwips nBottom = aBorderRect.Bottom_();
2868
2869 aL.SetRefMode( svx::frame::RefMode::Centered );
2870 aR.SetRefMode( svx::frame::RefMode::Centered );
2871 aT.SetRefMode( !bVert ? svx::frame::RefMode::Begin : svx::frame::RefMode::End );
2872 aB.SetRefMode( !bVert ? svx::frame::RefMode::Begin : svx::frame::RefMode::End );
2873
2874 if (bWordTableCell && bLeftIsOuter)
2875 {
2876 // Outer vertical lines are always mirrored in Word.
2877 aL.MirrorSelf();
2878 }
2879
2880 SwLineEntry aLeft (nLeft, nTop, nBottom, bLeftIsOuter,
2881 bVert ? aB : (bR2L ? aR : aL));
2882 if (bWordTableCell && rBoxItem.GetLeft())
2883 {
2884 aLeft.LimitVerticalEndPos(rFrame, SwLineEntry::VerticalType::LEFT);
2885 }
2886
2887 SwLineEntry aRight (nRight, nTop, nBottom, bRightIsOuter,
2888 bVert ? (bBottomAsTop ? aB : aT) : (bR2L ? aL : aR));
2889 if (bWordTableCell && rBoxItem.GetRight())
2890 {
2891 aRight.LimitVerticalEndPos(rFrame, SwLineEntry::VerticalType::RIGHT);
2892 }
2893
2894 SwLineEntry aTop (nTop, nLeft, nRight, bTopIsOuter,
2895 bVert ? aL : (bBottomAsTop ? aB : aT));
2896
2897 SwLineEntry aBottom(nBottom, nLeft, nRight, bBottomIsOuter,
2898 bVert ? aR : aB);
2899
2900 Insert( aLeft, false );
2901 Insert( aRight, false );
2902 Insert( aTop, true );
2903 Insert( aBottom, true );
2904}
2905
2906void SwTabFramePainter::Insert( SwLineEntry& rNew, bool bHori )
2907{
2908 // get all lines from structure, that have key entry of pLE
2909 SwLineEntryMap* pLine2 = bHori ? &maHoriLines : &maVertLines;
2910 const SwTwips nKey = rNew.mnKey;
2911 SwLineEntryMap::iterator aMapIter = pLine2->find( nKey );
2912
2913 SwLineEntrySet* pLineSet = aMapIter != pLine2->end() ? &((*aMapIter).second) : nullptr;
2914 if ( !pLineSet )
2915 {
2916 SwLineEntrySet aNewSet;
2917 (*pLine2)[ nKey ] = aNewSet;
2918 pLineSet = &(*pLine2)[ nKey ];
2919 }
2920 SwLineEntrySet::iterator aIter = pLineSet->begin();
2921
2922 while ( aIter != pLineSet->end() && rNew.mnStartPos < rNew.mnEndPos )
2923 {
2924 const SwLineEntry& rOld = *aIter;
2925
2926 if (rOld.mnLimitedEndPos || rOld.mbOuter != rNew.mbOuter)
2927 {
2928 // Don't merge with this line entry as it ends sooner than mnEndPos.
2929 ++aIter;
2930 continue;
2931 }
2932
2933 const SwLineEntry::OverlapType nOverlapType = rOld.Overlaps( rNew );
2934
2935 const svx::frame::Style& rOldAttr = rOld.maAttribute;
2936 const svx::frame::Style& rNewAttr = rNew.maAttribute;
2937 const svx::frame::Style& rCmpAttr = std::max(rNewAttr, rOldAttr);
2938
2939 if ( SwLineEntry::OVERLAP1 == nOverlapType )
2940 {
2941 OSL_ENSURE( rNew.mnStartPos >= rOld.mnStartPos, "Overlap type 3? How this?" );
2942
2943 // new left segment
2944 const SwLineEntry aLeft(nKey, rOld.mnStartPos, rNew.mnStartPos, rOld.mbOuter, rOldAttr);
2945
2946 // new middle segment
2947 const SwLineEntry aMiddle(nKey, rNew.mnStartPos, rOld.mnEndPos, rOld.mbOuter, rCmpAttr);
2948
2949 // new right segment
2950 rNew.mnStartPos = rOld.mnEndPos;
2951
2952 // update current lines set
2953 pLineSet->erase( aIter );
2954 if ( aLeft.mnStartPos < aLeft.mnEndPos ) pLineSet->insert( aLeft );
2955 if ( aMiddle.mnStartPos < aMiddle.mnEndPos ) pLineSet->insert( aMiddle );
2956
2957 aIter = pLineSet->begin();
2958
2959 continue; // start over
2960 }
2961 else if ( SwLineEntry::OVERLAP2 == nOverlapType )
2962 {
2963 // new left segment
2964 const SwLineEntry aLeft(nKey, rOld.mnStartPos, rNew.mnStartPos, rOld.mbOuter, rOldAttr);
2965
2966 // new middle segment
2967 const SwLineEntry aMiddle(nKey, rNew.mnStartPos, rNew.mnEndPos, rOld.mbOuter, rCmpAttr);
2968
2969 // new right segment
2970 const SwLineEntry aRight(nKey, rNew.mnEndPos, rOld.mnEndPos, rOld.mbOuter, rOldAttr);
2971
2972 // update current lines set
2973 pLineSet->erase( aIter );
2974 if ( aLeft.mnStartPos < aLeft.mnEndPos ) pLineSet->insert( aLeft );
2975 if ( aMiddle.mnStartPos < aMiddle.mnEndPos ) pLineSet->insert( aMiddle );
2976 if ( aRight.mnStartPos < aRight.mnEndPos ) pLineSet->insert( aRight );
2977
2978 rNew.mnStartPos = rNew.mnEndPos; // rNew should not be inserted!
2979
2980 break; // we are finished
2981 }
2982 else if ( SwLineEntry::OVERLAP3 == nOverlapType )
2983 {
2984 // new left segment
2985 const SwLineEntry aLeft(nKey, rNew.mnStartPos, rOld.mnStartPos, rOld.mbOuter, rNewAttr);
2986
2987 // new middle segment
2988 const SwLineEntry aMiddle(nKey, rOld.mnStartPos, rNew.mnEndPos, rOld.mbOuter, rCmpAttr);
2989
2990 // new right segment
2991 const SwLineEntry aRight(nKey, rNew.mnEndPos, rOld.mnEndPos, rOld.mbOuter, rOldAttr);
2992
2993 // update current lines set
2994 pLineSet->erase( aIter );
2995 if ( aLeft.mnStartPos < aLeft.mnEndPos ) pLineSet->insert( aLeft );
2996 if ( aMiddle.mnStartPos < aMiddle.mnEndPos ) pLineSet->insert( aMiddle );
2997 if ( aRight.mnStartPos < aRight.mnEndPos ) pLineSet->insert( aRight );
2998
2999 rNew.mnStartPos = rNew.mnEndPos; // rNew should not be inserted!
3000
3001 break; // we are finished
3002 }
3003
3004 ++aIter;
3005 }
3006
3007 if ( rNew.mnStartPos < rNew.mnEndPos ) // insert rest
3008 pLineSet->insert( rNew );
3009}
3010
3015namespace
3016{
3017 class SwViewObjectContactRedirector : public sdr::contact::ViewObjectContactRedirector
3018 {
3019 private:
3020 const SwViewShell& mrViewShell;
3021
3022 public:
3023 explicit SwViewObjectContactRedirector( const SwViewShell& rSh )
3024 : mrViewShell( rSh )
3025 {};
3026
3028 const sdr::contact::ViewObjectContact& rOriginal,
3029 const sdr::contact::DisplayInfo& rDisplayInfo,
3031 {
3032 bool bPaint( true );
3033
3034 SdrObject* pObj = rOriginal.GetViewContact().TryToGetSdrObject();
3035 if ( pObj )
3036 {
3037 bPaint = SwFlyFrame::IsPaint( pObj, &mrViewShell );
3038 }
3039
3040 if ( !bPaint )
3041 {
3042 return;
3043 }
3044
3046 rOriginal, rDisplayInfo, rVisitor );
3047 }
3048 };
3049
3050} // end of anonymous namespace
3051// <--
3052
3062void SwRootFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const& rRect, SwPrintData const*const pPrintData) const
3063{
3064 OSL_ENSURE( Lower() && Lower()->IsPageFrame(), "Lower of root is no page." );
3065
3066 PROTOCOL( this, PROT::FileInit, DbgAction::NONE, nullptr)
3067
3068 bool bResetRootPaint = false;
3069 SwViewShell *pSh = mpCurrShell;
3070
3071 if ( pSh->GetWin() )
3072 {
3073 if ( pSh->GetOut() == pSh->GetWin()->GetOutDev() && !pSh->GetWin()->IsVisible() )
3074 {
3075 return;
3076 }
3078 {
3079 SwPaintQueue::Add( pSh, rRect );
3080 return;
3081 }
3082 }
3083 else
3084 SwRootFrame::s_isInPaint = bResetRootPaint = true;
3085
3086 std::unique_ptr<SwSavePaintStatics> pStatics;
3087 if ( gProp.pSGlobalShell )
3088 pStatics.reset(new SwSavePaintStatics());
3089 gProp.pSGlobalShell = pSh;
3090
3091 if( !pSh->GetWin() )
3092 gProp.pSProgress = SfxProgress::GetActiveProgress( static_cast<SfxObjectShell*>(pSh->GetDoc()->GetDocShell()) );
3093
3094 ::SwCalcPixStatics( pSh->GetOut() );
3096
3097 // Copy rRect; for one, rRect could become dangling during the below action, and for another it
3098 // needs to be copied to aRect anyway as that is modified further down below:
3099 SwRect aRect( rRect );
3100
3101 //Trigger an action to clear things up if needed.
3102 //Using this trick we can ensure that all values are valid in all paints -
3103 //no problems, no special case(s).
3104 // #i92745#
3105 // Extend check on certain states of the 'current' <SwViewShell> instance to
3106 // all existing <SwViewShell> instances.
3107 bool bPerformLayoutAction( true );
3108 {
3109 for(SwViewShell& rTmpViewShell : pSh->GetRingContainer())
3110 {
3111 if ( rTmpViewShell.IsInEndAction() ||
3112 rTmpViewShell.IsPaintInProgress() ||
3113 ( rTmpViewShell.Imp()->IsAction() &&
3114 rTmpViewShell.Imp()->GetLayAction().IsActionInProgress() ) )
3115 {
3116 bPerformLayoutAction = false;
3117 }
3118
3119 if(!bPerformLayoutAction)
3120 break;
3121 }
3122 }
3123 if ( bPerformLayoutAction )
3124 {
3125 const_cast<SwRootFrame*>(this)->ResetTurbo();
3126 SwLayAction aAction( const_cast<SwRootFrame*>(this), pSh->Imp() );
3127 aAction.SetPaint( false );
3128 aAction.SetComplete( false );
3129 aAction.SetReschedule( gProp.pSProgress != nullptr );
3130 aAction.Action(&rRenderContext);
3132 if ( !pSh->ActionPend() )
3133 pSh->Imp()->DeletePaintRegion();
3134 }
3135
3136 aRect.Intersection( pSh->VisArea() );
3137
3138 const bool bExtraData = ::IsExtraData( GetFormat()->GetDoc() );
3139
3140 gProp.pSLines.reset(new SwLineRects); // Container for borders.
3141
3142 // #104289#. During painting, something (OLE) can
3143 // load the linguistic, which in turn can cause a reformat
3144 // of the document. Dangerous! We better set this flag to
3145 // avoid the reformat.
3146 const bool bOldAction = IsCallbackActionEnabled();
3147 const_cast<SwRootFrame*>(this)->SetCallbackActionEnabled( false );
3148
3149 const SwPageFrame *pPage = pSh->Imp()->GetFirstVisPage(&rRenderContext);
3150
3151 // #126222. The positions of headers and footers of the previous
3152 // pages have to be updated, else these headers and footers could
3153 // get visible at a wrong position.
3154 const SwPageFrame *pPageDeco = static_cast<const SwPageFrame*>(pPage->GetPrev());
3155 while (pPageDeco)
3156 {
3157 pPageDeco->PaintDecorators();
3158 OSL_ENSURE(!pPageDeco->GetPrev() || pPageDeco->GetPrev()->IsPageFrame(),
3159 "Neighbour of page is not a page.");
3160 pPageDeco = static_cast<const SwPageFrame*>(pPageDeco->GetPrev());
3161 }
3162
3163 const bool bBookMode = gProp.pSGlobalShell->GetViewOptions()->IsViewLayoutBookMode();
3164 if ( bBookMode && pPage->GetPrev() && static_cast<const SwPageFrame*>(pPage->GetPrev())->IsEmptyPage() )
3165 pPage = static_cast<const SwPageFrame*>(pPage->GetPrev());
3166
3167 // #i68597#
3168 const bool bGridPainting(pSh->GetWin() && pSh->Imp()->HasDrawView() && pSh->Imp()->GetDrawView()->IsGridVisible());
3169
3170 // Hide all page break controls before showing them again
3171 SwWrtShell* pWrtSh = dynamic_cast< SwWrtShell* >( gProp.pSGlobalShell );
3172 if ( pWrtSh )
3173 {
3174 SwEditWin& rEditWin = pWrtSh->GetView().GetEditWin();
3176 const SwPageFrame* pHiddenPage = pPage;
3177 while ( pHiddenPage->GetPrev() != nullptr )
3178 {
3179 pHiddenPage = static_cast< const SwPageFrame* >( pHiddenPage->GetPrev() );
3180 SwFrameControlPtr pControl = rMngr.GetControl( FrameControlType::PageBreak, pHiddenPage );
3181 if ( pControl )
3182 pControl->ShowAll( false );
3183 }
3184 }
3185
3186 // #i76669#
3187 SwViewObjectContactRedirector aSwRedirector( *pSh );
3188
3189 while ( pPage )
3190 {
3191 const bool bPaintRightShadow = pPage->IsRightShadowNeeded();
3192 const bool bPaintLeftShadow = pPage->IsLeftShadowNeeded();
3193 const bool bRightSidebar = pPage->SidebarPosition() == sw::sidebarwindows::SidebarPosition::RIGHT;
3194
3195 if ( !pPage->IsEmptyPage() )
3196 {
3197 SwRect aPaintRect;
3198 SwPageFrame::GetBorderAndShadowBoundRect( pPage->getFrameArea(), pSh, &rRenderContext, aPaintRect,
3199 bPaintLeftShadow, bPaintRightShadow, bRightSidebar );
3200
3201 if ( aRect.Overlaps( aPaintRect ) )
3202 {
3203 if ( pSh->GetWin() )
3204 {
3205 gProp.pSSubsLines.reset(new SwSubsRects);
3206 gProp.pSSpecSubsLines.reset(new SwSubsRects);
3207 }
3208 gProp.pBLines.reset(new BorderLines);
3209
3210 aPaintRect.Intersection_( aRect );
3211
3212 if ( bExtraData &&
3213 pSh->GetWin() && pSh->IsInEndAction() )
3214 {
3215 // enlarge paint rectangle to complete page width, subtract
3216 // current paint area and invalidate the resulting region.
3217 SwRectFnSet aRectFnSet(pPage);
3218 SwRect aPageRectTemp( aPaintRect );
3219 aRectFnSet.SetLeftAndWidth( aPageRectTemp,
3220 aRectFnSet.GetLeft(pPage->getFrameArea()),
3221 aRectFnSet.GetWidth(pPage->getFrameArea()) );
3222 aPageRectTemp.Intersection_( pSh->VisArea() );
3223 vcl::Region aPageRectRegion( aPageRectTemp.SVRect() );
3224 aPageRectRegion.Exclude( aPaintRect.SVRect() );
3225 pSh->GetWin()->Invalidate( aPageRectRegion, InvalidateFlags::Children );
3226 }
3227
3228 // #i80793#
3229 // enlarge paint rectangle for objects overlapping the same pixel
3230 // in all cases and before the DrawingLayer overlay is initialized.
3231 lcl_AdjustRectToPixelSize( aPaintRect, *(pSh->GetOut()) );
3232
3233 // #i68597#
3234 // moved paint pre-process for DrawingLayer overlay here since the above
3235 // code dependent from bExtraData may expand the PaintRect
3236 {
3237 // #i75172# if called from SwViewShell::ImplEndAction it should no longer
3238 // really be used but handled by SwViewShell::ImplEndAction already
3239 const vcl::Region aDLRegion(aPaintRect.SVRect());
3240 pSh->DLPrePaint2(aDLRegion);
3241 }
3242
3243 if(OUTDEV_WINDOW == gProp.pSGlobalShell->GetOut()->GetOutDevType())
3244 {
3245 // changed method SwLayVout::Enter(..)
3246 // 2nd parameter is no longer <const> and will be set to the
3247 // rectangle the virtual output device is calculated from <aPaintRect>,
3248 // if the virtual output is used.
3249 s_pVout->Enter(pSh, aPaintRect, !s_isNoVirDev);
3250
3251 // Adjust paint rectangle to pixel size
3252 // Thus, all objects overlapping on pixel level with the unadjusted
3253 // paint rectangle will be considered in the paint.
3254 lcl_AdjustRectToPixelSize( aPaintRect, *(pSh->GetOut()) );
3255 }
3256
3257 // maybe this can be put in the above scope. Since we are not sure, just leave it ATM
3258 s_pVout->SetOrgRect( aPaintRect );
3259
3260 // determine background color of page for <PaintLayer> method
3261 // calls, paint <hell> or <heaven>
3262 const Color aPageBackgrdColor(pPage->GetDrawBackgroundColor());
3263
3264 pPage->PaintBaBo( aPaintRect, pPage );
3265
3266 if ( pSh->Imp()->HasDrawView() )
3267 {
3268 gProp.pSLines->LockLines( true );
3270 pSh->Imp()->PaintLayer( rIDDMA.GetHellId(),
3271 pPrintData,
3272 *pPage, pPage->getFrameArea(),
3273 &aPageBackgrdColor,
3274 pPage->IsRightToLeft(),
3275 &aSwRedirector );
3276 gProp.pSLines->PaintLines( pSh->GetOut(), gProp );
3277 gProp.pSLines->LockLines( false );
3278 }
3279
3281 pPage->PaintBaBo( aPaintRect, pPage, /*bOnlyTextBackground=*/true );
3282
3283 if( pSh->GetWin() )
3284 {
3285 // collect sub-lines
3286 pPage->RefreshSubsidiary( aPaintRect );
3287 // paint special sub-lines
3288 gProp.pSSpecSubsLines->PaintSubsidiary( pSh->GetOut(), nullptr, gProp );
3289 }
3290
3291 pPage->PaintSwFrame( rRenderContext, aPaintRect );
3292
3293 // no paint of page border and shadow, if writer is in place mode.
3294 if( pSh->GetWin() && pSh->GetDoc()->GetDocShell() &&
3295 !pSh->GetDoc()->GetDocShell()->IsInPlaceActive() )
3296 {
3297 SwPageFrame::PaintBorderAndShadow( pPage->getFrameArea(), pSh, bPaintLeftShadow, bPaintRightShadow, bRightSidebar );
3298 SwPageFrame::PaintNotesSidebar( pPage->getFrameArea(), pSh, pPage->GetPhyPageNum(), bRightSidebar);
3299 }
3300
3301 gProp.pSLines->PaintLines( pSh->GetOut(), gProp );
3302 if ( pSh->GetWin() )
3303 {
3304 gProp.pSSubsLines->PaintSubsidiary( pSh->GetOut(), gProp.pSLines.get(), gProp );
3305 gProp.pSSubsLines.reset();
3306 gProp.pSSpecSubsLines.reset();
3307 }
3308 // fdo#42750: delay painting these until after subsidiary lines
3309 // fdo#45562: delay painting these until after hell layer
3310 // fdo#47717: but do it before heaven layer
3311 ProcessPrimitives(gProp.pBLines->GetBorderLines_Clear());
3312
3313 if ( pSh->Imp()->HasDrawView() )
3314 {
3316 pPrintData,
3317 *pPage, pPage->getFrameArea(),
3318 &aPageBackgrdColor,
3319 pPage->IsRightToLeft(),
3320 &aSwRedirector );
3321 }
3322
3323 if ( bExtraData )
3324 pPage->RefreshExtraData( aPaintRect );
3325
3326 gProp.pBLines.reset();
3327 s_pVout->Leave();
3328
3329 // #i68597#
3330 // needed to move grid painting inside Begin/EndDrawLayer bounds and to change
3331 // output rect for it accordingly
3332 if(bGridPainting)
3333 {
3334 SdrPaintView* pPaintView = pSh->Imp()->GetDrawView();
3335 SdrPageView* pPageView = pPaintView->GetSdrPageView();
3336 pPageView->DrawPageViewGrid(*pSh->GetOut(), aPaintRect.SVRect(), SwViewOption::GetTextGridColor() );
3337 }
3338
3339 // #i68597#
3340 // moved paint post-process for DrawingLayer overlay here, see above
3341 {
3342 pSh->DLPostPaint2(true);
3343 }
3344 }
3345
3346 pPage->PaintDecorators( );
3347 pPage->PaintBreak();
3348 }
3349 else if ( bBookMode && pSh->GetWin() && !pSh->GetDoc()->GetDocShell()->IsInPlaceActive() )
3350 {
3351 // paint empty page
3352 SwRect aPaintRect;
3353 SwRect aEmptyPageRect( pPage->getFrameArea() );
3354
3355 // code from vprint.cxx
3356 const SwPageFrame& rFormatPage = pPage->GetFormatPage();
3357 aEmptyPageRect.SSize( rFormatPage.getFrameArea().SSize() );
3358
3359 SwPageFrame::GetBorderAndShadowBoundRect( aEmptyPageRect, pSh, &rRenderContext, aPaintRect,
3360 bPaintLeftShadow, bPaintRightShadow, bRightSidebar );
3361 aPaintRect.Intersection_( aRect );
3362
3363 if ( aRect.Overlaps( aEmptyPageRect ) )
3364 {
3365 // #i75172# if called from SwViewShell::ImplEndAction it should no longer
3366 // really be used but handled by SwViewShell::ImplEndAction already
3367 {
3368 const vcl::Region aDLRegion(aPaintRect.SVRect());
3369 pSh->DLPrePaint2(aDLRegion);
3370 }
3371
3372 if( pSh->GetOut()->GetFillColor() != aGlobalRetoucheColor )
3374 // No line color
3375 pSh->GetOut()->SetLineColor();
3376 // Use aligned page rectangle
3377 {
3378 SwRect aTmpPageRect( aEmptyPageRect );
3379 ::SwAlignRect( aTmpPageRect, pSh, &rRenderContext );
3380 aEmptyPageRect = aTmpPageRect;
3381 }
3382
3383 pSh->GetOut()->DrawRect( aEmptyPageRect.SVRect() );
3384
3385 // paint empty page text
3386 const vcl::Font& rEmptyPageFont = SwPageFrame::GetEmptyPageFont();
3387 const vcl::Font aOldFont( pSh->GetOut()->GetFont() );
3388
3389 pSh->GetOut()->SetFont( rEmptyPageFont );
3390 pSh->GetOut()->DrawText( aEmptyPageRect.SVRect(), SwResId( STR_EMPTYPAGE ),
3391 DrawTextFlags::VCenter |
3392 DrawTextFlags::Center |
3393 DrawTextFlags::Clip );
3394
3395 pSh->GetOut()->SetFont( aOldFont );
3396 // paint shadow and border for empty page
3397 SwPageFrame::PaintBorderAndShadow( aEmptyPageRect, pSh, bPaintLeftShadow, bPaintRightShadow, bRightSidebar );
3398 SwPageFrame::PaintNotesSidebar( aEmptyPageRect, pSh, pPage->GetPhyPageNum(), bRightSidebar);
3399
3400 {
3401 pSh->DLPostPaint2(true);
3402 }
3403 }
3404 }
3405
3406 OSL_ENSURE( !pPage->GetNext() || pPage->GetNext()->IsPageFrame(),
3407 "Neighbour of page is not a page." );
3408 pPage = static_cast<const SwPageFrame*>(pPage->GetNext());
3409 }
3410
3411 gProp.pSLines.reset();
3412
3413 if ( bResetRootPaint )
3415 if ( pStatics )
3416 pStatics.reset();
3417 else
3418 {
3419 gProp.pSProgress = nullptr;
3420 gProp.pSGlobalShell = nullptr;
3421 }
3422
3423 const_cast<SwRootFrame*>(this)->SetCallbackActionEnabled( bOldAction );
3424}
3425
3427{
3428 vcl::RenderContext* pRenderContext = pCont->getRootFrame()->GetCurrShell()->GetOut();
3429
3430 //It's possible that the Cont will get destroyed.
3431 SwContentFrame *pCnt = pCont->ContainsContent();
3432 while ( pCnt && pCnt->IsInFootnote() )
3433 {
3434 pCnt->Calc(pRenderContext);
3435 pCnt = pCnt->GetNextContentFrame();
3436 }
3437}
3438
3439namespace {
3440
3441class SwShortCut
3442{
3443 SwRectDist m_fnCheck;
3444 tools::Long m_nLimit;
3445
3446public:
3447 SwShortCut( const SwFrame& rFrame, const SwRect& rRect );
3448 bool Stop(const SwRect& rRect) const { return (rRect.*m_fnCheck)(m_nLimit) > 0; }
3449};
3450
3451}
3452
3453SwShortCut::SwShortCut( const SwFrame& rFrame, const SwRect& rRect )
3454{
3455 bool bVert = rFrame.IsVertical();
3456 bool bR2L = rFrame.IsRightToLeft();
3457 if( rFrame.IsNeighbourFrame() && bVert == bR2L )
3458 {
3459 if( bVert )
3460 {
3461 m_fnCheck = &SwRect::GetBottomDistance;
3462 m_nLimit = rRect.Top();
3463 }
3464 else
3465 {
3466 m_fnCheck = &SwRect::GetLeftDistance;
3467 m_nLimit = rRect.Left() + rRect.Width();
3468 }
3469 }
3470 else if( bVert == rFrame.IsNeighbourFrame() )
3471 {
3472 m_fnCheck = &SwRect::GetTopDistance;
3473 m_nLimit = rRect.Top() + rRect.Height();
3474 }
3475 else
3476 {
3477 if ( rFrame.IsVertLR() )
3478 {
3479 m_fnCheck = &SwRect::GetLeftDistance;
3480 m_nLimit = rRect.Right();
3481 }
3482 else
3483 {
3484 m_fnCheck = &SwRect::GetRightDistance;
3485 m_nLimit = rRect.Left();
3486 }
3487 }
3488}
3489
3490void SwLayoutFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const& rRect, SwPrintData const*const) const
3491{
3492 // #i16816# tagged pdf support
3493 Frame_Info aFrameInfo( *this );
3494 SwTaggedPDFHelper aTaggedPDFHelper( nullptr, &aFrameInfo, nullptr, rRenderContext );
3495
3496 const SwFrame *pFrame = Lower();
3497 if ( !pFrame )
3498 return;
3499
3500 SwFrameDeleteGuard g(const_cast<SwLayoutFrame*>(this)); // lock because Calc() and recursion
3501 SwShortCut aShortCut( *pFrame, rRect );
3502 bool bCnt = pFrame->IsContentFrame();
3503 if ( bCnt )
3504 pFrame->Calc(&rRenderContext);
3505
3506 if ( pFrame->IsFootnoteContFrame() )
3507 {
3508 ::lcl_EmergencyFormatFootnoteCont( const_cast<SwFootnoteContFrame*>(static_cast<const SwFootnoteContFrame*>(pFrame)) );
3509 pFrame = Lower();
3510 }
3511
3512 const SwPageFrame *pPage = nullptr;
3513 bool bWin = gProp.pSGlobalShell->GetWin() != nullptr;
3515 // Tiled rendering is similar to printing in this case: painting transparently multiple
3516 // times will result in darker colors: avoid that.
3517 bWin = false;
3518
3519 while ( IsAnLower( pFrame ) )
3520 {
3521 SwRect aPaintRect( pFrame->GetPaintArea() );
3522 if( aShortCut.Stop( aPaintRect ) )
3523 break;
3524 if ( bCnt && gProp.pSProgress )
3526
3527 //We need to retouch if a frame explicitly requests it.
3528 //First do the retouch, because this could flatten the borders.
3529 if ( pFrame->IsRetouche() )
3530 {
3531 if ( pFrame->IsRetoucheFrame() && bWin && !pFrame->GetNext() )
3532 {
3533 if ( !pPage )
3534 pPage = FindPageFrame();
3535 pFrame->Retouch( pPage, rRect );
3536 }
3537 pFrame->ResetRetouche();
3538 }
3539
3540 if ( rRect.Overlaps( aPaintRect ) )
3541 {
3542 if ( bCnt && pFrame->IsCompletePaint() &&
3543 !rRect.Contains( aPaintRect ) && Application::AnyInput( VclInputFlags::KEYBOARD ) )
3544 {
3545 //fix(8104): It may happen, that the processing wasn't complete
3546 //but some parts of the paragraph were still repainted.
3547 //This could lead to the situation, that other parts of the
3548 //paragraph won't be repainted at all. The only solution seems
3549 //to be an invalidation of the window.
3550 //To not make it too severe the rectangle is limited by
3551 //painting the desired part and only invalidating the
3552 //remaining paragraph parts.
3553 if ( aPaintRect.Left() == rRect.Left() &&
3554 aPaintRect.Right() == rRect.Right() )
3555 {
3556 aPaintRect.Bottom( rRect.Top() - 1 );
3557 if ( aPaintRect.Height() > 0 )
3558 gProp.pSGlobalShell->InvalidateWindows(aPaintRect);
3559 aPaintRect.Top( rRect.Bottom() + 1 );
3560 aPaintRect.Bottom( pFrame->getFrameArea().Bottom() );
3561 if ( aPaintRect.Height() > 0 )
3562 gProp.pSGlobalShell->InvalidateWindows(aPaintRect);
3563 aPaintRect.Top( pFrame->getFrameArea().Top() );
3564 aPaintRect.Bottom( pFrame->getFrameArea().Bottom() );
3565 }
3566 else
3567 {
3568 gProp.pSGlobalShell->InvalidateWindows( aPaintRect );
3569 pFrame = pFrame->GetNext();
3570 if ( pFrame )
3571 {
3572 bCnt = pFrame->IsContentFrame();
3573 if ( bCnt )
3574 pFrame->Calc(&rRenderContext);
3575 }
3576 continue;
3577 }
3578 }
3579 pFrame->ResetCompletePaint();
3580 aPaintRect.Intersection_( rRect );
3581
3582 pFrame->PaintSwFrame( rRenderContext, aPaintRect );
3583
3584 if ( Lower() && Lower()->IsColumnFrame() )
3585 {
3586 //Paint the column separator line if needed. The page is
3587 //responsible for the page frame - not the upper.
3588 const SwFrameFormat *pFormat = GetUpper() && GetUpper()->IsPageFrame()
3589 ? GetUpper()->GetFormat()
3590 : GetFormat();
3591 const SwFormatCol &rCol = pFormat->GetCol();
3592 if ( rCol.GetLineAdj() != COLADJ_NONE )
3593 {
3594 if ( !pPage )
3595 pPage = pFrame->FindPageFrame();
3596
3597 PaintColLines( aPaintRect, rCol, pPage );
3598 }
3599 }
3600 }
3601 if ( !bCnt && pFrame->GetNext() && pFrame->GetNext()->IsFootnoteContFrame() )
3602 ::lcl_EmergencyFormatFootnoteCont( const_cast<SwFootnoteContFrame*>(static_cast<const SwFootnoteContFrame*>(pFrame->GetNext())) );
3603
3604 pFrame = pFrame->GetNext();
3605
3606 if ( pFrame )
3607 {
3608 bCnt = pFrame->IsContentFrame();
3609 if ( bCnt )
3610 pFrame->Calc(&rRenderContext);
3611 }
3612 }
3613}
3614
3616 const basegfx::B2DPoint& rStart, const basegfx::B2DPoint& rEnd,
3617 basegfx::BColor aColor )
3618{
3620
3621 std::vector< double > aStrokePattern;
3622 basegfx::B2DPolygon aLinePolygon;
3623 aLinePolygon.append(rStart);
3624 aLinePolygon.append(rEnd);
3625
3627 if ( rSettings.GetHighContrastMode( ) )
3628 {
3629 // Only a solid line in high contrast mode
3630 aColor = rSettings.GetDialogTextColor().getBColor();
3631 }
3632 else
3633 {
3634 // Get a color for the contrast
3635 basegfx::BColor aHslLine = basegfx::utils::rgb2hsl( aColor );
3636 double nLuminance = aHslLine.getZ() * 2.5;
3637 if ( nLuminance == 0 )
3638 nLuminance = 0.5;
3639 else if ( nLuminance >= 1.0 )
3640 nLuminance = aHslLine.getZ() * 0.4;
3641 aHslLine.setZ( nLuminance );
3642 const basegfx::BColor aOtherColor = basegfx::utils::hsl2rgb( aHslLine );
3643
3644 // Compute the plain line
3645 aSeq[0] =
3647 aLinePolygon, aOtherColor );
3648
3649 // Dashed line in twips
3650 aStrokePattern.push_back( 40 );
3651 aStrokePattern.push_back( 40 );
3652
3653 aSeq.resize( 2 );
3654 }
3655
3656 // Compute the dashed line primitive
3657 aSeq[ aSeq.size( ) - 1 ] =
3659 basegfx::B2DPolyPolygon( aLinePolygon ),
3661 drawinglayer::attribute::StrokeAttribute( std::move(aStrokePattern) ) );
3662
3663
3664 return aSeq;
3665}
3666
3668{
3669 if ( gProp.pSGlobalShell->GetOut()->GetOutDevType() == OUTDEV_PRINTER ||
3670 gProp.pSGlobalShell->GetViewOptions()->IsPDFExport() ||
3671 gProp.pSGlobalShell->GetViewOptions()->IsReadonly() ||
3672 gProp.pSGlobalShell->IsPreview() )
3673 return;
3674
3675 const SwFrame* pBodyFrame = Lower();
3676 while ( pBodyFrame && !pBodyFrame->IsBodyFrame() )
3677 pBodyFrame = pBodyFrame->GetNext();
3678
3679 if ( pBodyFrame )
3680 {
3681 const SwLayoutFrame* pLayBody = static_cast< const SwLayoutFrame* >( pBodyFrame );
3682 const SwFlowFrame *pFlowFrame = pLayBody->ContainsContent();
3683
3684 // Test if the first node is a table
3685 const SwFrame* pFirstFrame = pLayBody->Lower();
3686 if ( pFirstFrame && pFirstFrame->IsTabFrame() )
3687 pFlowFrame = static_cast< const SwTabFrame* >( pFirstFrame );
3688
3689 SwWrtShell* pWrtSh = dynamic_cast< SwWrtShell* >( gProp.pSGlobalShell );
3690 if ( pWrtSh )
3691 {
3692 SwEditWin& rEditWin = pWrtSh->GetView().GetEditWin();
3694
3695 if ( pFlowFrame && pFlowFrame->IsPageBreak( true ) )
3696 rMngr.SetPageBreakControl( this );
3697 else
3699 }
3700 }
3702}
3703
3705{
3706 if ( gProp.pSGlobalShell->GetOut()->GetOutDevType() == OUTDEV_PRINTER ||
3707 gProp.pSGlobalShell->GetViewOptions()->IsPDFExport() ||
3708 gProp.pSGlobalShell->GetViewOptions()->IsReadonly() ||
3709 gProp.pSGlobalShell->IsPreview() )
3710 return;
3711
3712 const SwFrame* pBodyFrame = Lower();
3713 while ( pBodyFrame && !pBodyFrame->IsBodyFrame() )
3714 pBodyFrame = pBodyFrame->GetNext();
3715
3716 if ( !pBodyFrame )
3717 return;
3718
3719 const SwContentFrame *pCnt = static_cast< const SwLayoutFrame* >( pBodyFrame )->ContainsContent();
3720 if ( !(pCnt && pCnt->IsColBreak( true )) )
3721 return;
3722
3723 // Paint the break only if:
3724 // * Not in header footer edition, to avoid conflicts with the
3725 // header/footer marker
3726 // * Non-printing characters are shown, as this is more consistent
3727 // with other formatting marks
3728 if ( !(!gProp.pSGlobalShell->IsShowHeaderFooterSeparator( FrameControlType::Header ) &&
3729 !gProp.pSGlobalShell->IsShowHeaderFooterSeparator( FrameControlType::Footer ) &&
3730 gProp.pSGlobalShell->GetViewOptions()->IsLineBreak()) )
3731 return;
3732
3733 SwRect aRect( pCnt->getFramePrintArea() );
3734 aRect.Pos() += pCnt->getFrameArea().Pos();
3735
3736 // Draw the line
3737 basegfx::B2DPoint aStart( double( aRect.Left() ), aRect.Top() );
3738 basegfx::B2DPoint aEnd( double( aRect.Right() ), aRect.Top() );
3739 double nWidth = aRect.Width();
3740 if ( IsVertical( ) )
3741 {
3742 aStart = basegfx::B2DPoint( double( aRect.Right() ), double( aRect.Top() ) );
3743 aEnd = basegfx::B2DPoint( double( aRect.Right() ), double( aRect.Bottom() ) );
3744 nWidth = aRect.Height();
3745 }
3746
3748
3750 lcl_CreateDashedIndicatorPrimitive( aStart, aEnd, aLineColor );
3751
3752 // Add the text above
3753 OUString aBreakText = SwResId(STR_COLUMN_BREAK);
3754
3755 basegfx::B2DVector aFontSize;
3756 OutputDevice* pOut = gProp.pSGlobalShell->GetOut();
3757 vcl::Font aFont = pOut->GetSettings().GetStyleSettings().GetToolFont();
3758 aFont.SetFontHeight( 8 * 20 );
3759 pOut->SetFont( aFont );
3761 aFontSize, aFont, IsRightToLeft(), false );
3762
3763 tools::Rectangle aTextRect;
3764 pOut->GetTextBoundRect( aTextRect, aBreakText );
3765 tools::Long nTextOff = ( nWidth - aTextRect.GetWidth() ) / 2;
3766
3768 aFontSize.getX(), aFontSize.getY(),
3769 aRect.Left() + nTextOff, aRect.Top() ) );
3770 if ( IsVertical() )
3771 {
3773 aFontSize.getX(), aFontSize.getY(), 0.0, M_PI_2,
3774 aRect.Right(), aRect.Top() + nTextOff );
3775 }
3776
3777 aSeq.push_back(
3779 aTextMatrix,
3780 aBreakText, 0, aBreakText.getLength(),
3781 std::vector< double >(),
3782 {},
3783 std::move(aFontAttr),
3784 lang::Locale(),
3785 aLineColor ) );
3786
3788}
3789
3791{
3792 const SwFrame* pFrame = Lower();
3793 while ( pFrame )
3794 {
3795 if ( pFrame->IsLayoutFrame() )
3796 static_cast< const SwLayoutFrame*>( pFrame )->PaintBreak( );
3797 pFrame = pFrame->GetNext();
3798 }
3799}
3800
3802{
3803 SwWrtShell* pWrtSh = dynamic_cast< SwWrtShell* >( gProp.pSGlobalShell );
3804 if ( !pWrtSh )
3805 return;
3806
3807 SwEditWin& rEditWin = pWrtSh->GetView().GetEditWin();
3808
3809 const SwLayoutFrame* pBody = FindBodyCont();
3810 if ( !pBody )
3811 return;
3812
3813 SwRect aBodyRect( pBody->getFrameArea() );
3814
3815 if ( !(gProp.pSGlobalShell->GetOut()->GetOutDevType() != OUTDEV_PRINTER &&
3816 !gProp.pSGlobalShell->GetViewOptions()->IsPDFExport() &&
3817 !gProp.pSGlobalShell->IsPreview() &&
3818 !gProp.pSGlobalShell->GetViewOptions()->IsReadonly() &&
3819 !gProp.pSGlobalShell->GetViewOptions()->getBrowseMode() &&
3820 ( gProp.pSGlobalShell->IsShowHeaderFooterSeparator( FrameControlType::Header ) ||
3821 gProp.pSGlobalShell->IsShowHeaderFooterSeparator( FrameControlType::Footer ) )) )
3822 return;
3823
3824 bool bRtl = AllSettings::GetLayoutRTL();
3825 const SwRect& rVisArea = gProp.pSGlobalShell->VisArea();
3826 tools::Long nXOff = std::min( aBodyRect.Right(), rVisArea.Right() );
3827 if ( bRtl )
3828 nXOff = std::max( aBodyRect.Left(), rVisArea.Left() );
3829
3830 // Header
3831 if ( gProp.pSGlobalShell->IsShowHeaderFooterSeparator( FrameControlType::Header ) )
3832 {
3833 const SwFrame* pHeaderFrame = Lower();
3834 if ( !pHeaderFrame->IsHeaderFrame() )
3835 pHeaderFrame = nullptr;
3836
3837 tools::Long nHeaderYOff = aBodyRect.Top();
3838 Point nOutputOff = rEditWin.LogicToPixel( Point( nXOff, nHeaderYOff ) );
3840 }
3841
3842 // Footer
3843 if ( !gProp.pSGlobalShell->IsShowHeaderFooterSeparator( FrameControlType::Footer ) )
3844 return;
3845
3846 const SwFrame* pFootnoteContFrame = Lower();
3847 while ( pFootnoteContFrame )
3848 {
3849 if ( pFootnoteContFrame->IsFootnoteContFrame() )
3850 aBodyRect.AddBottom( pFootnoteContFrame->getFrameArea().Bottom() - aBodyRect.Bottom() );
3851 pFootnoteContFrame = pFootnoteContFrame->GetNext();
3852 }
3853
3854 tools::Long nFooterYOff = aBodyRect.Bottom();
3855 Point nOutputOff = rEditWin.LogicToPixel( Point( nXOff, nFooterYOff ) );
3857}
3858
3873{
3874 bool bBackgroundTransparent = GetFormat()->IsBackgroundTransparent();
3875 if ( !bBackgroundTransparent &&
3876 GetFormat()->IsBackgroundBrushInherited() )
3877 {
3878 const SvxBrushItem* pBackgroundBrush = nullptr;
3879 std::optional<Color> xSectionTOXColor;
3880 SwRect aDummyRect;
3882
3883 if ( GetBackgroundBrush( aFillAttributes, pBackgroundBrush, xSectionTOXColor, aDummyRect, false, /*bConsiderTextBox=*/false) )
3884 {
3885 if ( xSectionTOXColor &&
3886 (xSectionTOXColor->IsTransparent()) &&
3887 (xSectionTOXColor != COL_TRANSPARENT) )
3888 {
3889 bBackgroundTransparent = true;
3890 }
3891 else if(aFillAttributes && aFillAttributes->isUsed())
3892 {
3893 bBackgroundTransparent = aFillAttributes->isTransparent();
3894 }
3895 else if ( pBackgroundBrush )
3896 {
3897 if ( (pBackgroundBrush->GetColor().IsTransparent()) &&
3898 (pBackgroundBrush->GetColor() != COL_TRANSPARENT) )
3899 {
3900 bBackgroundTransparent = true;
3901 }
3902 else
3903 {
3904 const GraphicObject *pTmpGrf =
3905 pBackgroundBrush->GetGraphicObject();
3906 if ( pTmpGrf &&
3907 (pTmpGrf->GetAttr().IsTransparent())
3908 )
3909 {
3910 bBackgroundTransparent = true;
3911 }
3912 }
3913 }
3914 }
3915 }
3916
3917 return bBackgroundTransparent;
3918};
3919
3921{
3922 SdrObjUserCall *pUserCall = GetUserCall(pObj);
3923
3924 if ( nullptr == pUserCall )
3925 return true;
3926
3927 //Attribute dependent, don't paint for printer or Preview
3928 bool bPaint = gProp.pSFlyOnlyDraw ||
3929 static_cast<SwContact*>(pUserCall)->GetFormat()->GetPrint().GetValue();
3930 if ( !bPaint )
3931 bPaint = pSh->GetWin() && !pSh->IsPreview();
3932
3933 if ( bPaint )
3934 {
3935 //The paint may be prevented by the superior Flys.
3936 SwFrame *pAnch = nullptr;
3937 if ( dynamic_cast< const SwFlyDrawObj *>( pObj ) != nullptr ) // i#117962#
3938 {
3939 bPaint = false;
3940 }
3941 if ( auto pFlyDraw = dynamic_cast<SwVirtFlyDrawObj *>( pObj ) )
3942 {
3943 SwFlyFrame *pFly = pFlyDraw->GetFlyFrame();
3944 if ( gProp.pSFlyOnlyDraw && gProp.pSFlyOnlyDraw == pFly )
3945 return true;
3946
3947 //Try to avoid displaying the intermediate stage, Flys which don't
3948 //overlap with the page on which they are anchored won't be
3949 //painted.
3950 //HACK: exception: printing of frames in tables, those can overlap
3951 //a page once in a while when dealing with oversized tables (HTML).
3952 SwPageFrame *pPage = pFly->FindPageFrame();
3953 if ( pPage && pPage->getFrameArea().Overlaps( pFly->getFrameArea() ) )
3954 {
3955 pAnch = pFly->AnchorFrame();
3956 }
3957
3958 }
3959 else
3960 {
3961 // Consider 'virtual' drawing objects
3962 SwDrawContact* pDrawContact = dynamic_cast<SwDrawContact*>(pUserCall);
3963 pAnch = pDrawContact ? pDrawContact->GetAnchorFrame(pObj) : nullptr;
3964 if ( pAnch )
3965 {
3966 if ( !pAnch->isFrameAreaPositionValid() )
3967 pAnch = nullptr;
3968 else if ( pSh->GetOut() == pSh->getIDocumentDeviceAccess().getPrinter( false ))
3969 {
3970 //HACK: we have to omit some of the objects for printing,
3971 //otherwise they would be printed twice.
3972 //The objects should get printed if the TableHack is active
3973 //right now. Afterwards they must not be printed if the
3974 //page over which they float position wise gets printed.
3975 const SwPageFrame *pPage = pAnch->FindPageFrame();
3976 if ( !pPage->getFrameArea().Overlaps( SwRect(pObj->GetCurrentBoundRect()) ) )
3977 pAnch = nullptr;
3978 }
3979 }
3980 else
3981 {
3982 if ( dynamic_cast< const SdrObjGroup *>( pObj ) == nullptr )
3983 {
3984 OSL_FAIL( "<SwFlyFrame::IsPaint(..)> - paint of drawing object without anchor frame!?" );
3985 }
3986 }
3987 }
3988 if ( pAnch )
3989 {
3990 if ( pAnch->IsInFly() )
3991 bPaint = SwFlyFrame::IsPaint( pAnch->FindFlyFrame()->GetVirtDrawObj(),
3992 pSh );
3993 else if ( gProp.pSFlyOnlyDraw )
3994 bPaint = false;
3995 }
3996 else
3997 bPaint = false;
3998 }
3999 return bPaint;
4000}
4001
4002void SwCellFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const& rRect, SwPrintData const*const) const
4003{
4004 if ( GetLayoutRowSpan() >= 1 )
4005 SwLayoutFrame::PaintSwFrame( rRenderContext, rRect );
4006}
4007
4008namespace {
4009
4010struct BorderLinesGuard
4011{
4012 explicit BorderLinesGuard() : m_pBorderLines(std::move(gProp.pBLines))
4013 {
4014 gProp.pBLines.reset(new BorderLines);
4015 }
4016 ~BorderLinesGuard()
4017 {
4018 gProp.pBLines = std::move(m_pBorderLines);
4019 }
4020private:
4021 std::unique_ptr<BorderLines> m_pBorderLines;
4022};
4023
4024}
4025
4026// set strikethrough for deleted objects anchored to character
4028{
4029 if ( SwSortedObjs *pObjs = GetDrawObjs() )
4030 {
4031 for (SwAnchoredObject* pAnchoredObj : *pObjs)
4032 {
4033 if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
4034 {
4035 pFly->SetDeleted(bDeleted);
4036 }
4037 }
4038 }
4039}
4040
4041void SwFlyFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const& rRect, SwPrintData const*const) const
4042{
4043 //optimize thumbnail generation and store procedure to improve odt saving performance, #i120030#
4044 SwViewShell *pShell = getRootFrame()->GetCurrShell();
4045 if (pShell && pShell->GetDoc() && pShell->GetDoc()->GetDocShell())
4046 {
4047 bool bInGenerateThumbnail = pShell->GetDoc()->GetDocShell()->IsInGenerateAndStoreThumbnail();
4048 if (bInGenerateThumbnail)
4049 {
4050 const SwRect& aVisRect = pShell->VisArea();
4051 if (!aVisRect.Overlaps(getFrameArea()))
4052 return;
4053 }
4054 }
4055
4056 //because of the overlapping of frames and drawing objects the flys have to
4057 //paint their borders (and those of the internal ones) directly.
4058 //e.g. #33066#
4059 gProp.pSLines->LockLines(true);
4060 BorderLinesGuard blg; // this should not paint borders added from PaintBaBo
4061
4062 SwRect aRect( rRect );
4063 aRect.Intersection_( getFrameArea() );
4064
4065 rRenderContext.Push( vcl::PushFlags::CLIPREGION );
4066 rRenderContext.SetClipRegion();
4067 const SwPageFrame* pPage = FindPageFrame();
4068
4069 const SwNoTextFrame *pNoText = Lower() && Lower()->IsNoTextFrame()
4070 ? static_cast<const SwNoTextFrame*>(Lower()) : nullptr;
4071
4072 bool bIsChart = false; //#i102950# don't paint additional borders for charts
4073 //check whether we have a chart
4074 if(pNoText)
4075 {
4076 const SwNoTextNode* pNoTNd = dynamic_cast<const SwNoTextNode*>(pNoText->GetNode());
4077 if( pNoTNd )
4078 {
4079 SwOLENode* pOLENd = const_cast<SwOLENode*>(pNoTNd->GetOLENode());
4080 if( pOLENd && pOLENd->GetOLEObj().GetObject().IsChart() )
4081 bIsChart = true;
4082 }
4083 }
4084
4085 {
4086 bool bContour = GetFormat()->GetSurround().IsContour();
4087 tools::PolyPolygon aPoly;
4088 if ( bContour )
4089 {
4090 // add 2nd parameter with value <true>
4091 // to indicate that method is called for paint in order to avoid
4092 // load of the intrinsic graphic.
4093 bContour = GetContour( aPoly, true );
4094 }
4095
4096 // #i47804# - distinguish complete background paint
4097 // and margin paint.
4098 // paint complete background for Writer text fly frames
4099 bool bPaintCompleteBack( !pNoText );
4100 // paint complete background for transparent graphic and contour,
4101 // if own background color exists.
4102 const bool bIsGraphicTransparent = pNoText && pNoText->IsTransparent();
4103 if ( !bPaintCompleteBack &&
4104 ( bIsGraphicTransparent|| bContour ) )
4105 {
4106 const SwFlyFrameFormat* pSwFrameFormat = GetFormat();
4107
4108 if (pSwFrameFormat && pSwFrameFormat->supportsFullDrawingLayerFillAttributeSet())
4109 {
4110 // check for transparency
4112
4113 // check if the new fill attributes are used
4114 if(aFillAttributes && aFillAttributes->isUsed())
4115 {
4116 bPaintCompleteBack = true;
4117 }
4118 }
4119 else
4120 {
4121 std::unique_ptr<SvxBrushItem> aBack = GetFormat()->makeBackgroundBrushItem();
4122 // to determine, if background has to be painted, by checking, if
4123 // background color is not COL_TRANSPARENT ("no fill"/"auto fill")
4124 // or a background graphic exists.
4125 bPaintCompleteBack =
4126 aBack->GetColor() != COL_TRANSPARENT ||
4127 aBack->GetGraphicPos() != GPOS_NONE;
4128 }
4129 }
4130 // paint of margin needed.
4131 const bool bPaintMarginOnly( !bPaintCompleteBack &&
4133
4134 // #i47804# - paint background of parent fly frame
4135 // for transparent graphics in layer Hell, if parent fly frame isn't
4136 // in layer Hell. It's only painted the intersection between the
4137 // parent fly frame area and the paint area <aRect>
4139
4140 if (bIsGraphicTransparent &&
4141 GetFormat()->GetDoc()->getIDocumentSettingAccess().get(DocumentSettingId::SUBTRACT_FLYS) &&
4142 GetVirtDrawObj()->GetLayer() == rIDDMA.GetHellId() &&
4144 {
4145 const SwFlyFrame* pParentFlyFrame = GetAnchorFrame()->FindFlyFrame();
4146 if ( pParentFlyFrame->GetDrawObj()->GetLayer() !=
4147 rIDDMA.GetHellId() )
4148 {
4149 SwFlyFrame* pOldRet = gProp.pSRetoucheFly2;
4150 gProp.pSRetoucheFly2 = const_cast<SwFlyFrame*>(this);
4151
4152 SwBorderAttrAccess aAccess( SwFrame::GetCache(), pParentFlyFrame );
4153 const SwBorderAttrs &rAttrs = *aAccess.Get();
4154 SwRect aPaintRect( aRect );
4155 aPaintRect.Intersection_( pParentFlyFrame->getFrameArea() );
4156 pParentFlyFrame->PaintSwFrameBackground( aPaintRect, pPage, rAttrs );
4157
4158 gProp.pSRetoucheFly2 = pOldRet;
4159 }
4160 }
4161
4162 if ( bPaintCompleteBack || bPaintMarginOnly )
4163 {
4164 //#24926# JP 01.02.96, PaintBaBo is here partially so PaintSwFrameShadowAndBorder
4165 //receives the original Rect but PaintSwFrameBackground only the limited
4166 //one.
4167
4169 rRenderContext.SetLineColor();
4170
4171 pPage = FindPageFrame();
4172
4173 SwBorderAttrAccess aAccess( SwFrame::GetCache(), static_cast<SwFrame const *>(this) );
4174 const SwBorderAttrs &rAttrs = *aAccess.Get();
4175
4176 // paint background
4177 {
4178 SwRegionRects aRegion( aRect );
4179 // #i80822#
4180 // suppress painting of background in printing area for
4181 // non-transparent graphics.
4182 if ( bPaintMarginOnly ||
4183 ( pNoText && !bIsGraphicTransparent ) )
4184 {
4185 //What we actually want to paint is the small stripe between
4186 //PrtArea and outer border.
4187 SwRect aTmp( getFramePrintArea() ); aTmp += getFrameArea().Pos();
4188 aRegion -= aTmp;
4189 }
4190 if ( bContour )
4191 {
4192 rRenderContext.Push();
4193 // #i80822#
4194 // apply clip region under the same conditions, which are
4195 // used in <SwNoTextFrame::PaintSwFrame(..)> to set the clip region
4196 // for painting the graphic/OLE. Thus, the clip region is
4197 // also applied for the PDF export.
4199
4200 if ( !rRenderContext.GetConnectMetaFile() || !pSh || !pSh->GetWin() )
4201 {
4202 rRenderContext.SetClipRegion(vcl::Region(aPoly));
4203 }
4204
4205 for ( size_t i = 0; i < aRegion.size(); ++i )
4206 {
4207 PaintSwFrameBackground( aRegion[i], pPage, rAttrs, false, true );
4208 }
4209
4210 rRenderContext.Pop();
4211 }
4212 else
4213 {
4214 for ( size_t i = 0; i < aRegion.size(); ++i )
4215 {
4216 PaintSwFrameBackground( aRegion[i], pPage, rAttrs, false, true );
4217 }
4218 }
4219 }
4220
4221 // paint border before painting background
4222 PaintSwFrameShadowAndBorder(rRect, pPage, rAttrs);
4223
4224 rRenderContext.Pop();
4225 }
4226 }
4227
4228 // fly frame will paint it's subsidiary lines and
4229 // the subsidiary lines of its lowers on its own, due to overlapping with
4230 // other fly frames or other objects.
4231 if( gProp.pSGlobalShell->GetWin()
4232 && !bIsChart ) //#i102950# don't paint additional borders for charts
4233 {
4234 bool bSubsLineRectsCreated;
4235 if ( gProp.pSSubsLines )
4236 {
4237 // Lock already existing subsidiary lines
4238 gProp.pSSubsLines->LockLines( true );
4239 bSubsLineRectsCreated = false;
4240 }
4241 else
4242 {
4243 // create new subsidiary lines
4244 gProp.pSSubsLines.reset(new SwSubsRects);
4245 bSubsLineRectsCreated = true;
4246 }
4247
4248 bool bSpecSubsLineRectsCreated;
4249 if ( gProp.pSSpecSubsLines )
4250 {
4251 // Lock already existing special subsidiary lines
4252 gProp.pSSpecSubsLines->LockLines( true );
4253 bSpecSubsLineRectsCreated = false;
4254 }
4255 else
4256 {
4257 // create new special subsidiary lines
4258 gProp.pSSpecSubsLines.reset(new SwSubsRects);
4259 bSpecSubsLineRectsCreated = true;
4260 }
4261 // Add subsidiary lines of fly frame and its lowers
4262 RefreshLaySubsidiary( pPage, aRect );
4263 // paint subsidiary lines of fly frame and its lowers
4264 gProp.pSSpecSubsLines->PaintSubsidiary( &rRenderContext, nullptr, gProp );
4265 gProp.pSSubsLines->PaintSubsidiary(&rRenderContext, gProp.pSLines.get(), gProp);
4266 if ( !bSubsLineRectsCreated )
4267 // unlock subsidiary lines
4268 gProp.pSSubsLines->LockLines( false );
4269 else
4270 {
4271 // delete created subsidiary lines container
4272 gProp.pSSubsLines.reset();
4273 }
4274
4275 if ( !bSpecSubsLineRectsCreated )
4276 // unlock special subsidiary lines
4277 gProp.pSSpecSubsLines->LockLines( false );
4278 else
4279 {
4280 // delete created special subsidiary lines container
4281 gProp.pSSpecSubsLines.reset();
4282 }
4283 }
4284
4285 SwLayoutFrame::PaintSwFrame( rRenderContext, aRect );
4286
4287 Validate();
4288
4289 {
4290 SwTaggedPDFHelper tag(nullptr, nullptr, nullptr, rRenderContext);
4291 // first paint lines added by fly frame paint
4292 // and then unlock other lines.
4293 gProp.pSLines->PaintLines( &rRenderContext, gProp );
4294 gProp.pSLines->LockLines( false );
4295 // have to paint frame borders added in heaven layer here...
4296 ProcessPrimitives(gProp.pBLines->GetBorderLines_Clear());
4297 }
4298
4300
4301 // crossing out for tracked deletion
4302 if ( GetAuthor() != std::string::npos && IsDeleted() )
4303 {
4304 tools::Long startX = aRect.Left( ), endX = aRect.Right();
4305 tools::Long startY = aRect.Top( ), endY = aRect.Bottom();
4307 rRenderContext.DrawLine(Point(startX, startY), Point(endX, endY));
4308 rRenderContext.DrawLine(Point(startX, endY), Point(endX, startY));
4309 }
4310
4311 rRenderContext.Pop();
4312
4313 if ( gProp.pSProgress && pNoText )
4315}
4316
4318{
4319 // Show the un-float button
4320 SwWrtShell* pWrtSh = dynamic_cast< SwWrtShell* >( gProp.pSGlobalShell );
4321 if ( pWrtSh )
4322 {
4324 }
4325}
4326
4328{
4329 SwWrtShell* pWrtSh = dynamic_cast<SwWrtShell*>(gProp.pSGlobalShell);
4330 if (pWrtSh && pWrtSh->GetViewOptions()->IsShowOutlineContentVisibilityButton())
4332}
4333
4334
4335void SwTabFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const& rRect, SwPrintData const*const) const
4336{
4337 const SwViewOption* pViewOption = gProp.pSGlobalShell->GetViewOptions();
4338 if (pViewOption->IsTable())
4339 {
4340 // #i29550#
4341 if ( IsCollapsingBorders() )
4342 {
4343 SwBorderAttrAccess aAccess( SwFrame::GetCache(), static_cast<SwFrame const *>(this) );
4344 const SwBorderAttrs &rAttrs = *aAccess.Get();
4345
4346 // paint shadow
4347 if ( rAttrs.GetShadow().GetLocation() != SvxShadowLocation::NONE )
4348 {
4349 SwRect aRect;
4350 ::lcl_CalcBorderRect( aRect, this, rAttrs, true, gProp );
4351 PaintShadow( rRect, aRect, rAttrs );
4352 }
4353
4354 SwTabFramePainter aHelper(*this);
4355 aHelper.PaintLines(rRenderContext, rRect);
4356 }
4357
4358 SwLayoutFrame::PaintSwFrame( rRenderContext, rRect );
4359 }
4360 // #i6467# - no light grey rectangle for page preview
4361 else if ( gProp.pSGlobalShell->GetWin() && !gProp.pSGlobalShell->IsPreview() )
4362 {
4363 // #i6467# - intersect output rectangle with table frame
4364 SwRect aTabRect( getFramePrintArea() );
4365 aTabRect.Pos() += getFrameArea().Pos();
4366 SwRect aTabOutRect( rRect );
4367 aTabOutRect.Intersection( aTabRect );
4368 SwViewOption::DrawRect( &rRenderContext, aTabOutRect, COL_LIGHTGRAY );
4369 }
4370 const_cast<SwTabFrame*>(this)->ResetComplete();
4371}
4372
4386static void lcl_PaintShadow( const SwRect& rRect, SwRect& rOutRect,
4387 const SvxShadowItem& rShadow, const bool bDrawFullShadowRectangle,
4388 const bool bTop, const bool bBottom,
4389 const bool bLeft, const bool bRight,
4390 SwPaintProperties const & properties)
4391{
4392 const tools::Long nWidth = ::lcl_AlignWidth ( rShadow.GetWidth(), properties );
4393 const tools::Long nHeight = ::lcl_AlignHeight( rShadow.GetWidth(), properties );
4394
4395 SwRects aRegion;
4396 SwRect aOut( rOutRect );
4397
4398 switch ( rShadow.GetLocation() )
4399 {
4400 case SvxShadowLocation::BottomRight:
4401 {
4402 if ( bDrawFullShadowRectangle )
4403 {
4404 // draw full shadow rectangle
4405 aOut.Top( rOutRect.Top() + nHeight );
4406 aOut.Left( rOutRect.Left() + nWidth );
4407 aRegion.push_back( aOut );
4408 }
4409 else
4410 {
4411 if( bBottom )
4412 {
4413 aOut.Top( rOutRect.Bottom() - nHeight );
4414 if( bLeft )
4415 aOut.Left( rOutRect.Left() + nWidth );
4416 aRegion.push_back( aOut );
4417 }
4418 if( bRight )
4419 {
4420 aOut.Left( rOutRect.Right() - nWidth );
4421 if( bTop )
4422 aOut.Top( rOutRect.Top() + nHeight );
4423 else
4424 aOut.Top( rOutRect.Top() );
4425 if( bBottom )
4426 aOut.Bottom( rOutRect.Bottom() - nHeight );
4427 aRegion.push_back( aOut );
4428 }
4429 }
4430
4431 if( bRight )
4432 rOutRect.AddRight(- nWidth );
4433 if( bBottom )
4434 rOutRect.AddBottom(- nHeight );
4435 }
4436 break;
4437 case SvxShadowLocation::TopLeft:
4438 {
4439 if ( bDrawFullShadowRectangle )
4440 {
4441 // draw full shadow rectangle
4442 aOut.Bottom( rOutRect.Bottom() - nHeight );
4443 aOut.Right( rOutRect.Right() - nWidth );
4444 aRegion.push_back( aOut );
4445 }
4446 else
4447 {
4448 if( bTop )
4449 {
4450 aOut.Bottom( rOutRect.Top() + nHeight );
4451 if( bRight )
4452 aOut.Right( rOutRect.Right() - nWidth );
4453 aRegion.push_back( aOut );
4454 }
4455 if( bLeft )
4456 {
4457 aOut.Right( rOutRect.Left() + nWidth );
4458 if( bBottom )
4459 aOut.Bottom( rOutRect.Bottom() - nHeight );
4460 else
4461 aOut.Bottom( rOutRect.Bottom() );
4462 if( bTop )
4463 aOut.Top( rOutRect.Top() + nHeight );
4464 aRegion.push_back( aOut );
4465 }
4466 }
4467
4468 if( bLeft )
4469 rOutRect.AddLeft( nWidth );
4470 if( bTop )
4471 rOutRect.AddTop( nHeight );
4472 }
4473 break;
4474 case SvxShadowLocation::TopRight:
4475 {
4476 if ( bDrawFullShadowRectangle )
4477 {
4478 // draw full shadow rectangle
4479 aOut.Bottom( rOutRect.Bottom() - nHeight);
4480 aOut.Left( rOutRect.Left() + nWidth );
4481 aRegion.push_back( aOut );
4482 }
4483 else
4484 {
4485 if( bTop )
4486 {
4487 aOut.Bottom( rOutRect.Top() + nHeight );
4488 if( bLeft )
4489 aOut.Left( rOutRect.Left() + nWidth );
4490 aRegion.push_back( aOut );
4491 }
4492 if( bRight )
4493 {
4494 aOut.Left( rOutRect.Right() - nWidth );
4495 if( bBottom )
4496 aOut.Bottom( rOutRect.Bottom() - nHeight );
4497 else
4498 aOut.Bottom( rOutRect.Bottom() );
4499 if( bTop )
4500 aOut.Top( rOutRect.Top() + nHeight );
4501 aRegion.push_back( aOut );
4502 }
4503 }
4504
4505 if( bRight )
4506 rOutRect.AddRight( - nWidth );
4507 if( bTop )
4508 rOutRect.AddTop( nHeight );
4509 }
4510 break;
4511 case SvxShadowLocation::BottomLeft:
4512 {
4513 if ( bDrawFullShadowRectangle )
4514 {
4515 // draw full shadow rectangle
4516 aOut.Top( rOutRect.Top() + nHeight );
4517 aOut.Right( rOutRect.Right() - nWidth );
4518 aRegion.push_back( aOut );
4519 }
4520 else
4521 {
4522 if( bBottom )
4523 {
4524 aOut.Top( rOutRect.Bottom()- nHeight );
4525 if( bRight )
4526 aOut.Right( rOutRect.Right() - nWidth );
4527 aRegion.push_back( aOut );
4528 }
4529 if( bLeft )
4530 {
4531 aOut.Right( rOutRect.Left() + nWidth );
4532 if( bTop )
4533 aOut.Top( rOutRect.Top() + nHeight );
4534 else
4535 aOut.Top( rOutRect.Top() );
4536 if( bBottom )
4537 aOut.Bottom( rOutRect.Bottom() - nHeight );
4538 aRegion.push_back( aOut );
4539 }
4540 }
4541
4542 if( bLeft )
4543 rOutRect.AddLeft( nWidth );
4544 if( bBottom )
4545 rOutRect.AddBottom( - nHeight );
4546 }
4547 break;
4548 default:
4549 assert(false);
4550 break;
4551 }
4552
4553 vcl::RenderContext *pOut = properties.pSGlobalShell->GetOut();
4554
4555 DrawModeFlags nOldDrawMode = pOut->GetDrawMode();
4556 Color aShadowColor( rShadow.GetColor().GetRGBColor() );
4557 if( !aRegion.empty() && properties.pSGlobalShell->GetWin() &&
4559 {
4560 // In high contrast mode, the output device has already set the
4561 // DrawModeFlags::SettingsFill flag. This causes the SetFillColor function
4562 // to ignore the setting of a new color. Therefore we have to reset
4563 // the drawing mode
4564 pOut->SetDrawMode( DrawModeFlags::Default );
4565 aShadowColor = SwViewOption::GetFontColor();
4566 }
4567
4568 if ( pOut->GetFillColor() != aShadowColor )
4569 pOut->SetFillColor( aShadowColor );
4570
4571 pOut->SetLineColor();
4572
4573 pOut->SetDrawMode( nOldDrawMode );
4574
4575 for (const SwRect & rOut : aRegion)
4576 {
4577 aOut = rOut;
4578 if ( rRect.Overlaps( aOut ) && aOut.Height() > 0 && aOut.Width() > 0 )
4579 {
4580 aOut.Intersection_( rRect );
4581 pOut->DrawRect( aOut.SVRect() );
4582 }
4583 }
4584}
4585
4595void SwFrame::PaintShadow( const SwRect& rRect, SwRect& rOutRect,
4596 const SwBorderAttrs &rAttrs ) const
4597{
4598 SvxShadowItem rShadow = rAttrs.GetShadow();
4599
4600 const bool bCnt = IsContentFrame();
4601 const bool bTop = !bCnt || rAttrs.GetTopLine ( *(this) );
4602 const bool bBottom = !bCnt || rAttrs.GetBottomLine( *(this) );
4603
4604 if( IsVertical() )
4605 {
4606 switch( rShadow.GetLocation() )
4607 {
4608 case SvxShadowLocation::BottomRight: rShadow.SetLocation(SvxShadowLocation::BottomLeft); break;
4609 case SvxShadowLocation::TopLeft: rShadow.SetLocation(SvxShadowLocation::TopRight); break;
4610 case SvxShadowLocation::TopRight: rShadow.SetLocation(SvxShadowLocation::BottomRight); break;
4611 case SvxShadowLocation::BottomLeft: rShadow.SetLocation(SvxShadowLocation::TopLeft); break;
4612 default: break;
4613 }
4614 }
4615
4616 // determine, if full shadow rectangle have to be drawn or only two shadow rectangles beside the frame.
4617 // draw full shadow rectangle, if frame background is drawn transparent.
4618 // Status Quo:
4619 // SwLayoutFrame can have transparent drawn backgrounds. Thus,
4620 // "asked" their frame format.
4621 const bool bDrawFullShadowRectangle =
4622 ( IsLayoutFrame() &&
4623 static_cast<const SwLayoutFrame*>(this)->GetFormat()->IsBackgroundTransparent()
4624 );
4625
4626 SwRectFnSet aRectFnSet(this);
4627 ::lcl_ExtendLeftAndRight( rOutRect, *(this), rAttrs, aRectFnSet.FnRect() );
4628
4629 lcl_PaintShadow(rRect, rOutRect, rShadow, bDrawFullShadowRectangle, bTop, bBottom, true, true, gProp);
4630}
4631
4633 const SwRect& rOutRect,
4634 const SwPageFrame * pPage,
4635 const Color *pColor,
4636 const SvxBorderLineStyle nStyle ) const
4637{
4638 if ( !rOutRect.Overlaps( rRect ) )
4639 return;
4640
4641 SwRect aOut( rOutRect );
4642 aOut.Intersection_( rRect );
4643
4644 const SwTabFrame *pTab = IsCellFrame() ? FindTabFrame() : nullptr;
4645 SubColFlags nSubCol = ( IsCellFrame() || IsRowFrame() )
4646 ? SubColFlags::Tab
4647 : ( IsInSct()
4648 ? SubColFlags::Sect
4649 : ( IsInFly() ? SubColFlags::Fly : SubColFlags::Page ) );
4650 if( pColor && gProp.pSGlobalShell->GetWin() &&
4652 {
4653 pColor = &SwViewOption::GetFontColor();
4654 }
4655
4656 if (pPage->GetSortedObjs() &&
4658 {
4659 SwRegionRects aRegion( aOut, 4 );
4661 ::lcl_SubtractFlys( this, pPage, aOut, aRegion, aClipState, gProp );
4662 for ( size_t i = 0; i < aRegion.size(); ++i )
4663 gProp.pSLines->AddLineRect( aRegion[i], pColor, nStyle, pTab, nSubCol, gProp );
4664 }
4665 else
4666 gProp.pSLines->AddLineRect( aOut, pColor, nStyle, pTab, nSubCol, gProp );
4667}
4668
4670{
4671 namespace {
4672
4673 class SwBorderRectanglePrimitive2D : public BufferedDecompositionPrimitive2D
4674 {
4675 private:
4678
4684
4685 protected:
4687 virtual void create2DDecomposition(
4688 Primitive2DContainer& rContainer,
4689 const geometry::ViewInformation2D& rViewInformation) const override;
4690
4691 public:
4693 SwBorderRectanglePrimitive2D(
4694 basegfx::B2DHomMatrix aB2DHomMatrix,
4695 const svx::frame::Style& rStyleTop,
4696 const svx::frame::Style& rStyleRight,
4697 const svx::frame::Style& rStyleBottom,
4698 const svx::frame::Style& rStyleLeft);
4699
4701 const basegfx::B2DHomMatrix& getB2DHomMatrix() const { return maB2DHomMatrix; }
4702 const svx::frame::Style& getStyleTop() const { return maStyleTop; }
4703 const svx::frame::Style& getStyleRight() const { return maStyleRight; }
4704 const svx::frame::Style& getStyleBottom() const { return maStyleBottom; }
4705 const svx::frame::Style& getStyleLeft() const { return maStyleLeft; }
4706
4708 virtual bool operator==(const BasePrimitive2D& rPrimitive) const override;
4709
4711 virtual basegfx::B2DRange getB2DRange(const geometry::ViewInformation2D& rViewInformation) const override;
4712
4714 virtual sal_uInt32 getPrimitive2DID() const override;
4715 };
4716
4717 }
4718
4719 void SwBorderRectanglePrimitive2D::create2DDecomposition(
4720 Primitive2DContainer& rContainer,
4721 const geometry::ViewInformation2D& /*rViewInformation*/) const
4722 {
4723 basegfx::B2DPoint aTopLeft(getB2DHomMatrix() * basegfx::B2DPoint(0.0, 0.0));
4724 basegfx::B2DPoint aTopRight(getB2DHomMatrix() * basegfx::B2DPoint(1.0, 0.0));
4725 basegfx::B2DPoint aBottomLeft(getB2DHomMatrix() * basegfx::B2DPoint(0.0, 1.0));
4726 basegfx::B2DPoint aBottomRight(getB2DHomMatrix() * basegfx::B2DPoint(1.0, 1.0));
4727
4728 // prepare SdrFrameBorderDataVector
4729 std::shared_ptr<drawinglayer::primitive2d::SdrFrameBorderDataVector> aData(
4730 std::make_shared<drawinglayer::primitive2d::SdrFrameBorderDataVector>());
4731
4732 if(getStyleTop().IsUsed())
4733 {
4734 // move top left/right inwards half border width
4735 basegfx::B2DVector aDown(getB2DHomMatrix() * basegfx::B2DVector(0.0, 1.0));
4736 aDown.setLength(getStyleTop().GetWidth() * 0.5);
4737 aTopLeft += aDown;
4738 aTopRight += aDown;
4739 }
4740
4741 if(getStyleBottom().IsUsed())
4742 {
4743 // move bottom left/right inwards half border width
4744 basegfx::B2DVector aUp(getB2DHomMatrix() * basegfx::B2DVector(0.0, -1.0));
4745 aUp.setLength(getStyleBottom().GetWidth() * 0.5);
4746 aBottomLeft += aUp;
4747 aBottomRight += aUp;
4748 }
4749
4750 if(getStyleLeft().IsUsed())
4751 {
4752 // move left top/bottom inwards half border width
4753 basegfx::B2DVector aRight(getB2DHomMatrix() * basegfx::B2DVector(1.0, 0.0));
4754 aRight.setLength(getStyleLeft().GetWidth() * 0.5);
4755 aTopLeft += aRight;
4756 aBottomLeft += aRight;
4757 }
4758
4759 if(getStyleRight().IsUsed())
4760 {
4761 // move right top/bottom inwards half border width
4762 basegfx::B2DVector aLeft(getB2DHomMatrix() * basegfx::B2DVector(-1.0, 0.0));
4763 aLeft.setLength(getStyleRight().GetWidth() * 0.5);
4764 aTopRight += aLeft;
4765 aBottomRight += aLeft;
4766 }
4767
4768 // go round-robin, from TopLeft to TopRight, down, left and back up. That
4769 // way, the borders will not need to be mirrored in any way
4770 if(getStyleTop().IsUsed())
4771 {
4772 // create BorderPrimitive(s) for top border
4773 const basegfx::B2DVector aVector(aTopRight - aTopLeft);
4774 aData->emplace_back(
4775 aTopLeft,
4776 aVector,
4777 getStyleTop(),
4778 nullptr);
4780
4781 if(getStyleLeft().IsUsed())
4782 {
4783 rInstance.addSdrConnectStyleData(true, getStyleLeft(), basegfx::B2DVector(aBottomLeft - aTopLeft), false);
4784 }
4785
4786 if(getStyleRight().IsUsed())
4787 {
4788 rInstance.addSdrConnectStyleData(false, getStyleRight(), basegfx::B2DVector(aBottomRight - aTopRight), false);
4789 }
4790 }
4791
4792 if(getStyleRight().IsUsed())
4793 {
4794 // create BorderPrimitive(s) for right border
4795 const basegfx::B2DVector aVector(aBottomRight - aTopRight);
4796 aData->emplace_back(
4797 aTopRight,
4798 aVector,
4799 getStyleRight(),
4800 nullptr);
4802
4803 if(getStyleTop().IsUsed())
4804 {
4805 rInstance.addSdrConnectStyleData(true, getStyleTop(), basegfx::B2DVector(aTopLeft - aTopRight), false);
4806 }
4807
4808 if(getStyleBottom().IsUsed())
4809 {
4810 rInstance.addSdrConnectStyleData(false, getStyleBottom(), basegfx::B2DVector(aBottomLeft - aBottomRight), false);
4811 }
4812 }
4813
4814 if(getStyleBottom().IsUsed())
4815 {
4816 // create BorderPrimitive(s) for bottom border
4817 const basegfx::B2DVector aVector(aBottomLeft - aBottomRight);
4818 aData->emplace_back(
4819 aBottomRight,
4820 aVector,
4821 getStyleBottom(),
4822 nullptr);
4824
4825 if(getStyleRight().IsUsed())
4826 {
4827 rInstance.addSdrConnectStyleData(true, getStyleRight(), basegfx::B2DVector(aTopRight - aBottomRight), false);
4828 }
4829
4830 if(getStyleLeft().IsUsed())
4831 {
4832 rInstance.addSdrConnectStyleData(false, getStyleLeft(), basegfx::B2DVector(aTopLeft - aBottomLeft), false);
4833 }
4834 }
4835
4836 if(getStyleLeft().IsUsed())
4837 {
4838 // create BorderPrimitive(s) for left border
4839 const basegfx::B2DVector aVector(aTopLeft - aBottomLeft);
4840 aData->emplace_back(
4841 aBottomLeft,
4842 aVector,
4843 getStyleLeft(),
4844 nullptr);
4846
4847 if(getStyleBottom().IsUsed())
4848 {
4849 rInstance.addSdrConnectStyleData(true, getStyleBottom(), basegfx::B2DVector(aBottomRight - aBottomLeft), false);
4850 }
4851
4852 if(getStyleTop().IsUsed())
4853 {
4854 rInstance.addSdrConnectStyleData(false, getStyleTop(), basegfx::B2DVector(aTopRight - aTopLeft), false);
4855 }
4856 }
4857
4858 // create instance of SdrFrameBorderPrimitive2D if
4859 // SdrFrameBorderDataVector is used
4860 if(!aData->empty())
4861 {
4862 rContainer.append(
4865 aData,
4866 true))); // force visualization to minimal one discrete unit (pixel)
4867 }
4868 }
4869
4870 SwBorderRectanglePrimitive2D::SwBorderRectanglePrimitive2D(
4871 basegfx::B2DHomMatrix aB2DHomMatrix,
4872 const svx::frame::Style& rStyleTop,
4873 const svx::frame::Style& rStyleRight,
4874 const svx::frame::Style& rStyleBottom,
4875 const svx::frame::Style& rStyleLeft)
4876 : maB2DHomMatrix(std::move(aB2DHomMatrix)),
4877 maStyleTop(rStyleTop),
4878 maStyleRight(rStyleRight),
4879 maStyleBottom(rStyleBottom),
4880 maStyleLeft(rStyleLeft)
4881 {
4882 }
4883
4884 bool SwBorderRectanglePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
4885 {
4886 if(BasePrimitive2D::operator==(rPrimitive))
4887 {
4888 const SwBorderRectanglePrimitive2D& rCompare = static_cast<const SwBorderRectanglePrimitive2D&>(rPrimitive);
4889
4890 return (getB2DHomMatrix() == rCompare.getB2DHomMatrix() &&
4891 getStyleTop() == rCompare.getStyleTop() &&
4892 getStyleRight() == rCompare.getStyleRight() &&
4893 getStyleBottom() == rCompare.getStyleBottom() &&
4894 getStyleLeft() == rCompare.getStyleLeft());
4895 }
4896
4897 return false;
4898 }
4899
4900 basegfx::B2DRange SwBorderRectanglePrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
4901 {
4902 basegfx::B2DRange aRetval(0.0, 0.0, 1.0, 1.0);
4903
4904 aRetval.transform(getB2DHomMatrix());
4905 return aRetval;
4906 }
4907
4908 // provide unique ID
4909 sal_uInt32 SwBorderRectanglePrimitive2D::getPrimitive2DID() const
4910 {
4912 }
4913
4914} // end of namespace drawinglayer::primitive2d
4915
4916namespace {
4917
4918editeng::SvxBorderLine const * get_ptr(std::optional<editeng::SvxBorderLine> const & opt) {
4919 return opt ? &*opt : nullptr;
4920}
4921
4922}
4923
4925 const SwFont& rFont,
4926 const SwRect& rPaintArea,
4927 const bool bVerticalLayout,
4928 const bool bVerticalLayoutLRBT,
4929 const bool bJoinWithPrev,
4930 const bool bJoinWithNext )
4931{
4932 SwRect aAlignedRect(rPaintArea);
4933 SwAlignRect(aAlignedRect, gProp.pSGlobalShell, gProp.pSGlobalShell->GetOut());
4934
4935 bool bTop = true;
4936 bool bBottom = true;
4937 bool bLeft = true;
4938 bool bRight = true;
4939
4940 switch (rFont.GetOrientation(bVerticalLayout, bVerticalLayoutLRBT).get())
4941 {
4942 case 0 :
4943 bLeft = !bJoinWithPrev;
4944 bRight = !bJoinWithNext;
4945 break;
4946 case 900 :
4947 bBottom = !bJoinWithPrev;
4948 bTop = !bJoinWithNext;
4949 break;
4950 case 1800 :
4951 bRight = !bJoinWithPrev;
4952 bLeft = !bJoinWithNext;
4953 break;
4954 case 2700 :
4955 bTop = !bJoinWithPrev;
4956 bBottom = !bJoinWithNext;
4957 break;
4958 }
4959
4960 // Paint shadow (reduce painting rect)
4961 {
4962 const SvxShadowItem aShadow(
4963 0, &rFont.GetShadowColor(), rFont.GetShadowWidth(),
4964 rFont.GetAbsShadowLocation(bVerticalLayout, bVerticalLayoutLRBT));
4965
4966 if( aShadow.GetLocation() != SvxShadowLocation::NONE )
4967 {
4968 lcl_PaintShadow( rPaintArea, aAlignedRect, aShadow,
4969 false, bTop, bBottom, bLeft, bRight, gProp);
4970 }
4971 }
4972
4973 const basegfx::B2DHomMatrix aBorderTransform(
4975 aAlignedRect.Width(), aAlignedRect.Height(),
4976 aAlignedRect.Left(), aAlignedRect.Top()));
4977 const svx::frame::Style aStyleTop(
4978 bTop ? get_ptr(rFont.GetAbsTopBorder(bVerticalLayout, bVerticalLayoutLRBT)) : nullptr,
4979 1.0);
4980 const svx::frame::Style aStyleRight(
4981 bRight ? get_ptr(rFont.GetAbsRightBorder(bVerticalLayout, bVerticalLayoutLRBT)) : nullptr,
4982 1.0);
4983 const svx::frame::Style aStyleBottom(
4984 bBottom ? get_ptr(rFont.GetAbsBottomBorder(bVerticalLayout, bVerticalLayoutLRBT))
4985 : nullptr,
4986 1.0);
4987 const svx::frame::Style aStyleLeft(
4988 bLeft ? get_ptr(rFont.GetAbsLeftBorder(bVerticalLayout, bVerticalLayoutLRBT)) : nullptr,
4989 1.0);
4991
4992 aBorderLineTarget.append(
4994 new drawinglayer::primitive2d::SwBorderRectanglePrimitive2D(
4995 aBorderTransform,
4996 aStyleTop,
4997 aStyleRight,
4998 aStyleBottom,
4999 aStyleLeft)));
5000 gProp.pBLines->AddBorderLines(std::move(aBorderLineTarget));
5001}
5002
5004static const SwFrame* lcl_HasNextCell( const SwFrame& rFrame )
5005{
5006 OSL_ENSURE( rFrame.IsCellFrame(),
5007 "lcl_HasNextCell( const SwFrame& rFrame ) should be called with SwCellFrame" );
5008
5009 const SwFrame* pTmpFrame = &rFrame;
5010 do
5011 {
5012 if ( pTmpFrame->GetNext() )
5013 return pTmpFrame->GetNext();
5014
5015 pTmpFrame = pTmpFrame->GetUpper()->GetUpper();
5016 }
5017 while ( pTmpFrame->IsCellFrame() );
5018
5019 return nullptr;
5020}
5021
5043static const SwFrame* lcl_GetCellFrameForBorderAttrs( const SwFrame* _pCellFrame,
5044 const SwBorderAttrs& _rCellBorderAttrs,
5045 const bool _bTop )
5046{
5047 OSL_ENSURE( _pCellFrame, "No cell frame available, dying soon" );
5048
5049 // determine, if cell frame is at bottom/top border of a table frame and
5050 // the table frame has/is a follow.
5051 const SwFrame* pTmpFrame = _pCellFrame;
5052 bool bCellAtBorder = true;
5053 bool bCellAtLeftBorder = !_pCellFrame->GetPrev();
5054 bool bCellAtRightBorder = !_pCellFrame->GetNext();
5055 while( !pTmpFrame->IsRowFrame() || !pTmpFrame->GetUpper()->IsTabFrame() )
5056 {
5057 pTmpFrame = pTmpFrame->GetUpper();
5058 if ( pTmpFrame->IsRowFrame() &&
5059 (_bTop ? pTmpFrame->GetPrev() : pTmpFrame->GetNext())
5060 )
5061 {
5062 bCellAtBorder = false;
5063 }
5064 if ( pTmpFrame->IsCellFrame() )
5065 {
5066 if ( pTmpFrame->GetPrev() )
5067 {
5068 bCellAtLeftBorder = false;
5069 }
5070 if ( pTmpFrame->GetNext() )
5071 {
5072 bCellAtRightBorder = false;
5073 }
5074 }
5075 }
5076 OSL_ENSURE( pTmpFrame && pTmpFrame->IsRowFrame(), "No RowFrame available" );
5077
5078 const SwLayoutFrame* pParentRowFrame = static_cast<const SwLayoutFrame*>(pTmpFrame);
5079 const SwTabFrame* pParentTabFrame =
5080 static_cast<const SwTabFrame*>(pParentRowFrame->GetUpper());
5081
5082 const bool bCellNeedsAttribute = bCellAtBorder &&
5083 ( _bTop ?
5084 // bCellInFirstRowWithMaster
5085 ( !pParentRowFrame->GetPrev() &&
5086 pParentTabFrame->IsFollow() &&
5087 0 == pParentTabFrame->GetTable()->GetRowsToRepeat() ) :
5088 // bCellInLastRowWithFollow
5089 ( !pParentRowFrame->GetNext() &&
5090 pParentTabFrame->GetFollow() )
5091 );
5092
5093 const SwFrame* pRet = _pCellFrame;
5094 if ( bCellNeedsAttribute )
5095 {
5096 // determine, if cell frame has no borders inside the table.
5097 const SwFrame* pNextCell = nullptr;
5098 bool bNoBordersInside = false;
5099
5100 if ( bCellAtLeftBorder && ( nullptr != ( pNextCell = lcl_HasNextCell( *_pCellFrame ) ) ) )
5101 {
5102 SwBorderAttrAccess aAccess( SwFrame::GetCache(), pNextCell );
5103 const SwBorderAttrs &rBorderAttrs = *aAccess.Get();
5104 const SvxBoxItem& rBorderBox = rBorderAttrs.GetBox();
5105 bCellAtRightBorder = !lcl_HasNextCell( *pNextCell );
5106 bNoBordersInside =
5107 ( !rBorderBox.GetTop() || !pParentRowFrame->GetPrev() ) &&
5108 !rBorderBox.GetLeft() &&
5109 ( !rBorderBox.GetRight() || bCellAtRightBorder ) &&
5110 ( !rBorderBox.GetBottom() || !pParentRowFrame->GetNext() );
5111 }
5112 else
5113 {
5114 const SvxBoxItem& rBorderBox = _rCellBorderAttrs.GetBox();
5115 bNoBordersInside =
5116 ( !rBorderBox.GetTop() || !pParentRowFrame->GetPrev() ) &&
5117 ( !rBorderBox.GetLeft() || bCellAtLeftBorder ) &&
5118 ( !rBorderBox.GetRight() || bCellAtRightBorder ) &&
5119 ( !rBorderBox.GetBottom() || !pParentRowFrame->GetNext() );
5120 }
5121
5122 if ( bNoBordersInside )
5123 {
5124 if ( _bTop && !_rCellBorderAttrs.GetBox().GetTop() )
5125 {
5126 //-hack
5127 // Cell frame has no top border and no border inside the table, but
5128 // it is at the top border of a table frame, which is a follow.
5129 // Thus, use border attributes of cell frame in first row of complete table.
5130 // First, determine first table frame of complete table.
5131 SwTabFrame* pMasterTabFrame = pParentTabFrame->FindMaster( true );
5132 // determine first row of complete table.
5133 const SwFrame* pFirstRow = pMasterTabFrame->GetLower();
5134 // return first cell in first row
5135 SwFrame* pLowerCell = const_cast<SwFrame*>(pFirstRow->GetLower());
5136 while ( !pLowerCell->IsCellFrame() ||
5137 ( pLowerCell->GetLower() && pLowerCell->GetLower()->IsRowFrame() )
5138 )
5139 {
5140 pLowerCell = pLowerCell->GetLower();
5141 }
5142 OSL_ENSURE( pLowerCell && pLowerCell->IsCellFrame(), "No CellFrame available" );
5143 pRet = pLowerCell;
5144 }
5145 else if ( !_bTop && !_rCellBorderAttrs.GetBox().GetBottom() )
5146 {
5147 //-hack
5148 // Cell frame has no bottom border and no border inside the table,
5149 // but it is at the bottom border of a table frame, which has a follow.
5150 // Thus, use border attributes of cell frame in last row of complete table.
5151 // First, determine last table frame of complete table.
5152 SwTabFrame* pLastTabFrame = const_cast<SwTabFrame*>(pParentTabFrame->GetFollow());
5153 while ( pLastTabFrame->GetFollow() )
5154 {
5155 pLastTabFrame = pLastTabFrame->GetFollow();
5156 }
5157 // determine last row of complete table.
5158 SwFrame* pLastRow = pLastTabFrame->GetLastLower();
5159 // return first bottom border cell in last row
5160 SwFrame* pLowerCell = pLastRow->GetLower();
5161 while ( !pLowerCell->IsCellFrame() ||
5162 ( pLowerCell->GetLower() && pLowerCell->GetLower()->IsRowFrame() )
5163 )
5164 {
5165 if ( pLowerCell->IsRowFrame() )
5166 {
5167 while ( pLowerCell->GetNext() )
5168 {
5169 pLowerCell = pLowerCell->GetNext();
5170 }
5171 }
5172 pLowerCell = pLowerCell->GetLower();
5173 }
5174 OSL_ENSURE( pLowerCell && pLowerCell->IsCellFrame(), "No CellFrame available" );
5175 pRet = pLowerCell;
5176 }
5177 }
5178 }
5179
5180 return pRet;
5181}
5182
5183std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> SwFrame::CreateProcessor2D( ) const
5184{
5185 basegfx::B2DRange aViewRange;
5186
5187 SdrPage *pDrawPage = getRootFrame()->GetCurrShell()->Imp()->GetPageView()->GetPage();
5189 aNewViewInfos.setViewTransformation(getRootFrame()->GetCurrShell()->GetOut()->GetViewTransformation());
5190 aNewViewInfos.setViewport(aViewRange);
5191 aNewViewInfos.setVisualizedPage(GetXDrawPageForSdrPage( pDrawPage ));
5192
5194 *getRootFrame()->GetCurrShell()->GetOut(),
5195 aNewViewInfos );
5196}
5197
5199{
5200 std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor2D = CreateProcessor2D();
5201 if ( pProcessor2D )
5202 {
5203 pProcessor2D->process( rSequence );
5204 }
5205}
5206
5209 const SwRect& rRect,
5210 const SwPageFrame* /*pPage*/,
5211 const SwBorderAttrs& rAttrs) const
5212{
5213 // There's nothing (Row,Body,Footnote,Root,Column,NoText) need to do here
5215 return;
5216
5217 if (IsCellFrame() && !gProp.pSGlobalShell->GetViewOptions()->IsTable())
5218 return;
5219
5220 // #i29550#
5221 if ( IsTabFrame() || IsCellFrame() || IsRowFrame() )
5222 {
5223 const SwTabFrame* pTabFrame = FindTabFrame();
5224 if ( pTabFrame->IsCollapsingBorders() )
5225 return;
5226
5227 if ( pTabFrame->GetTable()->IsNewModel() && ( !IsCellFrame() || IsCoveredCell() ) )
5228 return;
5229 }
5230
5231 const bool bLine = rAttrs.IsLine();
5232 const bool bShadow = rAttrs.GetShadow().GetLocation() != SvxShadowLocation::NONE;
5233
5234 // - flag to control,
5235 //-hack has to be used.
5236 const bool bb4779636HackActive = true;
5237
5238 const SwFrame* pCellFrameForBottomBorderAttrs = nullptr;
5239 const SwFrame* pCellFrameForTopBorderAttrs = nullptr;
5240 bool bFoundCellForTopOrBorderAttrs = false;
5241 if ( bb4779636HackActive && IsCellFrame() )
5242 {
5243 pCellFrameForBottomBorderAttrs = lcl_GetCellFrameForBorderAttrs( this, rAttrs, false );
5244 if ( pCellFrameForBottomBorderAttrs != this )
5245 bFoundCellForTopOrBorderAttrs = true;
5246 pCellFrameForTopBorderAttrs = lcl_GetCellFrameForBorderAttrs( this, rAttrs, true );
5247 if ( pCellFrameForTopBorderAttrs != this )
5248 bFoundCellForTopOrBorderAttrs = true;
5249 }
5250
5251 // - add condition <bFoundCellForTopOrBorderAttrs>
5252 //-hack
5253 if ( !(bLine || bShadow || bFoundCellForTopOrBorderAttrs) )
5254 return;
5255
5256 //If the rectangle is completely inside the PrtArea, no border needs to
5257 //be painted.
5258 //For the PrtArea the aligned value needs to be used, otherwise it could
5259 //happen, that some parts won't be processed.
5260 SwRect aRect( getFramePrintArea() );
5261 aRect += getFrameArea().Pos();
5262 ::SwAlignRect( aRect, gProp.pSGlobalShell, gProp.pSGlobalShell->GetOut() );
5263 // new local boolean variable in order to
5264 // suspend border paint under special cases - see below.
5265 // NOTE: This is a fix for the implementation of feature #99657#.
5266 bool bDrawOnlyShadowForTransparentFrame = false;
5267 if ( aRect.Contains( rRect ) )
5268 {
5269 // paint shadow, if background is transparent.
5270 // Because of introduced transparent background for fly frame #99657#,
5271 // the shadow have to be drawn if the background is transparent,
5272 // in spite the fact that the paint rectangle <rRect> lies fully
5273 // in the printing area.
5274 // NOTE to chosen solution:
5275 // On transparent background, continue processing, but suspend
5276 // drawing of border by setting <bDrawOnlyShadowForTransparentFrame>
5277 // to true.
5278 if ( IsLayoutFrame() &&
5279 static_cast<const SwLayoutFrame*>(this)->GetFormat()->IsBackgroundTransparent() )
5280 {
5281 bDrawOnlyShadowForTransparentFrame = true;
5282 }
5283 else
5284 {
5285 return;
5286 }
5287 }
5288
5289 ::lcl_CalcBorderRect( aRect, this, rAttrs, true, gProp );
5290 rAttrs.SetGetCacheLine( true );
5291
5292 if(bShadow)
5293 {
5294 PaintShadow(rRect, aRect, rAttrs);
5295 }
5296
5297 // suspend drawing of border
5298 // add condition < NOT bDrawOnlyShadowForTransparentFrame > - see above
5299 // - add condition <bFoundCellForTopOrBorderAttrs>
5300 //-hack.
5301 if((bLine || bFoundCellForTopOrBorderAttrs) && !bDrawOnlyShadowForTransparentFrame)
5302 {
5303 // define SvxBorderLine(s) to use
5304 const SvxBoxItem& rBox(rAttrs.GetBox());
5305 const SvxBorderLine* pLeftBorder(rBox.GetLeft());
5306 const SvxBorderLine* pRightBorder(rBox.GetRight());
5307 const SvxBorderLine* pTopBorder(rBox.GetTop());
5308 const SvxBorderLine* pBottomBorder(rBox.GetBottom());
5309
5310 // if R2L, exchange Right/Left
5311 const bool bR2L(IsCellFrame() && IsRightToLeft());
5312
5313 if(bR2L)
5314 {
5315 std::swap(pLeftBorder, pRightBorder);
5316 }
5317
5318 // if ContentFrame and joined Prev/Next, reset top/bottom as needed
5319 if(IsContentFrame())
5320 {
5321 const SwFrame* pDirRefFrame(IsCellFrame() ? FindTabFrame() : this);
5322 const SwRectFnSet aRectFnSet(pDirRefFrame);
5323 const SwRectFn& _rRectFn(aRectFnSet.FnRect());
5324
5325 if(rAttrs.JoinedWithPrev(*this))
5326 {
5327 // tdf#115296 re-add adaptation of vert distance to close the evtl.
5328 // existing gap to previous frame
5329 const SwFrame* pPrevFrame(GetPrev());
5330 (aRect.*_rRectFn->fnSetTop)( (pPrevFrame->*_rRectFn->fnGetPrtBottom)() );
5331
5332 // ...and disable top border paint/creation
5333 pTopBorder = nullptr;
5334 }
5335
5336 if(rAttrs.JoinedWithNext(*this))
5337 {
5338 // tdf#115296 re-add adaptation of vert distance to close the evtl.
5339 // existing gap to next frame
5340 const SwFrame* pNextFrame(GetNext());
5341 (aRect.*_rRectFn->fnSetBottom)( (pNextFrame->*_rRectFn->fnGetPrtTop)() );
5342
5343 // ...and disable bottom border paint/creation
5344 pBottomBorder = nullptr;
5345 }
5346 }
5347
5348 // necessary to replace TopBorder?
5349 if((!IsContentFrame() || rAttrs.GetTopLine(*this)) && IsCellFrame() && pCellFrameForTopBorderAttrs != this)
5350 {
5351 SwBorderAttrAccess aAccess(SwFrame::GetCache(), pCellFrameForTopBorderAttrs);
5352 pTopBorder = aAccess.Get()->GetBox().GetTop();
5353 }
5354
5355 // necessary to replace BottomBorder?
5356 if((!IsContentFrame() || rAttrs.GetBottomLine(*this)) && IsCellFrame() && pCellFrameForBottomBorderAttrs != this)
5357 {
5358 SwBorderAttrAccess aAccess(SwFrame::GetCache(), pCellFrameForBottomBorderAttrs);
5359 pBottomBorder = aAccess.Get()->GetBox().GetBottom();
5360 }
5361
5362 bool bWordBorder = false;
5363 SwViewShell* pShell = getRootFrame()->GetCurrShell();
5364 if (pShell)
5365 {
5366 const IDocumentSettingAccess& rIDSA = pShell->GetDoc()->getIDocumentSettingAccess();
5367 bWordBorder = rIDSA.get(DocumentSettingId::TABLE_ROW_KEEP);
5368 }
5369 bool bInWordTableCell = IsContentFrame() && GetUpper()->IsCellFrame() && bWordBorder;
5370 if (bInWordTableCell)
5371 {
5372 // Compat mode: don't paint bottom border if we know the bottom of the content was cut
5373 // off.
5374 auto pContentFrame = static_cast<const SwContentFrame*>(this);
5375 if (pContentFrame->IsUndersized())
5376 {
5377 pBottomBorder = nullptr;
5378 }
5379 }
5380
5381 if(nullptr != pLeftBorder || nullptr != pRightBorder || nullptr != pTopBorder || nullptr != pBottomBorder)
5382 {
5383 // now we have all SvxBorderLine(s) sorted out, create geometry
5384 const basegfx::B2DHomMatrix aBorderTransform(
5386 aRect.Width(), aRect.Height(),
5387 aRect.Left(), aRect.Top()));
5388 const svx::frame::Style aStyleTop(pTopBorder, 1.0);
5389 svx::frame::Style aStyleRight(pRightBorder, 1.0);
5390
5391 // Right/bottom page borders are always mirrored in Word.
5392 if (IsPageFrame() && bWordBorder)
5393 {
5394 aStyleRight.MirrorSelf();
5395 }
5396
5397 svx::frame::Style aStyleBottom(pBottomBorder, 1.0);
5398
5399 if (IsPageFrame() && bWordBorder)
5400 {
5401 aStyleBottom.MirrorSelf();
5402 }
5403
5404 const svx::frame::Style aStyleLeft(pLeftBorder, 1.0);
5406
5408 new drawinglayer::primitive2d::SwBorderRectanglePrimitive2D(
5409 aBorderTransform,
5410 aStyleTop,
5411 aStyleRight,
5412 aStyleBottom,
5413 aStyleLeft));
5414
5415 if (bInWordTableCell)
5416 {
5417 // Compat mode: cut off the borders which are outside of our own area.
5418 const SwRect& rClip = getFrameArea();
5419 basegfx::B2DRectangle aClip(rClip.Left(), rClip.Top(), rClip.Right(),
5420 rClip.Bottom());
5421 basegfx::B2DPolyPolygon aPolyPolygon(
5424 new drawinglayer::primitive2d::MaskPrimitive2D(std::move(aPolyPolygon), { aRetval }));
5425 aRetval = xClipped;
5426 }
5427
5428 aBorderLineTarget.append(aRetval);
5429 gProp.pBLines->AddBorderLines(std::move(aBorderLineTarget));
5430 }
5431 }
5432
5433 rAttrs.SetGetCacheLine( false );
5434}
5435
5443 const SwRect& rRect,
5444 const SwPageFrame* pPage,
5445 const SwBorderAttrs&) const
5446{
5447 //If the rectangle is completely inside the PrtArea, no border needs to
5448 //be painted.
5449 SwRect aRect( getFramePrintArea() );
5450 aRect.Pos() += getFrameArea().Pos();
5451 if ( !aRect.Contains( rRect ) )
5452 PaintLine( rRect, pPage );
5453}
5454
5457 const SwPageFrame *pPage ) const
5458{
5459 //The length of the line is derived from the percentual indication on the
5460 //PageDesc. The position is also stated on the PageDesc.
5461 //The pen can directly be taken from the PageDesc.
5462
5463 if ( !pPage )
5464 pPage = FindPageFrame();
5465 const SwPageFootnoteInfo &rInf = pPage->GetPageDesc()->GetFootnoteInfo();
5466
5467 SwRectFnSet aRectFnSet(this);
5468 SwTwips nPrtWidth = aRectFnSet.GetWidth(getFramePrintArea());
5469 Fraction aFract( nPrtWidth, 1 );
5470 aFract *= rInf.GetWidth();
5471 const SwTwips nWidth = static_cast<tools::Long>(aFract);
5472
5473 SwTwips nX = aRectFnSet.GetPrtLeft(*this);
5474 switch ( rInf.GetAdj() )
5475 {
5476 case css::text::HorizontalAdjust_CENTER:
5477 nX += nPrtWidth/2 - nWidth/2; break;
5478 case css::text::HorizontalAdjust_RIGHT:
5479 nX += nPrtWidth - nWidth; break;
5480 case css::text::HorizontalAdjust_LEFT:
5481 /* do nothing */; break;
5482 default:
5483 SAL_WARN("sw.core", "New adjustment for footnote lines?");
5484 assert(false);
5485 }
5487 const SwRect aLineRect = aRectFnSet.IsVert() ?
5489 nX), Size( nLineWidth, nWidth ) )
5490 : SwRect( Point( nX, getFrameArea().Pos().Y() + rInf.GetTopDist() ),
5491 Size( nWidth, rInf.GetLineWidth()));
5492 if ( aLineRect.HasArea() && rInf.GetLineStyle() != SvxBorderLineStyle::NONE)
5493 PaintBorderLine( rRect, aLineRect , pPage, &rInf.GetLineColor(),
5494 rInf.GetLineStyle() );
5495}
5496
5498void SwLayoutFrame::PaintColLines( const SwRect &rRect, const SwFormatCol &rFormatCol,
5499 const SwPageFrame *pPage ) const