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