android: Update app-specific/MIME type icons
[LibreOffice.git] / sw / source / core / text / itrpaint.cxx
blob06c9c56b5d07998f43a19ff51ab74b5e3cdd254b
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 <hintids.hxx>
21 #include <viewopt.hxx>
22 #include <tools/multisel.hxx>
23 #include <editeng/udlnitem.hxx>
24 #include <pagefrm.hxx>
25 #include <tgrditem.hxx>
27 #include <EnhancedPDFExportHelper.hxx>
28 #include <IDocumentSettingAccess.hxx>
30 #include <viewsh.hxx>
31 #include "itrpaint.hxx"
32 #include <txtfrm.hxx>
33 #include <swfont.hxx>
34 #include "txtpaint.hxx"
35 #include "porfld.hxx"
36 #include "portab.hxx"
37 #include <txatbase.hxx>
38 #include <charfmt.hxx>
39 #include "redlnitr.hxx"
40 #include "porrst.hxx"
41 #include "pormulti.hxx"
42 #include <doc.hxx>
44 // Returns, if we have an underline breaking situation
45 // Adding some more conditions here means you also have to change them
46 // in SwTextPainter::CheckSpecialUnderline
47 bool IsUnderlineBreak( const SwLinePortion& rPor, const SwFont& rFnt )
49 return LINESTYLE_NONE == rFnt.GetUnderline() ||
50 rPor.IsFlyPortion() || rPor.IsFlyCntPortion() ||
51 rPor.IsBreakPortion() || rPor.IsMarginPortion() ||
52 rPor.IsHolePortion() ||
53 ( rPor.IsMultiPortion() && ! static_cast<const SwMultiPortion&>(rPor).IsBidi() ) ||
54 rFnt.GetEscapement() < 0 || rFnt.IsWordLineMode() ||
55 SvxCaseMap::SmallCaps == rFnt.GetCaseMap();
58 static Color GetUnderColor( const SwFont *pFont )
60 return pFont->GetUnderColor() == COL_AUTO ?
61 pFont->GetColor() : pFont->GetUnderColor();
64 void SwTextPainter::CtorInitTextPainter( SwTextFrame *pNewFrame, SwTextPaintInfo *pNewInf )
66 CtorInitTextCursor( pNewFrame, pNewInf );
67 m_pInf = pNewInf;
68 SwFont *pMyFnt = GetFnt();
69 GetInfo().SetFont( pMyFnt );
70 m_bPaintDrop = false;
73 SwLinePortion *SwTextPainter::CalcPaintOfst(const SwRect &rPaint, bool& rbSkippedNumPortions)
75 SwLinePortion *pPor = m_pCurr->GetFirstPortion();
76 GetInfo().SetPaintOfst( 0 );
77 SwTwips nPaintOfst = rPaint.Left();
79 // nPaintOfst was exactly set to the end, therefore <=
80 // nPaintOfst is document global, therefore add up nLeftMar
81 // const sal_uInt16 nLeftMar = sal_uInt16(GetLeftMargin());
82 // 8310: paint of LineBreaks in empty lines.
83 if( nPaintOfst && m_pCurr->Width() )
85 SwLinePortion *pLast = nullptr;
86 // 7529 and 4757: not <= nPaintOfst
87 while( pPor && GetInfo().X() + pPor->Width() + (pPor->Height()/2)
88 < nPaintOfst )
90 if( pPor->InSpaceGrp() && GetInfo().GetSpaceAdd() )
92 tools::Long nTmp = GetInfo().X() +pPor->Width() +
93 pPor->CalcSpacing( GetInfo().GetSpaceAdd(), GetInfo() );
94 if( nTmp + (pPor->Height()/2) >= nPaintOfst )
95 break;
96 GetInfo().X( nTmp );
97 GetInfo().SetIdx( GetInfo().GetIdx() + pPor->GetLen() );
99 else
100 pPor->Move( GetInfo() );
101 if (pPor->InNumberGrp()
102 && !static_cast<SwNumberPortion const*>(pPor)->HasFollow())
104 rbSkippedNumPortions = true; // all numbering portions were skipped?
106 pLast = pPor;
107 pPor = pPor->GetNextPortion();
110 // 7529: if PostIts return also pLast.
111 if( pLast && !pLast->Width() && pLast->IsPostItsPortion() )
113 pPor = pLast;
114 GetInfo().SetIdx( GetInfo().GetIdx() - pPor->GetLen() );
117 return pPor;
120 // There are two possibilities to output transparent font:
121 // 1) DrawRect on the whole line and DrawText afterwards
122 // (objectively fast, subjectively slow)
123 // 2) For every portion a DrawRect with subsequent DrawText is done
124 // (objectively slow, subjectively fast)
125 // Since the user usually judges subjectively the second method is set as default.
126 void SwTextPainter::DrawTextLine( const SwRect &rPaint, SwSaveClip &rClip,
127 const bool bUnderSized,
128 ::std::optional<SwTaggedPDFHelper> & roTaggedLabel,
129 ::std::optional<SwTaggedPDFHelper> & roTaggedParagraph,
130 bool const isPDFTaggingEnabled)
132 #if OSL_DEBUG_LEVEL > 1
133 // sal_uInt16 nFntHeight = GetInfo().GetFont()->GetHeight( GetInfo().GetVsh(), GetInfo().GetOut() );
134 // sal_uInt16 nFntAscent = GetInfo().GetFont()->GetAscent( GetInfo().GetVsh(), GetInfo().GetOut() );
135 #endif
137 // maybe catch-up adjustment
138 GetAdjusted();
139 AddExtraBlankWidth();
140 GetInfo().SetpSpaceAdd( m_pCurr->GetpLLSpaceAdd() );
141 GetInfo().ResetSpaceIdx();
142 GetInfo().SetKanaComp( m_pCurr->GetpKanaComp() );
143 GetInfo().ResetKanaIdx();
144 // The size of the frame
145 GetInfo().SetIdx( GetStart() );
146 GetInfo().SetPos( GetTopLeft() );
148 const bool bDrawInWindow = GetInfo().OnWin();
150 // 6882: blank lines can't be optimized by removing them if Formatting Marks are shown
151 const bool bEndPor = GetInfo().GetOpt().IsParagraph() && GetInfo().GetText().isEmpty();
153 bool bSkippedNumPortions(false);
154 SwLinePortion *pPor = bEndPor ? m_pCurr->GetFirstPortion() : CalcPaintOfst(rPaint, bSkippedNumPortions);
156 if (bSkippedNumPortions // ugly but hard to check earlier in PaintSwFrame:
157 && !GetInfo().GetTextFrame()->GetTextNodeForParaProps()->IsOutline())
158 { // there is a num portion but it is outside of the frame area and not painted
159 assert(!roTaggedLabel);
160 assert(!roTaggedParagraph);
161 Frame_Info aFrameInfo(*m_pFrame, false); // open LBody
162 roTaggedParagraph.emplace(nullptr, &aFrameInfo, nullptr, *GetInfo().GetOut());
165 // Optimization!
166 SwTwips nMaxRight = std::min<SwTwips>( rPaint.Right(), Right() );
167 const SwTwips nTmpLeft = GetInfo().X();
168 //compatibility settings: allow tabstop text to exceed right margin
169 const auto& iDSA = GetInfo().GetTextFrame()->GetDoc().getIDocumentSettingAccess();
170 const bool bTabOverMargin = iDSA.get(DocumentSettingId::TAB_OVER_MARGIN);
171 const bool bTabOverSpacing = iDSA.get(DocumentSettingId::TAB_OVER_SPACING);
172 if (bTabOverMargin || bTabOverSpacing)
174 SwLinePortion* pPorIter = pPor;
175 while( pPorIter )
177 if( pPorIter->InTabGrp() )
179 const SwTabPortion* pTabPor = static_cast<SwTabPortion*>(pPorIter);
180 const SwTwips nTabPos = nTmpLeft + pTabPor->GetTabPos();
181 if( nMaxRight < nTabPos )
183 nMaxRight = rPaint.Right();
184 break;
187 pPorIter = pPorIter->GetNextPortion();
190 if( !bEndPor && nTmpLeft >= nMaxRight )
191 return;
193 // DropCaps!
194 // 7538: of course for the printer, too
195 if( !m_bPaintDrop )
197 // 8084: Optimization, less painting
198 // AMA: By 8084 7538 has been revived
199 // bDrawInWindow removed, so that DropCaps also can be printed
200 m_bPaintDrop = pPor == m_pCurr->GetFirstPortion()
201 && GetDropLines() >= GetLineNr();
204 SwTwips nTmpHeight, nTmpAscent;
205 CalcAscentAndHeight( nTmpAscent, nTmpHeight );
207 // bClip decides if there's a need to clip
208 // The whole thing must be done before retouching
210 bool bClip = ( bDrawInWindow || bUnderSized ) && !rClip.IsChg();
211 if( bClip && pPor )
213 // If TopLeft or BottomLeft of the line are outside, the we must clip.
214 // The check for Right() is done in the output loop ...
216 if( GetInfo().GetPos().X() < rPaint.Left() ||
217 GetInfo().GetPos().Y() < rPaint.Top() ||
218 GetInfo().GetPos().Y() + nTmpHeight > rPaint.Top() + rPaint.Height() )
220 bClip = false;
221 rClip.ChgClip( rPaint, m_pFrame, m_pCurr->HasUnderscore() );
223 #if OSL_DEBUG_LEVEL > 1
224 static bool bClipAlways = false;
225 if( bClip && bClipAlways )
226 { bClip = false;
227 rClip.ChgClip( rPaint );
229 #endif
232 // Alignment
233 OutputDevice* pOut = GetInfo().GetOut();
234 Point aPnt1( nTmpLeft, GetInfo().GetPos().Y() );
235 if ( aPnt1.X() < rPaint.Left() )
236 aPnt1.setX( rPaint.Left() );
237 if ( aPnt1.Y() < rPaint.Top() )
238 aPnt1.setY( rPaint.Top() );
239 Point aPnt2( GetInfo().GetPos().X() + nMaxRight - GetInfo().X(),
240 GetInfo().GetPos().Y() + nTmpHeight );
241 if ( aPnt2.X() > rPaint.Right() )
242 aPnt2.setX( rPaint.Right() );
243 if ( aPnt2.Y() > rPaint.Bottom() )
244 aPnt2.setY( rPaint.Bottom() );
246 const SwRect aLineRect( aPnt1, aPnt2 );
248 if( m_pCurr->IsClipping() )
250 const SwTextFrame& rFrame = *GetInfo().GetTextFrame();
251 // tdf#117448 at small fixed line height, enlarge clipping area in table cells
252 // to show previously clipped text content on the area of paragraph margins
253 if ( rFrame.IsInTab() )
254 rClip.ChgClip( aLineRect, m_pFrame, false, rFrame.GetTopMargin(), rFrame.GetBottomMargin() );
255 else
256 rClip.ChgClip( aLineRect, m_pFrame );
257 bClip = false;
260 if( !pPor && !bEndPor )
261 return;
263 // Baseline output also if non-TextPortion (compare TabPor with Fill)
264 // if no special vertical alignment is used,
265 // we calculate Y value for the whole line
266 SwTextGridItem const*const pGrid(GetGridItem(GetTextFrame()->FindPageFrame()));
267 const bool bAdjustBaseLine =
268 GetLineInfo().HasSpecialAlign( GetTextFrame()->IsVertical() ) ||
269 ( nullptr != pGrid ) || m_pCurr->GetHangingBaseline();
270 const SwTwips nLineBaseLine = GetInfo().GetPos().Y() + nTmpAscent;
271 if ( ! bAdjustBaseLine )
272 GetInfo().Y( nLineBaseLine );
274 // 7529: Pre-paint post-its
275 if( GetInfo().OnWin() && pPor && !pPor->Width() )
277 SeekAndChg( GetInfo() );
279 if( bAdjustBaseLine )
281 const SwTwips nOldY = GetInfo().Y();
283 GetInfo().Y( GetInfo().GetPos().Y() + AdjustBaseLine( *m_pCurr, nullptr,
284 GetInfo().GetFont()->GetHeight( GetInfo().GetVsh(), *pOut ),
285 GetInfo().GetFont()->GetAscent( GetInfo().GetVsh(), *pOut )
286 ) );
288 pPor->PrePaint( GetInfo(), pPor );
289 GetInfo().Y( nOldY );
291 else
292 pPor->PrePaint( GetInfo(), pPor );
295 // 7923: EndPortions output chars, too, that's why we change the font
296 if( bEndPor )
297 SeekStartAndChg( GetInfo() );
299 const bool bRest = m_pCurr->IsRest();
300 bool bFirst = true;
302 SwArrowPortion *pArrow = nullptr;
303 // Reference portion for the paragraph end portion
304 SwLinePortion* pEndTempl = m_pCurr->GetFirstPortion();
306 while( pPor )
308 bool bSeeked = true;
309 GetInfo().SetLen( pPor->GetLen() );
311 const SwTwips nOldY = GetInfo().Y();
313 if ( bAdjustBaseLine )
315 GetInfo().Y( GetInfo().GetPos().Y() + AdjustBaseLine( *m_pCurr, pPor ) );
317 // we store the last portion, because a possible paragraph
318 // end character has the same font as this portion
319 // (only in special vertical alignment case, otherwise the first
320 // portion of the line is used)
321 if ( pPor->Width() && pPor->InTextGrp() )
322 pEndTempl = pPor;
325 // set redlining for line break symbol
326 if ( pPor->IsBreakPortion() && GetInfo().GetOpt().IsParagraph() && GetRedln() )
328 SeekAndChg( GetInfo() );
329 if ( m_pCurr->GetRedlineEndType() != RedlineType::None )
330 static_cast<SwBreakPortion&>(*pPor).SetRedline( m_pCurr->GetRedlineEndType() );
333 // A special case are GluePortions which output blanks.
335 // 6168: Avoid that the rest of a FieldPortion gets the attributes of the
336 // next portion with SeekAndChgBefore():
337 if( bRest && pPor->InFieldGrp() && !pPor->GetLen() )
338 SeekAndChgBefore( GetInfo() );
339 else if ( pPor->IsQuoVadisPortion() )
341 // A remark on QuoVadis/ErgoSum:
342 // We use the Font set for the Paragraph for these portions.
343 // Thus, we initialize:
344 TextFrameIndex nOffset = GetInfo().GetIdx();
345 SeekStartAndChg( GetInfo(), true );
346 if( GetRedln() && m_pCurr->HasRedline() )
348 std::pair<SwTextNode const*, sal_Int32> const pos(
349 GetTextFrame()->MapViewToModel(nOffset));
350 GetRedln()->Seek(*m_pFont, pos.first->GetIndex(), pos.second, 0);
353 else if( pPor->InTextGrp() || pPor->InFieldGrp() || pPor->InTabGrp() )
354 SeekAndChg( GetInfo() );
355 else if ( !bFirst && pPor->IsBreakPortion() && GetInfo().GetOpt().IsParagraph() )
357 // Paragraph symbols should have the same font as the paragraph in front of them,
358 // except for the case that there's redlining in the paragraph
359 if( GetRedln() )
360 SeekAndChg( GetInfo() );
361 else
362 SeekAndChgBefore( GetInfo() );
364 else
365 bSeeked = false;
367 // bRest = false;
369 // If the end of the portion juts out, it is clipped.
370 // A safety distance of half the height is added, so that
371 // TTF-"f" isn't overlapping into the page margin.
372 if( bClip &&
373 GetInfo().X() + pPor->Width() + ( pPor->Height() / 2 ) > nMaxRight )
375 bClip = false;
376 rClip.ChgClip( rPaint, m_pFrame, m_pCurr->HasUnderscore() );
379 // Portions, which lay "below" the text like post-its
380 SwLinePortion *pNext = pPor->GetNextPortion();
381 if( GetInfo().OnWin() && pNext && !pNext->Width() )
383 // Fix 11289: Fields were omitted here because of Last!=Owner during
384 // loading Brief.sdw. Now the fields are allowed again,
385 // by bSeeked Last!=Owner is being avoided.
386 if ( !bSeeked )
387 SeekAndChg( GetInfo() );
388 pNext->PrePaint( GetInfo(), pPor );
391 // We calculate a separate font for underlining.
392 CheckSpecialUnderline( pPor, bAdjustBaseLine ? nOldY : 0 );
393 SwUnderlineFont* pUnderLineFnt = GetInfo().GetUnderFnt();
394 if ( pUnderLineFnt )
396 const Point aTmpPoint( GetInfo().X(),
397 bAdjustBaseLine ?
398 pUnderLineFnt->GetPos().Y() :
399 nLineBaseLine );
400 pUnderLineFnt->SetPos( aTmpPoint );
403 // in extended input mode we do not want a common underline font.
404 SwUnderlineFont* pOldUnderLineFnt = nullptr;
405 if ( GetRedln() && GetRedln()->ExtOn() )
407 pOldUnderLineFnt = GetInfo().GetUnderFnt();
408 GetInfo().SetUnderFnt( nullptr );
411 // multiple numbering portions are possible :(
412 if ((pPor->InNumberGrp() // also footnote label
413 // weird special case, bullet with soft hyphen
414 || (pPor->InHyphGrp() && pNext && pNext->InNumberGrp()))
415 && !GetInfo().GetTextFrame()->GetTextNodeForParaProps()->IsOutline()
416 && !roTaggedLabel) // note: CalcPaintOfst may skip some portions
418 assert(isPDFTaggingEnabled);
419 Por_Info aPorInfo(*pPor, *this, true); // open Lbl
420 roTaggedLabel.emplace(nullptr, nullptr, &aPorInfo, *pOut);
424 // #i16816# tagged pdf support
425 Por_Info aPorInfo(*pPor, *this, false);
426 SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, &aPorInfo, *pOut );
428 if( pPor->IsMultiPortion() )
429 PaintMultiPortion( rPaint, static_cast<SwMultiPortion&>(*pPor) );
430 else
431 pPor->Paint( GetInfo() );
434 // lazy open LBody and paragraph tag after num portions have been painted to Lbl
435 if (pPor->InNumberGrp() // also footnote label
436 // note: numbering portion may be split if it has multiple scripts
437 && !static_cast<SwNumberPortion const*>(pPor)->HasFollow()) // so wait for the last one
439 if (!GetInfo().GetTextFrame()->GetTextNodeForParaProps()->IsOutline())
441 assert(roTaggedLabel);
442 roTaggedLabel.reset(); // close Lbl
443 assert(!roTaggedParagraph);
444 Frame_Info aFrameInfo(*m_pFrame, false); // open LBody
445 roTaggedParagraph.emplace(nullptr, &aFrameInfo, nullptr, *pOut);
447 else
449 assert(!roTaggedLabel);
453 // reset underline font
454 if ( pOldUnderLineFnt )
455 GetInfo().SetUnderFnt( pOldUnderLineFnt );
457 // reset (for special vertical alignment)
458 GetInfo().Y( nOldY );
460 bFirst &= !pPor->GetLen();
461 if( pNext || !pPor->IsMarginPortion() )
462 pPor->Move( GetInfo() );
463 if( pPor->IsArrowPortion() && GetInfo().OnWin() && !pArrow )
464 pArrow = static_cast<SwArrowPortion*>(pPor);
466 pPor = bDrawInWindow || GetInfo().X() <= nMaxRight ||
467 // #i16816# tagged pdf support
468 ( GetInfo().GetVsh() &&
469 GetInfo().GetVsh()->GetViewOptions()->IsPDFExport() &&
470 pNext && pNext->IsHolePortion() ) ?
471 pNext :
472 nullptr;
473 if (!pPor && isPDFTaggingEnabled && (roTaggedLabel || !roTaggedParagraph))
474 { // check if the end of the list label is off-screen
475 auto FindEndOfNumbering = [&](SwLinePortion const* pP) {
476 while (pP)
478 if (pP->InNumberGrp()
479 && !static_cast<SwNumberPortion const*>(pP)->HasFollow())
481 if (roTaggedLabel)
483 roTaggedLabel.reset();
484 } // else, if the numbering isn't visible at all, no Lbl
485 if (!GetInfo().GetTextFrame()->GetTextNodeForParaProps()->IsOutline())
487 Frame_Info aFrameInfo(*m_pFrame, false); // open LBody
488 roTaggedParagraph.emplace(nullptr, &aFrameInfo, nullptr, *GetInfo().GetOut());
490 return true;
492 pP = pP->GetNextPortion();
494 return false;
496 if (!FindEndOfNumbering(pNext)) // check rest of current line
498 // check lines that will be cut off
499 if (rPaint.Bottom() < Y() + GetLineHeight())
501 for (SwLineLayout const* pLine = GetNext(); pLine; pLine = pLine->GetNext())
503 if (FindEndOfNumbering(pLine->GetFirstPortion()))
505 break;
513 // delete underline font
514 delete GetInfo().GetUnderFnt();
515 GetInfo().SetUnderFnt( nullptr );
517 // paint remaining stuff
518 if( bDrawInWindow )
520 // If special vertical alignment is enabled, GetInfo().Y() is the
521 // top of the current line. Therefore is has to be adjusted for
522 // the painting of the remaining stuff. We first store the old value.
523 const SwTwips nOldY = GetInfo().Y();
525 if( !GetNextLine() &&
526 GetInfo().GetVsh() && !GetInfo().GetVsh()->IsPreview() &&
527 GetInfo().GetOpt().IsParagraph() && !GetTextFrame()->GetFollow() &&
528 GetInfo().GetIdx() >= TextFrameIndex(GetInfo().GetText().getLength()))
530 bool bHasRedlineEnd( GetRedln() && m_pCurr->HasRedlineEnd() );
531 RedlineType eRedlineEnd = bHasRedlineEnd ? m_pCurr->GetRedlineEndType() : RedlineType::None;
532 if( bHasRedlineEnd )
534 TextFrameIndex nOffset = GetInfo().GetIdx();
535 SeekStartAndChg( GetInfo(), true );
536 std::pair<SwTextNode const*, sal_Int32> const pos(
537 GetTextFrame()->MapViewToModel(nOffset));
538 GetRedln()->Seek(*m_pFont, pos.first->GetIndex(), pos.second, 0);
540 const SwTmpEndPortion aEnd( *pEndTempl,
541 bHasRedlineEnd && eRedlineEnd != RedlineType::Delete ? m_pFont->GetUnderline() : LINESTYLE_NONE,
542 bHasRedlineEnd && eRedlineEnd == RedlineType::Delete ? m_pFont->GetStrikeout() : STRIKEOUT_NONE,
543 bHasRedlineEnd ? m_pFont->GetColor() : COL_AUTO );
544 GetFnt()->ChgPhysFnt( GetInfo().GetVsh(), *pOut );
546 if ( bAdjustBaseLine )
547 GetInfo().Y( GetInfo().GetPos().Y()
548 + AdjustBaseLine( *m_pCurr, &aEnd ) );
549 GetInfo().X( GetInfo().X() +
550 ( GetCurr()->IsHanging() ? GetCurr()->GetHangingMargin() : 0 ) );
551 aEnd.Paint( GetInfo() );
552 GetInfo().Y( nOldY );
554 if( GetInfo().GetVsh() && !GetInfo().GetVsh()->IsPreview() )
556 const bool bNextUndersized =
557 ( GetTextFrame()->GetNext() &&
558 0 == GetTextFrame()->GetNext()->getFramePrintArea().Height() &&
559 GetTextFrame()->GetNext()->IsTextFrame() &&
560 static_cast<SwTextFrame*>(GetTextFrame()->GetNext())->IsUndersized() ) ;
562 if( bUnderSized || bNextUndersized )
564 if ( bAdjustBaseLine )
565 GetInfo().Y( GetInfo().GetPos().Y() + m_pCurr->GetAscent() );
567 // Left arrow (text overflowing)
568 if( pArrow )
569 GetInfo().DrawRedArrow( *pArrow );
571 // GetInfo().Y() must be current baseline
572 SwTwips nDiff = GetInfo().Y() + nTmpHeight - nTmpAscent - GetTextFrame()->getFrameArea().Bottom();
573 if( ( nDiff > 0 &&
574 (GetEnd() < TextFrameIndex(GetInfo().GetText().getLength()) ||
575 ( nDiff > nTmpHeight/2 && GetPrevLine() ) ) ) ||
576 (nDiff >= 0 && bNextUndersized) )
579 // Right arrow (text overflowing)
580 SwArrowPortion aArrow( GetInfo() );
581 GetInfo().DrawRedArrow( aArrow );
584 GetInfo().Y( nOldY );
589 if( m_pCurr->IsClipping() )
590 rClip.ChgClip( rPaint, m_pFrame );
593 void SwTextPainter::CheckSpecialUnderline( const SwLinePortion* pPor,
594 tools::Long nAdjustBaseLine )
596 // Check if common underline should not be continued
597 if ( IsUnderlineBreak( *pPor, *m_pFont ) )
599 // delete underline font
600 delete GetInfo().GetUnderFnt();
601 GetInfo().SetUnderFnt( nullptr );
602 return;
604 // Reuse calculated underline font as much as possible.
605 if (GetInfo().GetUnderFnt() &&
606 GetInfo().GetIdx() + pPor->GetLen() <= GetInfo().GetUnderFnt()->GetEnd() + TextFrameIndex(1))
608 SwFont &rFont = GetInfo().GetUnderFnt()->GetFont();
609 const Color aColor = GetUnderColor( GetInfo().GetFont() );
610 if ( GetUnderColor( &rFont ) != aColor )
611 rFont.SetColor( aColor );
612 return;
615 // If current underline matches the common underline font, we continue
616 // to use the common underline font.
617 // Bug 120769:Color of underline display wrongly
618 if ( GetInfo().GetUnderFnt() &&
619 GetInfo().GetUnderFnt()->GetFont().GetUnderline() == GetFnt()->GetUnderline() &&
620 GetInfo().GetFont() && GetInfo().GetFont()->GetUnderColor() != COL_AUTO )
621 return;
622 //Bug 120769(End)
624 OSL_ENSURE( GetFnt() && LINESTYLE_NONE != GetFnt()->GetUnderline(),
625 "CheckSpecialUnderline without underlined font" );
626 MultiSelection aUnderMulti( Range( 0, GetInfo().GetText().getLength() ) );
627 const SwFont* pParaFnt = GetAttrHandler().GetFont();
628 if( pParaFnt && pParaFnt->GetUnderline() == GetFnt()->GetUnderline() )
629 aUnderMulti.SelectAll();
631 if (sw::MergedPara const*const pMerged = GetTextFrame()->GetMergedPara())
633 // first, add the paragraph properties to MultiSelection - if there are
634 // Hints too, they will override the positions if they're added later
635 sal_Int32 nTmp(0);
636 for (auto const& e : pMerged->extents)
638 if (const SvxUnderlineItem* pItem = e.pNode->GetSwAttrSet().GetItemIfSet(
639 RES_CHRATR_UNDERLINE))
641 const bool bUnderSelect(m_pFont->GetUnderline() ==
642 pItem->GetLineStyle());
643 aUnderMulti.Select(Range(nTmp, nTmp + e.nEnd - e.nStart - 1),
644 bUnderSelect);
646 nTmp += e.nEnd - e.nStart;
650 SwTextNode const* pNode(nullptr);
651 sw::MergedAttrIter iter(*GetTextFrame());
652 for (SwTextAttr const* pTextAttr = iter.NextAttr(&pNode); pTextAttr;
653 pTextAttr = iter.NextAttr(&pNode))
655 SvxUnderlineItem const*const pItem =
656 CharFormat::GetItem(*pTextAttr, RES_CHRATR_UNDERLINE);
658 if (pItem)
660 TextFrameIndex const nStart(
661 GetTextFrame()->MapModelToView(pNode, pTextAttr->GetStart()));
662 TextFrameIndex const nEnd(
663 GetTextFrame()->MapModelToView(pNode, *pTextAttr->End()));
664 if (nEnd > nStart)
666 const bool bUnderSelect = m_pFont->GetUnderline() == pItem->GetLineStyle();
667 aUnderMulti.Select(Range(sal_Int32(nStart), sal_Int32(nEnd) - 1),
668 bUnderSelect);
673 const TextFrameIndex nIndx = GetInfo().GetIdx();
674 TextFrameIndex nUnderEnd(0);
675 const size_t nCnt = aUnderMulti.GetRangeCount();
677 // find the underline range the current portion is contained in
678 for( size_t i = 0; i < nCnt; ++i )
680 const Range& rRange = aUnderMulti.GetRange( i );
681 if (nUnderEnd == TextFrameIndex(rRange.Min()))
682 nUnderEnd = TextFrameIndex(rRange.Max());
683 else if (nIndx >= TextFrameIndex(rRange.Min()))
685 nUnderEnd = TextFrameIndex(rRange.Max());
687 else
688 break;
691 if ( GetEnd() && GetEnd() <= nUnderEnd )
692 nUnderEnd = GetEnd() - TextFrameIndex(1);
694 // calculate the new common underline font
695 SwFont* pUnderlineFnt = nullptr;
696 Point aCommonBaseLine;
698 // check, if underlining is not isolated
699 if (nIndx + GetInfo().GetLen() < nUnderEnd + TextFrameIndex(1))
701 // here starts the algorithm for calculating the underline font
702 SwScriptInfo& rScriptInfo = GetInfo().GetParaPortion()->GetScriptInfo();
703 SwAttrIter aIter(*GetInfo().GetTextFrame()->GetTextNodeFirst(),
704 rScriptInfo, GetTextFrame());
706 TextFrameIndex nTmpIdx = nIndx;
707 sal_uLong nSumWidth = 0;
708 sal_uLong nSumHeight = 0;
709 sal_uLong nBold = 0;
710 sal_uInt16 nMaxBaseLineOfst = 0;
711 int nNumberOfPortions = 0;
713 while (nTmpIdx <= nUnderEnd && pPor)
715 if ( pPor->IsFlyPortion() || pPor->IsFlyCntPortion() ||
716 pPor->IsBreakPortion() || pPor->IsMarginPortion() ||
717 pPor->IsHolePortion() ||
718 ( pPor->IsMultiPortion() && ! static_cast<const SwMultiPortion*>(pPor)->IsBidi() ) )
719 break;
721 aIter.Seek( nTmpIdx );
722 if ( aIter.GetFnt()->GetEscapement() < 0 || m_pFont->IsWordLineMode() ||
723 SvxCaseMap::SmallCaps == m_pFont->GetCaseMap() )
724 break;
726 if ( !aIter.GetFnt()->GetEscapement() )
728 nSumWidth += pPor->Width();
729 const sal_uLong nFontHeight = aIter.GetFnt()->GetHeight();
731 // If we do not have a common baseline we take the baseline
732 // and the font of the lowest portion.
733 if ( nAdjustBaseLine )
735 const sal_uInt16 nTmpBaseLineOfst = AdjustBaseLine( *m_pCurr, pPor );
736 if ( nMaxBaseLineOfst < nTmpBaseLineOfst )
738 nMaxBaseLineOfst = nTmpBaseLineOfst;
739 nSumHeight = nFontHeight;
742 // in horizontal layout we build a weighted sum of the heights
743 else
744 nSumHeight += pPor->Width() * nFontHeight;
746 if ( WEIGHT_NORMAL != aIter.GetFnt()->GetWeight() )
747 nBold += pPor->Width();
750 ++nNumberOfPortions;
752 nTmpIdx += pPor->GetLen();
753 pPor = pPor->GetNextPortion();
756 // resulting height
757 if ( nNumberOfPortions > 1 && nSumWidth )
759 const sal_uLong nNewFontHeight = nAdjustBaseLine ?
760 nSumHeight :
761 nSumHeight / nSumWidth;
763 pUnderlineFnt = new SwFont( *GetInfo().GetFont() );
765 // font height
766 const SwFontScript nActual = pUnderlineFnt->GetActual();
767 pUnderlineFnt->SetSize( Size( pUnderlineFnt->GetSize( nActual ).Width(),
768 nNewFontHeight ), nActual );
770 // font weight
771 if ( 2 * nBold > nSumWidth )
772 pUnderlineFnt->SetWeight( WEIGHT_BOLD, nActual );
773 else
774 pUnderlineFnt->SetWeight( WEIGHT_NORMAL, nActual );
776 // common base line
777 aCommonBaseLine.setY( nAdjustBaseLine + nMaxBaseLineOfst );
781 // an escaped redlined portion should also have a special underlining
782 if( ! pUnderlineFnt && m_pFont->GetEscapement() > 0 && GetRedln() &&
783 GetRedln()->ChkSpecialUnderline() )
784 pUnderlineFnt = new SwFont( *m_pFont );
786 delete GetInfo().GetUnderFnt();
788 if ( pUnderlineFnt )
790 pUnderlineFnt->SetProportion( 100 );
791 pUnderlineFnt->SetEscapement( 0 );
792 pUnderlineFnt->SetStrikeout( STRIKEOUT_NONE );
793 pUnderlineFnt->SetOverline( LINESTYLE_NONE );
794 const Color aFillColor( COL_TRANSPARENT );
795 pUnderlineFnt->SetFillColor( aFillColor );
797 GetInfo().SetUnderFnt( new SwUnderlineFont( *pUnderlineFnt, nUnderEnd,
798 aCommonBaseLine ) );
800 else
801 // I'm sorry, we do not have a special underlining font for you.
802 GetInfo().SetUnderFnt( nullptr );
805 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */