tdf#35361 Add a Quick Look plugins for .od* files on macOS
[LibreOffice.git] / sw / source / core / text / itrpaint.cxx
blob6e9d48a3a8f3c3fe5ce1a5806a6e80d329cea05e
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 "porfly.hxx"
37 #include "portab.hxx"
38 #include <txatbase.hxx>
39 #include <charfmt.hxx>
40 #include "redlnitr.hxx"
41 #include "porrst.hxx"
42 #include "pormulti.hxx"
43 #include <doc.hxx>
44 #include <fmturl.hxx>
46 // Returns, if we have an underline breaking situation
47 // Adding some more conditions here means you also have to change them
48 // in SwTextPainter::CheckSpecialUnderline
49 bool IsUnderlineBreak( const SwLinePortion& rPor, const SwFont& rFnt )
51 return LINESTYLE_NONE == rFnt.GetUnderline() ||
52 rPor.IsFlyPortion() || rPor.IsFlyCntPortion() ||
53 rPor.IsBreakPortion() || rPor.IsMarginPortion() ||
54 rPor.IsHolePortion() ||
55 ( rPor.IsMultiPortion() && ! static_cast<const SwMultiPortion&>(rPor).IsBidi() ) ||
56 rFnt.GetEscapement() < 0 || rFnt.IsWordLineMode() ||
57 SvxCaseMap::SmallCaps == rFnt.GetCaseMap();
60 static Color GetUnderColor( const SwFont *pFont )
62 return pFont->GetUnderColor() == COL_AUTO ?
63 pFont->GetColor() : pFont->GetUnderColor();
66 void SwTextPainter::CtorInitTextPainter( SwTextFrame *pNewFrame, SwTextPaintInfo *pNewInf )
68 CtorInitTextCursor( pNewFrame, pNewInf );
69 m_pInf = pNewInf;
70 SwFont *pMyFnt = GetFnt();
71 GetInfo().SetFont( pMyFnt );
72 m_bPaintDrop = false;
75 SwLinePortion *SwTextPainter::CalcPaintOfst(const SwRect &rPaint, bool& rbSkippedNumPortions)
77 SwLinePortion *pPor = m_pCurr->GetFirstPortion();
78 GetInfo().SetPaintOfst( 0 );
79 SwTwips nPaintOfst = rPaint.Left();
81 // nPaintOfst was exactly set to the end, therefore <=
82 // nPaintOfst is document global, therefore add up nLeftMar
83 // const sal_uInt16 nLeftMar = sal_uInt16(GetLeftMargin());
84 // 8310: paint of LineBreaks in empty lines.
85 if( nPaintOfst && m_pCurr->Width() )
87 SwLinePortion *pLast = nullptr;
88 // 7529 and 4757: not <= nPaintOfst
89 while( pPor && GetInfo().X() + pPor->Width() + (pPor->Height()/2)
90 < nPaintOfst )
92 if( pPor->InSpaceGrp() && GetInfo().GetSpaceAdd() )
94 tools::Long nTmp = GetInfo().X() +pPor->Width() +
95 pPor->CalcSpacing( GetInfo().GetSpaceAdd(), GetInfo() );
96 if( nTmp + (pPor->Height()/2) >= nPaintOfst )
97 break;
98 GetInfo().X( nTmp );
99 GetInfo().SetIdx( GetInfo().GetIdx() + pPor->GetLen() );
101 else
102 pPor->Move( GetInfo() );
103 if (pPor->InNumberGrp()
104 && !static_cast<SwNumberPortion const*>(pPor)->HasFollow())
106 rbSkippedNumPortions = true; // all numbering portions were skipped?
108 pLast = pPor;
109 pPor = pPor->GetNextPortion();
112 // 7529: if PostIts return also pLast.
113 if( pLast && !pLast->Width() && pLast->IsPostItsPortion() )
115 pPor = pLast;
116 GetInfo().SetIdx( GetInfo().GetIdx() - pPor->GetLen() );
119 return pPor;
122 // There are two possibilities to output transparent font:
123 // 1) DrawRect on the whole line and DrawText afterwards
124 // (objectively fast, subjectively slow)
125 // 2) For every portion a DrawRect with subsequent DrawText is done
126 // (objectively slow, subjectively fast)
127 // Since the user usually judges subjectively the second method is set as default.
128 void SwTextPainter::DrawTextLine( const SwRect &rPaint, SwSaveClip &rClip,
129 const bool bUnderSized,
130 ::std::optional<SwTaggedPDFHelper> & roTaggedLabel,
131 ::std::optional<SwTaggedPDFHelper> & roTaggedParagraph,
132 bool const isPDFTaggingEnabled)
134 // maybe catch-up adjustment
135 GetAdjusted();
136 AddExtraBlankWidth();
137 GetInfo().SetpSpaceAdd( m_pCurr->GetpLLSpaceAdd() );
138 GetInfo().ResetSpaceIdx();
139 GetInfo().SetKanaComp( m_pCurr->GetpKanaComp() );
140 GetInfo().ResetKanaIdx();
141 // The size of the frame
142 GetInfo().SetIdx( GetStart() );
143 GetInfo().SetPos( GetTopLeft() );
145 const bool bDrawInWindow = GetInfo().OnWin();
147 // 6882: blank lines can't be optimized by removing them if Formatting Marks are shown
148 const bool bEndPor = GetInfo().GetOpt().IsParagraph() && GetInfo().GetText().isEmpty();
150 bool bSkippedNumPortions(false);
151 SwLinePortion *pPor = bEndPor ? m_pCurr->GetFirstPortion() : CalcPaintOfst(rPaint, bSkippedNumPortions);
153 if (bSkippedNumPortions // ugly but hard to check earlier in PaintSwFrame:
154 && !GetInfo().GetTextFrame()->GetTextNodeForParaProps()->IsOutline())
155 { // there is a num portion but it is outside of the frame area and not painted
156 assert(!roTaggedLabel);
157 assert(!roTaggedParagraph);
158 Frame_Info aFrameInfo(*m_pFrame, false); // open LBody
159 roTaggedParagraph.emplace(nullptr, &aFrameInfo, nullptr, *GetInfo().GetOut());
162 SwTaggedPDFHelper::EndCurrentLink(*GetInfo().GetOut());
164 // Optimization!
165 SwTwips nMaxRight = std::min<SwTwips>( rPaint.Right(), Right() );
166 const SwTwips nTmpLeft = GetInfo().X();
167 //compatibility settings: allow tabstop text to exceed right margin
168 const auto& iDSA = GetInfo().GetTextFrame()->GetDoc().getIDocumentSettingAccess();
169 const bool bTabOverMargin = iDSA.get(DocumentSettingId::TAB_OVER_MARGIN);
170 const bool bTabOverSpacing = iDSA.get(DocumentSettingId::TAB_OVER_SPACING);
171 if (bTabOverMargin || bTabOverSpacing)
173 SwLinePortion* pPorIter = pPor;
174 while( pPorIter )
176 if( pPorIter->InTabGrp() )
178 const SwTabPortion* pTabPor = static_cast<SwTabPortion*>(pPorIter);
179 const SwTwips nTabPos = nTmpLeft + pTabPor->GetTabPos();
180 if( nMaxRight < nTabPos )
182 nMaxRight = rPaint.Right();
183 break;
186 pPorIter = pPorIter->GetNextPortion();
189 if( !bEndPor && nTmpLeft >= nMaxRight )
190 return;
192 // DropCaps!
193 // 7538: of course for the printer, too
194 if( !m_bPaintDrop )
196 // 8084: Optimization, less painting
197 // AMA: By 8084 7538 has been revived
198 // bDrawInWindow removed, so that DropCaps also can be printed
199 m_bPaintDrop = pPor == m_pCurr->GetFirstPortion()
200 && GetDropLines() >= GetLineNr();
203 SwTwips nTmpHeight, nTmpAscent;
204 CalcAscentAndHeight( nTmpAscent, nTmpHeight );
206 // bClip decides if there's a need to clip
207 // The whole thing must be done before retouching
209 bool bClip = ( bDrawInWindow || bUnderSized ) && !rClip.IsChg();
210 if( bClip && pPor )
212 // If TopLeft or BottomLeft of the line are outside, the we must clip.
213 // The check for Right() is done in the output loop ...
215 if( GetInfo().GetPos().X() < rPaint.Left() ||
216 GetInfo().GetPos().Y() < rPaint.Top() ||
217 GetInfo().GetPos().Y() + nTmpHeight > rPaint.Top() + rPaint.Height() )
219 bClip = false;
220 rClip.ChgClip(rPaint, m_pFrame, m_pCurr->GetExtraAscent(), m_pCurr->GetExtraDescent());
222 #if OSL_DEBUG_LEVEL > 1
223 static bool bClipAlways = false;
224 if( bClip && bClipAlways )
225 { bClip = false;
226 rClip.ChgClip( rPaint );
228 #endif
231 // Alignment
232 OutputDevice* pOut = GetInfo().GetOut();
233 Point aPnt1( nTmpLeft, GetInfo().GetPos().Y() );
234 if ( aPnt1.X() < rPaint.Left() )
235 aPnt1.setX( rPaint.Left() );
236 if ( aPnt1.Y() < rPaint.Top() )
237 aPnt1.setY( rPaint.Top() );
238 Point aPnt2( GetInfo().GetPos().X() + nMaxRight - GetInfo().X(),
239 GetInfo().GetPos().Y() + nTmpHeight );
240 if ( aPnt2.X() > rPaint.Right() )
241 aPnt2.setX( rPaint.Right() );
242 if ( aPnt2.Y() > rPaint.Bottom() )
243 aPnt2.setY( rPaint.Bottom() );
245 const SwRect aLineRect( aPnt1, aPnt2 );
247 if( m_pCurr->IsClipping() )
249 const SwTextFrame& rFrame = *GetInfo().GetTextFrame();
250 // tdf#117448 at small fixed line height, enlarge clipping area in table cells
251 // to show previously clipped text content on the area of paragraph margins
252 if ( rFrame.IsInTab() )
253 rClip.ChgClip(aLineRect, m_pFrame, rFrame.GetTopMargin(), rFrame.GetBottomMargin());
254 else
255 rClip.ChgClip( aLineRect, m_pFrame );
256 bClip = false;
259 if( !pPor && !bEndPor )
260 return;
262 // Baseline output also if non-TextPortion (compare TabPor with Fill)
263 // if no special vertical alignment is used,
264 // we calculate Y value for the whole line
265 SwTextGridItem const*const pGrid(GetGridItem(GetTextFrame()->FindPageFrame()));
266 const bool bAdjustBaseLine =
267 GetLineInfo().HasSpecialAlign( GetTextFrame()->IsVertical() ) ||
268 ( nullptr != pGrid ) || m_pCurr->GetHangingBaseline();
269 const SwTwips nLineBaseLine = GetInfo().GetPos().Y() + nTmpAscent;
270 if ( ! bAdjustBaseLine )
271 GetInfo().Y( nLineBaseLine );
273 // 7529: Pre-paint post-its
274 if( GetInfo().OnWin() && pPor && !pPor->Width() )
276 SeekAndChg( GetInfo() );
278 if( bAdjustBaseLine )
280 const SwTwips nOldY = GetInfo().Y();
282 GetInfo().Y( GetInfo().GetPos().Y() + AdjustBaseLine( *m_pCurr, nullptr,
283 GetInfo().GetFont()->GetHeight( GetInfo().GetVsh(), *pOut ),
284 GetInfo().GetFont()->GetAscent( GetInfo().GetVsh(), *pOut )
285 ) );
287 pPor->PrePaint( GetInfo(), pPor );
288 GetInfo().Y( nOldY );
290 else
291 pPor->PrePaint( GetInfo(), pPor );
294 // 7923: EndPortions output chars, too, that's why we change the font
295 if( bEndPor )
296 SeekStartAndChg( GetInfo() );
298 const bool bRest = m_pCurr->IsRest();
299 bool bFirst = true;
301 SwArrowPortion *pArrow = nullptr;
302 // Reference portion for the paragraph end portion
303 SwLinePortion* pEndTempl = m_pCurr->GetFirstPortion();
305 while( pPor )
307 bool bSeeked = true;
308 GetInfo().SetLen( pPor->GetLen() );
310 const SwTwips nOldY = GetInfo().Y();
312 if ( bAdjustBaseLine )
314 GetInfo().Y( GetInfo().GetPos().Y() + AdjustBaseLine( *m_pCurr, pPor ) );
316 // we store the last portion, because a possible paragraph
317 // end character has the same font as this portion
318 // (only in special vertical alignment case, otherwise the first
319 // portion of the line is used)
320 if ( pPor->Width() && pPor->InTextGrp() )
321 pEndTempl = pPor;
324 // set redlining for line break symbol
325 if ( pPor->IsBreakPortion() && GetInfo().GetOpt().IsParagraph() && GetRedln() )
327 SeekAndChg( GetInfo() );
328 if ( m_pCurr->GetRedlineEndType() != RedlineType::None )
329 static_cast<SwBreakPortion&>(*pPor).SetRedline( m_pCurr->GetRedlineEndType() );
332 // A special case are GluePortions which output blanks.
334 // 6168: Avoid that the rest of a FieldPortion gets the attributes of the
335 // next portion with SeekAndChgBefore():
336 if( bRest && pPor->InFieldGrp() && !pPor->GetLen() )
337 SeekAndChgBefore( GetInfo() );
338 else if ( pPor->IsQuoVadisPortion() )
340 // A remark on QuoVadis/ErgoSum:
341 // We use the Font set for the Paragraph for these portions.
342 // Thus, we initialize:
343 TextFrameIndex nOffset = GetInfo().GetIdx();
344 SeekStartAndChg( GetInfo(), true );
345 if( GetRedln() && m_pCurr->HasRedline() )
347 std::pair<SwTextNode const*, sal_Int32> const pos(
348 GetTextFrame()->MapViewToModel(nOffset));
349 GetRedln()->Seek(*m_pFont, pos.first->GetIndex(), pos.second, 0);
352 else if( pPor->InTextGrp() || pPor->InFieldGrp() || pPor->InTabGrp() )
353 SeekAndChg( GetInfo() );
354 else if ( !bFirst && pPor->IsBreakPortion() && GetInfo().GetOpt().IsParagraph() )
356 // Paragraph symbols should have the same font as the paragraph in front of them,
357 // except for the case that there's redlining in the paragraph
358 if( GetRedln() )
359 SeekAndChg( GetInfo() );
360 else
361 SeekAndChgBefore( GetInfo() );
363 else
364 bSeeked = false;
366 // bRest = false;
368 // If the end of the portion juts out, it is clipped.
369 // A safety distance of half the height is added, so that
370 // TTF-"f" isn't overlapping into the page margin.
371 if( bClip &&
372 GetInfo().X() + pPor->Width() + ( pPor->Height() / 2 ) > nMaxRight )
374 bClip = false;
375 rClip.ChgClip(rPaint, m_pFrame, m_pCurr->GetExtraAscent(), m_pCurr->GetExtraDescent());
378 // Portions, which lay "below" the text like post-its
379 SwLinePortion *pNext = pPor->GetNextPortion();
380 if( GetInfo().OnWin() && pNext && !pNext->Width() )
382 // Fix 11289: Fields were omitted here because of Last!=Owner during
383 // loading Brief.sdw. Now the fields are allowed again,
384 // by bSeeked Last!=Owner is being avoided.
385 if ( !bSeeked )
386 SeekAndChg( GetInfo() );
387 pNext->PrePaint( GetInfo(), pPor );
390 // We calculate a separate font for underlining.
391 CheckSpecialUnderline( pPor, bAdjustBaseLine ? nOldY : 0 );
392 SwUnderlineFont* pUnderLineFnt = GetInfo().GetUnderFnt();
393 if ( pUnderLineFnt )
395 const Point aTmpPoint( GetInfo().X(),
396 bAdjustBaseLine ?
397 pUnderLineFnt->GetPos().Y() :
398 nLineBaseLine );
399 pUnderLineFnt->SetPos( aTmpPoint );
402 // in extended input mode we do not want a common underline font.
403 SwUnderlineFont* pOldUnderLineFnt = nullptr;
404 if ( GetRedln() && GetRedln()->ExtOn() )
406 pOldUnderLineFnt = GetInfo().GetUnderFnt();
407 GetInfo().SetUnderFnt( nullptr );
410 // multiple numbering portions are possible :(
411 if ((pPor->InNumberGrp() // also footnote label
412 // weird special case, bullet with soft hyphen
413 || (pPor->InHyphGrp() && pNext && pNext->InNumberGrp()))
414 && !GetInfo().GetTextFrame()->GetTextNodeForParaProps()->IsOutline()
415 && !roTaggedLabel) // note: CalcPaintOfst may skip some portions
417 assert(isPDFTaggingEnabled);
418 Por_Info aPorInfo(*pPor, *this, 1); // open Lbl
419 roTaggedLabel.emplace(nullptr, nullptr, &aPorInfo, *pOut);
423 // #i16816# tagged pdf support
424 Por_Info aPorInfo(*pPor, *this, 0);
425 SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, &aPorInfo, *pOut );
427 if( pPor->IsMultiPortion() )
428 PaintMultiPortion( rPaint, static_cast<SwMultiPortion&>(*pPor) );
429 else
430 pPor->Paint( GetInfo() );
433 // lazy open LBody and paragraph tag after num portions have been painted to Lbl
434 if (pPor->InNumberGrp() // also footnote label
435 // note: numbering portion may be split if it has multiple scripts
436 && !static_cast<SwNumberPortion const*>(pPor)->HasFollow()) // so wait for the last one
438 if (!GetInfo().GetTextFrame()->GetTextNodeForParaProps()->IsOutline())
440 assert(roTaggedLabel);
441 roTaggedLabel.reset(); // close Lbl
442 assert(!roTaggedParagraph);
443 Frame_Info aFrameInfo(*m_pFrame, false); // open LBody
444 roTaggedParagraph.emplace(nullptr, &aFrameInfo, nullptr, *pOut);
446 else
448 assert(!roTaggedLabel);
452 // reset underline font
453 if ( pOldUnderLineFnt )
454 GetInfo().SetUnderFnt( pOldUnderLineFnt );
456 // reset (for special vertical alignment)
457 GetInfo().Y( nOldY );
459 if (GetFnt()->IsURL() && pPor->InTextGrp())
460 GetInfo().NotifyURL(*pPor);
461 else if (pPor->IsFlyCntPortion())
463 if (auto* pFlyContentPortion = dynamic_cast<sw::FlyContentPortion*>(pPor))
465 if (auto* pFlyFrame = pFlyContentPortion->GetFlyFrame())
467 if (auto* pFormat = pFlyFrame->GetFormat())
469 auto& url = pFormat->GetURL();
470 if (!url.GetURL().isEmpty()) // TODO: url.GetMap() ?
471 GetInfo().NotifyURL(*pPor);
477 bFirst &= !pPor->GetLen();
478 if( pNext || !pPor->IsMarginPortion() )
479 pPor->Move( GetInfo() );
480 if( pPor->IsArrowPortion() && GetInfo().OnWin() && !pArrow )
481 pArrow = static_cast<SwArrowPortion*>(pPor);
483 pPor = bDrawInWindow || GetInfo().X() <= nMaxRight ||
484 // #i16816# tagged pdf support
485 ( GetInfo().GetVsh() &&
486 GetInfo().GetVsh()->GetViewOptions()->IsPDFExport() &&
487 pNext && pNext->IsHolePortion() ) ?
488 pNext :
489 nullptr;
490 if (!pPor && isPDFTaggingEnabled && (roTaggedLabel || !roTaggedParagraph))
491 { // check if the end of the list label is off-screen
492 auto FindEndOfNumbering = [&](SwLinePortion const* pP) {
493 while (pP)
495 if (pP->InNumberGrp()
496 && !static_cast<SwNumberPortion const*>(pP)->HasFollow())
498 if (roTaggedLabel)
500 roTaggedLabel.reset();
501 } // else, if the numbering isn't visible at all, no Lbl
502 if (!GetInfo().GetTextFrame()->GetTextNodeForParaProps()->IsOutline())
504 Frame_Info aFrameInfo(*m_pFrame, false); // open LBody
505 roTaggedParagraph.emplace(nullptr, &aFrameInfo, nullptr, *GetInfo().GetOut());
507 return true;
509 pP = pP->GetNextPortion();
511 return false;
513 if (!FindEndOfNumbering(pNext)) // check rest of current line
515 // check lines that will be cut off
516 if (rPaint.Bottom() < Y() + GetLineHeight())
518 for (SwLineLayout const* pLine = GetNext(); pLine; pLine = pLine->GetNext())
520 if (FindEndOfNumbering(pLine->GetFirstPortion()))
522 break;
530 // delete underline font
531 delete GetInfo().GetUnderFnt();
532 GetInfo().SetUnderFnt( nullptr );
534 // paint remaining stuff, e.g. the line ending symbols, pilcrow (¶) and the line break
535 if( bDrawInWindow )
537 // If special vertical alignment is enabled, GetInfo().Y() is the
538 // top of the current line. Therefore is has to be adjusted for
539 // the painting of the remaining stuff. We first store the old value.
540 const SwTwips nOldY = GetInfo().Y();
542 if( !GetNextLine() &&
543 GetInfo().GetVsh() && !GetInfo().GetVsh()->IsPreview() &&
544 GetInfo().GetOpt().IsParagraph() && !GetTextFrame()->GetFollow() &&
545 GetInfo().GetIdx() >= TextFrameIndex(GetInfo().GetText().getLength()))
547 bool bHasRedlineEnd( GetRedln() && m_pCurr->HasRedlineEnd() );
548 RedlineType eRedlineEnd = bHasRedlineEnd ? m_pCurr->GetRedlineEndType() : RedlineType::None;
549 if( bHasRedlineEnd )
551 TextFrameIndex nOffset = GetInfo().GetIdx();
552 SeekStartAndChg( GetInfo(), true );
553 std::pair<SwTextNode const*, sal_Int32> const pos(
554 GetTextFrame()->MapViewToModel(nOffset));
555 GetRedln()->Seek(*m_pFont, pos.first->GetIndex(), pos.second, 0);
557 const SwTmpEndPortion aEnd( *pEndTempl,
558 bHasRedlineEnd && eRedlineEnd != RedlineType::Delete ? m_pFont->GetUnderline() : LINESTYLE_NONE,
559 bHasRedlineEnd && eRedlineEnd == RedlineType::Delete ? m_pFont->GetStrikeout() : STRIKEOUT_NONE,
560 bHasRedlineEnd ? m_pFont->GetColor() : COL_AUTO );
561 GetFnt()->ChgPhysFnt( GetInfo().GetVsh(), *pOut );
563 if ( bAdjustBaseLine )
564 GetInfo().Y( GetInfo().GetPos().Y()
565 + AdjustBaseLine( *m_pCurr, &aEnd ) );
566 GetInfo().X( GetInfo().X() +
567 // tdf#163042 In the case of shrunk lines with a single portion, adjust
568 // the line width (if needed, i.e. if the shrunk line doesn't end in a space)
569 // to show the terminating pilcrow at the correct position, and not before that
570 ( ( !( pEndTempl->GetNextPortion() && pEndTempl->GetNextPortion()->IsHolePortion() ) &&
571 std::abs( m_pCurr->Width() - m_pCurr->GetFirstPortion()->Width() ) <= 1 && m_pCurr->ExtraShrunkWidth() > 0 )
572 ? m_pCurr->ExtraShrunkWidth() - m_pCurr->Width() : 0 ) +
573 ( GetCurr()->IsHanging() ? GetCurr()->GetHangingMargin() : 0 ) );
574 aEnd.Paint( GetInfo() );
575 GetInfo().Y( nOldY );
577 if( GetInfo().GetVsh() && !GetInfo().GetVsh()->IsPreview() )
579 const bool bNextUndersized =
580 ( GetTextFrame()->GetNext() &&
581 0 == GetTextFrame()->GetNext()->getFramePrintArea().Height() &&
582 GetTextFrame()->GetNext()->IsTextFrame() &&
583 static_cast<SwTextFrame*>(GetTextFrame()->GetNext())->IsUndersized() ) ;
585 if( bUnderSized || bNextUndersized )
587 if ( bAdjustBaseLine )
588 GetInfo().Y( GetInfo().GetPos().Y() + m_pCurr->GetAscent() );
590 // Left arrow (text overflowing)
591 if( pArrow )
592 GetInfo().DrawRedArrow( *pArrow );
594 // GetInfo().Y() must be current baseline
595 SwTwips nDiff = GetInfo().Y() + nTmpHeight - nTmpAscent - GetTextFrame()->getFrameArea().Bottom();
596 if( ( nDiff > 0 &&
597 (GetEnd() < TextFrameIndex(GetInfo().GetText().getLength()) ||
598 ( nDiff > nTmpHeight/2 && GetPrevLine() ) ) ) ||
599 (nDiff >= 0 && bNextUndersized) )
602 // Right arrow (text overflowing)
603 SwArrowPortion aArrow( GetInfo() );
604 GetInfo().DrawRedArrow( aArrow );
607 GetInfo().Y( nOldY );
612 if( m_pCurr->IsClipping() )
613 rClip.ChgClip( rPaint, m_pFrame );
616 void SwTextPainter::CheckSpecialUnderline( const SwLinePortion* pPor,
617 tools::Long nAdjustBaseLine )
619 // Check if common underline should not be continued
620 if ( IsUnderlineBreak( *pPor, *m_pFont ) )
622 // delete underline font
623 delete GetInfo().GetUnderFnt();
624 GetInfo().SetUnderFnt( nullptr );
625 return;
627 // Reuse calculated underline font as much as possible.
628 if (GetInfo().GetUnderFnt() &&
629 GetInfo().GetIdx() + pPor->GetLen() <= GetInfo().GetUnderFnt()->GetEnd() + TextFrameIndex(1))
631 SwFont &rFont = GetInfo().GetUnderFnt()->GetFont();
632 const Color aColor = GetUnderColor( GetInfo().GetFont() );
633 if ( GetUnderColor( &rFont ) != aColor )
634 rFont.SetColor( aColor );
635 return;
638 // If current underline matches the common underline font, we continue
639 // to use the common underline font.
640 // Bug 120769:Color of underline display wrongly
641 if ( GetInfo().GetUnderFnt() &&
642 GetInfo().GetUnderFnt()->GetFont().GetUnderline() == GetFnt()->GetUnderline() &&
643 GetInfo().GetFont() && GetInfo().GetFont()->GetUnderColor() != COL_AUTO )
644 return;
645 //Bug 120769(End)
647 OSL_ENSURE( GetFnt() && LINESTYLE_NONE != GetFnt()->GetUnderline(),
648 "CheckSpecialUnderline without underlined font" );
649 MultiSelection aUnderMulti( Range( 0, GetInfo().GetText().getLength() ) );
650 const SwFont* pParaFnt = GetAttrHandler().GetFont();
651 if( pParaFnt && pParaFnt->GetUnderline() == GetFnt()->GetUnderline() )
652 aUnderMulti.SelectAll();
654 if (sw::MergedPara const*const pMerged = GetTextFrame()->GetMergedPara())
656 // first, add the paragraph properties to MultiSelection - if there are
657 // Hints too, they will override the positions if they're added later
658 sal_Int32 nTmp(0);
659 for (auto const& e : pMerged->extents)
661 if (const SvxUnderlineItem* pItem = e.pNode->GetSwAttrSet().GetItemIfSet(
662 RES_CHRATR_UNDERLINE))
664 const bool bUnderSelect(m_pFont->GetUnderline() ==
665 pItem->GetLineStyle());
666 aUnderMulti.Select(Range(nTmp, nTmp + e.nEnd - e.nStart - 1),
667 bUnderSelect);
669 nTmp += e.nEnd - e.nStart;
673 SwTextNode const* pNode(nullptr);
674 sw::MergedAttrIter iter(*GetTextFrame());
675 for (SwTextAttr const* pTextAttr = iter.NextAttr(&pNode); pTextAttr;
676 pTextAttr = iter.NextAttr(&pNode))
678 SvxUnderlineItem const*const pItem =
679 CharFormat::GetItem(*pTextAttr, RES_CHRATR_UNDERLINE);
681 if (pItem)
683 TextFrameIndex const nStart(
684 GetTextFrame()->MapModelToView(pNode, pTextAttr->GetStart()));
685 TextFrameIndex const nEnd(
686 GetTextFrame()->MapModelToView(pNode, *pTextAttr->End()));
687 if (nEnd > nStart)
689 const bool bUnderSelect = m_pFont->GetUnderline() == pItem->GetLineStyle();
690 aUnderMulti.Select(Range(sal_Int32(nStart), sal_Int32(nEnd) - 1),
691 bUnderSelect);
696 const TextFrameIndex nIndx = GetInfo().GetIdx();
697 TextFrameIndex nUnderEnd(0);
698 const size_t nCnt = aUnderMulti.GetRangeCount();
700 // find the underline range the current portion is contained in
701 for( size_t i = 0; i < nCnt; ++i )
703 const Range& rRange = aUnderMulti.GetRange( i );
704 if (nUnderEnd == TextFrameIndex(rRange.Min()))
705 nUnderEnd = TextFrameIndex(rRange.Max());
706 else if (nIndx >= TextFrameIndex(rRange.Min()))
708 nUnderEnd = TextFrameIndex(rRange.Max());
710 else
711 break;
714 if ( GetEnd() && GetEnd() <= nUnderEnd )
715 nUnderEnd = GetEnd() - TextFrameIndex(1);
717 // calculate the new common underline font
718 SwFont* pUnderlineFnt = nullptr;
719 Point aCommonBaseLine;
721 // check, if underlining is not isolated
722 if (nIndx + GetInfo().GetLen() < nUnderEnd + TextFrameIndex(1))
724 // here starts the algorithm for calculating the underline font
725 SwScriptInfo& rScriptInfo = GetInfo().GetParaPortion()->GetScriptInfo();
726 SwAttrIter aIter(*GetInfo().GetTextFrame()->GetTextNodeFirst(),
727 rScriptInfo, GetTextFrame());
729 TextFrameIndex nTmpIdx = nIndx;
730 sal_uLong nSumWidth = 0;
731 sal_uLong nSumHeight = 0;
732 sal_uLong nBold = 0;
733 sal_uInt16 nMaxBaseLineOfst = 0;
734 int nNumberOfPortions = 0;
736 while (nTmpIdx <= nUnderEnd && pPor)
738 if ( pPor->IsFlyPortion() || pPor->IsFlyCntPortion() ||
739 pPor->IsBreakPortion() || pPor->IsMarginPortion() ||
740 pPor->IsHolePortion() ||
741 ( pPor->IsMultiPortion() && ! static_cast<const SwMultiPortion*>(pPor)->IsBidi() ) )
742 break;
744 aIter.Seek( nTmpIdx );
745 if ( aIter.GetFnt()->GetEscapement() < 0 || m_pFont->IsWordLineMode() ||
746 SvxCaseMap::SmallCaps == m_pFont->GetCaseMap() )
747 break;
749 if ( !aIter.GetFnt()->GetEscapement() )
751 nSumWidth += pPor->Width();
752 const sal_uLong nFontHeight = aIter.GetFnt()->GetHeight();
754 // If we do not have a common baseline we take the baseline
755 // and the font of the lowest portion.
756 if ( nAdjustBaseLine )
758 const sal_uInt16 nTmpBaseLineOfst = AdjustBaseLine( *m_pCurr, pPor );
759 if ( nMaxBaseLineOfst < nTmpBaseLineOfst )
761 nMaxBaseLineOfst = nTmpBaseLineOfst;
762 nSumHeight = nFontHeight;
765 // in horizontal layout we build a weighted sum of the heights
766 else
767 nSumHeight += pPor->Width() * nFontHeight;
769 if ( WEIGHT_NORMAL != aIter.GetFnt()->GetWeight() )
770 nBold += pPor->Width();
773 ++nNumberOfPortions;
775 nTmpIdx += pPor->GetLen();
776 pPor = pPor->GetNextPortion();
779 // resulting height
780 if ( nNumberOfPortions > 1 && nSumWidth )
782 const sal_uLong nNewFontHeight = nAdjustBaseLine ?
783 nSumHeight :
784 nSumHeight / nSumWidth;
786 pUnderlineFnt = new SwFont( *GetInfo().GetFont() );
788 // font height
789 const SwFontScript nActual = pUnderlineFnt->GetActual();
790 pUnderlineFnt->SetSize( Size( pUnderlineFnt->GetSize( nActual ).Width(),
791 nNewFontHeight ), nActual );
793 // font weight
794 if ( 2 * nBold > nSumWidth )
795 pUnderlineFnt->SetWeight( WEIGHT_BOLD, nActual );
796 else
797 pUnderlineFnt->SetWeight( WEIGHT_NORMAL, nActual );
799 // common base line
800 aCommonBaseLine.setY( nAdjustBaseLine + nMaxBaseLineOfst );
804 // an escaped redlined portion should also have a special underlining
805 if( ! pUnderlineFnt && m_pFont->GetEscapement() > 0 && GetRedln() &&
806 GetRedln()->ChkSpecialUnderline() )
807 pUnderlineFnt = new SwFont( *m_pFont );
809 delete GetInfo().GetUnderFnt();
811 if ( pUnderlineFnt )
813 pUnderlineFnt->SetProportion( 100 );
814 pUnderlineFnt->SetEscapement( 0 );
815 pUnderlineFnt->SetStrikeout( STRIKEOUT_NONE );
816 pUnderlineFnt->SetOverline( LINESTYLE_NONE );
817 const Color aFillColor( COL_TRANSPARENT );
818 pUnderlineFnt->SetFillColor( aFillColor );
820 GetInfo().SetUnderFnt( new SwUnderlineFont( *pUnderlineFnt, nUnderEnd,
821 aCommonBaseLine ) );
823 else
824 // I'm sorry, we do not have a special underlining font for you.
825 GetInfo().SetUnderFnt( nullptr );
828 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */