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