1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
31 #include "itrpaint.hxx"
34 #include "txtpaint.hxx"
37 #include <txatbase.hxx>
38 #include <charfmt.hxx>
39 #include "redlnitr.hxx"
41 #include "pormulti.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
);
68 SwFont
*pMyFnt
= GetFnt();
69 GetInfo().SetFont( pMyFnt
);
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)
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
)
97 GetInfo().SetIdx( GetInfo().GetIdx() + pPor
->GetLen() );
100 pPor
->Move( GetInfo() );
101 if (pPor
->InNumberGrp()
102 && !static_cast<SwNumberPortion
const*>(pPor
)->HasFollow())
104 rbSkippedNumPortions
= true; // all numbering portions were skipped?
107 pPor
= pPor
->GetNextPortion();
110 // 7529: if PostIts return also pLast.
111 if( pLast
&& !pLast
->Width() && pLast
->IsPostItsPortion() )
114 GetInfo().SetIdx( GetInfo().GetIdx() - pPor
->GetLen() );
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() );
137 // maybe catch-up adjustment
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());
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
;
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();
187 pPorIter
= pPorIter
->GetNextPortion();
190 if( !bEndPor
&& nTmpLeft
>= nMaxRight
)
194 // 7538: of course for the printer, too
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();
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() )
221 rClip
.ChgClip( rPaint
, m_pFrame
, m_pCurr
->HasUnderscore() );
223 #if OSL_DEBUG_LEVEL > 1
224 static bool bClipAlways
= false;
225 if( bClip
&& bClipAlways
)
227 rClip
.ChgClip( rPaint
);
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() );
256 rClip
.ChgClip( aLineRect
, m_pFrame
);
260 if( !pPor
&& !bEndPor
)
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
)
288 pPor
->PrePaint( GetInfo(), pPor
);
289 GetInfo().Y( nOldY
);
292 pPor
->PrePaint( GetInfo(), pPor
);
295 // 7923: EndPortions output chars, too, that's why we change the font
297 SeekStartAndChg( GetInfo() );
299 const bool bRest
= m_pCurr
->IsRest();
302 SwArrowPortion
*pArrow
= nullptr;
303 // Reference portion for the paragraph end portion
304 SwLinePortion
* pEndTempl
= m_pCurr
->GetFirstPortion();
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() )
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
360 SeekAndChg( GetInfo() );
362 SeekAndChgBefore( GetInfo() );
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.
373 GetInfo().X() + pPor
->Width() + ( pPor
->Height() / 2 ) > nMaxRight
)
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.
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();
396 const Point
aTmpPoint( GetInfo().X(),
398 pUnderLineFnt
->GetPos().Y() :
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
) );
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
);
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() ) ?
473 if (!pPor
&& isPDFTaggingEnabled
&& (roTaggedLabel
|| !roTaggedParagraph
))
474 { // check if the end of the list label is off-screen
475 auto FindEndOfNumbering
= [&](SwLinePortion
const* pP
) {
478 if (pP
->InNumberGrp()
479 && !static_cast<SwNumberPortion
const*>(pP
)->HasFollow())
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());
492 pP
= pP
->GetNextPortion();
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()))
513 // delete underline font
514 delete GetInfo().GetUnderFnt();
515 GetInfo().SetUnderFnt( nullptr );
517 // paint remaining stuff
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
;
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)
569 GetInfo().DrawRedArrow( *pArrow
);
571 // GetInfo().Y() must be current baseline
572 SwTwips nDiff
= GetInfo().Y() + nTmpHeight
- nTmpAscent
- GetTextFrame()->getFrameArea().Bottom();
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 );
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
);
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
)
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
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),
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
);
660 TextFrameIndex
const nStart(
661 GetTextFrame()->MapModelToView(pNode
, pTextAttr
->GetStart()));
662 TextFrameIndex
const nEnd(
663 GetTextFrame()->MapModelToView(pNode
, *pTextAttr
->End()));
666 const bool bUnderSelect
= m_pFont
->GetUnderline() == pItem
->GetLineStyle();
667 aUnderMulti
.Select(Range(sal_Int32(nStart
), sal_Int32(nEnd
) - 1),
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());
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;
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() ) )
721 aIter
.Seek( nTmpIdx
);
722 if ( aIter
.GetFnt()->GetEscapement() < 0 || m_pFont
->IsWordLineMode() ||
723 SvxCaseMap::SmallCaps
== m_pFont
->GetCaseMap() )
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
744 nSumHeight
+= pPor
->Width() * nFontHeight
;
746 if ( WEIGHT_NORMAL
!= aIter
.GetFnt()->GetWeight() )
747 nBold
+= pPor
->Width();
752 nTmpIdx
+= pPor
->GetLen();
753 pPor
= pPor
->GetNextPortion();
757 if ( nNumberOfPortions
> 1 && nSumWidth
)
759 const sal_uLong nNewFontHeight
= nAdjustBaseLine
?
761 nSumHeight
/ nSumWidth
;
763 pUnderlineFnt
= new SwFont( *GetInfo().GetFont() );
766 const SwFontScript nActual
= pUnderlineFnt
->GetActual();
767 pUnderlineFnt
->SetSize( Size( pUnderlineFnt
->GetSize( nActual
).Width(),
768 nNewFontHeight
), nActual
);
771 if ( 2 * nBold
> nSumWidth
)
772 pUnderlineFnt
->SetWeight( WEIGHT_BOLD
, nActual
);
774 pUnderlineFnt
->SetWeight( WEIGHT_NORMAL
, nActual
);
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();
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
,
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: */