android: Update app-specific/MIME type icons
[LibreOffice.git] / sw / source / core / text / porfld.cxx
blob580b4a2635a7a7b3ab9760b6d188626809549254
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 <utility>
26 #include <comphelper/string.hxx>
27 #include <vcl/graph.hxx>
28 #include <editeng/brushitem.hxx>
29 #include <vcl/metric.hxx>
30 #include <vcl/outdev.hxx>
31 #include <vcl/pdfextoutdevdata.hxx>
32 #include <vcl/pdfwriter.hxx>
33 #include <viewopt.hxx>
34 #include <SwPortionHandler.hxx>
35 #include "porlay.hxx"
36 #include "porfld.hxx"
37 #include "inftxt.hxx"
38 #include <fmtornt.hxx>
39 #include <frmatr.hxx>
40 #include <frmtool.hxx>
41 #include <viewsh.hxx>
42 #include <doc.hxx>
43 #include <IDocumentSettingAccess.hxx>
44 #include <rootfrm.hxx>
45 #include <breakit.hxx>
46 #include "porftn.hxx"
47 #include <accessibilityoptions.hxx>
48 #include <editeng/lrspitem.hxx>
49 #include <unicode/ubidi.h>
50 #include <bookmark.hxx>
51 #include <docufld.hxx>
53 using namespace ::com::sun::star;
55 SwLinePortion *SwFieldPortion::Compress()
56 { return (GetLen() || !m_aExpand.isEmpty() || SwLinePortion::Compress()) ? this : nullptr; }
58 SwFieldPortion *SwFieldPortion::Clone( const OUString &rExpand ) const
60 std::unique_ptr<SwFont> pNewFnt;
61 if( m_pFont )
63 pNewFnt.reset(new SwFont( *m_pFont ));
65 // #i107143#
66 // pass placeholder property to created <SwFieldPortion> instance.
67 SwFieldPortion* pClone = new SwFieldPortion(rExpand, std::move(pNewFnt));
68 pClone->SetNextOffset( m_nNextOffset );
69 pClone->m_bNoLength = m_bNoLength;
70 return pClone;
73 void SwFieldPortion::TakeNextOffset( const SwFieldPortion* pField )
75 OSL_ENSURE( pField, "TakeNextOffset: Missing Source" );
76 m_nNextOffset = pField->GetNextOffset();
77 m_aExpand = m_aExpand.replaceAt(0, sal_Int32(m_nNextOffset), u"");
78 m_bFollow = true;
81 SwFieldPortion::SwFieldPortion(OUString aExpand, std::unique_ptr<SwFont> pFont, TextFrameIndex const nFieldLen)
82 : m_aExpand(std::move(aExpand)), m_pFont(std::move(pFont)), m_nNextOffset(0)
83 , m_nNextScriptChg(COMPLETE_STRING), m_nFieldLen(nFieldLen), m_nViewWidth(0)
84 , m_bFollow( false ), m_bLeft( false), m_bHide( false)
85 , m_bCenter (false), m_bHasFollow( false )
86 , m_bAnimated( false), m_bNoPaint( false)
87 , m_bReplace(false)
88 , m_bNoLength( false )
90 SetWhichPor( PortionType::Field );
93 SwFieldPortion::SwFieldPortion( const SwFieldPortion& rField )
94 : SwExpandPortion( rField )
95 , m_aExpand( rField.GetExp() )
96 , m_nNextOffset( rField.GetNextOffset() )
97 , m_nNextScriptChg( rField.m_nNextScriptChg )
98 , m_nFieldLen(rField.m_nFieldLen)
99 , m_nViewWidth( rField.m_nViewWidth )
100 , m_bFollow( rField.IsFollow() )
101 , m_bLeft( rField.IsLeft() )
102 , m_bHide( rField.IsHide() )
103 , m_bCenter( rField.IsCenter() )
104 , m_bHasFollow( rField.HasFollow() )
105 , m_bAnimated ( rField.m_bAnimated )
106 , m_bNoPaint( rField.m_bNoPaint)
107 , m_bReplace( rField.m_bReplace )
108 , m_bNoLength( rField.m_bNoLength )
110 if ( rField.HasFont() )
111 m_pFont.reset( new SwFont( *rField.GetFont() ) );
113 SetWhichPor( PortionType::Field );
116 SwFieldPortion::~SwFieldPortion()
118 m_pFont.reset();
121 sal_uInt16 SwFieldPortion::GetViewWidth( const SwTextSizeInfo &rInf ) const
123 // even though this is const, nViewWidth should be computed at the very end:
124 SwFieldPortion* pThis = const_cast<SwFieldPortion*>(this);
125 if( !Width() && rInf.OnWin() && !rInf.GetOpt().IsPagePreview() &&
126 !rInf.GetOpt().IsReadonly() && rInf.GetOpt().IsFieldShadings() )
128 if( !m_nViewWidth )
129 pThis->m_nViewWidth = rInf.GetTextSize(OUString(' ')).Width();
131 else
132 pThis->m_nViewWidth = 0;
133 return m_nViewWidth;
136 namespace {
139 * Never just use SetLen(0)
141 class SwFieldSlot
143 std::shared_ptr<const vcl::text::TextLayoutCache> m_pOldCachedVclData;
144 const OUString *pOldText;
145 OUString aText;
146 TextFrameIndex nIdx;
147 TextFrameIndex nLen;
148 SwTextFormatInfo *pInf;
149 bool bOn;
150 public:
151 SwFieldSlot( const SwTextFormatInfo* pNew, const SwFieldPortion *pPor );
152 ~SwFieldSlot();
157 SwFieldSlot::SwFieldSlot( const SwTextFormatInfo* pNew, const SwFieldPortion *pPor )
158 : pOldText(nullptr)
159 , nIdx(0)
160 , nLen(0)
161 , pInf(nullptr)
163 bOn = pPor->GetExpText( *pNew, aText );
165 // The text will be replaced ...
166 if( !bOn )
167 return;
169 pInf = const_cast<SwTextFormatInfo*>(pNew);
170 nIdx = pInf->GetIdx();
171 nLen = pInf->GetLen();
172 pOldText = &(pInf->GetText());
173 m_pOldCachedVclData = pInf->GetCachedVclData();
174 pInf->SetLen(TextFrameIndex(aText.getLength()));
175 pInf->SetCachedVclData(nullptr);
176 if( pPor->IsFollow() )
178 pInf->SetFakeLineStart( nIdx > pInf->GetLineStart() );
179 pInf->SetIdx(TextFrameIndex(0));
181 else
183 TextFrameIndex nEnd(pOldText->getLength());
184 if (nIdx < nEnd)
186 sal_Int32 const nFieldLen(pPor->GetFieldLen());
187 aText = (*pOldText).replaceAt(sal_Int32(nIdx), nFieldLen, aText);
189 else if (nIdx == nEnd)
190 aText = *pOldText + aText;
191 else
192 SAL_WARN("sw.core", "SwFieldSlot bad SwFieldPortion index.");
194 pInf->SetText( aText );
197 SwFieldSlot::~SwFieldSlot()
199 if( bOn )
201 pInf->SetCachedVclData(m_pOldCachedVclData);
202 pInf->SetText( *pOldText );
203 pInf->SetIdx( nIdx );
204 pInf->SetLen( nLen );
205 pInf->SetFakeLineStart( false );
209 void SwFieldPortion::CheckScript( const SwTextSizeInfo &rInf )
211 OUString aText;
212 if (!GetExpText(rInf, aText) || aText.isEmpty())
213 return;
215 SwFontScript nActual = m_pFont ? m_pFont->GetActual() : rInf.GetFont()->GetActual();
216 sal_uInt16 nScript = g_pBreakIt->GetBreakIter()->getScriptType( aText, 0 );
217 sal_Int32 nChg = 0;
218 if( i18n::ScriptType::WEAK == nScript )
220 nChg = g_pBreakIt->GetBreakIter()->endOfScript(aText,0,nScript);
221 if (nChg < aText.getLength() && nChg >= 0)
222 nScript = g_pBreakIt->GetBreakIter()->getScriptType( aText, nChg );
225 // nNextScriptChg will be evaluated during SwFieldPortion::Format()
227 if (nChg < aText.getLength() && nChg >= 0)
228 m_nNextScriptChg = TextFrameIndex(
229 g_pBreakIt->GetBreakIter()->endOfScript(aText, nChg, nScript));
230 else
231 m_nNextScriptChg = TextFrameIndex(aText.getLength());
233 SwFontScript nTmp;
234 switch ( nScript ) {
235 case i18n::ScriptType::LATIN : nTmp = SwFontScript::Latin; break;
236 case i18n::ScriptType::ASIAN : nTmp = SwFontScript::CJK; break;
237 case i18n::ScriptType::COMPLEX : nTmp = SwFontScript::CTL; break;
238 default: nTmp = nActual;
241 // #i16354# Change script type for RTL text to CTL.
242 const SwScriptInfo& rSI = rInf.GetParaPortion()->GetScriptInfo();
243 // #i98418#
244 const sal_uInt8 nFieldDir = (IsNumberPortion() || IsFootnoteNumPortion())
245 ? rSI.GetDefaultDir()
246 : rSI.DirType(IsFollow() ? rInf.GetIdx() - m_nFieldLen : rInf.GetIdx());
249 UErrorCode nError = U_ZERO_ERROR;
250 UBiDi* pBidi = ubidi_openSized( aText.getLength(), 0, &nError );
251 ubidi_setPara( pBidi, reinterpret_cast<const UChar *>(aText.getStr()), aText.getLength(), nFieldDir, nullptr, &nError );
252 int32_t nEnd;
253 UBiDiLevel nCurrDir;
254 ubidi_getLogicalRun( pBidi, 0, &nEnd, &nCurrDir );
255 ubidi_close( pBidi );
256 const TextFrameIndex nNextDirChg(nEnd);
257 m_nNextScriptChg = std::min( m_nNextScriptChg, nNextDirChg );
259 // #i89825# change the script type also to CTL
260 // if there is no strong LTR char in the LTR run (numbers)
261 if (nCurrDir != UBIDI_RTL &&
262 (UBIDI_LTR != nFieldDir || i18n::ScriptType::COMPLEX == nScript))
264 nCurrDir = UBIDI_RTL;
265 for( sal_Int32 nCharIdx = 0; nCharIdx < nEnd; ++nCharIdx )
267 UCharDirection nCharDir = u_charDirection ( aText[ nCharIdx ]);
268 if ( nCharDir == U_LEFT_TO_RIGHT ||
269 nCharDir == U_LEFT_TO_RIGHT_EMBEDDING ||
270 nCharDir == U_LEFT_TO_RIGHT_OVERRIDE )
272 nCurrDir = UBIDI_LTR;
273 break;
278 if (nCurrDir == UBIDI_RTL)
280 nTmp = SwFontScript::CTL;
281 // If we decided that this range was RTL after all and the
282 // previous range was complex but clipped to the start of this
283 // range, then extend it to be complex over the additional RTL range
284 if (nScript == i18n::ScriptType::COMPLEX)
285 m_nNextScriptChg = nNextDirChg;
289 // #i98418#
290 // keep determined script type for footnote portions as preferred script type.
291 // For footnote portions a font can not be created directly - see footnote
292 // portion format method.
293 if ( IsFootnotePortion() )
295 static_cast<SwFootnotePortion*>(this)->SetPreferredScriptType( nTmp );
297 else if ( nTmp != nActual )
299 if( !m_pFont )
300 m_pFont.reset( new SwFont( *rInf.GetFont() ) );
301 m_pFont->SetActual( nTmp );
306 bool SwFieldPortion::Format( SwTextFormatInfo &rInf )
308 // Scope wegen aDiffText::DTOR!
309 bool bFull = false;
310 bool bEOL = false;
311 TextFrameIndex const nTextRest = TextFrameIndex(rInf.GetText().getLength()) - rInf.GetIdx();
313 TextFrameIndex nRest;
314 SwFieldSlot aDiffText( &rInf, this );
315 SwLayoutModeModifier aLayoutModeModifier( *rInf.GetOut() );
316 aLayoutModeModifier.SetAuto();
318 // Field portion has to be split in several parts if
319 // 1. There are script/direction changes inside the field
320 // 2. There are portion breaks (tab, break) inside the field:
321 const TextFrameIndex nOldFullLen = rInf.GetLen();
322 TextFrameIndex nFullLen = rInf.ScanPortionEnd(rInf.GetIdx(), rInf.GetIdx() + nOldFullLen) - rInf.GetIdx();
323 if ( m_nNextScriptChg < nFullLen )
325 nFullLen = m_nNextScriptChg;
326 rInf.SetHookChar( 0 );
328 rInf.SetLen( nFullLen );
330 if (TextFrameIndex(COMPLETE_STRING) != rInf.GetUnderScorePos() &&
331 rInf.GetUnderScorePos() > rInf.GetIdx() )
332 rInf.SetUnderScorePos( rInf.GetIdx() );
334 if( m_pFont )
335 m_pFont->AllocFontCacheId( rInf.GetVsh(), m_pFont->GetActual() );
337 SwFontSave aSave( rInf, m_pFont.get() );
339 // Length must be 0: the length is set for bFull after format
340 // and passed along in nRest. Or else the old length would be
341 // retained and be used for nRest!
342 SetLen(TextFrameIndex(0));
343 TextFrameIndex const nFollow(IsFollow() ? TextFrameIndex(0) : m_nFieldLen);
345 // As odd is may seem: the query for GetLen() must return false due
346 // to the ExpandPortions _after_ aDiffText (see SoftHyphs), caused
347 // by SetFull.
348 if( !nFullLen )
350 // Don't Init(), as we need height and ascent
351 Width(0);
352 bFull = rInf.Width() <= rInf.GetPos().X();
354 else
356 TextFrameIndex const nOldLineStart = rInf.GetLineStart();
357 if( IsFollow() )
358 rInf.SetLineStart(TextFrameIndex(0));
359 rInf.SetNotEOL( nFullLen == nOldFullLen && nTextRest > nFollow );
361 // the height depending on the fields font is set,
362 // this is required for SwTextGuess::Guess
363 Height( rInf.GetTextHeight() + rInf.GetFont()->GetTopBorderSpace() +
364 rInf.GetFont()->GetBottomBorderSpace() );
365 // If a kerning portion is inserted after our field portion,
366 // the ascent and height must be known
367 SetAscent( rInf.GetAscent() + rInf.GetFont()->GetTopBorderSpace() );
368 bFull = SwTextPortion::Format( rInf );
369 rInf.SetNotEOL( false );
370 rInf.SetLineStart( nOldLineStart );
372 TextFrameIndex const nTmpLen = GetLen();
373 bEOL = !nTmpLen && nFollow && bFull;
374 nRest = nOldFullLen - nTmpLen;
376 // The char is held in the first position
377 // Unconditionally after format!
378 SetLen( m_bNoLength ? TextFrameIndex(0) : nFollow );
380 if( nRest )
382 // aExpand has not yet been shortened; the new Ofst is a
383 // result of nRest
384 TextFrameIndex nNextOfst = TextFrameIndex(m_aExpand.getLength()) - nRest;
386 if ( IsQuoVadisPortion() )
387 nNextOfst = nNextOfst + TextFrameIndex(static_cast<SwQuoVadisPortion*>(this)->GetContText().getLength());
389 OUString aNew( m_aExpand.copy(sal_Int32(nNextOfst)) );
390 m_aExpand = m_aExpand.copy(0, sal_Int32(nNextOfst));
392 // These characters should not be contained in the follow
393 // field portion. They are handled via the HookChar mechanism.
394 const sal_Unicode nNew = !aNew.isEmpty() ? aNew[0] : 0;
395 auto IsHook = [](const sal_Unicode cNew) -> bool
397 switch (cNew)
399 case CH_BREAK:
400 case CH_TAB:
401 case CHAR_HARDHYPHEN: // non-breaking hyphen
402 case CHAR_SOFTHYPHEN:
403 case CHAR_HARDBLANK:
404 case CHAR_ZWSP:
405 case CHAR_WJ:
406 case CH_TXTATR_BREAKWORD:
407 case CH_TXTATR_INWORD:
409 return true;
411 default:
412 return false;
415 if (IsHook(nNew))
417 if (nNew == CH_BREAK)
419 bFull = true;
421 aNew = aNew.copy(1);
422 ++nNextOfst;
425 // Even if there is no more text left for a follow field,
426 // we have to build a follow field portion (without font),
427 // otherwise the HookChar mechanism would not work.
428 SwFieldPortion *pField = Clone( aNew );
429 if( !aNew.isEmpty() && !pField->GetFont() )
431 pField->SetFont( std::make_unique<SwFont>( *rInf.GetFont() ) );
433 if (IsFollow() || Compress())
434 { // empty this will be deleted in SwLineLayout::CalcLine()
435 // anyway so make sure pField doesn't have a stale flag
436 pField->SetFollow( true );
438 if (pField->Compress() && !std::all_of(std::u16string_view(aNew).begin(),
439 std::u16string_view(aNew).end(), IsHook))
440 { // empty pField will be deleted in SwLineLayout::CalcLine()
441 // anyway so make sure this one doesn't have a stale flag
442 SetHasFollow( true );
445 // For a newly created field, nNextOffset contains the Offset
446 // of its start of the original string
447 // If a FollowField is created when formatting, this FollowField's
448 // Offset is being held in nNextOffset
449 m_nNextOffset = m_nNextOffset + nNextOfst;
450 pField->SetNextOffset( m_nNextOffset );
451 rInf.SetRest( pField );
455 if( bEOL && rInf.GetLast() && !rInf.GetUnderflow() )
456 rInf.GetLast()->FormatEOL( rInf );
457 return bFull;
460 void SwFieldPortion::Paint( const SwTextPaintInfo &rInf ) const
462 SwFontSave aSave( rInf, m_pFont.get() );
464 // OSL_ENSURE(GetLen() <= TextFrameIndex(1), "SwFieldPortion::Paint: rest-portion pollution?");
465 if (Width() && !m_bContentControl)
467 // A very liberal use of the background
468 rInf.DrawViewOpt( *this, PortionType::Field );
469 SwExpandPortion::Paint( rInf );
473 bool SwFieldPortion::GetExpText( const SwTextSizeInfo &rInf, OUString &rText ) const
475 rText = m_aExpand;
476 if( rText.isEmpty() && rInf.OnWin() &&
477 !rInf.GetOpt().IsPagePreview() && !rInf.GetOpt().IsReadonly() &&
478 rInf.GetOpt().IsFieldShadings() &&
479 !HasFollow() )
480 rText = " ";
481 return true;
484 void SwFieldPortion::HandlePortion( SwPortionHandler& rPH ) const
486 rPH.Special( GetLen(), m_aExpand, GetWhichPor() );
489 void SwFieldPortion::dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText,
490 TextFrameIndex& nOffset) const
492 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwFieldPortion"));
493 dumpAsXmlAttributes(pWriter, rText, nOffset);
494 nOffset += GetLen();
496 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("expand"), BAD_CAST(m_aExpand.toUtf8().getStr()));
498 if (m_pFont)
500 m_pFont->dumpAsXml(pWriter);
503 (void)xmlTextWriterEndElement(pWriter);
506 SwPosSize SwFieldPortion::GetTextSize( const SwTextSizeInfo &rInf ) const
508 SwFontSave aSave( rInf, m_pFont.get() );
509 SwPosSize aSize( SwExpandPortion::GetTextSize( rInf ) );
510 return aSize;
513 SwFieldPortion *SwHiddenPortion::Clone(const OUString &rExpand ) const
515 std::unique_ptr<SwFont> pNewFnt;
516 if( m_pFont )
517 pNewFnt.reset(new SwFont( *m_pFont ));
518 return new SwHiddenPortion( rExpand, std::move(pNewFnt) );
521 void SwHiddenPortion::Paint( const SwTextPaintInfo &rInf ) const
523 if( Width() )
525 SwFontSave aSave( rInf, m_pFont.get() );
526 rInf.DrawViewOpt( *this, PortionType::Hidden );
527 SwExpandPortion::Paint( rInf );
531 bool SwHiddenPortion::GetExpText( const SwTextSizeInfo &rInf, OUString &rText ) const
533 // Do not query for IsHidden()!
534 return SwFieldPortion::GetExpText( rInf, rText );
537 SwNumberPortion::SwNumberPortion( const OUString &rExpand,
538 std::unique_ptr<SwFont> pFont,
539 const bool bLft,
540 const bool bCntr,
541 const sal_uInt16 nMinDst,
542 const bool bLabelAlignmentPosAndSpaceModeActive )
543 : SwFieldPortion(rExpand, std::move(pFont), TextFrameIndex(0))
544 , m_nFixWidth(0)
545 , m_nMinDist(nMinDst)
546 , mbLabelAlignmentPosAndSpaceModeActive(bLabelAlignmentPosAndSpaceModeActive)
548 SetWhichPor( PortionType::Number );
549 SetLeft( bLft );
550 SetHide( false );
551 SetCenter( bCntr );
554 TextFrameIndex SwNumberPortion::GetModelPositionForViewPoint(const sal_uInt16) const
556 return TextFrameIndex(0);
559 SwFieldPortion *SwNumberPortion::Clone( const OUString &rExpand ) const
561 std::unique_ptr<SwFont> pNewFnt;
562 if( m_pFont )
563 pNewFnt.reset(new SwFont( *m_pFont ));
565 return new SwNumberPortion( rExpand, std::move(pNewFnt), IsLeft(), IsCenter(),
566 m_nMinDist, mbLabelAlignmentPosAndSpaceModeActive );
570 * We can create multiple NumFields
571 * Tricky, if one enters enough previous-text in the dialog box
572 * to cause the line to overflow
573 * We need to keep the Fly's evasion tactics in mind
575 bool SwNumberPortion::Format( SwTextFormatInfo &rInf )
577 SetHide( false );
578 const bool bFull = SwFieldPortion::Format( rInf );
579 SetLen(TextFrameIndex(0));
580 // a numbering portion can be contained in a rotated portion!!!
581 m_nFixWidth = rInf.IsMulti() ? Height() : Width();
582 rInf.SetNumDone( !rInf.GetRest() );
583 if( rInf.IsNumDone() )
585 // SetAscent( rInf.GetAscent() );
586 OSL_ENSURE( Height() && mnAscent, "NumberPortions without Height | Ascent" );
588 tools::Long nDiff( 0 );
590 if ( !mbLabelAlignmentPosAndSpaceModeActive )
592 if (!rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING) &&
593 // #i32902#
594 !IsFootnoteNumPortion() )
596 nDiff = rInf.Left()
597 + rInf.GetTextFrame()->GetTextNodeForParaProps()->
598 GetSwAttrSet().GetFirstLineIndent().GetTextFirstLineOffset()
599 - rInf.First()
600 + rInf.ForcedLeftMargin();
602 else
604 nDiff = rInf.Left() - rInf.First() + rInf.ForcedLeftMargin();
607 // The text part of the numbering should always at least
608 // start at the left margin
609 if( nDiff < 0 )
610 nDiff = 0;
611 else if ( nDiff > rInf.X() )
612 nDiff -= rInf.X();
613 else
614 nDiff = 0;
616 if( nDiff < m_nFixWidth + m_nMinDist )
617 nDiff = m_nFixWidth + m_nMinDist;
619 // Numbering evades the Fly, no nDiff in the second round
620 // Tricky special case: FlyFrame is in an Area we're just about to
621 // acquire
622 // The NumberPortion is marked as hidden
623 const bool bFly = rInf.GetFly() ||
624 ( rInf.GetLast() && rInf.GetLast()->IsFlyPortion() );
625 if( nDiff > rInf.Width() )
627 nDiff = rInf.Width();
628 if ( bFly )
629 SetHide( true );
632 // A numbering portion can be inside a SwRotatedPortion. Then the
633 // Height has to be changed
634 if ( rInf.IsMulti() )
636 if ( Height() < nDiff )
637 Height( nDiff );
639 else if( Width() < nDiff )
640 Width( nDiff );
642 return bFull;
647 * A FormatEOL indicates that the subsequent text did not fit onto
648 * the line anymore. In order for the Numbering to follow through,
649 * we hide this NumberPortion
651 void SwNumberPortion::FormatEOL( SwTextFormatInfo& )
654 // This caused trouble with flys anchored as characters.
655 // If one of these is numbered but does not fit to the line,
656 // it calls this function, causing a loop because both the number
657 // portion and the fly portion go to the next line
658 // SetHide( true );
663 * A hidden NumberPortion is not displayed, unless there are TextPortions in
664 * this line or there's just one line at all
666 void SwNumberPortion::Paint( const SwTextPaintInfo &rInf ) const
668 if ( IsHide() && rInf.GetParaPortion() && rInf.GetParaPortion()->GetNext() )
670 SwLinePortion *pTmp = GetNextPortion();
671 while ( pTmp && !pTmp->InTextGrp() )
672 pTmp = pTmp->GetNextPortion();
673 if ( !pTmp )
674 return;
677 // calculate the width of the number portion, including follows
678 const sal_uInt16 nOldWidth = Width();
679 sal_uInt16 nSumWidth = 0;
680 sal_uInt16 nOffset = 0;
682 const SwLinePortion* pTmp = this;
683 while ( pTmp && pTmp->InNumberGrp() )
685 nSumWidth = nSumWidth + pTmp->Width();
686 if ( static_cast<const SwNumberPortion*>(pTmp)->HasFollow() )
687 pTmp = pTmp->GetNextPortion();
688 else
690 nOffset = pTmp->Width() - static_cast<const SwNumberPortion*>(pTmp)->m_nFixWidth;
691 break;
695 // The master portion takes care for painting the background of the
696 // follow field portions
697 if ( ! IsFollow() )
699 SwNumberPortion *pThis = const_cast<SwNumberPortion*>(this);
700 pThis->Width( nSumWidth );
701 rInf.DrawViewOpt( *this, PortionType::Number );
702 pThis->Width( nOldWidth );
705 if( m_aExpand.isEmpty() )
706 return;
708 const SwFont *pTmpFnt = rInf.GetFont();
709 bool bPaintSpace = ( LINESTYLE_NONE != pTmpFnt->GetUnderline() ||
710 LINESTYLE_NONE != pTmpFnt->GetOverline() ||
711 STRIKEOUT_NONE != pTmpFnt->GetStrikeout() ) &&
712 !pTmpFnt->IsWordLineMode();
713 if( bPaintSpace && m_pFont )
714 bPaintSpace = ( LINESTYLE_NONE != m_pFont->GetUnderline() ||
715 LINESTYLE_NONE != m_pFont->GetOverline() ||
716 STRIKEOUT_NONE != m_pFont->GetStrikeout() ) &&
717 !m_pFont->IsWordLineMode();
719 SwFontSave aSave( rInf, m_pFont.get() );
721 if( m_nFixWidth == Width() && ! HasFollow() )
722 SwExpandPortion::Paint( rInf );
723 else
725 // logical const: reset width
726 SwNumberPortion *pThis = const_cast<SwNumberPortion*>(this);
727 bPaintSpace = bPaintSpace && m_nFixWidth < nOldWidth;
728 sal_uInt16 nSpaceOffs = m_nFixWidth;
729 pThis->Width( m_nFixWidth );
731 if( ( IsLeft() && ! rInf.GetTextFrame()->IsRightToLeft() ) ||
732 ( ! IsLeft() && ! IsCenter() && rInf.GetTextFrame()->IsRightToLeft() ) )
733 SwExpandPortion::Paint( rInf );
734 else
736 SwTextPaintInfo aInf( rInf );
737 if( nOffset < m_nMinDist )
738 nOffset = 0;
739 else
741 if( IsCenter() )
743 /* #110778# a / 2 * 2 == a is not a tautology */
744 sal_uInt16 nTmpOffset = nOffset;
745 nOffset /= 2;
746 if( nOffset < m_nMinDist )
747 nOffset = nTmpOffset - m_nMinDist;
749 else
750 nOffset = nOffset - m_nMinDist;
752 aInf.X( aInf.X() + nOffset );
753 SwExpandPortion::Paint( aInf );
754 if( bPaintSpace )
755 nSpaceOffs = nSpaceOffs + nOffset;
757 if( bPaintSpace && nOldWidth > nSpaceOffs )
759 SwTextPaintInfo aInf( rInf );
760 aInf.X( aInf.X() + nSpaceOffs );
762 // #i53199# Adjust position of underline:
763 if ( rInf.GetUnderFnt() )
765 const Point aNewPos( aInf.GetPos().X(), rInf.GetUnderFnt()->GetPos().Y() );
766 rInf.GetUnderFnt()->SetPos( aNewPos );
769 pThis->Width( nOldWidth - nSpaceOffs + 12 );
771 SwTextSlot aDiffText( &aInf, this, true, false, " " );
772 aInf.DrawText( *this, aInf.GetLen(), true );
775 pThis->Width( nOldWidth );
779 SwBulletPortion::SwBulletPortion( const sal_UCS4 cBullet,
780 std::u16string_view rBulletFollowedBy,
781 std::unique_ptr<SwFont> pFont,
782 const bool bLft,
783 const bool bCntr,
784 const sal_uInt16 nMinDst,
785 const bool bLabelAlignmentPosAndSpaceModeActive )
786 : SwNumberPortion( OUString(&cBullet, 1) + rBulletFollowedBy,
787 std::move(pFont), bLft, bCntr, nMinDst,
788 bLabelAlignmentPosAndSpaceModeActive )
790 SetWhichPor( PortionType::Bullet );
793 #define GRFNUM_SECURE 10
795 SwGrfNumPortion::SwGrfNumPortion(
796 const OUString& rGraphicFollowedBy,
797 const SvxBrushItem* pGrfBrush, OUString const & referer,
798 const SwFormatVertOrient* pGrfOrient, const Size& rGrfSize,
799 const bool bLft, const bool bCntr, const sal_uInt16 nMinDst,
800 const bool bLabelAlignmentPosAndSpaceModeActive ) :
801 SwNumberPortion( rGraphicFollowedBy, nullptr, bLft, bCntr, nMinDst,
802 bLabelAlignmentPosAndSpaceModeActive ),
803 m_pBrush( new SvxBrushItem(RES_BACKGROUND) ), m_nId( 0 )
805 SetWhichPor( PortionType::GrfNum );
806 SetAnimated( false );
807 m_bReplace = false;
808 if( pGrfBrush )
810 m_pBrush.reset(pGrfBrush->Clone());
811 const Graphic* pGraph = pGrfBrush->GetGraphic(referer);
812 if( pGraph )
813 SetAnimated( pGraph->IsAnimated() );
814 else
815 m_bReplace = true;
817 if( pGrfOrient )
819 m_nYPos = pGrfOrient->GetPos();
820 m_eOrient = pGrfOrient->GetVertOrient();
822 else
824 m_nYPos = 0;
825 m_eOrient = text::VertOrientation::TOP;
827 Width( rGrfSize.Width() + 2 * GRFNUM_SECURE );
828 m_nFixWidth = Width();
829 m_nGrfHeight = rGrfSize.Height() + 2 * GRFNUM_SECURE;
830 Height( sal_uInt16(m_nGrfHeight) );
831 m_bNoPaint = false;
834 SwGrfNumPortion::~SwGrfNumPortion()
836 if ( IsAnimated() )
838 Graphic* pGraph = const_cast<Graphic*>(m_pBrush->GetGraphic());
839 if (pGraph)
840 pGraph->StopAnimation( nullptr, m_nId );
842 m_pBrush.reset();
845 void SwGrfNumPortion::StopAnimation( const OutputDevice* pOut )
847 if ( IsAnimated() )
849 Graphic* pGraph = const_cast<Graphic*>(m_pBrush->GetGraphic());
850 if (pGraph)
851 pGraph->StopAnimation( pOut, m_nId );
855 bool SwGrfNumPortion::Format( SwTextFormatInfo &rInf )
857 SetHide( false );
858 // Width( nFixWidth );
859 sal_uInt16 nFollowedByWidth( 0 );
860 if ( mbLabelAlignmentPosAndSpaceModeActive )
862 SwFieldPortion::Format( rInf );
863 nFollowedByWidth = Width();
864 SetLen(TextFrameIndex(0));
866 Width( m_nFixWidth + nFollowedByWidth );
867 const bool bFull = rInf.Width() < rInf.X() + Width();
868 const bool bFly = rInf.GetFly() ||
869 ( rInf.GetLast() && rInf.GetLast()->IsFlyPortion() );
870 SetAscent( GetRelPos() > 0 ? GetRelPos() : 0 );
871 if( GetAscent() > Height() )
872 Height( GetAscent() );
874 if( bFull )
876 Width( rInf.Width() - rInf.X() );
877 if( bFly )
879 SetLen(TextFrameIndex(0));
880 m_bNoPaint = true;
881 rInf.SetNumDone( false );
882 return true;
885 rInf.SetNumDone( true );
886 // long nDiff = rInf.Left() - rInf.First() + rInf.ForcedLeftMargin();
887 tools::Long nDiff = mbLabelAlignmentPosAndSpaceModeActive
889 : rInf.Left() - rInf.First() + rInf.ForcedLeftMargin();
890 // The TextPortion should at least always start on the
891 // left margin
892 if( nDiff < 0 )
893 nDiff = 0;
894 else if ( nDiff > rInf.X() )
895 nDiff -= rInf.X();
896 if( nDiff < m_nFixWidth + m_nMinDist )
897 nDiff = m_nFixWidth + m_nMinDist;
899 // Numbering evades Fly, no nDiff in the second round
900 // Tricky special case: FlyFrame is in the Area we were just
901 // about to get a hold of.
902 // The NumberPortion is marked as hidden
903 if( nDiff > rInf.Width() )
905 nDiff = rInf.Width();
906 if( bFly )
907 SetHide( true );
910 if( Width() < nDiff )
911 Width( nDiff );
912 return bFull;
917 * A hidden NumberPortion is not displayed, unless there are TextPortions in
918 * this line or there's only one line at all
920 void SwGrfNumPortion::Paint( const SwTextPaintInfo &rInf ) const
922 if( m_bNoPaint )
923 return;
924 if ( IsHide() && rInf.GetParaPortion() && rInf.GetParaPortion()->GetNext() )
926 SwLinePortion *pTmp = GetNextPortion();
927 while ( pTmp && !pTmp->InTextGrp() )
928 pTmp = pTmp->GetNextPortion();
929 if ( !pTmp )
930 return;
932 Point aPos( rInf.X() + GRFNUM_SECURE, rInf.Y() - GetRelPos() + GRFNUM_SECURE );
933 tools::Long nTmpWidth = std::max( tools::Long(0), static_cast<tools::Long>(m_nFixWidth - 2 * GRFNUM_SECURE) );
934 Size aSize( nTmpWidth, GetGrfHeight() - 2 * GRFNUM_SECURE );
936 const bool bTmpLeft = mbLabelAlignmentPosAndSpaceModeActive ||
937 ( IsLeft() && ! rInf.GetTextFrame()->IsRightToLeft() ) ||
938 ( ! IsLeft() && ! IsCenter() && rInf.GetTextFrame()->IsRightToLeft() );
940 if( m_nFixWidth < Width() && !bTmpLeft )
942 sal_uInt16 nOffset = Width() - m_nFixWidth;
943 if( nOffset < m_nMinDist )
944 nOffset = 0;
945 else
947 if( IsCenter() )
949 nOffset /= 2;
950 if( nOffset < m_nMinDist )
951 nOffset = Width() - m_nFixWidth - m_nMinDist;
953 else
954 nOffset = nOffset - m_nMinDist;
956 aPos.AdjustX(nOffset );
959 if( m_bReplace )
961 const tools::Long nTmpH = GetNextPortion() ? GetNextPortion()->GetAscent() : 120;
962 aSize = Size( nTmpH, nTmpH );
963 aPos.setY( rInf.Y() - nTmpH );
965 SwRect aTmp( aPos, aSize );
967 bool bDraw = true;
969 if ( IsAnimated() )
971 bDraw = !rInf.GetOpt().IsGraphic();
972 if( !m_nId )
974 SetId( reinterpret_cast<sal_IntPtr>( rInf.GetTextFrame() ) );
975 rInf.GetTextFrame()->SetAnimation();
977 if( aTmp.Overlaps( rInf.GetPaintRect() ) && !bDraw )
979 rInf.NoteAnimation();
980 const SwViewShell* pViewShell = rInf.GetVsh();
982 // virtual device, not pdf export
983 if( OUTDEV_VIRDEV == rInf.GetOut()->GetOutDevType() &&
984 pViewShell && pViewShell->GetWin() )
986 Graphic* pGraph = const_cast<Graphic*>(m_pBrush->GetGraphic());
987 if (pGraph)
988 pGraph->StopAnimation(nullptr,m_nId);
989 rInf.GetTextFrame()->getRootFrame()->GetCurrShell()->InvalidateWindows( aTmp );
992 else if ( pViewShell &&
993 !pViewShell->GetAccessibilityOptions()->IsStopAnimatedGraphics() &&
994 !pViewShell->IsPreview() &&
995 // #i9684# Stop animation during printing/pdf export.
996 pViewShell->GetWin() )
998 Graphic* pGraph = const_cast<Graphic*>(m_pBrush->GetGraphic());
999 if (pGraph)
1001 const OutputDevice* pOut = rInf.GetOut();
1002 assert(pOut);
1003 pGraph->StartAnimation(
1004 *const_cast<OutputDevice*>(pOut), aPos, aSize, m_nId);
1008 // pdf export, printing, preview, stop animations...
1009 else
1010 bDraw = true;
1012 if( bDraw )
1015 Graphic* pGraph = const_cast<Graphic*>(m_pBrush->GetGraphic());
1016 if (pGraph)
1017 pGraph->StopAnimation( nullptr, m_nId );
1021 SwRect aRepaint( rInf.GetPaintRect() );
1022 const SwTextFrame& rFrame = *rInf.GetTextFrame();
1023 if( rFrame.IsVertical() )
1025 rFrame.SwitchHorizontalToVertical( aTmp );
1026 rFrame.SwitchHorizontalToVertical( aRepaint );
1029 if( rFrame.IsRightToLeft() )
1031 rFrame.SwitchLTRtoRTL( aTmp );
1032 rFrame.SwitchLTRtoRTL( aRepaint );
1035 if( bDraw && aTmp.HasArea() )
1037 const OutputDevice* pOut = rInf.GetOut();
1038 assert(pOut);
1039 DrawGraphic( m_pBrush.get(), *const_cast<OutputDevice*>(pOut),
1040 aTmp, aRepaint, m_bReplace ? GRFNUM_REPLACE : GRFNUM_YES );
1044 void SwGrfNumPortion::SetBase( tools::Long nLnAscent, tools::Long nLnDescent,
1045 tools::Long nFlyAsc, tools::Long nFlyDesc )
1047 if ( GetOrient() == text::VertOrientation::NONE )
1048 return;
1050 SetRelPos( 0 );
1051 if ( GetOrient() == text::VertOrientation::CENTER )
1052 SetRelPos( GetGrfHeight() / 2 );
1053 else if ( GetOrient() == text::VertOrientation::TOP )
1054 SetRelPos( GetGrfHeight() - GRFNUM_SECURE );
1055 else if ( GetOrient() == text::VertOrientation::BOTTOM )
1057 else if ( GetOrient() == text::VertOrientation::CHAR_CENTER )
1058 SetRelPos( ( GetGrfHeight() + nLnAscent - nLnDescent ) / 2 );
1059 else if ( GetOrient() == text::VertOrientation::CHAR_TOP )
1060 SetRelPos( nLnAscent );
1061 else if ( GetOrient() == text::VertOrientation::CHAR_BOTTOM )
1062 SetRelPos( GetGrfHeight() - nLnDescent );
1063 else
1065 if( GetGrfHeight() >= nFlyAsc + nFlyDesc )
1067 // If I'm as large as the line, I do not need to adjust
1068 // at the line; I'll leave the max. ascent unchanged
1069 SetRelPos( nFlyAsc );
1071 else if ( GetOrient() == text::VertOrientation::LINE_CENTER )
1072 SetRelPos( ( GetGrfHeight() + nFlyAsc - nFlyDesc ) / 2 );
1073 else if ( GetOrient() == text::VertOrientation::LINE_TOP )
1074 SetRelPos( nFlyAsc );
1075 else if ( GetOrient() == text::VertOrientation::LINE_BOTTOM )
1076 SetRelPos( GetGrfHeight() - nFlyDesc );
1080 void SwTextFrame::StopAnimation( const OutputDevice* pOut )
1082 OSL_ENSURE( HasAnimation(), "SwTextFrame::StopAnimation: Which Animation?" );
1083 if( !HasPara() )
1084 return;
1086 SwLineLayout *pLine = GetPara();
1087 while( pLine )
1089 SwLinePortion *pPor = pLine->GetNextPortion();
1090 while( pPor )
1092 if( pPor->IsGrfNumPortion() )
1093 static_cast<SwGrfNumPortion*>(pPor)->StopAnimation( pOut );
1094 // The NumberPortion is always at the first char,
1095 // which means we can cancel as soon as we've reached a portion
1096 // with a length > 0
1097 pPor = pPor->GetLen() ? nullptr : pPor->GetNextPortion();
1099 pLine = pLine->GetLen() ? nullptr : pLine->GetNext();
1104 * Initializes the script array and clears the width array
1106 SwCombinedPortion::SwCombinedPortion( const OUString &rText )
1107 : SwFieldPortion( rText )
1108 , m_aWidth{ static_cast<sal_uInt16>(0),
1109 static_cast<sal_uInt16>(0),
1110 static_cast<sal_uInt16>(0) }
1111 , m_nUpPos(0)
1112 , m_nLowPos(0)
1113 , m_nProportion(55)
1115 SetLen(TextFrameIndex(1));
1116 SetWhichPor( PortionType::Combined );
1117 if( m_aExpand.getLength() > 6 )
1118 m_aExpand = m_aExpand.copy( 0, 6 );
1120 // Initialization of the scripttype array,
1121 // the arrays of width and position are filled by the format function
1122 assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());
1124 SwFontScript nScr = SW_SCRIPTS;
1125 for( sal_Int32 i = 0; i < rText.getLength(); ++i )
1127 switch ( g_pBreakIt->GetBreakIter()->getScriptType( rText, i ) ) {
1128 case i18n::ScriptType::LATIN : nScr = SwFontScript::Latin; break;
1129 case i18n::ScriptType::ASIAN : nScr = SwFontScript::CJK; break;
1130 case i18n::ScriptType::COMPLEX : nScr = SwFontScript::CTL; break;
1132 m_aScrType[i] = nScr;
1136 void SwCombinedPortion::Paint( const SwTextPaintInfo &rInf ) const
1138 OSL_ENSURE(GetLen() <= TextFrameIndex(1), "SwFieldPortion::Paint: rest-portion pollution?");
1139 if( !Width() )
1140 return;
1142 rInf.DrawBackBrush( *this );
1143 rInf.DrawViewOpt( *this, PortionType::Field );
1145 // do we have to repaint a post it portion?
1146 if( rInf.OnWin() && mpNextPortion && !mpNextPortion->Width() )
1147 mpNextPortion->PrePaint( rInf, this );
1149 const sal_Int32 nCount = m_aExpand.getLength();
1150 if( !nCount )
1151 return;
1152 OSL_ENSURE( nCount < 7, "Too much combined characters" );
1154 // the first character of the second row
1155 const sal_Int32 nTop = ( nCount + 1 ) / 2;
1157 SwFont aTmpFont( *rInf.GetFont() );
1158 aTmpFont.SetProportion( m_nProportion ); // a smaller font
1159 SwFontSave aFontSave( rInf, &aTmpFont );
1161 Point aOldPos = rInf.GetPos();
1162 Point aOutPos( aOldPos.X(), aOldPos.Y() - m_nUpPos );// Y of the first row
1163 for( sal_Int32 i = 0 ; i < nCount; ++i )
1165 if( i == nTop ) // change the row
1166 aOutPos.setY( aOldPos.Y() + m_nLowPos ); // Y of the second row
1167 aOutPos.setX( aOldPos.X() + m_aPos[i] ); // X position
1168 const SwFontScript nAct = m_aScrType[i]; // script type
1169 aTmpFont.SetActual( nAct );
1171 // if there're more than 4 characters to display, we choose fonts
1172 // with 2/3 of the original font width.
1173 if( m_aWidth[ nAct ] )
1175 Size aTmpSz = aTmpFont.GetSize( nAct );
1176 if( aTmpSz.Width() != m_aWidth[ nAct ] )
1178 aTmpSz.setWidth( m_aWidth[ nAct ] );
1179 aTmpFont.SetSize( aTmpSz, nAct );
1182 const_cast<SwTextPaintInfo&>(rInf).SetPos( aOutPos );
1183 rInf.DrawText(m_aExpand, *this, TextFrameIndex(i), TextFrameIndex(1));
1185 // rInf is const, so we have to take back our manipulations
1186 const_cast<SwTextPaintInfo&>(rInf).SetPos( aOldPos );
1190 bool SwCombinedPortion::Format( SwTextFormatInfo &rInf )
1192 const sal_Int32 nCount = m_aExpand.getLength();
1193 if( !nCount )
1195 Width( 0 );
1196 return false;
1199 OSL_ENSURE( nCount < 7, "Too much combined characters" );
1201 // If there are leading "weak"-scripttyped characters in this portion,
1202 // they get the actual scripttype.
1203 for( sal_Int32 i = 0; i < nCount && SW_SCRIPTS == m_aScrType[i]; ++i )
1204 m_aScrType[i] = rInf.GetFont()->GetActual();
1205 if( nCount > 4 )
1207 // more than four? Ok, then we need the 2/3 font width
1208 for( sal_Int32 i = 0; i < m_aExpand.getLength(); ++i )
1210 OSL_ENSURE( m_aScrType[i] < SW_SCRIPTS, "Combined: Script fault" );
1211 if( !m_aWidth[ m_aScrType[i] ] )
1213 rInf.GetOut()->SetFont( rInf.GetFont()->GetFnt( m_aScrType[i] ) );
1214 m_aWidth[ m_aScrType[i] ] =
1215 o3tl::narrowing<sal_uInt16>(2 * rInf.GetOut()->GetFontMetric().GetFontSize().Width() / 3);
1220 const sal_Int32 nTop = ( nCount + 1 ) / 2; // the first character of the second line
1221 SwViewShell *pSh = rInf.GetTextFrame()->getRootFrame()->GetCurrShell();
1222 SwFont aTmpFont( *rInf.GetFont() );
1223 SwFontSave aFontSave( rInf, &aTmpFont );
1224 m_nProportion = 55;
1225 // In nMainAscent/Descent we store the ascent and descent
1226 // of the original surrounding font
1227 sal_uInt16 nMaxDescent, nMaxAscent, nMaxWidth;
1228 sal_uInt16 nMainDescent = rInf.GetFont()->GetHeight( pSh, *rInf.GetOut() );
1229 const sal_uInt16 nMainAscent = rInf.GetFont()->GetAscent( pSh, *rInf.GetOut() );
1230 nMainDescent = nMainDescent - nMainAscent;
1231 // we start with a 50% font, but if we notice that the combined portion
1232 // becomes bigger than the surrounding font, we check 45% and maybe 40%.
1235 m_nProportion -= 5;
1236 aTmpFont.SetProportion( m_nProportion );
1237 memset( &m_aPos, 0, sizeof(m_aPos) );
1238 nMaxDescent = 0;
1239 nMaxAscent = 0;
1240 nMaxWidth = 0;
1241 m_nUpPos = m_nLowPos = 0;
1243 // Now we get the width of all characters.
1244 // The ascent and the width of the first line are stored in the
1245 // ascent member of the portion, the descent in nLowPos.
1246 // The ascent, descent and width of the second line are stored in the
1247 // local nMaxAscent, nMaxDescent and nMaxWidth variables.
1248 for( sal_Int32 i = 0; i < nCount; ++i )
1250 SwFontScript nScrp = m_aScrType[i];
1251 aTmpFont.SetActual( nScrp );
1252 if( m_aWidth[ nScrp ] )
1254 Size aFontSize( aTmpFont.GetSize( nScrp ) );
1255 aFontSize.setWidth( m_aWidth[ nScrp ] );
1256 aTmpFont.SetSize( aFontSize, nScrp );
1259 SwDrawTextInfo aDrawInf(pSh, *rInf.GetOut(), m_aExpand, i, 1);
1260 Size aSize = aTmpFont.GetTextSize_( aDrawInf );
1261 const sal_uInt16 nAsc = aTmpFont.GetAscent( pSh, *rInf.GetOut() );
1262 m_aPos[ i ] = o3tl::narrowing<sal_uInt16>(aSize.Width());
1263 if( i == nTop ) // enter the second line
1265 m_nLowPos = nMaxDescent;
1266 Height( nMaxDescent + nMaxAscent );
1267 Width( nMaxWidth );
1268 SetAscent( nMaxAscent );
1269 nMaxAscent = 0;
1270 nMaxDescent = 0;
1271 nMaxWidth = 0;
1273 nMaxWidth = nMaxWidth + m_aPos[ i ];
1274 if( nAsc > nMaxAscent )
1275 nMaxAscent = nAsc;
1276 if( aSize.Height() - nAsc > nMaxDescent )
1277 nMaxDescent = aSize.Height() - nAsc;
1279 // for one or two characters we double the width of the portion
1280 if( nCount < 3 )
1282 nMaxWidth *= 2;
1283 Width( 2*Width() );
1284 if( nCount < 2 )
1286 Height( nMaxAscent + nMaxDescent );
1287 m_nLowPos = nMaxDescent;
1290 Height( Height() + nMaxDescent + nMaxAscent );
1291 m_nUpPos = nMaxAscent;
1292 SetAscent( Height() - nMaxDescent - m_nLowPos );
1293 } while( m_nProportion > 40 && ( GetAscent() > nMainAscent ||
1294 Height() - GetAscent() > nMainDescent ) );
1295 // if the combined portion is smaller than the surrounding text,
1296 // the portion grows. This looks better, if there's a character background.
1297 if( GetAscent() < nMainAscent )
1299 Height( Height() + nMainAscent - GetAscent() );
1300 SetAscent( nMainAscent );
1302 if( Height() < nMainAscent + nMainDescent )
1303 Height( nMainAscent + nMainDescent );
1305 // We calculate the x positions of the characters in both lines...
1306 sal_uInt16 nTopDiff = 0;
1307 sal_uInt16 nBotDiff = 0;
1308 if( nMaxWidth > Width() )
1310 nTopDiff = ( nMaxWidth - Width() ) / 2;
1311 Width( nMaxWidth );
1313 else
1314 nBotDiff = ( Width() - nMaxWidth ) / 2;
1315 switch( nTop)
1317 case 3: m_aPos[1] = m_aPos[0] + nTopDiff;
1318 [[fallthrough]];
1319 case 2: m_aPos[nTop-1] = Width() - m_aPos[nTop-1];
1321 m_aPos[0] = 0;
1322 switch( nCount )
1324 case 5: m_aPos[4] = m_aPos[3] + nBotDiff;
1325 [[fallthrough]];
1326 case 3: m_aPos[nTop] = nBotDiff; break;
1327 case 6: m_aPos[4] = m_aPos[3] + nBotDiff;
1328 [[fallthrough]];
1329 case 4: m_aPos[nTop] = 0;
1330 [[fallthrough]];
1331 case 2: m_aPos[nCount-1] = Width() - m_aPos[nCount-1];
1334 // Does the combined portion fit the line?
1335 const bool bFull = rInf.Width() < rInf.X() + Width();
1336 if( bFull )
1338 if( rInf.GetLineStart() == rInf.GetIdx() && (!rInf.GetLast()->InFieldGrp()
1339 || !static_cast<SwFieldPortion*>(rInf.GetLast())->IsFollow() ) )
1340 Width( rInf.Width() - rInf.X() );
1341 else
1343 Truncate();
1344 Width( 0 );
1345 SetLen(TextFrameIndex(0));
1346 if( rInf.GetLast() )
1347 rInf.GetLast()->FormatEOL( rInf );
1350 return bFull;
1353 sal_uInt16 SwCombinedPortion::GetViewWidth( const SwTextSizeInfo &rInf ) const
1355 if( !GetLen() ) // for the dummy part at the end of the line, where
1356 return 0; // the combined portion doesn't fit.
1357 return SwFieldPortion::GetViewWidth( rInf );
1360 SwFieldPortion *SwFieldFormDropDownPortion::Clone(const OUString &rExpand) const
1362 return new SwFieldFormDropDownPortion(m_pFieldMark, rExpand);
1365 void SwFieldFormDropDownPortion::Paint( const SwTextPaintInfo &rInf ) const
1367 SwFieldPortion::Paint( rInf );
1369 ::sw::mark::DropDownFieldmark* pDropDownField = dynamic_cast< ::sw::mark::DropDownFieldmark* >(m_pFieldMark);
1370 if(pDropDownField)
1372 SwRect aPaintArea;
1373 rInf.CalcRect( *this, &aPaintArea );
1374 pDropDownField->SetPortionPaintArea(aPaintArea);
1378 SwFieldPortion *SwFieldFormDatePortion::Clone(const OUString &/*rExpand*/) const
1380 return new SwFieldFormDatePortion(m_pFieldMark, m_bStart);
1383 void SwFieldFormDatePortion::Paint( const SwTextPaintInfo &rInf ) const
1385 SwFieldPortion::Paint( rInf );
1387 ::sw::mark::DateFieldmark* pDateField = dynamic_cast< ::sw::mark::DateFieldmark* >(m_pFieldMark);
1388 if(pDateField)
1390 SwRect aPaintArea;
1391 rInf.CalcRect( *this, &aPaintArea );
1392 if(m_bStart)
1393 pDateField->SetPortionPaintAreaStart(aPaintArea);
1394 else
1395 pDateField->SetPortionPaintAreaEnd(aPaintArea);
1399 SwFieldPortion* SwJumpFieldPortion::Clone(const OUString& rExpand) const
1401 auto pRet = new SwJumpFieldPortion(*this);
1402 pRet->m_aExpand = rExpand;
1403 return pRet;
1406 bool SwJumpFieldPortion::DescribePDFControl(const SwTextPaintInfo& rInf) const
1408 auto pPDFExtOutDevData
1409 = dynamic_cast<vcl::PDFExtOutDevData*>(rInf.GetOut()->GetExtOutDevData());
1410 if (!pPDFExtOutDevData)
1411 return false;
1413 if (!pPDFExtOutDevData->GetIsExportFormFields())
1414 return false;
1416 if (m_nFormat != SwJumpEditFormat::JE_FMT_TEXT)
1417 return false;
1419 vcl::PDFWriter::EditWidget aDescriptor;
1421 aDescriptor.Border = true;
1422 aDescriptor.BorderColor = COL_BLACK;
1424 SwRect aLocation;
1425 rInf.CalcRect(*this, &aLocation);
1426 aDescriptor.Location = aLocation.SVRect();
1428 // Map the text of the field to the descriptor's text.
1429 static sal_Unicode constexpr aForbidden[] = { CH_TXTATR_BREAKWORD, 0 };
1430 aDescriptor.Text = comphelper::string::removeAny(GetExp(), aForbidden);
1432 // Description for accessibility purposes.
1433 if (!m_sHelp.isEmpty())
1434 aDescriptor.Description = m_sHelp;
1436 pPDFExtOutDevData->WrapBeginStructureElement(vcl::PDFWriter::Form);
1437 pPDFExtOutDevData->CreateControl(aDescriptor);
1438 pPDFExtOutDevData->EndStructureElement();
1440 return true;
1443 void SwJumpFieldPortion::Paint(const SwTextPaintInfo& rInf) const
1445 if (Width() && DescribePDFControl(rInf))
1446 return;
1448 if (rInf.GetOpt().IsShowPlaceHolderFields())
1449 SwFieldPortion::Paint(rInf);
1452 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */