sc: factor out some more code
[LibreOffice.git] / sw / source / core / text / frmpaint.cxx
blob6d116cdbc15607274b2149251fda45eeac4ec689
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <memory>
21 #include <com/sun/star/text/HoriOrientation.hpp>
22 #include <editeng/pgrditem.hxx>
23 #include <editeng/lrspitem.hxx>
24 #include <tgrditem.hxx>
25 #include <paratr.hxx>
27 #include <fmtline.hxx>
28 #include <lineinfo.hxx>
29 #include <charfmt.hxx>
30 #include <rootfrm.hxx>
31 #include <pagefrm.hxx>
32 #include <viewsh.hxx>
33 #include <viewopt.hxx>
34 #include <frmatr.hxx>
35 #include <txtfrm.hxx>
36 #include "itrpaint.hxx"
37 #include "txtpaint.hxx"
38 #include "txtcache.hxx"
39 #include <flyfrm.hxx>
40 #include "redlnitr.hxx"
41 #include <redline.hxx>
42 #include <swmodule.hxx>
43 #include <tabfrm.hxx>
44 #include <numrule.hxx>
45 #include <wrong.hxx>
47 #include <EnhancedPDFExportHelper.hxx>
49 #include <IDocumentRedlineAccess.hxx>
50 #include <IDocumentStylePoolAccess.hxx>
52 #define REDLINE_DISTANCE 567/4
53 #define REDLINE_MINDIST 567/10
55 using namespace ::com::sun::star;
57 static bool bInitFont = true;
59 namespace {
61 class SwExtraPainter
63 SwSaveClip m_aClip;
64 SwRect m_aRect;
65 const SwTextFrame* m_pTextFrame;
66 SwViewShell *m_pSh;
67 std::unique_ptr<SwFont> m_pFnt;
68 const SwLineNumberInfo &m_rLineInf;
69 SwTwips m_nX;
70 SwTwips m_nRedX;
71 sal_Int32 m_nLineNr;
72 sal_uInt16 m_nDivider;
73 bool m_bGoLeft;
74 bool IsClipChg() const { return m_aClip.IsChg(); }
76 SwExtraPainter(const SwExtraPainter&) = delete;
77 SwExtraPainter& operator=(const SwExtraPainter&) = delete;
79 public:
80 SwExtraPainter( const SwTextFrame *pFrame, SwViewShell *pVwSh,
81 const SwLineNumberInfo &rLnInf, const SwRect &rRct,
82 sal_Int16 eHor, bool bLnNm );
83 SwFont* GetFont() const { return m_pFnt.get(); }
84 void IncLineNr() { ++m_nLineNr; }
85 bool HasNumber() const {
86 assert( m_rLineInf.GetCountBy() != 0 );
87 if( m_rLineInf.GetCountBy() == 0 )
88 return false;
89 return !( m_nLineNr % static_cast<sal_Int32>(m_rLineInf.GetCountBy()) );
91 bool HasDivider() const {
92 assert( m_rLineInf.GetDividerCountBy() != 0 );
93 if( !m_nDivider || m_rLineInf.GetDividerCountBy() == 0 )
94 return false;
95 return !(m_nLineNr % m_rLineInf.GetDividerCountBy());
98 void PaintExtra( SwTwips nY, tools::Long nAsc, tools::Long nMax, bool bRed, const OUString* pRedlineText = nullptr );
99 void PaintRedline( SwTwips nY, tools::Long nMax );
104 SwExtraPainter::SwExtraPainter( const SwTextFrame *pFrame, SwViewShell *pVwSh,
105 const SwLineNumberInfo &rLnInf, const SwRect &rRct,
106 sal_Int16 eHor, bool bLineNum )
107 : m_aClip( pVwSh->GetWin() || pFrame->IsUndersized() ? pVwSh->GetOut() : nullptr )
108 , m_aRect( rRct )
109 , m_pTextFrame( pFrame )
110 , m_pSh( pVwSh )
111 , m_rLineInf( rLnInf )
112 , m_nX(0)
113 , m_nRedX(0)
114 , m_nLineNr( 1 )
115 , m_nDivider(0)
116 , m_bGoLeft(false)
118 if( pFrame->IsUndersized() )
120 SwTwips nBottom = pFrame->getFrameArea().Bottom();
121 if( m_aRect.Bottom() > nBottom )
122 m_aRect.Bottom( nBottom );
124 std::optional<bool> oIsRightPage;
126 /* Initializes the Members necessary for line numbering:
128 nDivider, how often do we want a substring; 0 == never
129 nX, line number's x position
130 pFnt, line number's font
131 nLineNr, the first line number
132 bLineNum is set back to false if the numbering is completely
133 outside of the paint rect
135 m_nDivider = !m_rLineInf.GetDivider().isEmpty() ? m_rLineInf.GetDividerCountBy() : 0;
136 m_nX = pFrame->getFrameArea().Left();
137 SwCharFormat* pFormat = m_rLineInf.GetCharFormat( const_cast<IDocumentStylePoolAccess&>(pFrame->GetDoc().getIDocumentStylePoolAccess()) );
138 assert(pFormat && "PaintExtraData without CharFormat");
139 m_pFnt.reset( new SwFont(&pFormat->GetAttrSet(), &pFrame->GetDoc().getIDocumentSettingAccess()) );
140 m_pFnt->Invalidate();
141 m_pFnt->ChgPhysFnt( m_pSh, *m_pSh->GetOut() );
142 m_pFnt->SetVertical( 0_deg10, pFrame->IsVertical() );
145 if( bLineNum )
147 m_nLineNr += pFrame->GetAllLines() - pFrame->GetThisLines();
148 LineNumberPosition ePos = m_rLineInf.GetPos();
149 if( ePos != LINENUMBER_POS_LEFT && ePos != LINENUMBER_POS_RIGHT )
151 if( pFrame->FindPageFrame()->OnRightPage() )
153 oIsRightPage = true;
154 ePos = ePos == LINENUMBER_POS_INSIDE ?
155 LINENUMBER_POS_LEFT : LINENUMBER_POS_RIGHT;
157 else
159 oIsRightPage = false;
160 ePos = ePos == LINENUMBER_POS_OUTSIDE ?
161 LINENUMBER_POS_LEFT : LINENUMBER_POS_RIGHT;
164 if( LINENUMBER_POS_LEFT == ePos )
166 m_bGoLeft = true;
167 m_nX -= m_rLineInf.GetPosFromLeft();
169 else
171 m_bGoLeft = false;
172 m_nX += pFrame->getFrameArea().Width() + m_rLineInf.GetPosFromLeft();
175 if( eHor == text::HoriOrientation::NONE )
176 return;
178 if( text::HoriOrientation::INSIDE == eHor || text::HoriOrientation::OUTSIDE == eHor )
180 if (!oIsRightPage.has_value())
181 oIsRightPage = pFrame->FindPageFrame()->OnRightPage();
182 if (*oIsRightPage)
183 eHor = eHor == text::HoriOrientation::INSIDE ? text::HoriOrientation::LEFT : text::HoriOrientation::RIGHT;
184 else
185 eHor = eHor == text::HoriOrientation::OUTSIDE ? text::HoriOrientation::LEFT : text::HoriOrientation::RIGHT;
187 const SwFrame* pTmpFrame = pFrame->FindTabFrame();
188 if( !pTmpFrame )
189 pTmpFrame = pFrame;
190 m_nRedX = text::HoriOrientation::LEFT == eHor ? pTmpFrame->getFrameArea().Left() - REDLINE_DISTANCE :
191 pTmpFrame->getFrameArea().Right() + REDLINE_DISTANCE;
194 void SwExtraPainter::PaintExtra( SwTwips nY, tools::Long nAsc, tools::Long nMax, bool bRed, const OUString* pRedlineText )
196 const OUString aTmp( pRedlineText
197 // Tracked change is stronger than the line number
198 ? *pRedlineText
199 : ( HasNumber()
200 // Line number is stronger than the divider
201 ? m_rLineInf.GetNumType().GetNumStr( m_nLineNr )
202 : m_rLineInf.GetDivider() ) );
204 // Get script type of line numbering:
205 m_pFnt->SetActual( SwScriptInfo::WhichFont(0, aTmp) );
207 if ( pRedlineText )
209 m_pFnt->SetColor(m_pSh->GetViewOptions()->GetNonPrintingCharacterColor());
210 // don't strike out text in Insertions In Margin mode
211 if ( !m_pSh->GetViewOptions()->IsShowChangesInMargin2() )
212 m_pFnt->SetStrikeout( STRIKEOUT_SINGLE );
213 m_pFnt->SetSize( Size( 0, 200), m_pFnt->GetActual() );
216 SwDrawTextInfo aDrawInf( m_pSh, *m_pSh->GetOut(), aTmp, 0, aTmp.getLength() );
217 aDrawInf.SetSpace( 0 );
218 aDrawInf.SetWrong( nullptr );
219 aDrawInf.SetGrammarCheck( nullptr );
220 aDrawInf.SetSmartTags( nullptr );
221 aDrawInf.SetFrame( m_pTextFrame );
222 aDrawInf.SetFont( m_pFnt.get() );
223 aDrawInf.SetSnapToGrid( false );
224 aDrawInf.SetIgnoreFrameRTL( true );
226 bool bTooBig = m_pFnt->GetSize( m_pFnt->GetActual() ).Height() > nMax &&
227 m_pFnt->GetHeight( m_pSh, *m_pSh->GetOut() ) > nMax;
228 SwFont* pTmpFnt;
229 if( bTooBig )
231 pTmpFnt = new SwFont( *GetFont() );
232 if( nMax >= 20 )
234 nMax *= 17;
235 nMax /= 20;
237 pTmpFnt->SetSize( Size( 0, nMax ), pTmpFnt->GetActual() );
239 else
240 pTmpFnt = GetFont();
241 Point aTmpPos( m_nX, nY );
242 aTmpPos.AdjustY(nAsc );
243 if ( pRedlineText )
245 Size aSize = pTmpFnt->GetTextSize_( aDrawInf );
246 aTmpPos.AdjustX( -(aSize.Width()) - 200 );
248 bool bPaint = true;
249 if( !IsClipChg() )
251 Size aSize = pTmpFnt->GetTextSize_( aDrawInf );
252 if( m_bGoLeft )
253 aTmpPos.AdjustX( -(aSize.Width()) );
254 // calculate rectangle containing the line number
255 SwRect aRct( Point( aTmpPos.X(),
256 aTmpPos.Y() - pTmpFnt->GetAscent( m_pSh, *m_pSh->GetOut() )
257 ), aSize );
258 if( !m_aRect.Contains( aRct ) )
260 if( aRct.Intersection( m_aRect ).IsEmpty() )
261 bPaint = false;
262 else
263 m_aClip.ChgClip( m_aRect, m_pTextFrame );
266 else if( m_bGoLeft )
267 aTmpPos.AdjustX( -(pTmpFnt->GetTextSize_( aDrawInf ).Width()) );
268 aDrawInf.SetPos( aTmpPos );
269 if( bPaint )
270 pTmpFnt->DrawText_( aDrawInf );
272 if( bTooBig )
273 delete pTmpFnt;
274 if( bRed )
276 tools::Long nDiff = m_bGoLeft ? m_nRedX - m_nX : m_nX - m_nRedX;
277 if( nDiff > REDLINE_MINDIST )
278 PaintRedline( nY, nMax );
282 void SwExtraPainter::PaintRedline( SwTwips nY, tools::Long nMax )
284 Point aStart( m_nRedX, nY );
285 Point aEnd( m_nRedX, nY + nMax );
287 if( !IsClipChg() )
289 SwRect aRct( aStart, aEnd );
290 if( !m_aRect.Contains( aRct ) )
292 if( aRct.Intersection( m_aRect ).IsEmpty() )
293 return;
294 m_aClip.ChgClip( m_aRect, m_pTextFrame );
297 const Color aOldCol( m_pSh->GetOut()->GetLineColor() );
298 m_pSh->GetOut()->SetLineColor(SwModule::get()->GetRedlineMarkColor());
300 if ( m_pTextFrame->IsVertical() )
302 m_pTextFrame->SwitchHorizontalToVertical( aStart );
303 m_pTextFrame->SwitchHorizontalToVertical( aEnd );
306 m_pSh->GetOut()->DrawLine( aStart, aEnd );
307 m_pSh->GetOut()->SetLineColor( aOldCol );
310 void SwTextFrame::PaintExtraData( const SwRect &rRect ) const
312 if( getFrameArea().Top() > rRect.Bottom() || getFrameArea().Bottom() < rRect.Top() )
313 return;
315 PaintOutlineContentVisibilityButton();
317 SwDoc const& rDoc(GetDoc());
318 const IDocumentRedlineAccess& rIDRA = rDoc.getIDocumentRedlineAccess();
319 const SwLineNumberInfo &rLineInf = rDoc.GetLineNumberInfo();
320 const SwFormatLineNumber &rLineNum = GetAttrSet()->GetLineNumber();
321 bool bLineNum = !IsInTab() && rLineInf.IsPaintLineNumbers() &&
322 ( !IsInFly() || rLineInf.IsCountInFlys() ) && rLineNum.IsCount();
323 sal_Int16 eHor = static_cast<sal_Int16>(SwModule::get()->GetRedlineMarkPos());
324 if (eHor != text::HoriOrientation::NONE
325 && (!IDocumentRedlineAccess::IsShowChanges(rIDRA.GetRedlineFlags())
326 || getRootFrame()->IsHideRedlines()))
328 eHor = text::HoriOrientation::NONE;
330 bool bRedLine = eHor != text::HoriOrientation::NONE;
331 if ( !bLineNum && !bRedLine )
332 return;
334 if( IsLocked() || IsHiddenNow() || !getFramePrintArea().Height() )
335 return;
336 SwViewShell *pSh = getRootFrame()->GetCurrShell();
338 SwSwapIfNotSwapped swap(const_cast<SwTextFrame *>(this));
339 SwRect rOldRect( rRect );
341 if ( IsVertical() )
342 SwitchVerticalToHorizontal( const_cast<SwRect&>(rRect) );
344 SwLayoutModeModifier aLayoutModeModifier( *pSh->GetOut() );
345 aLayoutModeModifier.Modify( false );
347 // #i16816# tagged pdf support
348 SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, nullptr, *pSh->GetOut() );
350 SwExtraPainter aExtra( this, pSh, rLineInf, rRect, eHor, bLineNum );
352 if( HasPara() )
354 TextFrameLockGuard aLock(const_cast<SwTextFrame*>(this));
356 SwTextLineAccess aAccess( this );
357 aAccess.GetPara();
359 SwTextPaintInfo aInf( const_cast<SwTextFrame*>(this), rRect );
361 aLayoutModeModifier.Modify( false );
363 SwTextPainter aLine( const_cast<SwTextFrame*>(this), &aInf );
364 bool bNoDummy = !aLine.GetNext(); // Only one empty line!
366 while( aLine.Y() + aLine.GetLineHeight() <= rRect.Top() )
368 if( !aLine.GetCurr()->IsDummy() &&
369 ( rLineInf.IsCountBlankLines() ||
370 aLine.GetCurr()->HasContent() ) )
371 aExtra.IncLineNr();
372 if( !aLine.Next() )
374 const_cast<SwRect&>(rRect) = rOldRect;
375 return;
379 tools::Long nBottom = rRect.Bottom();
381 const bool bIsShowChangesInMargin = pSh->GetViewOptions()->IsShowChangesInMargin();
384 if( bNoDummy || !aLine.GetCurr()->IsDummy() )
386 bool bRed = bRedLine && aLine.GetCurr()->HasRedline();
387 if( rLineInf.IsCountBlankLines() || aLine.GetCurr()->HasContent() )
389 bool bRedInMargin = bIsShowChangesInMargin && bRed;
390 bool bNum = bLineNum && ( aExtra.HasNumber() || aExtra.HasDivider() );
391 if( bRedInMargin || bNum )
393 SwTwips nTmpHeight, nTmpAscent;
394 aLine.CalcAscentAndHeight( nTmpAscent, nTmpHeight );
395 if ( bRedInMargin )
397 const OUString* pRedlineText = aLine.GetCurr()->GetRedlineText();
398 if( !pRedlineText->isEmpty() )
400 aExtra.PaintExtra( aLine.Y(), nTmpAscent,
401 nTmpHeight, bRed, pRedlineText );
402 bRed = false;
403 bNum = false;
406 if ( bNum )
408 aExtra.PaintExtra( aLine.Y(), nTmpAscent, nTmpHeight, bRed );
409 bRed = false;
412 aExtra.IncLineNr();
414 if( bRed )
415 aExtra.PaintRedline( aLine.Y(), aLine.GetLineHeight() );
417 } while( aLine.Next() && aLine.Y() <= nBottom );
419 else
421 if (SwRedlineTable::npos == rIDRA.GetRedlinePos(*GetTextNodeFirst(), RedlineType::Any))
423 bRedLine = false;
426 if( bLineNum && rLineInf.IsCountBlankLines() &&
427 ( aExtra.HasNumber() || aExtra.HasDivider() ) )
429 aExtra.PaintExtra( getFrameArea().Top()+getFramePrintArea().Top(), aExtra.GetFont()
430 ->GetAscent( pSh, *pSh->GetOut() ), getFramePrintArea().Height(), bRedLine );
432 else if( bRedLine )
433 aExtra.PaintRedline( getFrameArea().Top()+getFramePrintArea().Top(), getFramePrintArea().Height() );
436 const_cast<SwRect&>(rRect) = rOldRect;
440 SwRect SwTextFrame::GetPaintSwRect()
442 // finger layout
443 OSL_ENSURE( isFrameAreaPositionValid(), "+SwTextFrame::GetPaintSwRect: no Calc()" );
445 SwRect aRet( getFramePrintArea() );
446 if ( IsEmpty() || !HasPara() )
447 aRet += getFrameArea().Pos();
448 else
450 // We return the right paint rect. Use the calculated PaintOfst as the
451 // left margin
452 SwRepaint& rRepaint = GetPara()->GetRepaint();
453 tools::Long l;
455 if ( IsVertLR() && !IsVertLRBT()) // mba: the following line was added, but we don't need it for the existing directions; kept for IsVertLR(), but should be checked
456 rRepaint.Chg( GetUpper()->getFrameArea().Pos() + GetUpper()->getFramePrintArea().Pos(), GetUpper()->getFramePrintArea().SSize() );
458 if( rRepaint.GetOffset() )
459 rRepaint.Left( rRepaint.GetOffset() );
461 l = rRepaint.GetRightOfst();
462 if( l && l > rRepaint.Right() )
463 rRepaint.Right( l );
464 rRepaint.SetOffset( 0 );
465 aRet = rRepaint;
467 // In case our left edge is the same as the body frame's left edge,
468 // then extend the rectangle to include the page margin as well,
469 // otherwise some font will be clipped.
470 SwLayoutFrame* pBodyFrame = GetUpper();
471 if (pBodyFrame->IsBodyFrame() && aRet.Left() == (pBodyFrame->getFrameArea().Left() + pBodyFrame->getFramePrintArea().Left()))
472 if (SwLayoutFrame* pPageFrame = pBodyFrame->GetUpper())
473 aRet.Left(pPageFrame->getFrameArea().Left());
475 if ( IsRightToLeft() )
476 SwitchLTRtoRTL( aRet );
478 if ( IsVertical() )
479 SwitchHorizontalToVertical( aRet );
481 ResetRepaint();
483 return aRet;
486 bool SwTextFrame::PaintEmpty( const SwRect &rRect, bool bCheck ) const
488 PaintParagraphStylesHighlighting();
490 SwViewShell *pSh = getRootFrame()->GetCurrShell();
491 if( pSh && ( pSh->GetViewOptions()->IsParagraph() || bInitFont ) )
493 bInitFont = false;
494 SwTextFly aTextFly( this );
495 aTextFly.SetTopRule();
496 SwRect aRect;
497 if( bCheck && aTextFly.IsOn() && aTextFly.IsAnyObj( aRect ) )
498 return false;
499 else if( pSh->GetWin() )
501 std::unique_ptr<SwFont> pFnt;
502 RedlineType eRedline = RedlineType::None;
503 const SwTextNode& rTextNode = *GetTextNodeForParaProps();
504 if ( rTextNode.HasSwAttrSet() )
506 const SwAttrSet *pAttrSet = &( rTextNode.GetSwAttrSet() );
507 pFnt.reset(new SwFont( pAttrSet, rTextNode.getIDocumentSettingAccess() ));
509 else
511 SwFontAccess aFontAccess( &rTextNode.GetAnyFormatColl(), pSh );
512 pFnt.reset(new SwFont( aFontAccess.Get()->GetFont() ));
515 const IDocumentRedlineAccess& rIDRA = rTextNode.getIDocumentRedlineAccess();
516 if (IDocumentRedlineAccess::IsShowChanges(rIDRA.GetRedlineFlags())
517 && !getRootFrame()->IsHideRedlines())
519 const SwRedlineTable::size_type nRedlPos = rIDRA.GetRedlinePos( rTextNode, RedlineType::Any );
520 if( SwRedlineTable::npos != nRedlPos )
522 SwAttrHandler aAttrHandler;
523 aAttrHandler.Init( rTextNode.GetSwAttrSet(),
524 *rTextNode.getIDocumentSettingAccess() );
525 SwRedlineItr aRedln(rTextNode, *pFnt, aAttrHandler, nRedlPos, SwRedlineItr::Mode::Show);
526 const SwRangeRedline* pRedline = rIDRA.GetRedlineTable()[nRedlPos];
527 // show redlining only on the inserted/deleted empty paragraph, but not on the next one
528 if ( rTextNode.GetIndex() != pRedline->End()->GetNodeIndex() )
529 eRedline = pRedline->GetType();
530 // except if the next empty paragraph starts a new redline (e.g. deletion after insertion)
531 else if ( nRedlPos + 1 < rIDRA.GetRedlineTable().size() )
533 const SwRangeRedline* pNextRedline = rIDRA.GetRedlineTable()[nRedlPos + 1];
534 if ( rTextNode.GetIndex() == pNextRedline->Start()->GetNodeIndex() )
535 eRedline = pNextRedline->GetType();
540 if( pSh->GetViewOptions()->IsParagraph() && getFramePrintArea().Height() )
542 if( RTL_TEXTENCODING_SYMBOL == pFnt->GetCharSet( SwFontScript::Latin ) &&
543 pFnt->GetName( SwFontScript::Latin ) != numfunc::GetDefBulletFontname() )
545 pFnt->SetFamily( FAMILY_DONTKNOW, SwFontScript::Latin );
546 pFnt->SetName( numfunc::GetDefBulletFontname(), SwFontScript::Latin );
547 pFnt->SetStyleName(OUString(), SwFontScript::Latin);
548 pFnt->SetCharSet( RTL_TEXTENCODING_SYMBOL, SwFontScript::Latin );
550 pFnt->SetVertical( 0_deg10, IsVertical() );
551 SwFrameSwapper aSwapper( this, true );
552 SwLayoutModeModifier aLayoutModeModifier( *pSh->GetOut() );
553 aLayoutModeModifier.Modify( IsRightToLeft() );
555 pFnt->Invalidate();
556 pFnt->ChgPhysFnt( pSh, *pSh->GetOut() );
557 Point aPos = getFrameArea().Pos() + getFramePrintArea().Pos();
559 const SvxFirstLineIndentItem& rFirstLine(
560 GetTextNodeForParaProps()->GetSwAttrSet().GetFirstLineIndent());
562 if (0.0 < rFirstLine.GetTextFirstLineOffset().m_dValue)
564 aPos.AdjustX(rFirstLine.ResolveTextFirstLineOffset({}));
567 std::unique_ptr<SwSaveClip, o3tl::default_delete<SwSaveClip>> xClip;
568 if( IsUndersized() )
570 xClip.reset(new SwSaveClip( pSh->GetOut() ));
571 xClip->ChgClip( rRect );
574 aPos.AdjustY(pFnt->GetAscent( pSh, *pSh->GetOut() ) );
576 if (GetTextNodeForParaProps()->GetSwAttrSet().GetParaGrid().GetValue() &&
577 IsInDocBody() )
579 SwTextGridItem const*const pGrid(GetGridItem(FindPageFrame()));
580 if ( pGrid )
582 // center character in grid line
583 aPos.AdjustY(( pGrid->GetBaseHeight() -
584 pFnt->GetHeight( pSh, *pSh->GetOut() ) ) / 2 );
586 if ( ! pGrid->GetRubyTextBelow() )
587 aPos.AdjustY(pGrid->GetRubyHeight() );
591 // Don't show the paragraph mark for collapsed paragraphs, when they are hidden
592 // No paragraph marker in the non-last part of a split fly anchor, either.
593 if ( EmptyHeight( ) > 1 && !HasNonLastSplitFlyDrawObj() )
595 SwDrawTextInfo aDrawInf( pSh, *pSh->GetOut(), CH_PAR, 0, 1 );
596 aDrawInf.SetPos( aPos );
597 aDrawInf.SetSpace( 0 );
598 aDrawInf.SetKanaComp( 0 );
599 aDrawInf.SetWrong( nullptr );
600 aDrawInf.SetGrammarCheck( nullptr );
601 aDrawInf.SetSmartTags( nullptr );
602 aDrawInf.SetFrame( this );
603 aDrawInf.SetFont( pFnt.get() );
604 aDrawInf.SetSnapToGrid( false );
606 // show redline color and settings drawing a background pilcrow,
607 // but keep also other formattings (with neutral pilcrow color)
608 if ( eRedline != RedlineType::None )
610 pFnt->DrawText_( aDrawInf );
611 if ( eRedline == RedlineType::Delete )
612 pFnt->SetStrikeout( STRIKEOUT_NONE );
613 else
614 pFnt->SetUnderline( LINESTYLE_NONE );
617 pFnt->SetColor(pSh->GetViewOptions()->GetNonPrintingCharacterColor());
618 pFnt->DrawText_( aDrawInf );
621 return true;
624 else
625 return true;
626 return false;
629 void SwTextFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const& rRect, PaintFrameMode) const
631 ResetRepaint();
633 // #i16816# tagged pdf support
634 SwViewShell *pSh = getRootFrame()->GetCurrShell();
636 if( IsEmpty() && PaintEmpty( rRect, true ) )
637 return;
639 if( IsLocked() || IsHiddenNow() || ! getFramePrintArea().HasArea() )
640 return;
642 // It can happen that the IdleCollector withdrew my cached information
643 if( !HasPara() )
645 OSL_ENSURE( isFrameAreaPositionValid(), "+SwTextFrame::PaintSwFrame: no Calc()" );
647 // #i29062# pass info that we are currently
648 // painting.
649 const_cast<SwTextFrame*>(this)->GetFormatted( true );
650 if( IsEmpty() )
652 PaintEmpty( rRect, false );
653 return;
655 if( !HasPara() )
657 OSL_ENSURE( false, "+SwTextFrame::PaintSwFrame: missing format information" );
658 return;
662 // tdf140219-2.odt text frame with only fly portions and a follow is not
663 // actually a paragraph - delay creating all structured elements to follow.
664 bool const isPDFTaggingEnabled(!HasFollow() || GetPara()->HasContentPortions());
665 ::std::optional<SwTaggedPDFHelper> oTaggedPDFHelperNumbering;
666 if (isPDFTaggingEnabled)
668 Num_Info aNumInfo(*this);
669 oTaggedPDFHelperNumbering.emplace(&aNumInfo, nullptr, nullptr, rRenderContext);
672 // Lbl unfortunately must be able to contain multiple numbering portions
673 // that may be on multiple lines of text (but apparently always in the
674 // master frame), so it gets complicated.
675 ::std::optional<SwTaggedPDFHelper> oTaggedLabel;
676 // Paragraph tag - if there is a list label, opening should be delayed.
677 ::std::optional<SwTaggedPDFHelper> oTaggedParagraph;
679 if (isPDFTaggingEnabled
680 && (GetTextNodeForParaProps()->IsOutline()
681 || !GetPara()->HasNumberingPortion(SwParaPortion::FootnoteToo)))
682 { // no Lbl needed => open paragraph tag now
683 Frame_Info aFrameInfo(*this, false);
684 oTaggedParagraph.emplace(nullptr, &aFrameInfo, nullptr, rRenderContext);
687 // We don't want to be interrupted while painting.
688 // Do that after thr Format()!
689 TextFrameLockGuard aLock(const_cast<SwTextFrame*>(this));
691 // We only paint the part of the TextFrame which changed, is within the
692 // range and was requested to paint.
693 // One could think that the area rRect _needs_ to be painted, although
694 // rRepaint is set. Indeed, we cannot avoid this problem from a formal
695 // perspective. Luckily we can assume rRepaint to be empty when we need
696 // paint the while Frame.
697 SwTextLineAccess aAccess( this );
698 SwParaPortion *pPara = aAccess.GetPara();
700 SwRepaint &rRepaint = pPara->GetRepaint();
702 // Switch off recycling when in the FlyContentFrame.
703 // A DrawRect is called for repainting the line anyways.
704 if( rRepaint.GetOffset() )
706 const SwFlyFrame *pFly = FindFlyFrame();
707 if( pFly && pFly->IsFlyInContentFrame() )
708 rRepaint.SetOffset( 0 );
711 // Ge the String for painting. The length is of special interest.
713 // Rectangle
714 OSL_ENSURE( ! IsSwapped(), "A frame is swapped before Paint" );
715 SwRect aOldRect( rRect );
718 SwSwapIfNotSwapped swap(const_cast<SwTextFrame *>(this));
720 if ( IsVertical() )
721 SwitchVerticalToHorizontal( const_cast<SwRect&>(rRect) );
723 if ( IsRightToLeft() )
724 SwitchRTLtoLTR( const_cast<SwRect&>(rRect) );
726 SwTextPaintInfo aInf( const_cast<SwTextFrame*>(this), rRect );
727 sw::WrongListIterator iterWrong(*this, &SwTextNode::GetWrong);
728 sw::WrongListIterator iterGrammar(*this, &SwTextNode::GetGrammarCheck);
729 sw::WrongListIterator iterSmartTags(*this, &SwTextNode::GetSmartTags);
730 if (iterWrong.LooksUseful())
732 aInf.SetWrongList( &iterWrong );
734 if (iterGrammar.LooksUseful())
736 aInf.SetGrammarCheckList( &iterGrammar );
738 if (iterSmartTags.LooksUseful())
740 aInf.SetSmartTags( &iterSmartTags );
742 aInf.GetTextFly().SetTopRule();
744 SwTextPainter aLine( const_cast<SwTextFrame*>(this), &aInf );
745 // Optimization: if no free flying Frame overlaps into our line, the
746 // SwTextFly just switches off
747 aInf.GetTextFly().Relax();
749 OutputDevice* pOut = aInf.GetOut();
750 const bool bOnWin = pSh->GetWin() != nullptr;
752 SwSaveClip aClip( bOnWin || IsUndersized() ? pOut : nullptr );
754 // Output loop: For each Line ... (which is still visible) ...
755 // adapt rRect (Top + 1, Bottom - 1)
756 // Because the Iterator attaches the Lines without a gap to each other
757 aLine.TwipsToLine( rRect.Top() + 1 );
758 tools::Long nBottom = rRect.Bottom();
762 aLine.DrawTextLine(rRect, aClip, IsUndersized(), oTaggedLabel, oTaggedParagraph, isPDFTaggingEnabled);
764 } while( aLine.Next() && aLine.Y() <= nBottom );
766 // Once is enough:
767 if( aLine.IsPaintDrop() )
768 aLine.PaintDropPortion();
770 if( rRepaint.HasArea() )
771 rRepaint.Clear();
774 PaintParagraphStylesHighlighting();
776 const_cast<SwRect&>(rRect) = aOldRect;
778 OSL_ENSURE( ! IsSwapped(), "A frame is swapped after Paint" );
780 assert(!oTaggedLabel); // must have been closed if opened
781 assert(!isPDFTaggingEnabled || oTaggedParagraph || rRect.GetIntersection(getFrameArea()) != getFrameArea()); // must have been created during complete paint (PDF export is always complete paint)
784 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */