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