1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <hintids.hxx>
21 
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 <blink.hxx>
34 #include <fmtornt.hxx>
35 #include <frmatr.hxx>
36 #include <frmtool.hxx>
37 #include <viewsh.hxx>
38 #include <docsh.hxx>
39 #include <doc.hxx>
40 #include <IDocumentSettingAccess.hxx>
41 #include <rootfrm.hxx>
42 #include <breakit.hxx>
43 #include "porrst.hxx"
44 #include "porftn.hxx"
45 #include <accessibilityoptions.hxx>
46 #include <editeng/lrspitem.hxx>
47 #include <unicode/ubidi.h>
48 #include <bookmrk.hxx>
49 
50 using namespace ::com::sun::star;
51 
Compress()52 SwLinePortion *SwFieldPortion::Compress()
53 { return (GetLen() || !m_aExpand.isEmpty() || SwLinePortion::Compress()) ? this : nullptr; }
54 
Clone(const OUString & rExpand) const55 SwFieldPortion *SwFieldPortion::Clone( const OUString &rExpand ) const
56 {
57     std::unique_ptr<SwFont> pNewFnt;
58     if( m_pFont )
59     {
60         pNewFnt.reset(new SwFont( *m_pFont ));
61     }
62     // #i107143#
63     // pass placeholder property to created <SwFieldPortion> instance.
64     SwFieldPortion* pClone = new SwFieldPortion( rExpand, std::move(pNewFnt), m_bPlaceHolder );
65     pClone->SetNextOffset( m_nNextOffset );
66     pClone->m_bNoLength = m_bNoLength;
67     return pClone;
68 }
69 
TakeNextOffset(const SwFieldPortion * pField)70 void SwFieldPortion::TakeNextOffset( const SwFieldPortion* pField )
71 {
72     OSL_ENSURE( pField, "TakeNextOffset: Missing Source" );
73     m_nNextOffset = pField->GetNextOffset();
74     m_aExpand = m_aExpand.replaceAt(0, sal_Int32(m_nNextOffset), "");
75     m_bFollow = true;
76 }
77 
SwFieldPortion(const OUString & rExpand,std::unique_ptr<SwFont> pFont,bool bPlaceHold)78 SwFieldPortion::SwFieldPortion( const OUString &rExpand, std::unique_ptr<SwFont> pFont, bool bPlaceHold )
79     : m_aExpand(rExpand), m_pFont(std::move(pFont)), m_nNextOffset(0), m_nNextScriptChg(COMPLETE_STRING), m_nViewWidth(0)
80     , m_bFollow( false ), m_bLeft( false), m_bHide( false)
81     , m_bCenter (false), m_bHasFollow( false )
82     , m_bAnimated( false), m_bNoPaint( false)
83     , m_bReplace( false), m_bPlaceHolder( bPlaceHold )
84     , m_bNoLength( false )
85     , m_nAttrFieldType(0)
86 {
87     SetWhichPor( PortionType::Field );
88 }
89 
SwFieldPortion(const SwFieldPortion & rField)90 SwFieldPortion::SwFieldPortion( const SwFieldPortion& rField )
91     : SwExpandPortion( rField )
92     , m_aExpand( rField.GetExp() )
93     , m_nNextOffset( rField.GetNextOffset() )
94     , m_nNextScriptChg( rField.m_nNextScriptChg )
95     , m_nViewWidth( rField.m_nViewWidth )
96     , m_bFollow( rField.IsFollow() )
97     , m_bLeft( rField.IsLeft() )
98     , m_bHide( rField.IsHide() )
99     , m_bCenter( rField.IsCenter() )
100     , m_bHasFollow( rField.HasFollow() )
101     , m_bAnimated ( rField.m_bAnimated )
102     , m_bNoPaint( rField.m_bNoPaint)
103     , m_bReplace( rField.m_bReplace )
104     , m_bPlaceHolder( rField.m_bPlaceHolder )
105     , m_bNoLength( rField.m_bNoLength )
106     , m_nAttrFieldType( rField.m_nAttrFieldType)
107 {
108     if ( rField.HasFont() )
109         m_pFont.reset( new SwFont( *rField.GetFont() ) );
110 
111     SetWhichPor( PortionType::Field );
112 }
113 
~SwFieldPortion()114 SwFieldPortion::~SwFieldPortion()
115 {
116     m_pFont.reset();
117     if( pBlink )
118         pBlink->Delete( this );
119 }
120 
GetViewWidth(const SwTextSizeInfo & rInf) const121 sal_uInt16 SwFieldPortion::GetViewWidth( const SwTextSizeInfo &rInf ) const
122 {
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() && SwViewOption::IsFieldShadings() )
127     {
128         if( !m_nViewWidth )
129             pThis->m_nViewWidth = rInf.GetTextSize(OUString(' ')).Width();
130     }
131     else
132         pThis->m_nViewWidth = 0;
133     return m_nViewWidth;
134 }
135 
136 /**
137  * Never just use SetLen(0)
138  */
139 class SwFieldSlot
140 {
141     std::shared_ptr<vcl::TextLayoutCache> m_pOldCachedVclData;
142     const OUString *pOldText;
143     OUString aText;
144     TextFrameIndex nIdx;
145     TextFrameIndex nLen;
146     SwTextFormatInfo *pInf;
147     bool bOn;
148 public:
149     SwFieldSlot( const SwTextFormatInfo* pNew, const SwFieldPortion *pPor );
150     ~SwFieldSlot();
151 };
152 
SwFieldSlot(const SwTextFormatInfo * pNew,const SwFieldPortion * pPor)153 SwFieldSlot::SwFieldSlot( const SwTextFormatInfo* pNew, const SwFieldPortion *pPor )
154     : pOldText(nullptr)
155     , nIdx(0)
156     , nLen(0)
157     , pInf(nullptr)
158 {
159     bOn = pPor->GetExpText( *pNew, aText );
160 
161     // The text will be replaced ...
162     if( bOn )
163     {
164         pInf = const_cast<SwTextFormatInfo*>(pNew);
165         nIdx = pInf->GetIdx();
166         nLen = pInf->GetLen();
167         pOldText = &(pInf->GetText());
168         m_pOldCachedVclData = pInf->GetCachedVclData();
169         pInf->SetLen(TextFrameIndex(aText.getLength()));
170         pInf->SetCachedVclData(nullptr);
171         if( pPor->IsFollow() )
172         {
173             pInf->SetFakeLineStart( nIdx > pInf->GetLineStart() );
174             pInf->SetIdx(TextFrameIndex(0));
175         }
176         else if (nIdx < TextFrameIndex(pOldText->getLength()))
177         {
178             aText = (*pOldText).replaceAt(sal_Int32(nIdx), 1, aText);
179         }
180         pInf->SetText( aText );
181     }
182 }
183 
~SwFieldSlot()184 SwFieldSlot::~SwFieldSlot()
185 {
186     if( bOn )
187     {
188         pInf->SetCachedVclData(m_pOldCachedVclData);
189         pInf->SetText( *pOldText );
190         pInf->SetIdx( nIdx );
191         pInf->SetLen( nLen );
192         pInf->SetFakeLineStart( false );
193     }
194 }
195 
CheckScript(const SwTextSizeInfo & rInf)196 void SwFieldPortion::CheckScript( const SwTextSizeInfo &rInf )
197 {
198     OUString aText;
199     if (!GetExpText(rInf, aText) || aText.isEmpty())
200         return;
201 
202     SwFontScript nActual = m_pFont ? m_pFont->GetActual() : rInf.GetFont()->GetActual();
203     sal_uInt16 nScript = g_pBreakIt->GetBreakIter()->getScriptType( aText, 0 );
204     sal_Int32 nChg = 0;
205     if( i18n::ScriptType::WEAK == nScript )
206     {
207         nChg = g_pBreakIt->GetBreakIter()->endOfScript(aText,0,nScript);
208         if (nChg < aText.getLength() && nChg >= 0)
209             nScript = g_pBreakIt->GetBreakIter()->getScriptType( aText, nChg );
210     }
211 
212     // nNextScriptChg will be evaluated during SwFieldPortion::Format()
213 
214     if (nChg < aText.getLength() && nChg >= 0)
215         m_nNextScriptChg = TextFrameIndex(
216                 g_pBreakIt->GetBreakIter()->endOfScript(aText, nChg, nScript));
217     else
218         m_nNextScriptChg = TextFrameIndex(aText.getLength());
219 
220     SwFontScript nTmp;
221     switch ( nScript ) {
222         case i18n::ScriptType::LATIN : nTmp = SwFontScript::Latin; break;
223         case i18n::ScriptType::ASIAN : nTmp = SwFontScript::CJK; break;
224         case i18n::ScriptType::COMPLEX : nTmp = SwFontScript::CTL; break;
225         default: nTmp = nActual;
226     }
227 
228     // #i16354# Change script type for RTL text to CTL.
229     const SwScriptInfo& rSI = rInf.GetParaPortion()->GetScriptInfo();
230     // #i98418#
231     const sal_uInt8 nFieldDir = (IsNumberPortion() || IsFootnoteNumPortion())
232         ? rSI.GetDefaultDir()
233         : rSI.DirType(IsFollow() ? rInf.GetIdx() - TextFrameIndex(1) : rInf.GetIdx());
234 
235     {
236         UErrorCode nError = U_ZERO_ERROR;
237         UBiDi* pBidi = ubidi_openSized( aText.getLength(), 0, &nError );
238         ubidi_setPara( pBidi, reinterpret_cast<const UChar *>(aText.getStr()), aText.getLength(), nFieldDir, nullptr, &nError );
239         int32_t nEnd;
240         UBiDiLevel nCurrDir;
241         ubidi_getLogicalRun( pBidi, 0, &nEnd, &nCurrDir );
242         ubidi_close( pBidi );
243         const TextFrameIndex nNextDirChg(nEnd);
244         m_nNextScriptChg = std::min( m_nNextScriptChg, nNextDirChg );
245 
246         // #i89825# change the script type also to CTL
247         // if there is no strong LTR char in the LTR run (numbers)
248         if (nCurrDir != UBIDI_RTL &&
249             (UBIDI_LTR != nFieldDir || i18n::ScriptType::COMPLEX == nScript))
250         {
251             nCurrDir = UBIDI_RTL;
252             for( sal_Int32 nCharIdx = 0; nCharIdx < nEnd; ++nCharIdx )
253             {
254                 UCharDirection nCharDir = u_charDirection ( aText[ nCharIdx ]);
255                 if ( nCharDir == U_LEFT_TO_RIGHT ||
256                      nCharDir == U_LEFT_TO_RIGHT_EMBEDDING ||
257                      nCharDir == U_LEFT_TO_RIGHT_OVERRIDE )
258                 {
259                     nCurrDir = UBIDI_LTR;
260                     break;
261                 }
262             }
263         }
264 
265         if (nCurrDir == UBIDI_RTL)
266         {
267             nTmp = SwFontScript::CTL;
268             // If we decided that this range was RTL after all and the
269             // previous range was complex but clipped to the start of this
270             // range, then extend it to be complex over the additional RTL range
271             if (nScript == i18n::ScriptType::COMPLEX)
272                 m_nNextScriptChg = nNextDirChg;
273         }
274     }
275 
276     // #i98418#
277     // keep determined script type for footnote portions as preferred script type.
278     // For footnote portions a font can not be created directly - see footnote
279     // portion format method.
280     if ( IsFootnotePortion() )
281     {
282         static_cast<SwFootnotePortion*>(this)->SetPreferredScriptType( nTmp );
283     }
284     else if ( nTmp != nActual )
285     {
286         if( !m_pFont )
287             m_pFont.reset( new SwFont( *rInf.GetFont() ) );
288         m_pFont->SetActual( nTmp );
289     }
290 
291 }
292 
Format(SwTextFormatInfo & rInf)293 bool SwFieldPortion::Format( SwTextFormatInfo &rInf )
294 {
295     // Scope wegen aDiffText::DTOR!
296     TextFrameIndex nRest;
297     bool bFull = false;
298     bool bEOL = false;
299     TextFrameIndex const nTextRest = TextFrameIndex(rInf.GetText().getLength()) - rInf.GetIdx();
300     {
301         SwFieldSlot aDiffText( &rInf, this );
302         SwLayoutModeModifier aLayoutModeModifier( *rInf.GetOut() );
303         aLayoutModeModifier.SetAuto();
304 
305         // Field portion has to be split in several parts if
306         // 1. There are script/direction changes inside the field
307         // 2. There are portion breaks (tab, break) inside the field:
308         const TextFrameIndex nOldFullLen = rInf.GetLen();
309         TextFrameIndex nFullLen = rInf.ScanPortionEnd(rInf.GetIdx(), rInf.GetIdx() + nOldFullLen) - rInf.GetIdx();
310         if ( m_nNextScriptChg < nFullLen )
311         {
312             nFullLen = m_nNextScriptChg;
313             rInf.SetHookChar( 0 );
314         }
315         rInf.SetLen( nFullLen );
316 
317         if (TextFrameIndex(COMPLETE_STRING) != rInf.GetUnderScorePos() &&
318              rInf.GetUnderScorePos() > rInf.GetIdx() )
319              rInf.SetUnderScorePos( rInf.GetIdx() );
320 
321         if( m_pFont )
322             m_pFont->AllocFontCacheId( rInf.GetVsh(), m_pFont->GetActual() );
323 
324         SwFontSave aSave( rInf, m_pFont.get() );
325 
326         // Length must be 0: the length is set for bFull after format
327         // and passed along in nRest. Or else the old length would be
328         // retained and be used for nRest!
329         SetLen(TextFrameIndex(0));
330         TextFrameIndex const nFollow(IsFollow() ? 0 : 1);
331 
332         // As odd is may seem: the query for GetLen() must return false due
333         // to the ExpandPortions _after_ aDiffText (see SoftHyphs), caused
334         // by SetFull.
335         if( !nFullLen )
336         {
337             // Don't Init(), as we need height and ascent
338             Width(0);
339             bFull = rInf.Width() <= rInf.GetPos().X();
340         }
341         else
342         {
343             TextFrameIndex const nOldLineStart = rInf.GetLineStart();
344             if( IsFollow() )
345                 rInf.SetLineStart(TextFrameIndex(0));
346             rInf.SetNotEOL( nFullLen == nOldFullLen && nTextRest > nFollow );
347 
348             // the height depending on the fields font is set,
349             // this is required for SwTextGuess::Guess
350             Height( rInf.GetTextHeight() + rInf.GetFont()->GetTopBorderSpace() +
351                     rInf.GetFont()->GetBottomBorderSpace() );
352             // If a kerning portion is inserted after our field portion,
353             // the ascent and height must be known
354             SetAscent( rInf.GetAscent() + rInf.GetFont()->GetTopBorderSpace() );
355             bFull = SwTextPortion::Format( rInf );
356             rInf.SetNotEOL( false );
357             rInf.SetLineStart( nOldLineStart );
358         }
359         TextFrameIndex const nTmpLen = GetLen();
360         bEOL = !nTmpLen && nFollow && bFull;
361         nRest = nOldFullLen - nTmpLen;
362 
363         // The char is held in the first position
364         // Unconditionally after format!
365         SetLen( m_bNoLength ? TextFrameIndex(0) : nFollow );
366 
367         if( nRest )
368         {
369             // aExpand has not yet been shortened; the new Ofst is a
370             // result of nRest
371             TextFrameIndex nNextOfst = TextFrameIndex(m_aExpand.getLength()) - nRest;
372 
373             if ( IsQuoVadisPortion() )
374                 nNextOfst = nNextOfst + TextFrameIndex(static_cast<SwQuoVadisPortion*>(this)->GetContText().getLength());
375 
376             OUString aNew( m_aExpand.copy(sal_Int32(nNextOfst)) );
377             m_aExpand = m_aExpand.copy(0, sal_Int32(nNextOfst));
378 
379             // These characters should not be contained in the follow
380             // field portion. They are handled via the HookChar mechanism.
381             const sal_Unicode nNew = !aNew.isEmpty() ? aNew[0] : 0;
382             switch (nNew)
383             {
384                 case CH_BREAK  : bFull = true;
385                     [[fallthrough]];
386                 case ' ' :
387                 case CH_TAB    :
388                 case CHAR_HARDHYPHEN:               // non-breaking hyphen
389                 case CHAR_SOFTHYPHEN:
390                 case CHAR_HARDBLANK:
391                 case CHAR_ZWSP :
392                 case CHAR_ZWNBSP :
393                 case CH_TXTATR_BREAKWORD:
394                 case CH_TXTATR_INWORD:
395                 {
396                     aNew = aNew.copy( 1 );
397                     ++nNextOfst;
398                     break;
399                 }
400                 default: ;
401             }
402 
403             // Even if there is no more text left for a follow field,
404             // we have to build a follow field portion (without font),
405             // otherwise the HookChar mechanism would not work.
406             SwFieldPortion *pField = Clone( aNew );
407             if( !aNew.isEmpty() && !pField->GetFont() )
408             {
409                 pField->SetFont( std::make_unique<SwFont>( *rInf.GetFont() ) );
410             }
411             pField->SetFollow( true );
412             SetHasFollow( true );
413 
414             // For a newly created field, nNextOffset contains the Offset
415             // of its start of the original string
416             // If a FollowField is created when formatting, this FollowField's
417             // Offset is being held in nNextOffset
418             m_nNextOffset = m_nNextOffset + nNextOfst;
419             pField->SetNextOffset( m_nNextOffset );
420             rInf.SetRest( pField );
421         }
422     }
423 
424     if( bEOL && rInf.GetLast() && !rInf.GetUnderflow() )
425         rInf.GetLast()->FormatEOL( rInf );
426     return bFull;
427 }
428 
Paint(const SwTextPaintInfo & rInf) const429 void SwFieldPortion::Paint( const SwTextPaintInfo &rInf ) const
430 {
431     SwFontSave aSave( rInf, m_pFont.get() );
432 
433     OSL_ENSURE(GetLen() <= TextFrameIndex(1), "SwFieldPortion::Paint: rest-portion pollution?");
434     if( Width() && ( !m_bPlaceHolder || rInf.GetOpt().IsShowPlaceHolderFields() ) )
435     {
436         // A very liberal use of the background
437         rInf.DrawViewOpt( *this, PortionType::Field );
438         SwExpandPortion::Paint( rInf );
439     }
440 }
441 
GetExpText(const SwTextSizeInfo & rInf,OUString & rText) const442 bool SwFieldPortion::GetExpText( const SwTextSizeInfo &rInf, OUString &rText ) const
443 {
444     rText = m_aExpand;
445     if( rText.isEmpty() && rInf.OnWin() &&
446         !rInf.GetOpt().IsPagePreview() && !rInf.GetOpt().IsReadonly() &&
447             SwViewOption::IsFieldShadings() &&
448             !HasFollow() )
449         rText = " ";
450     return true;
451 }
452 
HandlePortion(SwPortionHandler & rPH) const453 void SwFieldPortion::HandlePortion( SwPortionHandler& rPH ) const
454 {
455     sal_Int32 nH = 0;
456     sal_Int32 nW = 0;
457     if (m_pFont)
458     {
459         nH = m_pFont->GetSize(m_pFont->GetActual()).Height();
460         nW = m_pFont->GetSize(m_pFont->GetActual()).Width();
461     }
462     rPH.Special( GetLen(), m_aExpand, GetWhichPor(), nH, nW, m_pFont.get() );
463 }
464 
GetTextSize(const SwTextSizeInfo & rInf) const465 SwPosSize SwFieldPortion::GetTextSize( const SwTextSizeInfo &rInf ) const
466 {
467     SwFontSave aSave( rInf, m_pFont.get() );
468     SwPosSize aSize( SwExpandPortion::GetTextSize( rInf ) );
469     return aSize;
470 }
471 
Clone(const OUString & rExpand) const472 SwFieldPortion *SwHiddenPortion::Clone(const OUString &rExpand ) const
473 {
474     std::unique_ptr<SwFont> pNewFnt;
475     if( m_pFont )
476         pNewFnt.reset(new SwFont( *m_pFont ));
477     return new SwHiddenPortion( rExpand, std::move(pNewFnt) );
478 }
479 
Paint(const SwTextPaintInfo & rInf) const480 void SwHiddenPortion::Paint( const SwTextPaintInfo &rInf ) const
481 {
482     if( Width() )
483     {
484         SwFontSave aSave( rInf, m_pFont.get() );
485         rInf.DrawViewOpt( *this, PortionType::Hidden );
486         SwExpandPortion::Paint( rInf );
487     }
488 }
489 
GetExpText(const SwTextSizeInfo & rInf,OUString & rText) const490 bool SwHiddenPortion::GetExpText( const SwTextSizeInfo &rInf, OUString &rText ) const
491 {
492     // Do not query for IsHidden()!
493     return SwFieldPortion::GetExpText( rInf, rText );
494 }
495 
SwNumberPortion(const OUString & rExpand,std::unique_ptr<SwFont> pFont,const bool bLft,const bool bCntr,const sal_uInt16 nMinDst,const bool bLabelAlignmentPosAndSpaceModeActive)496 SwNumberPortion::SwNumberPortion( const OUString &rExpand,
497                                   std::unique_ptr<SwFont> pFont,
498                                   const bool bLft,
499                                   const bool bCntr,
500                                   const sal_uInt16 nMinDst,
501                                   const bool bLabelAlignmentPosAndSpaceModeActive )
502         : SwFieldPortion( rExpand, std::move(pFont) ),
503           nFixWidth(0),
504           nMinDist( nMinDst ),
505           mbLabelAlignmentPosAndSpaceModeActive( bLabelAlignmentPosAndSpaceModeActive )
506 {
507     SetWhichPor( PortionType::Number );
508     SetLeft( bLft );
509     SetHide( false );
510     SetCenter( bCntr );
511 }
512 
GetCursorOfst(const sal_uInt16) const513 TextFrameIndex SwNumberPortion::GetCursorOfst(const sal_uInt16) const
514 {
515     return TextFrameIndex(0);
516 }
517 
Clone(const OUString & rExpand) const518 SwFieldPortion *SwNumberPortion::Clone( const OUString &rExpand ) const
519 {
520     std::unique_ptr<SwFont> pNewFnt;
521     if( m_pFont )
522         pNewFnt.reset(new SwFont( *m_pFont ));
523 
524     return new SwNumberPortion( rExpand, std::move(pNewFnt), IsLeft(), IsCenter(),
525                                 nMinDist, mbLabelAlignmentPosAndSpaceModeActive );
526 }
527 
528 /**
529  * We can create multiple NumFields
530  * Tricky, if one enters enough previous-text in the dialog box
531  * to cause the line to overflow
532  * We need to keep the Fly's evasion tactics in mind
533  */
Format(SwTextFormatInfo & rInf)534 bool SwNumberPortion::Format( SwTextFormatInfo &rInf )
535 {
536     SetHide( false );
537     const bool bFull = SwFieldPortion::Format( rInf );
538     SetLen(TextFrameIndex(0));
539     // a numbering portion can be contained in a rotated portion!!!
540     nFixWidth = rInf.IsMulti() ? Height() : Width();
541     rInf.SetNumDone( !rInf.GetRest() );
542     if( rInf.IsNumDone() )
543     {
544 //        SetAscent( rInf.GetAscent() );
545         OSL_ENSURE( Height() && nAscent, "NumberPortions without Height | Ascent" );
546 
547         long nDiff( 0 );
548 
549         if ( !mbLabelAlignmentPosAndSpaceModeActive )
550         {
551             if (!rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING) &&
552                  // #i32902#
553                  !IsFootnoteNumPortion() )
554             {
555                 nDiff = rInf.Left()
556                     + rInf.GetTextFrame()->GetTextNodeForParaProps()->
557                     GetSwAttrSet().GetLRSpace().GetTextFirstLineOfst()
558                     - rInf.First()
559                     + rInf.ForcedLeftMargin();
560             }
561             else
562             {
563                 nDiff = rInf.Left() - rInf.First() + rInf.ForcedLeftMargin();
564             }
565         }
566         // The text part of the numbering should always at least
567         // start at the left margin
568         if( nDiff < 0 )
569             nDiff = 0;
570         else if ( nDiff > rInf.X() )
571             nDiff -= rInf.X();
572         else
573             nDiff = 0;
574 
575         if( nDiff < nFixWidth + nMinDist )
576             nDiff = nFixWidth + nMinDist;
577 
578         // Numbering evades the Fly, no nDiff in the second round
579         // Tricky special case: FlyFrame is in an Area we're just about to
580         // acquire
581         // The NumberPortion is marked as hidden
582         const bool bFly = rInf.GetFly() ||
583             ( rInf.GetLast() && rInf.GetLast()->IsFlyPortion() );
584         if( nDiff > rInf.Width() )
585         {
586             nDiff = rInf.Width();
587             if ( bFly )
588                 SetHide( true );
589         }
590 
591         // A numbering portion can be inside a SwRotatedPortion. Then the
592         // Height has to be changed
593         if ( rInf.IsMulti() )
594         {
595             if ( Height() < nDiff )
596                 Height( sal_uInt16( nDiff ) );
597         }
598         else if( Width() < nDiff )
599             Width( sal_uInt16(nDiff) );
600     }
601     return bFull;
602 }
603 
604 
605 /**
606  * A FormatEOL indicates that the subsequent text did not fit onto
607  * the line anymore. In order for the Numbering to follow through,
608  * we hide this NumberPortion
609  */
FormatEOL(SwTextFormatInfo &)610 void SwNumberPortion::FormatEOL( SwTextFormatInfo& )
611 {
612 
613     // This caused trouble with flys anchored as characters.
614     // If one of these is numbered but does not fit to the line,
615     // it calls this function, causing a loop because both the number
616     // portion and the fly portion go to the next line
617 //    SetHide( true );
618 }
619 
620 
621 /**
622  * A hidden NumberPortion is not displayed, unless there are TextPortions in
623  * this line or there's just one line at all
624  */
Paint(const SwTextPaintInfo & rInf) const625 void SwNumberPortion::Paint( const SwTextPaintInfo &rInf ) const
626 {
627     if ( IsHide() && rInf.GetParaPortion() && rInf.GetParaPortion()->GetNext() )
628     {
629         SwLinePortion *pTmp = GetNextPortion();
630         while ( pTmp && !pTmp->InTextGrp() )
631             pTmp = pTmp->GetNextPortion();
632         if ( !pTmp )
633             return;
634     }
635 
636     // calculate the width of the number portion, including follows
637     const sal_uInt16 nOldWidth = Width();
638     sal_uInt16 nSumWidth = 0;
639     sal_uInt16 nOffset = 0;
640 
641     const SwLinePortion* pTmp = this;
642     while ( pTmp && pTmp->InNumberGrp() )
643     {
644         nSumWidth = nSumWidth + pTmp->Width();
645         if ( static_cast<const SwNumberPortion*>(pTmp)->HasFollow() )
646             pTmp = pTmp->GetNextPortion();
647         else
648         {
649             nOffset = pTmp->Width() - static_cast<const SwNumberPortion*>(pTmp)->nFixWidth;
650             break;
651         }
652     }
653 
654     // The master portion takes care for painting the background of the
655     // follow field portions
656     if ( ! IsFollow() )
657     {
658         SwNumberPortion *pThis = const_cast<SwNumberPortion*>(this);
659         pThis->Width( nSumWidth );
660         rInf.DrawViewOpt( *this, PortionType::Number );
661         pThis->Width( nOldWidth );
662     }
663 
664     if( !m_aExpand.isEmpty() )
665     {
666         const SwFont *pTmpFnt = rInf.GetFont();
667         bool bPaintSpace = ( LINESTYLE_NONE != pTmpFnt->GetUnderline() ||
668                                  LINESTYLE_NONE != pTmpFnt->GetOverline()  ||
669                                  STRIKEOUT_NONE != pTmpFnt->GetStrikeout() ) &&
670                                  !pTmpFnt->IsWordLineMode();
671         if( bPaintSpace && m_pFont )
672             bPaintSpace = ( LINESTYLE_NONE != m_pFont->GetUnderline() ||
673                             LINESTYLE_NONE != m_pFont->GetOverline()  ||
674                             STRIKEOUT_NONE != m_pFont->GetStrikeout() ) &&
675                             !m_pFont->IsWordLineMode();
676 
677         SwFontSave aSave( rInf, m_pFont.get() );
678 
679         if( nFixWidth == Width() && ! HasFollow() )
680             SwExpandPortion::Paint( rInf );
681         else
682         {
683             // logical const: reset width
684             SwNumberPortion *pThis = const_cast<SwNumberPortion*>(this);
685             bPaintSpace = bPaintSpace && nFixWidth < nOldWidth;
686             sal_uInt16 nSpaceOffs = nFixWidth;
687             pThis->Width( nFixWidth );
688 
689             if( ( IsLeft() && ! rInf.GetTextFrame()->IsRightToLeft() ) ||
690                 ( ! IsLeft() && ! IsCenter() && rInf.GetTextFrame()->IsRightToLeft() ) )
691                 SwExpandPortion::Paint( rInf );
692             else
693             {
694                 SwTextPaintInfo aInf( rInf );
695                 if( nOffset < nMinDist )
696                     nOffset = 0;
697                 else
698                 {
699                     if( IsCenter() )
700                     {
701                         /* #110778# a / 2 * 2 == a is not a tautology */
702                         sal_uInt16 nTmpOffset = nOffset;
703                         nOffset /= 2;
704                         if( nOffset < nMinDist )
705                             nOffset = nTmpOffset - nMinDist;
706                     }
707                     else
708                         nOffset = nOffset - nMinDist;
709                 }
710                 aInf.X( aInf.X() + nOffset );
711                 SwExpandPortion::Paint( aInf );
712                 if( bPaintSpace )
713                     nSpaceOffs = nSpaceOffs + nOffset;
714             }
715             if( bPaintSpace && nOldWidth > nSpaceOffs )
716             {
717                 SwTextPaintInfo aInf( rInf );
718                 aInf.X( aInf.X() + nSpaceOffs );
719 
720                 // #i53199# Adjust position of underline:
721                 if ( rInf.GetUnderFnt() )
722                 {
723                     const Point aNewPos( aInf.GetPos().X(), rInf.GetUnderFnt()->GetPos().Y() );
724                     rInf.GetUnderFnt()->SetPos( aNewPos );
725                 }
726 
727                 pThis->Width( nOldWidth - nSpaceOffs + 12 );
728                 {
729                     SwTextSlot aDiffText( &aInf, this, true, false, "  " );
730                     aInf.DrawText( *this, aInf.GetLen(), true );
731                 }
732             }
733             pThis->Width( nOldWidth );
734         }
735     }
736 }
737 
SwBulletPortion(const sal_Unicode cBullet,const OUString & rBulletFollowedBy,std::unique_ptr<SwFont> pFont,const bool bLft,const bool bCntr,const sal_uInt16 nMinDst,const bool bLabelAlignmentPosAndSpaceModeActive)738 SwBulletPortion::SwBulletPortion( const sal_Unicode cBullet,
739                                   const OUString& rBulletFollowedBy,
740                                   std::unique_ptr<SwFont> pFont,
741                                   const bool bLft,
742                                   const bool bCntr,
743                                   const sal_uInt16 nMinDst,
744                                   const bool bLabelAlignmentPosAndSpaceModeActive )
745     : SwNumberPortion( OUStringChar(cBullet) + rBulletFollowedBy,
746                        std::move(pFont), bLft, bCntr, nMinDst,
747                        bLabelAlignmentPosAndSpaceModeActive )
748 {
749     SetWhichPor( PortionType::Bullet );
750 }
751 
752 #define GRFNUM_SECURE 10
753 
SwGrfNumPortion(const OUString & rGraphicFollowedBy,const SvxBrushItem * pGrfBrush,OUString const & referer,const SwFormatVertOrient * pGrfOrient,const Size & rGrfSize,const bool bLft,const bool bCntr,const sal_uInt16 nMinDst,const bool bLabelAlignmentPosAndSpaceModeActive)754 SwGrfNumPortion::SwGrfNumPortion(
755         const OUString& rGraphicFollowedBy,
756         const SvxBrushItem* pGrfBrush, OUString const & referer,
757         const SwFormatVertOrient* pGrfOrient, const Size& rGrfSize,
758         const bool bLft, const bool bCntr, const sal_uInt16 nMinDst,
759         const bool bLabelAlignmentPosAndSpaceModeActive ) :
760     SwNumberPortion( rGraphicFollowedBy, nullptr, bLft, bCntr, nMinDst,
761                      bLabelAlignmentPosAndSpaceModeActive ),
762     pBrush( new SvxBrushItem(RES_BACKGROUND) ), nId( 0 )
763 {
764     SetWhichPor( PortionType::GrfNum );
765     SetAnimated( false );
766     m_bReplace = false;
767     if( pGrfBrush )
768     {
769         pBrush.reset(static_cast<SvxBrushItem*>(pGrfBrush->Clone()));
770         const Graphic* pGraph = pGrfBrush->GetGraphic(referer);
771         if( pGraph )
772             SetAnimated( pGraph->IsAnimated() );
773         else
774             m_bReplace = true;
775     }
776     if( pGrfOrient )
777     {
778         nYPos = pGrfOrient->GetPos();
779         eOrient = pGrfOrient->GetVertOrient();
780     }
781     else
782     {
783         nYPos = 0;
784         eOrient = text::VertOrientation::TOP;
785     }
786     Width( static_cast<sal_uInt16>(rGrfSize.Width() + 2 * GRFNUM_SECURE) );
787     nFixWidth = Width();
788     nGrfHeight = rGrfSize.Height() + 2 * GRFNUM_SECURE;
789     Height( sal_uInt16(nGrfHeight) );
790     m_bNoPaint = false;
791 }
792 
~SwGrfNumPortion()793 SwGrfNumPortion::~SwGrfNumPortion()
794 {
795     if ( IsAnimated() )
796     {
797         Graphic* pGraph = const_cast<Graphic*>(pBrush->GetGraphic());
798         if (pGraph)
799             pGraph->StopAnimation( nullptr, nId );
800     }
801     pBrush.reset();
802 }
803 
StopAnimation(OutputDevice * pOut)804 void SwGrfNumPortion::StopAnimation( OutputDevice* pOut )
805 {
806     if ( IsAnimated() )
807     {
808         Graphic* pGraph = const_cast<Graphic*>(pBrush->GetGraphic());
809         if (pGraph)
810             pGraph->StopAnimation( pOut, nId );
811     }
812 }
813 
Format(SwTextFormatInfo & rInf)814 bool SwGrfNumPortion::Format( SwTextFormatInfo &rInf )
815 {
816     SetHide( false );
817 //    Width( nFixWidth );
818     sal_uInt16 nFollowedByWidth( 0 );
819     if ( mbLabelAlignmentPosAndSpaceModeActive )
820     {
821         SwFieldPortion::Format( rInf );
822         nFollowedByWidth = Width();
823         SetLen(TextFrameIndex(0));
824     }
825     Width( nFixWidth + nFollowedByWidth );
826     const bool bFull = rInf.Width() < rInf.X() + Width();
827     const bool bFly = rInf.GetFly() ||
828         ( rInf.GetLast() && rInf.GetLast()->IsFlyPortion() );
829     SetAscent( GetRelPos() > 0 ? GetRelPos() : 0 );
830     if( GetAscent() > Height() )
831         Height( GetAscent() );
832 
833     if( bFull )
834     {
835         Width( rInf.Width() - static_cast<sal_uInt16>(rInf.X()) );
836         if( bFly )
837         {
838             SetLen(TextFrameIndex(0));
839             m_bNoPaint = true;
840             rInf.SetNumDone( false );
841             return true;
842         }
843     }
844     rInf.SetNumDone( true );
845 //    long nDiff = rInf.Left() - rInf.First() + rInf.ForcedLeftMargin();
846     long nDiff = mbLabelAlignmentPosAndSpaceModeActive
847                  ? 0
848                  : rInf.Left() - rInf.First() + rInf.ForcedLeftMargin();
849     // The TextPortion should at least always start on the
850     // left margin
851     if( nDiff < 0 )
852         nDiff = 0;
853     else if ( nDiff > rInf.X() )
854         nDiff -= rInf.X();
855     if( nDiff < nFixWidth + nMinDist )
856         nDiff = nFixWidth + nMinDist;
857 
858     // Numbering evades Fly, no nDiff in the second round
859     // Tricky special case: FlyFrame is in the Area we were just
860     // about to get a hold of.
861     // The NumberPortion is marked as hidden
862     if( nDiff > rInf.Width() )
863     {
864         nDiff = rInf.Width();
865         if( bFly )
866             SetHide( true );
867     }
868 
869     if( Width() < nDiff )
870         Width( sal_uInt16(nDiff) );
871     return bFull;
872 }
873 
874 
875 /**
876  * A hidden NumberPortion is not displayed, unless there are TextPortions in
877  * this line or there's only one line at all
878  */
Paint(const SwTextPaintInfo & rInf) const879 void SwGrfNumPortion::Paint( const SwTextPaintInfo &rInf ) const
880 {
881     if( m_bNoPaint )
882         return;
883     if ( IsHide() && rInf.GetParaPortion() && rInf.GetParaPortion()->GetNext() )
884     {
885         SwLinePortion *pTmp = GetNextPortion();
886         while ( pTmp && !pTmp->InTextGrp() )
887             pTmp = pTmp->GetNextPortion();
888         if ( !pTmp )
889             return;
890     }
891     Point aPos( rInf.X() + GRFNUM_SECURE, rInf.Y() - GetRelPos() + GRFNUM_SECURE );
892     long nTmpWidth = std::max( long(0), static_cast<long>(nFixWidth - 2 * GRFNUM_SECURE) );
893     Size aSize( nTmpWidth, GetGrfHeight() - 2 * GRFNUM_SECURE );
894 
895     const bool bTmpLeft = mbLabelAlignmentPosAndSpaceModeActive ||
896                               ( IsLeft() && ! rInf.GetTextFrame()->IsRightToLeft() ) ||
897                               ( ! IsLeft() && ! IsCenter() && rInf.GetTextFrame()->IsRightToLeft() );
898 
899     if( nFixWidth < Width() && !bTmpLeft )
900     {
901         sal_uInt16 nOffset = Width() - nFixWidth;
902         if( nOffset < nMinDist )
903             nOffset = 0;
904         else
905         {
906             if( IsCenter() )
907             {
908                 nOffset /= 2;
909                 if( nOffset < nMinDist )
910                     nOffset = Width() - nFixWidth - nMinDist;
911             }
912             else
913                 nOffset = nOffset - nMinDist;
914         }
915         aPos.AdjustX(nOffset );
916     }
917 
918     if( m_bReplace )
919     {
920         const long nTmpH = GetNextPortion() ? GetNextPortion()->GetAscent() : 120;
921         aSize = Size( nTmpH, nTmpH );
922         aPos.setY( rInf.Y() - nTmpH );
923     }
924     SwRect aTmp( aPos, aSize );
925 
926     bool bDraw = true;
927 
928     if ( IsAnimated() )
929     {
930         bDraw = !rInf.GetOpt().IsGraphic();
931         if( !nId )
932         {
933             SetId( sal_IntPtr( rInf.GetTextFrame() ) );
934             rInf.GetTextFrame()->SetAnimation();
935         }
936         if( aTmp.IsOver( rInf.GetPaintRect() ) && !bDraw )
937         {
938             rInf.NoteAnimation();
939             const SwViewShell* pViewShell = rInf.GetVsh();
940 
941             // virtual device, not pdf export
942             if( OUTDEV_VIRDEV == rInf.GetOut()->GetOutDevType() &&
943                 pViewShell && pViewShell->GetWin()  )
944             {
945                 Graphic* pGraph = const_cast<Graphic*>(pBrush->GetGraphic());
946                 if (pGraph)
947                     pGraph->StopAnimation(nullptr,nId);
948                 rInf.GetTextFrame()->getRootFrame()->GetCurrShell()->InvalidateWindows( aTmp );
949             }
950 
951             else if ( pViewShell &&
952                      !pViewShell->GetAccessibilityOptions()->IsStopAnimatedGraphics() &&
953                      !pViewShell->IsPreview() &&
954                       // #i9684# Stop animation during printing/pdf export.
955                       pViewShell->GetWin() )
956             {
957                 Graphic* pGraph = const_cast<Graphic*>(pBrush->GetGraphic());
958                 if (pGraph)
959                 {
960                     pGraph->StartAnimation(
961                         const_cast<OutputDevice*>(rInf.GetOut()), aPos, aSize, nId );
962                 }
963             }
964 
965             // pdf export, printing, preview, stop animations...
966             else
967                 bDraw = true;
968         }
969         if( bDraw )
970         {
971 
972             Graphic* pGraph = const_cast<Graphic*>(pBrush->GetGraphic());
973             if (pGraph)
974                 pGraph->StopAnimation( nullptr, nId );
975         }
976     }
977 
978     SwRect aRepaint( rInf.GetPaintRect() );
979     const SwTextFrame& rFrame = *rInf.GetTextFrame();
980     if( rFrame.IsVertical() )
981     {
982         rFrame.SwitchHorizontalToVertical( aTmp );
983         rFrame.SwitchHorizontalToVertical( aRepaint );
984     }
985 
986     if( rFrame.IsRightToLeft() )
987     {
988         rFrame.SwitchLTRtoRTL( aTmp );
989         rFrame.SwitchLTRtoRTL( aRepaint );
990     }
991 
992     if( bDraw && aTmp.HasArea() )
993     {
994         DrawGraphic( pBrush.get(), const_cast<OutputDevice*>(rInf.GetOut()),
995             aTmp, aRepaint, m_bReplace ? GRFNUM_REPLACE : GRFNUM_YES );
996     }
997 }
998 
SetBase(long nLnAscent,long nLnDescent,long nFlyAsc,long nFlyDesc)999 void SwGrfNumPortion::SetBase( long nLnAscent, long nLnDescent,
1000                                long nFlyAsc, long nFlyDesc )
1001 {
1002     if ( GetOrient() != text::VertOrientation::NONE )
1003     {
1004         SetRelPos( 0 );
1005         if ( GetOrient() == text::VertOrientation::CENTER )
1006             SetRelPos( GetGrfHeight() / 2 );
1007         else if ( GetOrient() == text::VertOrientation::TOP )
1008             SetRelPos( GetGrfHeight() - GRFNUM_SECURE );
1009         else if ( GetOrient() == text::VertOrientation::BOTTOM )
1010             ;
1011         else if ( GetOrient() == text::VertOrientation::CHAR_CENTER )
1012             SetRelPos( ( GetGrfHeight() + nLnAscent - nLnDescent ) / 2 );
1013         else if ( GetOrient() == text::VertOrientation::CHAR_TOP )
1014             SetRelPos( nLnAscent );
1015         else if ( GetOrient() == text::VertOrientation::CHAR_BOTTOM )
1016             SetRelPos( GetGrfHeight() - nLnDescent );
1017         else
1018         {
1019             if( GetGrfHeight() >= nFlyAsc + nFlyDesc )
1020             {
1021                 // If I'm as large as the line, I do not need to adjust
1022                 // at the line; I'll leave the max. ascent unchanged
1023                 SetRelPos( nFlyAsc );
1024             }
1025             else if ( GetOrient() == text::VertOrientation::LINE_CENTER )
1026                 SetRelPos( ( GetGrfHeight() + nFlyAsc - nFlyDesc ) / 2 );
1027             else if ( GetOrient() == text::VertOrientation::LINE_TOP )
1028                 SetRelPos( nFlyAsc );
1029             else if ( GetOrient() == text::VertOrientation::LINE_BOTTOM )
1030                 SetRelPos( GetGrfHeight() - nFlyDesc );
1031         }
1032     }
1033 }
1034 
StopAnimation(OutputDevice * pOut)1035 void SwTextFrame::StopAnimation( OutputDevice* pOut )
1036 {
1037     OSL_ENSURE( HasAnimation(), "SwTextFrame::StopAnimation: Which Animation?" );
1038     if( HasPara() )
1039     {
1040         SwLineLayout *pLine = GetPara();
1041         while( pLine )
1042         {
1043             SwLinePortion *pPor = pLine->GetNextPortion();
1044             while( pPor )
1045             {
1046                 if( pPor->IsGrfNumPortion() )
1047                     static_cast<SwGrfNumPortion*>(pPor)->StopAnimation( pOut );
1048                 // The NumberPortion is always at the first char,
1049                 // which means we can cancel as soon as we've reached a portion
1050                 // with a length > 0
1051                 pPor = pPor->GetLen() ? nullptr : pPor->GetNextPortion();
1052             }
1053             pLine = pLine->GetLen() ? nullptr : pLine->GetNext();
1054         }
1055     }
1056 }
1057 
1058 /**
1059  * Initializes the script array and clears the width array
1060  */
SwCombinedPortion(const OUString & rText)1061 SwCombinedPortion::SwCombinedPortion( const OUString &rText )
1062     : SwFieldPortion( rText )
1063     , nUpPos(0)
1064     , nLowPos(0)
1065     , nProportion(55)
1066 {
1067     SetLen(TextFrameIndex(1));
1068     SetWhichPor( PortionType::Combined );
1069     if( m_aExpand.getLength() > 6 )
1070         m_aExpand = m_aExpand.copy( 0, 6 );
1071 
1072     // Initialization of the scripttype array,
1073     // the arrays of width and position are filled by the format function
1074     assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());
1075 
1076     SwFontScript nScr = SW_SCRIPTS;
1077     for( sal_Int32 i = 0; i < rText.getLength(); ++i )
1078     {
1079         switch ( g_pBreakIt->GetBreakIter()->getScriptType( rText, i ) ) {
1080             case i18n::ScriptType::LATIN : nScr = SwFontScript::Latin; break;
1081             case i18n::ScriptType::ASIAN : nScr = SwFontScript::CJK; break;
1082             case i18n::ScriptType::COMPLEX : nScr = SwFontScript::CTL; break;
1083         }
1084         aScrType[i] = nScr;
1085     }
1086 }
1087 
Paint(const SwTextPaintInfo & rInf) const1088 void SwCombinedPortion::Paint( const SwTextPaintInfo &rInf ) const
1089 {
1090     OSL_ENSURE(GetLen() <= TextFrameIndex(1), "SwFieldPortion::Paint: rest-portion pollution?");
1091     if( !Width() )
1092         return;
1093 
1094     rInf.DrawBackBrush( *this );
1095     rInf.DrawViewOpt( *this, PortionType::Field );
1096 
1097     // do we have to repaint a post it portion?
1098     if( rInf.OnWin() && mpNextPortion && !mpNextPortion->Width() )
1099         mpNextPortion->PrePaint( rInf, this );
1100 
1101     const sal_Int32 nCount = m_aExpand.getLength();
1102     if( !nCount )
1103         return;
1104     OSL_ENSURE( nCount < 7, "Too much combined characters" );
1105 
1106     // the first character of the second row
1107     const sal_Int32 nTop = ( nCount + 1 ) / 2;
1108 
1109     SwFont aTmpFont( *rInf.GetFont() );
1110     aTmpFont.SetProportion( nProportion );  // a smaller font
1111     SwFontSave aFontSave( rInf, &aTmpFont );
1112 
1113     Point aOldPos = rInf.GetPos();
1114     Point aOutPos( aOldPos.X(), aOldPos.Y() - nUpPos );// Y of the first row
1115     for( sal_Int32 i = 0 ; i < nCount; ++i )
1116     {
1117         if( i == nTop ) // change the row
1118             aOutPos.setY( aOldPos.Y() + nLowPos );    // Y of the second row
1119         aOutPos.setX( aOldPos.X() + aPos[i] );        // X position
1120         const SwFontScript nAct = aScrType[i];        // script type
1121         aTmpFont.SetActual( nAct );
1122 
1123         // if there're more than 4 characters to display, we choose fonts
1124         // with 2/3 of the original font width.
1125         if( aWidth[ nAct ] )
1126         {
1127             Size aTmpSz = aTmpFont.GetSize( nAct );
1128             if( aTmpSz.Width() != aWidth[ nAct ] )
1129             {
1130                 aTmpSz.setWidth( aWidth[ nAct ] );
1131                 aTmpFont.SetSize( aTmpSz, nAct );
1132             }
1133         }
1134         const_cast<SwTextPaintInfo&>(rInf).SetPos( aOutPos );
1135         rInf.DrawText(m_aExpand, *this, TextFrameIndex(i), TextFrameIndex(1));
1136     }
1137     // rInf is const, so we have to take back our manipulations
1138     const_cast<SwTextPaintInfo&>(rInf).SetPos( aOldPos );
1139 
1140 }
1141 
Format(SwTextFormatInfo & rInf)1142 bool SwCombinedPortion::Format( SwTextFormatInfo &rInf )
1143 {
1144     const sal_Int32 nCount = m_aExpand.getLength();
1145     if( !nCount )
1146     {
1147         Width( 0 );
1148         return false;
1149     }
1150 
1151     OSL_ENSURE( nCount < 7, "Too much combined characters" );
1152 
1153     // If there are leading "weak"-scripttyped characters in this portion,
1154     // they get the actual scripttype.
1155     for( sal_Int32 i = 0; i < nCount && SW_SCRIPTS == aScrType[i]; ++i )
1156         aScrType[i] = rInf.GetFont()->GetActual();
1157     if( nCount > 4 )
1158     {
1159         // more than four? Ok, then we need the 2/3 font width
1160         for( sal_Int32 i = 0; i < m_aExpand.getLength(); ++i )
1161         {
1162             OSL_ENSURE( aScrType[i] < SW_SCRIPTS, "Combined: Script fault" );
1163             if( !aWidth[ aScrType[i] ] )
1164             {
1165                 rInf.GetOut()->SetFont( rInf.GetFont()->GetFnt( aScrType[i] ) );
1166                 aWidth[ aScrType[i] ] =
1167                         static_cast<sal_uInt16>(2 * rInf.GetOut()->GetFontMetric().GetFontSize().Width() / 3);
1168             }
1169         }
1170     }
1171 
1172     const sal_Int32 nTop = ( nCount + 1 ) / 2; // the first character of the second line
1173     SwViewShell *pSh = rInf.GetTextFrame()->getRootFrame()->GetCurrShell();
1174     SwFont aTmpFont( *rInf.GetFont() );
1175     SwFontSave aFontSave( rInf, &aTmpFont );
1176     nProportion = 55;
1177     // In nMainAscent/Descent we store the ascent and descent
1178     // of the original surrounding font
1179     sal_uInt16 nMaxDescent, nMaxAscent, nMaxWidth;
1180     sal_uInt16 nMainDescent = rInf.GetFont()->GetHeight( pSh, *rInf.GetOut() );
1181     const sal_uInt16 nMainAscent = rInf.GetFont()->GetAscent( pSh, *rInf.GetOut() );
1182     nMainDescent = nMainDescent - nMainAscent;
1183     // we start with a 50% font, but if we notice that the combined portion
1184     // becomes bigger than the surrounding font, we check 45% and maybe 40%.
1185     do
1186     {
1187         nProportion -= 5;
1188         aTmpFont.SetProportion( nProportion );
1189         memset( &aPos, 0, sizeof(aPos) );
1190         nMaxDescent = 0;
1191         nMaxAscent = 0;
1192         nMaxWidth = 0;
1193         nUpPos = nLowPos = 0;
1194 
1195         // Now we get the width of all characters.
1196         // The ascent and the width of the first line are stored in the
1197         // ascent member of the portion, the descent in nLowPos.
1198         // The ascent, descent and width of the second line are stored in the
1199         // local nMaxAscent, nMaxDescent and nMaxWidth variables.
1200         for( sal_Int32 i = 0; i < nCount; ++i )
1201         {
1202             SwFontScript nScrp = aScrType[i];
1203             aTmpFont.SetActual( nScrp );
1204             if( aWidth[ nScrp ] )
1205             {
1206                 Size aFontSize( aTmpFont.GetSize( nScrp ) );
1207                 aFontSize.setWidth( aWidth[ nScrp ] );
1208                 aTmpFont.SetSize( aFontSize, nScrp );
1209             }
1210 
1211             SwDrawTextInfo aDrawInf(pSh, *rInf.GetOut(), m_aExpand, i, 1);
1212             Size aSize = aTmpFont.GetTextSize_( aDrawInf );
1213             const sal_uInt16 nAsc = aTmpFont.GetAscent( pSh, *rInf.GetOut() );
1214             aPos[ i ] = static_cast<sal_uInt16>(aSize.Width());
1215             if( i == nTop ) // enter the second line
1216             {
1217                 nLowPos = nMaxDescent;
1218                 Height( nMaxDescent + nMaxAscent );
1219                 Width( nMaxWidth );
1220                 SetAscent( nMaxAscent );
1221                 nMaxAscent = 0;
1222                 nMaxDescent = 0;
1223                 nMaxWidth = 0;
1224             }
1225             nMaxWidth = nMaxWidth + aPos[ i ];
1226             if( nAsc > nMaxAscent )
1227                 nMaxAscent = nAsc;
1228             if( aSize.Height() - nAsc > nMaxDescent )
1229                 nMaxDescent = static_cast<sal_uInt16>(aSize.Height() - nAsc);
1230         }
1231         // for one or two characters we double the width of the portion
1232         if( nCount < 3 )
1233         {
1234             nMaxWidth *= 2;
1235             Width( 2*Width() );
1236             if( nCount < 2 )
1237             {
1238                 Height( nMaxAscent + nMaxDescent );
1239                 nLowPos = nMaxDescent;
1240             }
1241         }
1242         Height( Height() + nMaxDescent + nMaxAscent );
1243         nUpPos = nMaxAscent;
1244         SetAscent( Height() - nMaxDescent - nLowPos );
1245     } while( nProportion > 40 && ( GetAscent() > nMainAscent ||
1246                                     Height() - GetAscent() > nMainDescent ) );
1247     // if the combined portion is smaller than the surrounding text,
1248     // the portion grows. This looks better, if there's a character background.
1249     if( GetAscent() < nMainAscent )
1250     {
1251         Height( Height() + nMainAscent - GetAscent() );
1252         SetAscent( nMainAscent );
1253     }
1254     if( Height() < nMainAscent + nMainDescent )
1255         Height( nMainAscent + nMainDescent );
1256 
1257     // We calculate the x positions of the characters in both lines...
1258     sal_uInt16 nTopDiff = 0;
1259     sal_uInt16 nBotDiff = 0;
1260     if( nMaxWidth > Width() )
1261     {
1262         nTopDiff = ( nMaxWidth - Width() ) / 2;
1263         Width( nMaxWidth );
1264     }
1265     else
1266         nBotDiff = ( Width() - nMaxWidth ) / 2;
1267     switch( nTop)
1268     {
1269         case 3: aPos[1] = aPos[0] + nTopDiff;
1270             [[fallthrough]];
1271         case 2: aPos[nTop-1] = Width() - aPos[nTop-1];
1272     }
1273     aPos[0] = 0;
1274     switch( nCount )
1275     {
1276         case 5: aPos[4] = aPos[3] + nBotDiff;
1277             [[fallthrough]];
1278         case 3: aPos[nTop] = nBotDiff;          break;
1279         case 6: aPos[4] = aPos[3] + nBotDiff;
1280             [[fallthrough]];
1281         case 4: aPos[nTop] = 0;
1282             [[fallthrough]];
1283         case 2: aPos[nCount-1] = Width() - aPos[nCount-1];
1284     }
1285 
1286     // Does the combined portion fit the line?
1287     const bool bFull = rInf.Width() < rInf.X() + Width();
1288     if( bFull )
1289     {
1290         if( rInf.GetLineStart() == rInf.GetIdx() && (!rInf.GetLast()->InFieldGrp()
1291             || !static_cast<SwFieldPortion*>(rInf.GetLast())->IsFollow() ) )
1292             Width( static_cast<sal_uInt16>( rInf.Width() - rInf.X() ) );
1293         else
1294         {
1295             Truncate();
1296             Width( 0 );
1297             SetLen(TextFrameIndex(0));
1298             if( rInf.GetLast() )
1299                 rInf.GetLast()->FormatEOL( rInf );
1300         }
1301     }
1302     return bFull;
1303 }
1304 
GetViewWidth(const SwTextSizeInfo & rInf) const1305 sal_uInt16 SwCombinedPortion::GetViewWidth( const SwTextSizeInfo &rInf ) const
1306 {
1307     if( !GetLen() ) // for the dummy part at the end of the line, where
1308         return 0;   // the combined portion doesn't fit.
1309     return SwFieldPortion::GetViewWidth( rInf );
1310 }
1311 
Clone(const OUString & rExpand) const1312 SwFieldPortion *SwFieldFormDropDownPortion::Clone(const OUString &rExpand) const
1313 {
1314     return new SwFieldFormDropDownPortion(m_pFieldMark, rExpand);
1315 }
1316 
Paint(const SwTextPaintInfo & rInf) const1317 void SwFieldFormDropDownPortion::Paint( const SwTextPaintInfo &rInf ) const
1318 {
1319     SwFieldPortion::Paint( rInf );
1320 
1321     ::sw::mark::DropDownFieldmark* pDropDownField = dynamic_cast< ::sw::mark::DropDownFieldmark* >(m_pFieldMark);
1322     if(pDropDownField)
1323     {
1324         SwRect aPaintArea;
1325         rInf.CalcRect( *this, &aPaintArea );
1326         pDropDownField->SetPortionPaintArea(aPaintArea);
1327     }
1328 }
1329 
Clone(const OUString &) const1330 SwFieldPortion *SwFieldFormDatePortion::Clone(const OUString &/*rExpand*/) const
1331 {
1332     return new SwFieldFormDatePortion(m_pFieldMark, m_bStart);
1333 }
1334 
Paint(const SwTextPaintInfo & rInf) const1335 void SwFieldFormDatePortion::Paint( const SwTextPaintInfo &rInf ) const
1336 {
1337     SwFieldPortion::Paint( rInf );
1338 
1339     ::sw::mark::DateFieldmark* pDateField = dynamic_cast< ::sw::mark::DateFieldmark* >(m_pFieldMark);
1340     if(pDateField)
1341     {
1342         SwRect aPaintArea;
1343         rInf.CalcRect( *this, &aPaintArea );
1344         if(m_bStart)
1345             pDateField->SetPortionPaintAreaStart(aPaintArea);
1346         else
1347             pDateField->SetPortionPaintAreaEnd(aPaintArea);
1348     }
1349 }
1350 
1351 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1352