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"
38 #include <txatbase.hxx>
39 #include <charfmt.hxx>
40 #include "redlnitr.hxx"
42 #include "pormulti.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
);
70 SwFont
*pMyFnt
= GetFnt();
71 GetInfo().SetFont( pMyFnt
);
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)
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
)
99 GetInfo().SetIdx( GetInfo().GetIdx() + pPor
->GetLen() );
102 pPor
->Move( GetInfo() );
103 if (pPor
->InNumberGrp()
104 && !static_cast<SwNumberPortion
const*>(pPor
)->HasFollow())
106 rbSkippedNumPortions
= true; // all numbering portions were skipped?
109 pPor
= pPor
->GetNextPortion();
112 // 7529: if PostIts return also pLast.
113 if( pLast
&& !pLast
->Width() && pLast
->IsPostItsPortion() )
116 GetInfo().SetIdx( GetInfo().GetIdx() - pPor
->GetLen() );
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
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());
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
;
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();
186 pPorIter
= pPorIter
->GetNextPortion();
189 if( !bEndPor
&& nTmpLeft
>= nMaxRight
)
193 // 7538: of course for the printer, too
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();
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() )
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
)
226 rClip
.ChgClip( rPaint
);
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());
255 rClip
.ChgClip( aLineRect
, m_pFrame
);
259 if( !pPor
&& !bEndPor
)
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
)
287 pPor
->PrePaint( GetInfo(), pPor
);
288 GetInfo().Y( nOldY
);
291 pPor
->PrePaint( GetInfo(), pPor
);
294 // 7923: EndPortions output chars, too, that's why we change the font
296 SeekStartAndChg( GetInfo() );
298 const bool bRest
= m_pCurr
->IsRest();
301 SwArrowPortion
*pArrow
= nullptr;
302 // Reference portion for the paragraph end portion
303 SwLinePortion
* pEndTempl
= m_pCurr
->GetFirstPortion();
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() )
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
359 SeekAndChg( GetInfo() );
361 SeekAndChgBefore( GetInfo() );
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.
372 GetInfo().X() + pPor
->Width() + ( pPor
->Height() / 2 ) > nMaxRight
)
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.
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();
395 const Point
aTmpPoint( GetInfo().X(),
397 pUnderLineFnt
->GetPos().Y() :
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
) );
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
);
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() ) ?
490 if (!pPor
&& isPDFTaggingEnabled
&& (roTaggedLabel
|| !roTaggedParagraph
))
491 { // check if the end of the list label is off-screen
492 auto FindEndOfNumbering
= [&](SwLinePortion
const* pP
) {
495 if (pP
->InNumberGrp()
496 && !static_cast<SwNumberPortion
const*>(pP
)->HasFollow())
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());
509 pP
= pP
->GetNextPortion();
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()))
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
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
;
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)
592 GetInfo().DrawRedArrow( *pArrow
);
594 // GetInfo().Y() must be current baseline
595 SwTwips nDiff
= GetInfo().Y() + nTmpHeight
- nTmpAscent
- GetTextFrame()->getFrameArea().Bottom();
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 );
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
);
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
)
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
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),
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
);
683 TextFrameIndex
const nStart(
684 GetTextFrame()->MapModelToView(pNode
, pTextAttr
->GetStart()));
685 TextFrameIndex
const nEnd(
686 GetTextFrame()->MapModelToView(pNode
, *pTextAttr
->End()));
689 const bool bUnderSelect
= m_pFont
->GetUnderline() == pItem
->GetLineStyle();
690 aUnderMulti
.Select(Range(sal_Int32(nStart
), sal_Int32(nEnd
) - 1),
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());
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;
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() ) )
744 aIter
.Seek( nTmpIdx
);
745 if ( aIter
.GetFnt()->GetEscapement() < 0 || m_pFont
->IsWordLineMode() ||
746 SvxCaseMap::SmallCaps
== m_pFont
->GetCaseMap() )
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
767 nSumHeight
+= pPor
->Width() * nFontHeight
;
769 if ( WEIGHT_NORMAL
!= aIter
.GetFnt()->GetWeight() )
770 nBold
+= pPor
->Width();
775 nTmpIdx
+= pPor
->GetLen();
776 pPor
= pPor
->GetNextPortion();
780 if ( nNumberOfPortions
> 1 && nSumWidth
)
782 const sal_uLong nNewFontHeight
= nAdjustBaseLine
?
784 nSumHeight
/ nSumWidth
;
786 pUnderlineFnt
= new SwFont( *GetInfo().GetFont() );
789 const SwFontScript nActual
= pUnderlineFnt
->GetActual();
790 pUnderlineFnt
->SetSize( Size( pUnderlineFnt
->GetSize( nActual
).Width(),
791 nNewFontHeight
), nActual
);
794 if ( 2 * nBold
> nSumWidth
)
795 pUnderlineFnt
->SetWeight( WEIGHT_BOLD
, nActual
);
797 pUnderlineFnt
->SetWeight( WEIGHT_NORMAL
, nActual
);
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();
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
,
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: */