crashtesting: empty brace init of o3tl::enumarray doesn't zero each element
[LibreOffice.git] / sw / source / core / text / porfld.cxx
blob6110edc415fe9f9bff40f2a40272b32e868e9867
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>
22 #include <com/sun/star/i18n/ScriptType.hpp>
23 #include <com/sun/star/i18n/XBreakIterator.hpp>
24 #include <vcl/graph.hxx>
25 #include <editeng/brushitem.hxx>
26 #include <vcl/metric.hxx>
27 #include <vcl/outdev.hxx>
28 #include <viewopt.hxx>
29 #include <SwPortionHandler.hxx>
30 #include "porlay.hxx"
31 #include "porfld.hxx"
32 #include "inftxt.hxx"
33 #include <fmtornt.hxx>
34 #include <frmatr.hxx>
35 #include <frmtool.hxx>
36 #include <viewsh.hxx>
37 #include <doc.hxx>
38 #include <IDocumentSettingAccess.hxx>
39 #include <rootfrm.hxx>
40 #include <breakit.hxx>
41 #include "porftn.hxx"
42 #include <accessibilityoptions.hxx>
43 #include <editeng/lrspitem.hxx>
44 #include <unicode/ubidi.h>
45 #include <bookmark.hxx>
47 using namespace ::com::sun::star;
49 SwLinePortion *SwFieldPortion::Compress()
50 { return (GetLen() || !m_aExpand.isEmpty() || SwLinePortion::Compress()) ? this : nullptr; }
52 SwFieldPortion *SwFieldPortion::Clone( const OUString &rExpand ) const
54 std::unique_ptr<SwFont> pNewFnt;
55 if( m_pFont )
57 pNewFnt.reset(new SwFont( *m_pFont ));
59 // #i107143#
60 // pass placeholder property to created <SwFieldPortion> instance.
61 SwFieldPortion* pClone = new SwFieldPortion( rExpand, std::move(pNewFnt), m_bPlaceHolder );
62 pClone->SetNextOffset( m_nNextOffset );
63 pClone->m_bNoLength = m_bNoLength;
64 return pClone;
67 void SwFieldPortion::TakeNextOffset( const SwFieldPortion* pField )
69 OSL_ENSURE( pField, "TakeNextOffset: Missing Source" );
70 m_nNextOffset = pField->GetNextOffset();
71 m_aExpand = m_aExpand.replaceAt(0, sal_Int32(m_nNextOffset), u"");
72 m_bFollow = true;
75 SwFieldPortion::SwFieldPortion(const OUString &rExpand, std::unique_ptr<SwFont> pFont, bool bPlaceHold, TextFrameIndex const nFieldLen)
76 : m_aExpand(rExpand), m_pFont(std::move(pFont)), m_nNextOffset(0)
77 , m_nNextScriptChg(COMPLETE_STRING), m_nFieldLen(nFieldLen), m_nViewWidth(0)
78 , m_bFollow( false ), m_bLeft( false), m_bHide( false)
79 , m_bCenter (false), m_bHasFollow( false )
80 , m_bAnimated( false), m_bNoPaint( false)
81 , m_bReplace( false), m_bPlaceHolder( bPlaceHold )
82 , m_bNoLength( false )
83 , m_nAttrFieldType(0)
85 SetWhichPor( PortionType::Field );
88 SwFieldPortion::SwFieldPortion( const SwFieldPortion& rField )
89 : SwExpandPortion( rField )
90 , m_aExpand( rField.GetExp() )
91 , m_nNextOffset( rField.GetNextOffset() )
92 , m_nNextScriptChg( rField.m_nNextScriptChg )
93 , m_nFieldLen(rField.m_nFieldLen)
94 , m_nViewWidth( rField.m_nViewWidth )
95 , m_bFollow( rField.IsFollow() )
96 , m_bLeft( rField.IsLeft() )
97 , m_bHide( rField.IsHide() )
98 , m_bCenter( rField.IsCenter() )
99 , m_bHasFollow( rField.HasFollow() )
100 , m_bAnimated ( rField.m_bAnimated )
101 , m_bNoPaint( rField.m_bNoPaint)
102 , m_bReplace( rField.m_bReplace )
103 , m_bPlaceHolder( rField.m_bPlaceHolder )
104 , m_bNoLength( rField.m_bNoLength )
105 , m_nAttrFieldType( rField.m_nAttrFieldType)
107 if ( rField.HasFont() )
108 m_pFont.reset( new SwFont( *rField.GetFont() ) );
110 SetWhichPor( PortionType::Field );
113 SwFieldPortion::~SwFieldPortion()
115 m_pFont.reset();
118 sal_uInt16 SwFieldPortion::GetViewWidth( const SwTextSizeInfo &rInf ) const
120 // even though this is const, nViewWidth should be computed at the very end:
121 SwFieldPortion* pThis = const_cast<SwFieldPortion*>(this);
122 if( !Width() && rInf.OnWin() && !rInf.GetOpt().IsPagePreview() &&
123 !rInf.GetOpt().IsReadonly() && SwViewOption::IsFieldShadings() )
125 if( !m_nViewWidth )
126 pThis->m_nViewWidth = rInf.GetTextSize(OUString(' ')).Width();
128 else
129 pThis->m_nViewWidth = 0;
130 return m_nViewWidth;
133 namespace {
136 * Never just use SetLen(0)
138 class SwFieldSlot
140 std::shared_ptr<vcl::text::TextLayoutCache> m_pOldCachedVclData;
141 const OUString *pOldText;
142 OUString aText;
143 TextFrameIndex nIdx;
144 TextFrameIndex nLen;
145 SwTextFormatInfo *pInf;
146 bool bOn;
147 public:
148 SwFieldSlot( const SwTextFormatInfo* pNew, const SwFieldPortion *pPor );
149 ~SwFieldSlot();
154 SwFieldSlot::SwFieldSlot( const SwTextFormatInfo* pNew, const SwFieldPortion *pPor )
155 : pOldText(nullptr)
156 , nIdx(0)
157 , nLen(0)
158 , pInf(nullptr)
160 bOn = pPor->GetExpText( *pNew, aText );
162 // The text will be replaced ...
163 if( !bOn )
164 return;
166 pInf = const_cast<SwTextFormatInfo*>(pNew);
167 nIdx = pInf->GetIdx();
168 nLen = pInf->GetLen();
169 pOldText = &(pInf->GetText());
170 m_pOldCachedVclData = pInf->GetCachedVclData();
171 pInf->SetLen(TextFrameIndex(aText.getLength()));
172 pInf->SetCachedVclData(nullptr);
173 if( pPor->IsFollow() )
175 pInf->SetFakeLineStart( nIdx > pInf->GetLineStart() );
176 pInf->SetIdx(TextFrameIndex(0));
178 else
180 TextFrameIndex nEnd(pOldText->getLength());
181 if (nIdx < nEnd)
183 sal_Int32 const nFieldLen(pPor->GetFieldLen());
184 aText = (*pOldText).replaceAt(sal_Int32(nIdx), nFieldLen, aText);
186 else if (nIdx == nEnd)
187 aText = *pOldText + aText;
188 else
189 SAL_WARN("sw.core", "SwFieldSlot bad SwFieldPortion index.");
191 pInf->SetText( aText );
194 SwFieldSlot::~SwFieldSlot()
196 if( bOn )
198 pInf->SetCachedVclData(m_pOldCachedVclData);
199 pInf->SetText( *pOldText );
200 pInf->SetIdx( nIdx );
201 pInf->SetLen( nLen );
202 pInf->SetFakeLineStart( false );
206 void SwFieldPortion::CheckScript( const SwTextSizeInfo &rInf )
208 OUString aText;
209 if (!GetExpText(rInf, aText) || aText.isEmpty())
210 return;
212 SwFontScript nActual = m_pFont ? m_pFont->GetActual() : rInf.GetFont()->GetActual();
213 sal_uInt16 nScript = g_pBreakIt->GetBreakIter()->getScriptType( aText, 0 );
214 sal_Int32 nChg = 0;
215 if( i18n::ScriptType::WEAK == nScript )
217 nChg = g_pBreakIt->GetBreakIter()->endOfScript(aText,0,nScript);
218 if (nChg < aText.getLength() && nChg >= 0)
219 nScript = g_pBreakIt->GetBreakIter()->getScriptType( aText, nChg );
222 // nNextScriptChg will be evaluated during SwFieldPortion::Format()
224 if (nChg < aText.getLength() && nChg >= 0)
225 m_nNextScriptChg = TextFrameIndex(
226 g_pBreakIt->GetBreakIter()->endOfScript(aText, nChg, nScript));
227 else
228 m_nNextScriptChg = TextFrameIndex(aText.getLength());
230 SwFontScript nTmp;
231 switch ( nScript ) {
232 case i18n::ScriptType::LATIN : nTmp = SwFontScript::Latin; break;
233 case i18n::ScriptType::ASIAN : nTmp = SwFontScript::CJK; break;
234 case i18n::ScriptType::COMPLEX : nTmp = SwFontScript::CTL; break;
235 default: nTmp = nActual;
238 // #i16354# Change script type for RTL text to CTL.
239 const SwScriptInfo& rSI = rInf.GetParaPortion()->GetScriptInfo();
240 // #i98418#
241 const sal_uInt8 nFieldDir = (IsNumberPortion() || IsFootnoteNumPortion())
242 ? rSI.GetDefaultDir()
243 : rSI.DirType(IsFollow() ? rInf.GetIdx() - m_nFieldLen : rInf.GetIdx());
246 UErrorCode nError = U_ZERO_ERROR;
247 UBiDi* pBidi = ubidi_openSized( aText.getLength(), 0, &nError );
248 ubidi_setPara( pBidi, reinterpret_cast<const UChar *>(aText.getStr()), aText.getLength(), nFieldDir, nullptr, &nError );
249 int32_t nEnd;
250 UBiDiLevel nCurrDir;
251 ubidi_getLogicalRun( pBidi, 0, &nEnd, &nCurrDir );
252 ubidi_close( pBidi );
253 const TextFrameIndex nNextDirChg(nEnd);
254 m_nNextScriptChg = std::min( m_nNextScriptChg, nNextDirChg );
256 // #i89825# change the script type also to CTL
257 // if there is no strong LTR char in the LTR run (numbers)
258 if (nCurrDir != UBIDI_RTL &&
259 (UBIDI_LTR != nFieldDir || i18n::ScriptType::COMPLEX == nScript))
261 nCurrDir = UBIDI_RTL;
262 for( sal_Int32 nCharIdx = 0; nCharIdx < nEnd; ++nCharIdx )
264 UCharDirection nCharDir = u_charDirection ( aText[ nCharIdx ]);
265 if ( nCharDir == U_LEFT_TO_RIGHT ||
266 nCharDir == U_LEFT_TO_RIGHT_EMBEDDING ||
267 nCharDir == U_LEFT_TO_RIGHT_OVERRIDE )
269 nCurrDir = UBIDI_LTR;
270 break;
275 if (nCurrDir == UBIDI_RTL)
277 nTmp = SwFontScript::CTL;
278 // If we decided that this range was RTL after all and the
279 // previous range was complex but clipped to the start of this
280 // range, then extend it to be complex over the additional RTL range
281 if (nScript == i18n::ScriptType::COMPLEX)
282 m_nNextScriptChg = nNextDirChg;
286 // #i98418#
287 // keep determined script type for footnote portions as preferred script type.
288 // For footnote portions a font can not be created directly - see footnote
289 // portion format method.
290 if ( IsFootnotePortion() )
292 static_cast<SwFootnotePortion*>(this)->SetPreferredScriptType( nTmp );
294 else if ( nTmp != nActual )
296 if( !m_pFont )
297 m_pFont.reset( new SwFont( *rInf.GetFont() ) );
298 m_pFont->SetActual( nTmp );
303 bool SwFieldPortion::Format( SwTextFormatInfo &rInf )
305 // Scope wegen aDiffText::DTOR!
306 bool bFull = false;
307 bool bEOL = false;
308 TextFrameIndex const nTextRest = TextFrameIndex(rInf.GetText().getLength()) - rInf.GetIdx();
310 TextFrameIndex nRest;
311 SwFieldSlot aDiffText( &rInf, this );
312 SwLayoutModeModifier aLayoutModeModifier( *rInf.GetOut() );
313 aLayoutModeModifier.SetAuto();
315 // Field portion has to be split in several parts if
316 // 1. There are script/direction changes inside the field
317 // 2. There are portion breaks (tab, break) inside the field:
318 const TextFrameIndex nOldFullLen = rInf.GetLen();
319 TextFrameIndex nFullLen = rInf.ScanPortionEnd(rInf.GetIdx(), rInf.GetIdx() + nOldFullLen) - rInf.GetIdx();
320 if ( m_nNextScriptChg < nFullLen )
322 nFullLen = m_nNextScriptChg;
323 rInf.SetHookChar( 0 );
325 rInf.SetLen( nFullLen );
327 if (TextFrameIndex(COMPLETE_STRING) != rInf.GetUnderScorePos() &&
328 rInf.GetUnderScorePos() > rInf.GetIdx() )
329 rInf.SetUnderScorePos( rInf.GetIdx() );
331 if( m_pFont )
332 m_pFont->AllocFontCacheId( rInf.GetVsh(), m_pFont->GetActual() );
334 SwFontSave aSave( rInf, m_pFont.get() );
336 // Length must be 0: the length is set for bFull after format
337 // and passed along in nRest. Or else the old length would be
338 // retained and be used for nRest!
339 SetLen(TextFrameIndex(0));
340 TextFrameIndex const nFollow(IsFollow() ? TextFrameIndex(0) : m_nFieldLen);
342 // As odd is may seem: the query for GetLen() must return false due
343 // to the ExpandPortions _after_ aDiffText (see SoftHyphs), caused
344 // by SetFull.
345 if( !nFullLen )
347 // Don't Init(), as we need height and ascent
348 Width(0);
349 bFull = rInf.Width() <= rInf.GetPos().X();
351 else
353 TextFrameIndex const nOldLineStart = rInf.GetLineStart();
354 if( IsFollow() )
355 rInf.SetLineStart(TextFrameIndex(0));
356 rInf.SetNotEOL( nFullLen == nOldFullLen && nTextRest > nFollow );
358 // the height depending on the fields font is set,
359 // this is required for SwTextGuess::Guess
360 Height( rInf.GetTextHeight() + rInf.GetFont()->GetTopBorderSpace() +
361 rInf.GetFont()->GetBottomBorderSpace() );
362 // If a kerning portion is inserted after our field portion,
363 // the ascent and height must be known
364 SetAscent( rInf.GetAscent() + rInf.GetFont()->GetTopBorderSpace() );
365 bFull = SwTextPortion::Format( rInf );
366 rInf.SetNotEOL( false );
367 rInf.SetLineStart( nOldLineStart );
369 TextFrameIndex const nTmpLen = GetLen();
370 bEOL = !nTmpLen && nFollow && bFull;
371 nRest = nOldFullLen - nTmpLen;
373 // The char is held in the first position
374 // Unconditionally after format!
375 SetLen( m_bNoLength ? TextFrameIndex(0) : nFollow );
377 if( nRest )
379 // aExpand has not yet been shortened; the new Ofst is a
380 // result of nRest
381 TextFrameIndex nNextOfst = TextFrameIndex(m_aExpand.getLength()) - nRest;
383 if ( IsQuoVadisPortion() )
384 nNextOfst = nNextOfst + TextFrameIndex(static_cast<SwQuoVadisPortion*>(this)->GetContText().getLength());
386 OUString aNew( m_aExpand.copy(sal_Int32(nNextOfst)) );
387 m_aExpand = m_aExpand.copy(0, sal_Int32(nNextOfst));
389 // These characters should not be contained in the follow
390 // field portion. They are handled via the HookChar mechanism.
391 const sal_Unicode nNew = !aNew.isEmpty() ? aNew[0] : 0;
392 switch (nNew)
394 case CH_BREAK : bFull = true;
395 [[fallthrough]];
396 case ' ' :
397 case CH_TAB :
398 case CHAR_HARDHYPHEN: // non-breaking hyphen
399 case CHAR_SOFTHYPHEN:
400 case CHAR_HARDBLANK:
401 case CHAR_ZWSP :
402 case CHAR_WJ :
403 case CH_TXTATR_BREAKWORD:
404 case CH_TXTATR_INWORD:
406 aNew = aNew.copy( 1 );
407 ++nNextOfst;
408 break;
410 default: ;
413 // Even if there is no more text left for a follow field,
414 // we have to build a follow field portion (without font),
415 // otherwise the HookChar mechanism would not work.
416 SwFieldPortion *pField = Clone( aNew );
417 if( !aNew.isEmpty() && !pField->GetFont() )
419 pField->SetFont( std::make_unique<SwFont>( *rInf.GetFont() ) );
421 pField->SetFollow( true );
422 SetHasFollow( true );
424 // For a newly created field, nNextOffset contains the Offset
425 // of its start of the original string
426 // If a FollowField is created when formatting, this FollowField's
427 // Offset is being held in nNextOffset
428 m_nNextOffset = m_nNextOffset + nNextOfst;
429 pField->SetNextOffset( m_nNextOffset );
430 rInf.SetRest( pField );
434 if( bEOL && rInf.GetLast() && !rInf.GetUnderflow() )
435 rInf.GetLast()->FormatEOL( rInf );
436 return bFull;
439 void SwFieldPortion::Paint( const SwTextPaintInfo &rInf ) const
441 SwFontSave aSave( rInf, m_pFont.get() );
443 // OSL_ENSURE(GetLen() <= TextFrameIndex(1), "SwFieldPortion::Paint: rest-portion pollution?");
444 if( Width() && ( !m_bPlaceHolder || rInf.GetOpt().IsShowPlaceHolderFields() ) )
446 // A very liberal use of the background
447 rInf.DrawViewOpt( *this, PortionType::Field );
448 SwExpandPortion::Paint( rInf );
452 bool SwFieldPortion::GetExpText( const SwTextSizeInfo &rInf, OUString &rText ) const
454 rText = m_aExpand;
455 if( rText.isEmpty() && rInf.OnWin() &&
456 !rInf.GetOpt().IsPagePreview() && !rInf.GetOpt().IsReadonly() &&
457 SwViewOption::IsFieldShadings() &&
458 !HasFollow() )
459 rText = " ";
460 return true;
463 void SwFieldPortion::HandlePortion( SwPortionHandler& rPH ) const
465 sal_Int32 nH = 0;
466 sal_Int32 nW = 0;
467 if (m_pFont)
469 nH = m_pFont->GetSize(m_pFont->GetActual()).Height();
470 nW = m_pFont->GetSize(m_pFont->GetActual()).Width();
472 rPH.Special( GetLen(), m_aExpand, GetWhichPor(), nH, nW, m_pFont.get() );
475 SwPosSize SwFieldPortion::GetTextSize( const SwTextSizeInfo &rInf ) const
477 SwFontSave aSave( rInf, m_pFont.get() );
478 SwPosSize aSize( SwExpandPortion::GetTextSize( rInf ) );
479 return aSize;
482 SwFieldPortion *SwHiddenPortion::Clone(const OUString &rExpand ) const
484 std::unique_ptr<SwFont> pNewFnt;
485 if( m_pFont )
486 pNewFnt.reset(new SwFont( *m_pFont ));
487 return new SwHiddenPortion( rExpand, std::move(pNewFnt) );
490 void SwHiddenPortion::Paint( const SwTextPaintInfo &rInf ) const
492 if( Width() )
494 SwFontSave aSave( rInf, m_pFont.get() );
495 rInf.DrawViewOpt( *this, PortionType::Hidden );
496 SwExpandPortion::Paint( rInf );
500 bool SwHiddenPortion::GetExpText( const SwTextSizeInfo &rInf, OUString &rText ) const
502 // Do not query for IsHidden()!
503 return SwFieldPortion::GetExpText( rInf, rText );
506 SwNumberPortion::SwNumberPortion( const OUString &rExpand,
507 std::unique_ptr<SwFont> pFont,
508 const bool bLft,
509 const bool bCntr,
510 const sal_uInt16 nMinDst,
511 const bool bLabelAlignmentPosAndSpaceModeActive )
512 : SwFieldPortion( rExpand, std::move(pFont) ),
513 m_nFixWidth(0),
514 m_nMinDist( nMinDst ),
515 mbLabelAlignmentPosAndSpaceModeActive( bLabelAlignmentPosAndSpaceModeActive )
517 SetWhichPor( PortionType::Number );
518 SetLeft( bLft );
519 SetHide( false );
520 SetCenter( bCntr );
523 TextFrameIndex SwNumberPortion::GetModelPositionForViewPoint(const sal_uInt16) const
525 return TextFrameIndex(0);
528 SwFieldPortion *SwNumberPortion::Clone( const OUString &rExpand ) const
530 std::unique_ptr<SwFont> pNewFnt;
531 if( m_pFont )
532 pNewFnt.reset(new SwFont( *m_pFont ));
534 return new SwNumberPortion( rExpand, std::move(pNewFnt), IsLeft(), IsCenter(),
535 m_nMinDist, mbLabelAlignmentPosAndSpaceModeActive );
539 * We can create multiple NumFields
540 * Tricky, if one enters enough previous-text in the dialog box
541 * to cause the line to overflow
542 * We need to keep the Fly's evasion tactics in mind
544 bool SwNumberPortion::Format( SwTextFormatInfo &rInf )
546 SetHide( false );
547 const bool bFull = SwFieldPortion::Format( rInf );
548 SetLen(TextFrameIndex(0));
549 // a numbering portion can be contained in a rotated portion!!!
550 m_nFixWidth = rInf.IsMulti() ? Height() : Width();
551 rInf.SetNumDone( !rInf.GetRest() );
552 if( rInf.IsNumDone() )
554 // SetAscent( rInf.GetAscent() );
555 OSL_ENSURE( Height() && mnAscent, "NumberPortions without Height | Ascent" );
557 tools::Long nDiff( 0 );
559 if ( !mbLabelAlignmentPosAndSpaceModeActive )
561 if (!rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING) &&
562 // #i32902#
563 !IsFootnoteNumPortion() )
565 nDiff = rInf.Left()
566 + rInf.GetTextFrame()->GetTextNodeForParaProps()->
567 GetSwAttrSet().GetLRSpace().GetTextFirstLineOffset()
568 - rInf.First()
569 + rInf.ForcedLeftMargin();
571 else
573 nDiff = rInf.Left() - rInf.First() + rInf.ForcedLeftMargin();
576 // The text part of the numbering should always at least
577 // start at the left margin
578 if( nDiff < 0 )
579 nDiff = 0;
580 else if ( nDiff > rInf.X() )
581 nDiff -= rInf.X();
582 else
583 nDiff = 0;
585 if( nDiff < m_nFixWidth + m_nMinDist )
586 nDiff = m_nFixWidth + m_nMinDist;
588 // Numbering evades the Fly, no nDiff in the second round
589 // Tricky special case: FlyFrame is in an Area we're just about to
590 // acquire
591 // The NumberPortion is marked as hidden
592 const bool bFly = rInf.GetFly() ||
593 ( rInf.GetLast() && rInf.GetLast()->IsFlyPortion() );
594 if( nDiff > rInf.Width() )
596 nDiff = rInf.Width();
597 if ( bFly )
598 SetHide( true );
601 // A numbering portion can be inside a SwRotatedPortion. Then the
602 // Height has to be changed
603 if ( rInf.IsMulti() )
605 if ( Height() < nDiff )
606 Height( nDiff );
608 else if( Width() < nDiff )
609 Width( nDiff );
611 return bFull;
616 * A FormatEOL indicates that the subsequent text did not fit onto
617 * the line anymore. In order for the Numbering to follow through,
618 * we hide this NumberPortion
620 void SwNumberPortion::FormatEOL( SwTextFormatInfo& )
623 // This caused trouble with flys anchored as characters.
624 // If one of these is numbered but does not fit to the line,
625 // it calls this function, causing a loop because both the number
626 // portion and the fly portion go to the next line
627 // SetHide( true );
632 * A hidden NumberPortion is not displayed, unless there are TextPortions in
633 * this line or there's just one line at all
635 void SwNumberPortion::Paint( const SwTextPaintInfo &rInf ) const
637 if ( IsHide() && rInf.GetParaPortion() && rInf.GetParaPortion()->GetNext() )
639 SwLinePortion *pTmp = GetNextPortion();
640 while ( pTmp && !pTmp->InTextGrp() )
641 pTmp = pTmp->GetNextPortion();
642 if ( !pTmp )
643 return;
646 // calculate the width of the number portion, including follows
647 const sal_uInt16 nOldWidth = Width();
648 sal_uInt16 nSumWidth = 0;
649 sal_uInt16 nOffset = 0;
651 const SwLinePortion* pTmp = this;
652 while ( pTmp && pTmp->InNumberGrp() )
654 nSumWidth = nSumWidth + pTmp->Width();
655 if ( static_cast<const SwNumberPortion*>(pTmp)->HasFollow() )
656 pTmp = pTmp->GetNextPortion();
657 else
659 nOffset = pTmp->Width() - static_cast<const SwNumberPortion*>(pTmp)->m_nFixWidth;
660 break;
664 // The master portion takes care for painting the background of the
665 // follow field portions
666 if ( ! IsFollow() )
668 SwNumberPortion *pThis = const_cast<SwNumberPortion*>(this);
669 pThis->Width( nSumWidth );
670 rInf.DrawViewOpt( *this, PortionType::Number );
671 pThis->Width( nOldWidth );
674 if( m_aExpand.isEmpty() )
675 return;
677 const SwFont *pTmpFnt = rInf.GetFont();
678 bool bPaintSpace = ( LINESTYLE_NONE != pTmpFnt->GetUnderline() ||
679 LINESTYLE_NONE != pTmpFnt->GetOverline() ||
680 STRIKEOUT_NONE != pTmpFnt->GetStrikeout() ) &&
681 !pTmpFnt->IsWordLineMode();
682 if( bPaintSpace && m_pFont )
683 bPaintSpace = ( LINESTYLE_NONE != m_pFont->GetUnderline() ||
684 LINESTYLE_NONE != m_pFont->GetOverline() ||
685 STRIKEOUT_NONE != m_pFont->GetStrikeout() ) &&
686 !m_pFont->IsWordLineMode();
688 SwFontSave aSave( rInf, m_pFont.get() );
690 if( m_nFixWidth == Width() && ! HasFollow() )
691 SwExpandPortion::Paint( rInf );
692 else
694 // logical const: reset width
695 SwNumberPortion *pThis = const_cast<SwNumberPortion*>(this);
696 bPaintSpace = bPaintSpace && m_nFixWidth < nOldWidth;
697 sal_uInt16 nSpaceOffs = m_nFixWidth;
698 pThis->Width( m_nFixWidth );
700 if( ( IsLeft() && ! rInf.GetTextFrame()->IsRightToLeft() ) ||
701 ( ! IsLeft() && ! IsCenter() && rInf.GetTextFrame()->IsRightToLeft() ) )
702 SwExpandPortion::Paint( rInf );
703 else
705 SwTextPaintInfo aInf( rInf );
706 if( nOffset < m_nMinDist )
707 nOffset = 0;
708 else
710 if( IsCenter() )
712 /* #110778# a / 2 * 2 == a is not a tautology */
713 sal_uInt16 nTmpOffset = nOffset;
714 nOffset /= 2;
715 if( nOffset < m_nMinDist )
716 nOffset = nTmpOffset - m_nMinDist;
718 else
719 nOffset = nOffset - m_nMinDist;
721 aInf.X( aInf.X() + nOffset );
722 SwExpandPortion::Paint( aInf );
723 if( bPaintSpace )
724 nSpaceOffs = nSpaceOffs + nOffset;
726 if( bPaintSpace && nOldWidth > nSpaceOffs )
728 SwTextPaintInfo aInf( rInf );
729 aInf.X( aInf.X() + nSpaceOffs );
731 // #i53199# Adjust position of underline:
732 if ( rInf.GetUnderFnt() )
734 const Point aNewPos( aInf.GetPos().X(), rInf.GetUnderFnt()->GetPos().Y() );
735 rInf.GetUnderFnt()->SetPos( aNewPos );
738 pThis->Width( nOldWidth - nSpaceOffs + 12 );
740 SwTextSlot aDiffText( &aInf, this, true, false, " " );
741 aInf.DrawText( *this, aInf.GetLen(), true );
744 pThis->Width( nOldWidth );
748 SwBulletPortion::SwBulletPortion( const sal_UCS4 cBullet,
749 std::u16string_view rBulletFollowedBy,
750 std::unique_ptr<SwFont> pFont,
751 const bool bLft,
752 const bool bCntr,
753 const sal_uInt16 nMinDst,
754 const bool bLabelAlignmentPosAndSpaceModeActive )
755 : SwNumberPortion( OUString(&cBullet, 1) + rBulletFollowedBy,
756 std::move(pFont), bLft, bCntr, nMinDst,
757 bLabelAlignmentPosAndSpaceModeActive )
759 SetWhichPor( PortionType::Bullet );
762 #define GRFNUM_SECURE 10
764 SwGrfNumPortion::SwGrfNumPortion(
765 const OUString& rGraphicFollowedBy,
766 const SvxBrushItem* pGrfBrush, OUString const & referer,
767 const SwFormatVertOrient* pGrfOrient, const Size& rGrfSize,
768 const bool bLft, const bool bCntr, const sal_uInt16 nMinDst,
769 const bool bLabelAlignmentPosAndSpaceModeActive ) :
770 SwNumberPortion( rGraphicFollowedBy, nullptr, bLft, bCntr, nMinDst,
771 bLabelAlignmentPosAndSpaceModeActive ),
772 m_pBrush( new SvxBrushItem(RES_BACKGROUND) ), m_nId( 0 )
774 SetWhichPor( PortionType::GrfNum );
775 SetAnimated( false );
776 m_bReplace = false;
777 if( pGrfBrush )
779 m_pBrush.reset(pGrfBrush->Clone());
780 const Graphic* pGraph = pGrfBrush->GetGraphic(referer);
781 if( pGraph )
782 SetAnimated( pGraph->IsAnimated() );
783 else
784 m_bReplace = true;
786 if( pGrfOrient )
788 m_nYPos = pGrfOrient->GetPos();
789 m_eOrient = pGrfOrient->GetVertOrient();
791 else
793 m_nYPos = 0;
794 m_eOrient = text::VertOrientation::TOP;
796 Width( rGrfSize.Width() + 2 * GRFNUM_SECURE );
797 m_nFixWidth = Width();
798 m_nGrfHeight = rGrfSize.Height() + 2 * GRFNUM_SECURE;
799 Height( sal_uInt16(m_nGrfHeight) );
800 m_bNoPaint = false;
803 SwGrfNumPortion::~SwGrfNumPortion()
805 if ( IsAnimated() )
807 Graphic* pGraph = const_cast<Graphic*>(m_pBrush->GetGraphic());
808 if (pGraph)
809 pGraph->StopAnimation( nullptr, m_nId );
811 m_pBrush.reset();
814 void SwGrfNumPortion::StopAnimation( const OutputDevice* pOut )
816 if ( IsAnimated() )
818 Graphic* pGraph = const_cast<Graphic*>(m_pBrush->GetGraphic());
819 if (pGraph)
820 pGraph->StopAnimation( pOut, m_nId );
824 bool SwGrfNumPortion::Format( SwTextFormatInfo &rInf )
826 SetHide( false );
827 // Width( nFixWidth );
828 sal_uInt16 nFollowedByWidth( 0 );
829 if ( mbLabelAlignmentPosAndSpaceModeActive )
831 SwFieldPortion::Format( rInf );
832 nFollowedByWidth = Width();
833 SetLen(TextFrameIndex(0));
835 Width( m_nFixWidth + nFollowedByWidth );
836 const bool bFull = rInf.Width() < rInf.X() + Width();
837 const bool bFly = rInf.GetFly() ||
838 ( rInf.GetLast() && rInf.GetLast()->IsFlyPortion() );
839 SetAscent( GetRelPos() > 0 ? GetRelPos() : 0 );
840 if( GetAscent() > Height() )
841 Height( GetAscent() );
843 if( bFull )
845 Width( rInf.Width() - rInf.X() );
846 if( bFly )
848 SetLen(TextFrameIndex(0));
849 m_bNoPaint = true;
850 rInf.SetNumDone( false );
851 return true;
854 rInf.SetNumDone( true );
855 // long nDiff = rInf.Left() - rInf.First() + rInf.ForcedLeftMargin();
856 tools::Long nDiff = mbLabelAlignmentPosAndSpaceModeActive
858 : rInf.Left() - rInf.First() + rInf.ForcedLeftMargin();
859 // The TextPortion should at least always start on the
860 // left margin
861 if( nDiff < 0 )
862 nDiff = 0;
863 else if ( nDiff > rInf.X() )
864 nDiff -= rInf.X();
865 if( nDiff < m_nFixWidth + m_nMinDist )
866 nDiff = m_nFixWidth + m_nMinDist;
868 // Numbering evades Fly, no nDiff in the second round
869 // Tricky special case: FlyFrame is in the Area we were just
870 // about to get a hold of.
871 // The NumberPortion is marked as hidden
872 if( nDiff > rInf.Width() )
874 nDiff = rInf.Width();
875 if( bFly )
876 SetHide( true );
879 if( Width() < nDiff )
880 Width( nDiff );
881 return bFull;
886 * A hidden NumberPortion is not displayed, unless there are TextPortions in
887 * this line or there's only one line at all
889 void SwGrfNumPortion::Paint( const SwTextPaintInfo &rInf ) const
891 if( m_bNoPaint )
892 return;
893 if ( IsHide() && rInf.GetParaPortion() && rInf.GetParaPortion()->GetNext() )
895 SwLinePortion *pTmp = GetNextPortion();
896 while ( pTmp && !pTmp->InTextGrp() )
897 pTmp = pTmp->GetNextPortion();
898 if ( !pTmp )
899 return;
901 Point aPos( rInf.X() + GRFNUM_SECURE, rInf.Y() - GetRelPos() + GRFNUM_SECURE );
902 tools::Long nTmpWidth = std::max( tools::Long(0), static_cast<tools::Long>(m_nFixWidth - 2 * GRFNUM_SECURE) );
903 Size aSize( nTmpWidth, GetGrfHeight() - 2 * GRFNUM_SECURE );
905 const bool bTmpLeft = mbLabelAlignmentPosAndSpaceModeActive ||
906 ( IsLeft() && ! rInf.GetTextFrame()->IsRightToLeft() ) ||
907 ( ! IsLeft() && ! IsCenter() && rInf.GetTextFrame()->IsRightToLeft() );
909 if( m_nFixWidth < Width() && !bTmpLeft )
911 sal_uInt16 nOffset = Width() - m_nFixWidth;
912 if( nOffset < m_nMinDist )
913 nOffset = 0;
914 else
916 if( IsCenter() )
918 nOffset /= 2;
919 if( nOffset < m_nMinDist )
920 nOffset = Width() - m_nFixWidth - m_nMinDist;
922 else
923 nOffset = nOffset - m_nMinDist;
925 aPos.AdjustX(nOffset );
928 if( m_bReplace )
930 const tools::Long nTmpH = GetNextPortion() ? GetNextPortion()->GetAscent() : 120;
931 aSize = Size( nTmpH, nTmpH );
932 aPos.setY( rInf.Y() - nTmpH );
934 SwRect aTmp( aPos, aSize );
936 bool bDraw = true;
938 if ( IsAnimated() )
940 bDraw = !rInf.GetOpt().IsGraphic();
941 if( !m_nId )
943 SetId( reinterpret_cast<sal_IntPtr>( rInf.GetTextFrame() ) );
944 rInf.GetTextFrame()->SetAnimation();
946 if( aTmp.Overlaps( rInf.GetPaintRect() ) && !bDraw )
948 rInf.NoteAnimation();
949 const SwViewShell* pViewShell = rInf.GetVsh();
951 // virtual device, not pdf export
952 if( OUTDEV_VIRDEV == rInf.GetOut()->GetOutDevType() &&
953 pViewShell && pViewShell->GetWin() )
955 Graphic* pGraph = const_cast<Graphic*>(m_pBrush->GetGraphic());
956 if (pGraph)
957 pGraph->StopAnimation(nullptr,m_nId);
958 rInf.GetTextFrame()->getRootFrame()->GetCurrShell()->InvalidateWindows( aTmp );
961 else if ( pViewShell &&
962 !pViewShell->GetAccessibilityOptions()->IsStopAnimatedGraphics() &&
963 !pViewShell->IsPreview() &&
964 // #i9684# Stop animation during printing/pdf export.
965 pViewShell->GetWin() )
967 Graphic* pGraph = const_cast<Graphic*>(m_pBrush->GetGraphic());
968 if (pGraph)
970 const OutputDevice* pOut = rInf.GetOut();
971 assert(pOut);
972 pGraph->StartAnimation(
973 *const_cast<OutputDevice*>(pOut), aPos, aSize, m_nId);
977 // pdf export, printing, preview, stop animations...
978 else
979 bDraw = true;
981 if( bDraw )
984 Graphic* pGraph = const_cast<Graphic*>(m_pBrush->GetGraphic());
985 if (pGraph)
986 pGraph->StopAnimation( nullptr, m_nId );
990 SwRect aRepaint( rInf.GetPaintRect() );
991 const SwTextFrame& rFrame = *rInf.GetTextFrame();
992 if( rFrame.IsVertical() )
994 rFrame.SwitchHorizontalToVertical( aTmp );
995 rFrame.SwitchHorizontalToVertical( aRepaint );
998 if( rFrame.IsRightToLeft() )
1000 rFrame.SwitchLTRtoRTL( aTmp );
1001 rFrame.SwitchLTRtoRTL( aRepaint );
1004 if( bDraw && aTmp.HasArea() )
1006 const OutputDevice* pOut = rInf.GetOut();
1007 assert(pOut);
1008 DrawGraphic( m_pBrush.get(), *const_cast<OutputDevice*>(pOut),
1009 aTmp, aRepaint, m_bReplace ? GRFNUM_REPLACE : GRFNUM_YES );
1013 void SwGrfNumPortion::SetBase( tools::Long nLnAscent, tools::Long nLnDescent,
1014 tools::Long nFlyAsc, tools::Long nFlyDesc )
1016 if ( GetOrient() == text::VertOrientation::NONE )
1017 return;
1019 SetRelPos( 0 );
1020 if ( GetOrient() == text::VertOrientation::CENTER )
1021 SetRelPos( GetGrfHeight() / 2 );
1022 else if ( GetOrient() == text::VertOrientation::TOP )
1023 SetRelPos( GetGrfHeight() - GRFNUM_SECURE );
1024 else if ( GetOrient() == text::VertOrientation::BOTTOM )
1026 else if ( GetOrient() == text::VertOrientation::CHAR_CENTER )
1027 SetRelPos( ( GetGrfHeight() + nLnAscent - nLnDescent ) / 2 );
1028 else if ( GetOrient() == text::VertOrientation::CHAR_TOP )
1029 SetRelPos( nLnAscent );
1030 else if ( GetOrient() == text::VertOrientation::CHAR_BOTTOM )
1031 SetRelPos( GetGrfHeight() - nLnDescent );
1032 else
1034 if( GetGrfHeight() >= nFlyAsc + nFlyDesc )
1036 // If I'm as large as the line, I do not need to adjust
1037 // at the line; I'll leave the max. ascent unchanged
1038 SetRelPos( nFlyAsc );
1040 else if ( GetOrient() == text::VertOrientation::LINE_CENTER )
1041 SetRelPos( ( GetGrfHeight() + nFlyAsc - nFlyDesc ) / 2 );
1042 else if ( GetOrient() == text::VertOrientation::LINE_TOP )
1043 SetRelPos( nFlyAsc );
1044 else if ( GetOrient() == text::VertOrientation::LINE_BOTTOM )
1045 SetRelPos( GetGrfHeight() - nFlyDesc );
1049 void SwTextFrame::StopAnimation( const OutputDevice* pOut )
1051 OSL_ENSURE( HasAnimation(), "SwTextFrame::StopAnimation: Which Animation?" );
1052 if( !HasPara() )
1053 return;
1055 SwLineLayout *pLine = GetPara();
1056 while( pLine )
1058 SwLinePortion *pPor = pLine->GetNextPortion();
1059 while( pPor )
1061 if( pPor->IsGrfNumPortion() )
1062 static_cast<SwGrfNumPortion*>(pPor)->StopAnimation( pOut );
1063 // The NumberPortion is always at the first char,
1064 // which means we can cancel as soon as we've reached a portion
1065 // with a length > 0
1066 pPor = pPor->GetLen() ? nullptr : pPor->GetNextPortion();
1068 pLine = pLine->GetLen() ? nullptr : pLine->GetNext();
1073 * Initializes the script array and clears the width array
1075 SwCombinedPortion::SwCombinedPortion( const OUString &rText )
1076 : SwFieldPortion( rText )
1077 , m_aWidth{ static_cast<sal_uInt16>(0),
1078 static_cast<sal_uInt16>(0),
1079 static_cast<sal_uInt16>(0) }
1080 , m_nUpPos(0)
1081 , m_nLowPos(0)
1082 , m_nProportion(55)
1084 SetLen(TextFrameIndex(1));
1085 SetWhichPor( PortionType::Combined );
1086 if( m_aExpand.getLength() > 6 )
1087 m_aExpand = m_aExpand.copy( 0, 6 );
1089 // Initialization of the scripttype array,
1090 // the arrays of width and position are filled by the format function
1091 assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());
1093 SwFontScript nScr = SW_SCRIPTS;
1094 for( sal_Int32 i = 0; i < rText.getLength(); ++i )
1096 switch ( g_pBreakIt->GetBreakIter()->getScriptType( rText, i ) ) {
1097 case i18n::ScriptType::LATIN : nScr = SwFontScript::Latin; break;
1098 case i18n::ScriptType::ASIAN : nScr = SwFontScript::CJK; break;
1099 case i18n::ScriptType::COMPLEX : nScr = SwFontScript::CTL; break;
1101 m_aScrType[i] = nScr;
1105 void SwCombinedPortion::Paint( const SwTextPaintInfo &rInf ) const
1107 OSL_ENSURE(GetLen() <= TextFrameIndex(1), "SwFieldPortion::Paint: rest-portion pollution?");
1108 if( !Width() )
1109 return;
1111 rInf.DrawBackBrush( *this );
1112 rInf.DrawViewOpt( *this, PortionType::Field );
1114 // do we have to repaint a post it portion?
1115 if( rInf.OnWin() && mpNextPortion && !mpNextPortion->Width() )
1116 mpNextPortion->PrePaint( rInf, this );
1118 const sal_Int32 nCount = m_aExpand.getLength();
1119 if( !nCount )
1120 return;
1121 OSL_ENSURE( nCount < 7, "Too much combined characters" );
1123 // the first character of the second row
1124 const sal_Int32 nTop = ( nCount + 1 ) / 2;
1126 SwFont aTmpFont( *rInf.GetFont() );
1127 aTmpFont.SetProportion( m_nProportion ); // a smaller font
1128 SwFontSave aFontSave( rInf, &aTmpFont );
1130 Point aOldPos = rInf.GetPos();
1131 Point aOutPos( aOldPos.X(), aOldPos.Y() - m_nUpPos );// Y of the first row
1132 for( sal_Int32 i = 0 ; i < nCount; ++i )
1134 if( i == nTop ) // change the row
1135 aOutPos.setY( aOldPos.Y() + m_nLowPos ); // Y of the second row
1136 aOutPos.setX( aOldPos.X() + m_aPos[i] ); // X position
1137 const SwFontScript nAct = m_aScrType[i]; // script type
1138 aTmpFont.SetActual( nAct );
1140 // if there're more than 4 characters to display, we choose fonts
1141 // with 2/3 of the original font width.
1142 if( m_aWidth[ nAct ] )
1144 Size aTmpSz = aTmpFont.GetSize( nAct );
1145 if( aTmpSz.Width() != m_aWidth[ nAct ] )
1147 aTmpSz.setWidth( m_aWidth[ nAct ] );
1148 aTmpFont.SetSize( aTmpSz, nAct );
1151 const_cast<SwTextPaintInfo&>(rInf).SetPos( aOutPos );
1152 rInf.DrawText(m_aExpand, *this, TextFrameIndex(i), TextFrameIndex(1));
1154 // rInf is const, so we have to take back our manipulations
1155 const_cast<SwTextPaintInfo&>(rInf).SetPos( aOldPos );
1159 bool SwCombinedPortion::Format( SwTextFormatInfo &rInf )
1161 const sal_Int32 nCount = m_aExpand.getLength();
1162 if( !nCount )
1164 Width( 0 );
1165 return false;
1168 OSL_ENSURE( nCount < 7, "Too much combined characters" );
1170 // If there are leading "weak"-scripttyped characters in this portion,
1171 // they get the actual scripttype.
1172 for( sal_Int32 i = 0; i < nCount && SW_SCRIPTS == m_aScrType[i]; ++i )
1173 m_aScrType[i] = rInf.GetFont()->GetActual();
1174 if( nCount > 4 )
1176 // more than four? Ok, then we need the 2/3 font width
1177 for( sal_Int32 i = 0; i < m_aExpand.getLength(); ++i )
1179 OSL_ENSURE( m_aScrType[i] < SW_SCRIPTS, "Combined: Script fault" );
1180 if( !m_aWidth[ m_aScrType[i] ] )
1182 rInf.GetOut()->SetFont( rInf.GetFont()->GetFnt( m_aScrType[i] ) );
1183 m_aWidth[ m_aScrType[i] ] =
1184 o3tl::narrowing<sal_uInt16>(2 * rInf.GetOut()->GetFontMetric().GetFontSize().Width() / 3);
1189 const sal_Int32 nTop = ( nCount + 1 ) / 2; // the first character of the second line
1190 SwViewShell *pSh = rInf.GetTextFrame()->getRootFrame()->GetCurrShell();
1191 SwFont aTmpFont( *rInf.GetFont() );
1192 SwFontSave aFontSave( rInf, &aTmpFont );
1193 m_nProportion = 55;
1194 // In nMainAscent/Descent we store the ascent and descent
1195 // of the original surrounding font
1196 sal_uInt16 nMaxDescent, nMaxAscent, nMaxWidth;
1197 sal_uInt16 nMainDescent = rInf.GetFont()->GetHeight( pSh, *rInf.GetOut() );
1198 const sal_uInt16 nMainAscent = rInf.GetFont()->GetAscent( pSh, *rInf.GetOut() );
1199 nMainDescent = nMainDescent - nMainAscent;
1200 // we start with a 50% font, but if we notice that the combined portion
1201 // becomes bigger than the surrounding font, we check 45% and maybe 40%.
1204 m_nProportion -= 5;
1205 aTmpFont.SetProportion( m_nProportion );
1206 memset( &m_aPos, 0, sizeof(m_aPos) );
1207 nMaxDescent = 0;
1208 nMaxAscent = 0;
1209 nMaxWidth = 0;
1210 m_nUpPos = m_nLowPos = 0;
1212 // Now we get the width of all characters.
1213 // The ascent and the width of the first line are stored in the
1214 // ascent member of the portion, the descent in nLowPos.
1215 // The ascent, descent and width of the second line are stored in the
1216 // local nMaxAscent, nMaxDescent and nMaxWidth variables.
1217 for( sal_Int32 i = 0; i < nCount; ++i )
1219 SwFontScript nScrp = m_aScrType[i];
1220 aTmpFont.SetActual( nScrp );
1221 if( m_aWidth[ nScrp ] )
1223 Size aFontSize( aTmpFont.GetSize( nScrp ) );
1224 aFontSize.setWidth( m_aWidth[ nScrp ] );
1225 aTmpFont.SetSize( aFontSize, nScrp );
1228 SwDrawTextInfo aDrawInf(pSh, *rInf.GetOut(), m_aExpand, i, 1);
1229 Size aSize = aTmpFont.GetTextSize_( aDrawInf );
1230 const sal_uInt16 nAsc = aTmpFont.GetAscent( pSh, *rInf.GetOut() );
1231 m_aPos[ i ] = o3tl::narrowing<sal_uInt16>(aSize.Width());
1232 if( i == nTop ) // enter the second line
1234 m_nLowPos = nMaxDescent;
1235 Height( nMaxDescent + nMaxAscent );
1236 Width( nMaxWidth );
1237 SetAscent( nMaxAscent );
1238 nMaxAscent = 0;
1239 nMaxDescent = 0;
1240 nMaxWidth = 0;
1242 nMaxWidth = nMaxWidth + m_aPos[ i ];
1243 if( nAsc > nMaxAscent )
1244 nMaxAscent = nAsc;
1245 if( aSize.Height() - nAsc > nMaxDescent )
1246 nMaxDescent = aSize.Height() - nAsc;
1248 // for one or two characters we double the width of the portion
1249 if( nCount < 3 )
1251 nMaxWidth *= 2;
1252 Width( 2*Width() );
1253 if( nCount < 2 )
1255 Height( nMaxAscent + nMaxDescent );
1256 m_nLowPos = nMaxDescent;
1259 Height( Height() + nMaxDescent + nMaxAscent );
1260 m_nUpPos = nMaxAscent;
1261 SetAscent( Height() - nMaxDescent - m_nLowPos );
1262 } while( m_nProportion > 40 && ( GetAscent() > nMainAscent ||
1263 Height() - GetAscent() > nMainDescent ) );
1264 // if the combined portion is smaller than the surrounding text,
1265 // the portion grows. This looks better, if there's a character background.
1266 if( GetAscent() < nMainAscent )
1268 Height( Height() + nMainAscent - GetAscent() );
1269 SetAscent( nMainAscent );
1271 if( Height() < nMainAscent + nMainDescent )
1272 Height( nMainAscent + nMainDescent );
1274 // We calculate the x positions of the characters in both lines...
1275 sal_uInt16 nTopDiff = 0;
1276 sal_uInt16 nBotDiff = 0;
1277 if( nMaxWidth > Width() )
1279 nTopDiff = ( nMaxWidth - Width() ) / 2;
1280 Width( nMaxWidth );
1282 else
1283 nBotDiff = ( Width() - nMaxWidth ) / 2;
1284 switch( nTop)
1286 case 3: m_aPos[1] = m_aPos[0] + nTopDiff;
1287 [[fallthrough]];
1288 case 2: m_aPos[nTop-1] = Width() - m_aPos[nTop-1];
1290 m_aPos[0] = 0;
1291 switch( nCount )
1293 case 5: m_aPos[4] = m_aPos[3] + nBotDiff;
1294 [[fallthrough]];
1295 case 3: m_aPos[nTop] = nBotDiff; break;
1296 case 6: m_aPos[4] = m_aPos[3] + nBotDiff;
1297 [[fallthrough]];
1298 case 4: m_aPos[nTop] = 0;
1299 [[fallthrough]];
1300 case 2: m_aPos[nCount-1] = Width() - m_aPos[nCount-1];
1303 // Does the combined portion fit the line?
1304 const bool bFull = rInf.Width() < rInf.X() + Width();
1305 if( bFull )
1307 if( rInf.GetLineStart() == rInf.GetIdx() && (!rInf.GetLast()->InFieldGrp()
1308 || !static_cast<SwFieldPortion*>(rInf.GetLast())->IsFollow() ) )
1309 Width( rInf.Width() - rInf.X() );
1310 else
1312 Truncate();
1313 Width( 0 );
1314 SetLen(TextFrameIndex(0));
1315 if( rInf.GetLast() )
1316 rInf.GetLast()->FormatEOL( rInf );
1319 return bFull;
1322 sal_uInt16 SwCombinedPortion::GetViewWidth( const SwTextSizeInfo &rInf ) const
1324 if( !GetLen() ) // for the dummy part at the end of the line, where
1325 return 0; // the combined portion doesn't fit.
1326 return SwFieldPortion::GetViewWidth( rInf );
1329 SwFieldPortion *SwFieldFormDropDownPortion::Clone(const OUString &rExpand) const
1331 return new SwFieldFormDropDownPortion(m_pFieldMark, rExpand);
1334 void SwFieldFormDropDownPortion::Paint( const SwTextPaintInfo &rInf ) const
1336 SwFieldPortion::Paint( rInf );
1338 ::sw::mark::DropDownFieldmark* pDropDownField = dynamic_cast< ::sw::mark::DropDownFieldmark* >(m_pFieldMark);
1339 if(pDropDownField)
1341 SwRect aPaintArea;
1342 rInf.CalcRect( *this, &aPaintArea );
1343 pDropDownField->SetPortionPaintArea(aPaintArea);
1347 SwFieldPortion *SwFieldFormDatePortion::Clone(const OUString &/*rExpand*/) const
1349 return new SwFieldFormDatePortion(m_pFieldMark, m_bStart);
1352 void SwFieldFormDatePortion::Paint( const SwTextPaintInfo &rInf ) const
1354 SwFieldPortion::Paint( rInf );
1356 ::sw::mark::DateFieldmark* pDateField = dynamic_cast< ::sw::mark::DateFieldmark* >(m_pFieldMark);
1357 if(pDateField)
1359 SwRect aPaintArea;
1360 rInf.CalcRect( *this, &aPaintArea );
1361 if(m_bStart)
1362 pDateField->SetPortionPaintAreaStart(aPaintArea);
1363 else
1364 pDateField->SetPortionPaintAreaEnd(aPaintArea);
1368 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */