xref: /core/sw/source/core/text/itrpaint.cxx (revision 180f2860)
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 <hintids.hxx>
21 #include <flyfrm.hxx>
22 #include <viewopt.hxx>
23 #include <txtatr.hxx>
24 #include <tools/multisel.hxx>
25 #include <editeng/escapementitem.hxx>
26 #include <editeng/udlnitem.hxx>
27 #include <editeng/lrspitem.hxx>
28 #include <txtinet.hxx>
29 #include <fchrfmt.hxx>
30 #include <frmatr.hxx>
31 #include <sfx2/printer.hxx>
32 #include <fmtfld.hxx>
33 #include <fldbas.hxx>
34 #include <rootfrm.hxx>
35 #include <pagefrm.hxx>
36 #include <pagedesc.hxx>
37 #include <tgrditem.hxx>
38 
39 #include <EnhancedPDFExportHelper.hxx>
40 #include <IDocumentSettingAccess.hxx>
41 
42 #include <flyfrms.hxx>
43 #include <viewsh.hxx>
44 #include "itrpaint.hxx"
45 #include <txtfrm.hxx>
46 #include <txtfly.hxx>
47 #include <swfont.hxx>
48 #include "txtpaint.hxx"
49 #include "portab.hxx"
50 #include "porfly.hxx"
51 #include "porfld.hxx"
52 #include <frmfmt.hxx>
53 #include <txatbase.hxx>
54 #include <charfmt.hxx>
55 #include "redlnitr.hxx"
56 #include "porrst.hxx"
57 #include "pormulti.hxx"
58 
59 // Returns, if we have an underline breaking situation
60 // Adding some more conditions here means you also have to change them
61 // in SwTextPainter::CheckSpecialUnderline
62 bool IsUnderlineBreak( const SwLinePortion& rPor, const SwFont& rFnt )
63 {
64     return LINESTYLE_NONE == rFnt.GetUnderline() ||
65            rPor.IsFlyPortion() || rPor.IsFlyCntPortion() ||
66            rPor.IsBreakPortion() || rPor.IsMarginPortion() ||
67            rPor.IsHolePortion() ||
68           ( rPor.IsMultiPortion() && ! static_cast<const SwMultiPortion&>(rPor).IsBidi() ) ||
69            rFnt.GetEscapement() < 0 || rFnt.IsWordLineMode() ||
70            SvxCaseMap::SmallCaps == rFnt.GetCaseMap();
71 }
72 
73 const Color GetUnderColor( const SwFont *pFont )
74 {
75     return pFont->GetUnderColor() == Color( COL_AUTO ) ?
76         pFont->GetColor() : pFont->GetUnderColor();
77 }
78 
79 void SwTextPainter::CtorInitTextPainter( SwTextFrame *pNewFrame, SwTextPaintInfo *pNewInf )
80 {
81     CtorInitTextCursor( pNewFrame, pNewInf );
82     m_pInf = pNewInf;
83     SwFont *pMyFnt = GetFnt();
84     GetInfo().SetFont( pMyFnt );
85     bPaintDrop = false;
86 }
87 
88 SwLinePortion *SwTextPainter::CalcPaintOfst( const SwRect &rPaint )
89 {
90     SwLinePortion *pPor = m_pCurr->GetFirstPortion();
91     GetInfo().SetPaintOfst( 0 );
92     SwTwips nPaintOfst = rPaint.Left();
93 
94     // nPaintOfst was exactly set to the end, therefore <=
95     // nPaintOfst is document global, therefore add up nLeftMar
96     // const sal_uInt16 nLeftMar = sal_uInt16(GetLeftMargin());
97     // 8310: paint of LineBreaks in empty lines.
98     if( nPaintOfst && m_pCurr->Width() )
99     {
100         SwLinePortion *pLast = nullptr;
101         // 7529 and 4757: not <= nPaintOfst
102         while( pPor && GetInfo().X() + pPor->Width() + (pPor->Height()/2)
103                        < nPaintOfst )
104         {
105             if( pPor->InSpaceGrp() && GetInfo().GetSpaceAdd() )
106             {
107                 long nTmp = GetInfo().X() +pPor->Width() +
108                     pPor->CalcSpacing( GetInfo().GetSpaceAdd(), GetInfo() );
109                 if( nTmp + (pPor->Height()/2) >= nPaintOfst )
110                     break;
111                 GetInfo().X( nTmp );
112                 GetInfo().SetIdx( GetInfo().GetIdx() + pPor->GetLen() );
113             }
114             else
115                 pPor->Move( GetInfo() );
116             pLast = pPor;
117             pPor = pPor->GetPortion();
118         }
119 
120         // 7529: if PostIts return also pLast.
121         if( pLast && !pLast->Width() && pLast->IsPostItsPortion() )
122         {
123             pPor = pLast;
124             GetInfo().SetIdx( GetInfo().GetIdx() - pPor->GetLen() );
125         }
126     }
127     return pPor;
128 }
129 
130 // There are two possibilities to output transparent font:
131 // 1) DrawRect on the whole line and DrawText afterwards
132 //    (objectively fast, subjectively slow)
133 // 2) For every portion a DrawRect with subsequent DrawText is done
134 //    (objectively slow, subjectively fast)
135 // Since the user usually judges subjectively the second method is set as default.
136 void SwTextPainter::DrawTextLine( const SwRect &rPaint, SwSaveClip &rClip,
137                                  const bool bUnderSz )
138 {
139 #if OSL_DEBUG_LEVEL > 1
140 //    sal_uInt16 nFntHeight = GetInfo().GetFont()->GetHeight( GetInfo().GetVsh(), GetInfo().GetOut() );
141 //    sal_uInt16 nFntAscent = GetInfo().GetFont()->GetAscent( GetInfo().GetVsh(), GetInfo().GetOut() );
142 #endif
143 
144     // maybe catch-up adjustment
145     GetAdjusted();
146     GetInfo().SetpSpaceAdd( m_pCurr->GetpLLSpaceAdd() );
147     GetInfo().ResetSpaceIdx();
148     GetInfo().SetKanaComp( m_pCurr->GetpKanaComp() );
149     GetInfo().ResetKanaIdx();
150     // The size of the frame
151     GetInfo().SetIdx( GetStart() );
152     GetInfo().SetPos( GetTopLeft() );
153 
154     const bool bDrawInWindow = GetInfo().OnWin();
155 
156     // 6882: blank lines can't be optimized by removing them if Formatting Marks are shown
157     const bool bEndPor = GetInfo().GetOpt().IsParagraph() && GetInfo().GetText().isEmpty();
158 
159     SwLinePortion *pPor = bEndPor ? m_pCurr->GetFirstPortion() : CalcPaintOfst( rPaint );
160 
161     // Optimization!
162     SwTwips nMaxRight = std::min( rPaint.Right(), Right() );
163     const SwTwips nTmpLeft = GetInfo().X();
164     //compatibility setting: allow tabstop text to exceed right margin
165     if( GetInfo().GetTextFrame()->GetTextNode()->getIDocumentSettingAccess()->get(DocumentSettingId::TAB_OVER_MARGIN) )
166     {
167         SwLinePortion* pPorIter = pPor;
168         while( pPorIter )
169         {
170             if( pPorIter->InTabGrp() )
171             {
172                const SwTabPortion* pTabPor = static_cast<SwTabPortion*>(pPorIter);
173                const SwTwips nTabPos = nTmpLeft + pTabPor->GetTabPos();
174                 if( nMaxRight < nTabPos )
175                 {
176                     nMaxRight = rPaint.Right();
177                     break;
178                 }
179             }
180             pPorIter = pPorIter->GetPortion();
181         }
182     }
183     if( !bEndPor && nTmpLeft >= nMaxRight )
184         return;
185 
186     // DropCaps!
187     // 7538: of course for the printer, too
188     if( !bPaintDrop )
189     {
190         // 8084: Optimization, less painting
191         // AMA: By 8084 7538 has been revived
192         // bDrawInWindow removed, so that DropCaps also can be printed
193         bPaintDrop = pPor == m_pCurr->GetFirstPortion()
194                      && GetDropLines() >= GetLineNr();
195     }
196 
197     sal_uInt16 nTmpHeight, nTmpAscent;
198     CalcAscentAndHeight( nTmpAscent, nTmpHeight );
199 
200     // bClip decides if there's a need to clip
201     // The whole thing must be done before retouching
202 
203     bool bClip = ( bDrawInWindow || bUnderSz ) && !rClip.IsChg();
204     if( bClip && pPor )
205     {
206         // If TopLeft or BottomLeft of the line are outside, the we must clip.
207         // The check for Right() is done in the output loop ...
208 
209         if( GetInfo().GetPos().X() < rPaint.Left() ||
210             GetInfo().GetPos().Y() < rPaint.Top() ||
211             GetInfo().GetPos().Y() + nTmpHeight > rPaint.Top() + rPaint.Height() )
212         {
213             bClip = false;
214             rClip.ChgClip( rPaint, m_pFrame, m_pCurr->HasUnderscore() );
215         }
216 #if OSL_DEBUG_LEVEL > 1
217         static bool bClipAlways = false;
218         if( bClip && bClipAlways )
219         {   bClip = false;
220             rClip.ChgClip( rPaint );
221         }
222 #endif
223     }
224 
225     // Alignment
226     OutputDevice* pOut = GetInfo().GetOut();
227     Point aPnt1( nTmpLeft, GetInfo().GetPos().Y() );
228     if ( aPnt1.X() < rPaint.Left() )
229         aPnt1.setX( rPaint.Left() );
230     if ( aPnt1.Y() < rPaint.Top() )
231         aPnt1.setY( rPaint.Top() );
232     Point aPnt2( GetInfo().GetPos().X() + nMaxRight - GetInfo().X(),
233                  GetInfo().GetPos().Y() + nTmpHeight );
234     if ( aPnt2.X() > rPaint.Right() )
235         aPnt2.setX( rPaint.Right() );
236     if ( aPnt2.Y() > rPaint.Bottom() )
237         aPnt2.setY( rPaint.Bottom() );
238 
239     const SwRect aLineRect( aPnt1, aPnt2 );
240 
241     if( m_pCurr->IsClipping() )
242     {
243         rClip.ChgClip( aLineRect, m_pFrame );
244         bClip = false;
245     }
246 
247     if( !pPor && !bEndPor )
248         return;
249 
250     // Baseline output also if non-TextPortion (compare TabPor with Fill)
251     // if no special vertical alignment is used,
252     // we calculate Y value for the whole line
253     SwTextGridItem const*const pGrid(GetGridItem(GetTextFrame()->FindPageFrame()));
254     const bool bAdjustBaseLine =
255         GetLineInfo().HasSpecialAlign( GetTextFrame()->IsVertical() ) ||
256         ( nullptr != pGrid );
257     const SwTwips nLineBaseLine = GetInfo().GetPos().Y() + nTmpAscent;
258     if ( ! bAdjustBaseLine )
259         GetInfo().Y( nLineBaseLine );
260 
261     // 7529: Pre-paint post-its
262     if( GetInfo().OnWin() && pPor && !pPor->Width() )
263     {
264         SeekAndChg( GetInfo() );
265 
266         if( bAdjustBaseLine )
267         {
268             const SwTwips nOldY = GetInfo().Y();
269 
270             GetInfo().Y( GetInfo().GetPos().Y() + AdjustBaseLine( *m_pCurr, nullptr,
271                 GetInfo().GetFont()->GetHeight( GetInfo().GetVsh(), *pOut ),
272                 GetInfo().GetFont()->GetAscent( GetInfo().GetVsh(), *pOut )
273             ) );
274 
275             pPor->PrePaint( GetInfo(), pPor );
276             GetInfo().Y( nOldY );
277         }
278         else
279             pPor->PrePaint( GetInfo(), pPor );
280     }
281 
282     // 7923: EndPortions output chars, too, that's why we change the font
283     if( bEndPor )
284         SeekStartAndChg( GetInfo() );
285 
286     const bool bRest = m_pCurr->IsRest();
287     bool bFirst = true;
288 
289     SwArrowPortion *pArrow = nullptr;
290     // Reference portion for the paragraph end portion
291     SwLinePortion* pEndTempl = m_pCurr->GetFirstPortion();
292 
293     while( pPor )
294     {
295         bool bSeeked = true;
296         GetInfo().SetLen( pPor->GetLen() );
297 
298         const SwTwips nOldY = GetInfo().Y();
299 
300         if ( bAdjustBaseLine )
301         {
302             GetInfo().Y( GetInfo().GetPos().Y() + AdjustBaseLine( *m_pCurr, pPor ) );
303 
304             // we store the last portion, because a possible paragraph
305             // end character has the same font as this portion
306             // (only in special vertical alignment case, otherwise the first
307             // portion of the line is used)
308             if ( pPor->Width() && pPor->InTextGrp() )
309                 pEndTempl = pPor;
310         }
311 
312         // A special case are GluePortions which output blanks.
313 
314         // 6168: Avoid that the rest of a FieldPortion gets the attributes of the
315         // next portion with SeekAndChgBefore():
316         if( bRest && pPor->InFieldGrp() && !pPor->GetLen() )
317             SeekAndChgBefore( GetInfo() );
318         else if ( pPor->IsQuoVadisPortion() )
319         {
320             sal_Int32 nOffset = GetInfo().GetIdx();
321             SeekStartAndChg( GetInfo(), true );
322             if( GetRedln() && m_pCurr->HasRedline() )
323                 GetRedln()->Seek( *m_pFont, nOffset, 0 );
324         }
325         else if( pPor->InTextGrp() || pPor->InFieldGrp() || pPor->InTabGrp() )
326             SeekAndChg( GetInfo() );
327         else if ( !bFirst && pPor->IsBreakPortion() && GetInfo().GetOpt().IsParagraph() )
328         {
329             // Paragraph symbols should have the same font as the paragraph in front of them,
330             // except for the case that there's redlining in the paragraph
331             if( GetRedln() )
332                 SeekAndChg( GetInfo() );
333             else
334                 SeekAndChgBefore( GetInfo() );
335         }
336         else
337             bSeeked = false;
338 
339         // bRest = false;
340 
341         // If the end of the portion juts out, it is clipped.
342         // A safety distance of half the height is added, so that
343         // TTF-"f" isn't overlapping into the page margin.
344         if( bClip &&
345             GetInfo().X() + pPor->Width() + ( pPor->Height() / 2 ) > nMaxRight )
346         {
347             bClip = false;
348             rClip.ChgClip( rPaint, m_pFrame, m_pCurr->HasUnderscore() );
349         }
350 
351         // Portions, which lay "below" the text like post-its
352         SwLinePortion *pNext = pPor->GetPortion();
353         if( GetInfo().OnWin() && pNext && !pNext->Width() )
354         {
355             // Fix 11289: Fields were omitted here because of Last!=Owner during
356             // loading Brief.sdw. Now the fields are allowed again,
357             // by bSeeked Last!=Owner is being avoided.
358             if ( !bSeeked )
359                 SeekAndChg( GetInfo() );
360             pNext->PrePaint( GetInfo(), pPor );
361         }
362 
363         // We calculate a separate font for underlining.
364         CheckSpecialUnderline( pPor, bAdjustBaseLine ? nOldY : 0 );
365         SwUnderlineFont* pUnderLineFnt = GetInfo().GetUnderFnt();
366         if ( pUnderLineFnt )
367         {
368             const Point aTmpPoint( GetInfo().X(),
369                                    bAdjustBaseLine ?
370                                    pUnderLineFnt->GetPos().Y() :
371                                    nLineBaseLine );
372             pUnderLineFnt->SetPos( aTmpPoint );
373         }
374 
375         // in extended input mode we do not want a common underline font.
376         SwUnderlineFont* pOldUnderLineFnt = nullptr;
377         if ( GetRedln() && GetRedln()->ExtOn() )
378         {
379             pOldUnderLineFnt = GetInfo().GetUnderFnt();
380             GetInfo().SetUnderFnt( nullptr );
381         }
382 
383         {
384             // #i16816# tagged pdf support
385             Por_Info aPorInfo( *pPor, *this );
386             SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, &aPorInfo, *pOut );
387 
388             if( pPor->IsMultiPortion() )
389                 PaintMultiPortion( rPaint, static_cast<SwMultiPortion&>(*pPor) );
390             else
391                 pPor->Paint( GetInfo() );
392         }
393 
394         // reset underline font
395         if ( pOldUnderLineFnt )
396             GetInfo().SetUnderFnt( pOldUnderLineFnt );
397 
398         // reset (for special vertical alignment)
399         GetInfo().Y( nOldY );
400 
401         bFirst &= !pPor->GetLen();
402         if( pNext || !pPor->IsMarginPortion() )
403             pPor->Move( GetInfo() );
404         if( pPor->IsArrowPortion() && GetInfo().OnWin() && !pArrow )
405             pArrow = static_cast<SwArrowPortion*>(pPor);
406 
407         pPor = bDrawInWindow || GetInfo().X() <= nMaxRight ||
408                // #i16816# tagged pdf support
409                ( GetInfo().GetVsh() &&
410                  GetInfo().GetVsh()->GetViewOptions()->IsPDFExport() &&
411                  pNext && pNext->IsHolePortion() ) ?
412                pNext :
413                nullptr;
414     }
415 
416     // delete underline font
417     delete GetInfo().GetUnderFnt();
418     GetInfo().SetUnderFnt( nullptr );
419 
420     // paint remaining stuff
421     if( bDrawInWindow )
422     {
423         // If special vertical alignment is enabled, GetInfo().Y() is the
424         // top of the current line. Therefore is has to be adjusted for
425         // the painting of the remaining stuff. We first store the old value.
426         const SwTwips nOldY = GetInfo().Y();
427 
428         if( !GetNextLine() &&
429             GetInfo().GetVsh() && !GetInfo().GetVsh()->IsPreview() &&
430             GetInfo().GetOpt().IsParagraph() && !GetTextFrame()->GetFollow() &&
431             GetInfo().GetIdx() >= GetInfo().GetText().getLength() )
432         {
433             const SwTmpEndPortion aEnd( *pEndTempl );
434             GetFnt()->ChgPhysFnt( GetInfo().GetVsh(), *pOut );
435 
436             if ( bAdjustBaseLine )
437                 GetInfo().Y( GetInfo().GetPos().Y()
438                            + AdjustBaseLine( *m_pCurr, &aEnd ) );
439             GetInfo().X( GetInfo().X() +
440                     ( GetCurr()->IsHanging() ? GetCurr()->GetHangingMargin() : 0 ) );
441             aEnd.Paint( GetInfo() );
442             GetInfo().Y( nOldY );
443         }
444         if( GetInfo().GetVsh() && !GetInfo().GetVsh()->IsPreview() )
445         {
446             const bool bNextUndersized =
447                 ( GetTextFrame()->GetNext() &&
448                   0 == GetTextFrame()->GetNext()->getFramePrintArea().Height() &&
449                   GetTextFrame()->GetNext()->IsTextFrame() &&
450                   static_cast<SwTextFrame*>(GetTextFrame()->GetNext())->IsUndersized() ) ;
451 
452             if( bUnderSz || bNextUndersized )
453             {
454                 if ( bAdjustBaseLine )
455                     GetInfo().Y( GetInfo().GetPos().Y() + m_pCurr->GetAscent() );
456 
457                 if( pArrow )
458                     GetInfo().DrawRedArrow( *pArrow );
459 
460                 // GetInfo().Y() must be current baseline
461                 SwTwips nDiff = GetInfo().Y() + nTmpHeight - nTmpAscent - GetTextFrame()->getFrameArea().Bottom();
462                 if( ( nDiff > 0 &&
463                       ( GetEnd() < GetInfo().GetText().getLength() ||
464                         ( nDiff > nTmpHeight/2 && GetPrevLine() ) ) ) ||
465                     (nDiff >= 0 && bNextUndersized) )
466 
467                 {
468                     SwArrowPortion aArrow( GetInfo() );
469                     GetInfo().DrawRedArrow( aArrow );
470                 }
471 
472                 GetInfo().Y( nOldY );
473             }
474         }
475     }
476 
477     if( m_pCurr->IsClipping() )
478         rClip.ChgClip( rPaint, m_pFrame );
479 }
480 
481 void SwTextPainter::CheckSpecialUnderline( const SwLinePortion* pPor,
482                                           long nAdjustBaseLine )
483 {
484     // Check if common underline should not be continued
485     if ( IsUnderlineBreak( *pPor, *m_pFont ) )
486     {
487         // delete underline font
488         delete GetInfo().GetUnderFnt();
489         GetInfo().SetUnderFnt( nullptr );
490         return;
491     }
492     // Reuse calculated underline font as much as possible.
493     if ( GetInfo().GetUnderFnt() && GetInfo().GetIdx() + pPor->GetLen() <= GetInfo().GetUnderFnt()->GetEnd() + 1 )
494     {
495         SwFont &rFont = GetInfo().GetUnderFnt()->GetFont();
496         const Color aColor = GetUnderColor( GetInfo().GetFont() );
497         if ( GetUnderColor( &rFont ) != aColor )
498             rFont.SetColor( aColor );
499         return;
500     }
501 
502     // If current underline matches the common underline font, we continue
503     // to use the common underline font.
504     // Bug 120769:Color of underline display wrongly
505     if ( GetInfo().GetUnderFnt() &&
506         GetInfo().GetUnderFnt()->GetFont().GetUnderline() == GetFnt()->GetUnderline() &&
507         GetInfo().GetFont() && GetInfo().GetFont()->GetUnderColor() != Color(COL_AUTO) )
508         return;
509     //Bug 120769(End)
510 
511     OSL_ENSURE( GetFnt() && LINESTYLE_NONE != GetFnt()->GetUnderline(),
512             "CheckSpecialUnderline without underlined font" );
513     MultiSelection aUnderMulti( Range( 0, GetInfo().GetText().getLength() ) );
514     const SwFont* pParaFnt = GetAttrHandler().GetFont();
515     if( pParaFnt && pParaFnt->GetUnderline() == GetFnt()->GetUnderline() )
516         aUnderMulti.SelectAll();
517 
518     if( HasHints() )
519     {
520         for ( size_t nTmp = 0; nTmp < m_pHints->Count(); ++nTmp )
521         {
522             SwTextAttr* const pTextAttr = m_pHints->Get( nTmp );
523 
524             const SvxUnderlineItem* pItem = CharFormat::GetItem( *pTextAttr, RES_CHRATR_UNDERLINE );
525 
526             if ( pItem )
527             {
528                 const sal_Int32 nSt = pTextAttr->GetStart();
529                 const sal_Int32 nEnd = *pTextAttr->GetEnd();
530                 if( nEnd > nSt )
531                 {
532                     const bool bUnderSelect = m_pFont->GetUnderline() == pItem->GetLineStyle();
533                     aUnderMulti.Select( Range( nSt, nEnd - 1 ), bUnderSelect );
534                 }
535             }
536         }
537     }
538 
539     const sal_Int32 nIndx = GetInfo().GetIdx();
540     long nUnderEnd = 0;
541     const size_t nCnt = aUnderMulti.GetRangeCount();
542 
543     // find the underline range the current portion is contained in
544     for( size_t i = 0; i < nCnt; ++i )
545     {
546         const Range& rRange = aUnderMulti.GetRange( i );
547         if( nUnderEnd == rRange.Min() )
548             nUnderEnd = rRange.Max();
549         else if( nIndx >= rRange.Min() )
550         {
551             nUnderEnd = rRange.Max();
552         }
553         else
554             break;
555     }
556 
557     if ( GetEnd() && GetEnd() <= nUnderEnd )
558         nUnderEnd = GetEnd() - 1;
559 
560     // calculate the new common underline font
561     SwFont* pUnderlineFnt = nullptr;
562     Point aCommonBaseLine;
563 
564     // check, if underlining is not isolated
565     if ( nIndx + GetInfo().GetLen() < nUnderEnd + 1 )
566     {
567         // here starts the algorithm for calculating the underline font
568         SwScriptInfo& rScriptInfo = GetInfo().GetParaPortion()->GetScriptInfo();
569         SwAttrIter aIter( *GetInfo().GetTextFrame()->GetTextNode(),
570                           rScriptInfo );
571 
572         sal_Int32 nTmpIdx = nIndx;
573         sal_uLong nSumWidth = 0;
574         sal_uLong nSumHeight = 0;
575         sal_uLong nBold = 0;
576         sal_uInt16 nMaxBaseLineOfst = 0;
577         int nNumberOfPortions = 0;
578 
579         while( sal::static_int_cast<long>(nTmpIdx) <= nUnderEnd && pPor )
580         {
581             if ( pPor->IsFlyPortion() || pPor->IsFlyCntPortion() ||
582                 pPor->IsBreakPortion() || pPor->IsMarginPortion() ||
583                 pPor->IsHolePortion() ||
584                 ( pPor->IsMultiPortion() && ! static_cast<const SwMultiPortion*>(pPor)->IsBidi() ) )
585                 break;
586 
587             aIter.Seek( nTmpIdx );
588             if ( aIter.GetFnt()->GetEscapement() < 0 || m_pFont->IsWordLineMode() ||
589                  SvxCaseMap::SmallCaps == m_pFont->GetCaseMap() )
590                 break;
591 
592             if ( !aIter.GetFnt()->GetEscapement() )
593             {
594                 nSumWidth += pPor->Width();
595                 const sal_uLong nFontHeight = aIter.GetFnt()->GetHeight();
596 
597                 // If we do not have a common baseline we take the baseline
598                 // and the font of the lowest portion.
599                 if ( nAdjustBaseLine )
600                 {
601                     const sal_uInt16 nTmpBaseLineOfst = AdjustBaseLine( *m_pCurr, pPor );
602                     if ( nMaxBaseLineOfst < nTmpBaseLineOfst )
603                     {
604                         nMaxBaseLineOfst = nTmpBaseLineOfst;
605                         nSumHeight = nFontHeight;
606                     }
607                 }
608                 // in horizontal layout we build a weighted sum of the heights
609                 else
610                     nSumHeight += pPor->Width() * nFontHeight;
611 
612                 if ( WEIGHT_NORMAL != aIter.GetFnt()->GetWeight() )
613                     nBold += pPor->Width();
614             }
615 
616             ++nNumberOfPortions;
617 
618             nTmpIdx += pPor->GetLen();
619             pPor = pPor->GetPortion();
620         }
621 
622         // resulting height
623         if ( nNumberOfPortions > 1 && nSumWidth )
624         {
625             const sal_uLong nNewFontHeight = nAdjustBaseLine ?
626                                          nSumHeight :
627                                          nSumHeight / nSumWidth;
628 
629             pUnderlineFnt = new SwFont( *GetInfo().GetFont() );
630 
631             // font height
632             const SwFontScript nActual = pUnderlineFnt->GetActual();
633             pUnderlineFnt->SetSize( Size( pUnderlineFnt->GetSize( nActual ).Width(),
634                                           nNewFontHeight ), nActual );
635 
636             // font weight
637             if ( 2 * nBold > nSumWidth )
638                 pUnderlineFnt->SetWeight( WEIGHT_BOLD, nActual );
639             else
640                 pUnderlineFnt->SetWeight( WEIGHT_NORMAL, nActual );
641 
642             // common base line
643             aCommonBaseLine.setY( nAdjustBaseLine + nMaxBaseLineOfst );
644         }
645     }
646 
647     // an escaped redlined portion should also have a special underlining
648     if( ! pUnderlineFnt && m_pFont->GetEscapement() > 0 && GetRedln() &&
649         GetRedln()->ChkSpecialUnderline() )
650         pUnderlineFnt = new SwFont( *m_pFont );
651 
652     delete GetInfo().GetUnderFnt();
653 
654     if ( pUnderlineFnt )
655     {
656         pUnderlineFnt->SetProportion( 100 );
657         pUnderlineFnt->SetEscapement( 0 );
658         pUnderlineFnt->SetStrikeout( STRIKEOUT_NONE );
659         pUnderlineFnt->SetOverline( LINESTYLE_NONE );
660         const Color aFillColor( COL_TRANSPARENT );
661         pUnderlineFnt->SetFillColor( aFillColor );
662 
663         GetInfo().SetUnderFnt( new SwUnderlineFont( *pUnderlineFnt, nUnderEnd,
664                                                      aCommonBaseLine ) );
665     }
666     else
667         // I'm sorry, we do not have a special underlining font for you.
668         GetInfo().SetUnderFnt( nullptr );
669 }
670 
671 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
672