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