Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/sw/source/core/text/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 50 kB image not shown  

Quelle  porfld.cxx   Sprache: C

 
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */


#include <hintids.hxx>

#include <com/sun/star/i18n/ScriptType.hpp>
#include <com/sun/star/i18n/XBreakIterator.hpp>
#include <utility>

#include <comphelper/string.hxx>
#include <editeng/brushitem.hxx>
#include <o3tl/deleter.hxx>
#include <vcl/graph.hxx>
#include <vcl/metric.hxx>
#include <vcl/outdev.hxx>
#include <vcl/pdfextoutdevdata.hxx>
#include <vcl/pdfwriter.hxx>
#include <viewopt.hxx>
#include <SwPortionHandler.hxx>
#include "porlay.hxx"
#include "porfld.hxx"
#include "inftxt.hxx"
#include <fmtornt.hxx>
#include <frmatr.hxx>
#include <frmtool.hxx>
#include <viewsh.hxx>
#include <doc.hxx>
#include <IDocumentSettingAccess.hxx>
#include <rootfrm.hxx>
#include <breakit.hxx>
#include "porftn.hxx"
#include <accessibilityoptions.hxx>
#include <editeng/lrspitem.hxx>
#include <unicode/ubidi.h>
#include <bookmark.hxx>
#include <docufld.hxx>

using namespace ::com::sun::star;

SwLinePortion *SwFieldPortion::Compress()
return (GetLen() || !m_aExpand.isEmpty() || SwLinePortion::Compress()) ? this : nullptr; }

SwFieldPortion *SwFieldPortion::Clone( const OUString &rExpand ) const
{
    std::unique_ptr<SwFont> pNewFnt;
    if( m_pFont )
    {
        pNewFnt.reset(new SwFont( *m_pFont ));
    }
    // #i107143#
    // pass placeholder property to created <SwFieldPortion> instance.
    SwFieldPortion* pClone = new SwFieldPortion(rExpand, std::move(pNewFnt));
    pClone->SetNextOffset( m_nNextOffset );
    pClone->m_bNoLength = m_bNoLength;
    return pClone;
}

void SwFieldPortion::TakeNextOffset( const SwFieldPortion* pField )
{
    assert(pField && "TakeNextOffset: Missing Source");
    m_nNextOffset = pField->GetNextOffset();
    m_aExpand = m_aExpand.replaceAt(0, sal_Int32(m_nNextOffset), u"");
    m_bFollow = true;
}

SwFieldPortion::SwFieldPortion(OUString aExpand, std::unique_ptr<SwFont> pFont, TextFrameIndex const nFieldLen)
    : m_aExpand(std::move(aExpand)), m_pFont(std::move(pFont)), m_nNextOffset(0)
    , m_nNextScriptChg(COMPLETE_STRING), m_nFieldLen(nFieldLen), m_nViewWidth(0)
    , m_bFollow( false ), m_bLeft( false), m_bHide( false)
    , m_bCenter (false), m_bHasFollow( false )
    , m_bAnimated( false), m_bNoPaint( false)
    , m_bReplace(false)
    , m_bNoLength( false )
{
    SetWhichPor( PortionType::Field );
}

SwFieldPortion::SwFieldPortion( const SwFieldPortion& rField )
    : SwExpandPortion( rField )
    , m_aExpand( rField.GetExp() )
    , m_nNextOffset( rField.GetNextOffset() )
    , m_nNextScriptChg( rField.m_nNextScriptChg )
    , m_nFieldLen(rField.m_nFieldLen)
    , m_nViewWidth( rField.m_nViewWidth )
    , m_bFollow( rField.IsFollow() )
    , m_bLeft( rField.IsLeft() )
    , m_bHide( rField.IsHide() )
    , m_bCenter( rField.IsCenter() )
    , m_bHasFollow( rField.HasFollow() )
    , m_bAnimated ( rField.m_bAnimated )
    , m_bNoPaint( rField.m_bNoPaint)
    , m_bReplace( rField.m_bReplace )
    , m_bNoLength( rField.m_bNoLength )
{
    if ( rField.HasFont() )
        m_pFont.reset( new SwFont( *rField.GetFont() ) );

    SetWhichPor( PortionType::Field );
}

SwFieldPortion::~SwFieldPortion()
{
    m_pFont.reset();
}

SwTwips SwFieldPortion::GetViewWidth(const SwTextSizeInfo& rInf) const
{
    // even though this is const, nViewWidth should be computed at the very end:
    SwFieldPortion* pThis = const_cast<SwFieldPortion*>(this);
    if( !Width() && rInf.OnWin() && !rInf.GetOpt().IsPagePreview() &&
            !rInf.GetOpt().IsReadonly() && rInf.GetOpt().IsFieldShadings() )
    {
        if( !m_nViewWidth )
            pThis->m_nViewWidth = rInf.GetTextSize(OUString(' ')).Width();
    }
    else
        pThis->m_nViewWidth = 0;
    return m_nViewWidth;
}

namespace {

/**
 * Never just use SetLen(0)
 */

class SwFieldSlot
{
    std::shared_ptr<const vcl::text::TextLayoutCache> m_pOldCachedVclData;
    const OUString *pOldText;
    OUString aText;
    TextFrameIndex nIdx;
    TextFrameIndex nLen;
    sal_Unicode nOrigHookChar;
    SwTextFormatInfo *pInf;
    bool bOn;
public:
    SwFieldSlot( const SwTextFormatInfo* pNew, const SwFieldPortion *pPor );
    ~SwFieldSlot();
};

}

SwFieldSlot::SwFieldSlot( const SwTextFormatInfo* pNew, const SwFieldPortion *pPor )
    : pOldText(nullptr)
    , nIdx(0)
    , nLen(0)
    , nOrigHookChar(0)
    , pInf(nullptr)
{
    bOn = pPor->GetExpText( *pNew, aText );

    // The text will be replaced ...
    if( !bOn )
        return;

    pInf = const_cast<SwTextFormatInfo*>(pNew);
    nIdx = pInf->GetIdx();
    nLen = pInf->GetLen();
    pOldText = &(pInf->GetText());
    nOrigHookChar = pInf->GetHookChar();
    m_pOldCachedVclData = pInf->GetCachedVclData();
    pInf->SetLen(TextFrameIndex(aText.getLength()));
    pInf->SetCachedVclData(nullptr);
    if( pPor->IsFollow() )
    {
        pInf->SetFakeLineStart( nIdx > pInf->GetLineStart() );
        pInf->SetIdx(TextFrameIndex(0));
    }
    else
    {
        TextFrameIndex nEnd(pOldText->getLength());
        if (nIdx < nEnd)
        {
            sal_Int32 const nFieldLen(pPor->GetFieldLen());
            aText = (*pOldText).replaceAt(sal_Int32(nIdx), nFieldLen, aText);
        }
        else if (nIdx == nEnd)
            aText = *pOldText + aText;
        else
            SAL_WARN("sw.core""SwFieldSlot bad SwFieldPortion index.");
    }
    pInf->SetText( aText );
}

SwFieldSlot::~SwFieldSlot()
{
    if( bOn )
    {
        pInf->SetCachedVclData(m_pOldCachedVclData);
        pInf->SetText( *pOldText );
        // ofz#64109 at last for ruby-text when we restore the original text to
        // continue laying out the 'body' text of the ruby, then a tab or other
        // 'hook char' in the text drawn above it shouldn't affect the 'body'
        // While there are other cases, such as tdf#148360, where the tab in an
        // inline expanded field, that should affect the body.
        if (pInf->IsRuby())
            pInf->SetHookChar(nOrigHookChar);
        pInf->SetIdx( nIdx );
        pInf->SetLen( nLen );
        pInf->SetFakeLineStart( false );
    }
}

void SwFieldPortion::CheckScript( const SwTextSizeInfo &rInf )
{
    OUString aText;
    if (!GetExpText(rInf, aText) || aText.isEmpty())
        return;

    SwFontScript nActual = m_pFont ? m_pFont->GetActual() : rInf.GetFont()->GetActual();
    sal_Int16 nScript = g_pBreakIt->GetBreakIter()->getScriptType( aText, 0 );
    sal_Int32 nChg = 0;
    if( i18n::ScriptType::WEAK == nScript )
    {
        nChg = g_pBreakIt->GetBreakIter()->endOfScript(aText,0,nScript);
        if (nChg < aText.getLength() && nChg >= 0)
            nScript = g_pBreakIt->GetBreakIter()->getScriptType( aText, nChg );
    }

    // nNextScriptChg will be evaluated during SwFieldPortion::Format()

    if (nChg < aText.getLength() && nChg >= 0)
        m_nNextScriptChg = TextFrameIndex(
                g_pBreakIt->GetBreakIter()->endOfScript(aText, nChg, nScript));
    else
        m_nNextScriptChg = TextFrameIndex(aText.getLength());

    SwFontScript nTmp;
    switch ( nScript ) {
        case i18n::ScriptType::LATIN : nTmp = SwFontScript::Latin; break;
        case i18n::ScriptType::ASIAN : nTmp = SwFontScript::CJK; break;
        case i18n::ScriptType::COMPLEX : nTmp = SwFontScript::CTL; break;
        default: nTmp = nActual;
    }

    // #i16354# Change script type for RTL text to CTL.
    const SwScriptInfo& rSI = rInf.GetParaPortion()->GetScriptInfo();
    // #i98418#
    const sal_uInt8 nFieldDir = (IsNumberPortion() || IsFootnoteNumPortion())
        ? rSI.GetDefaultDir()
        : rSI.DirType(IsFollow() ? rInf.GetIdx() - m_nFieldLen : rInf.GetIdx());

    {
        UErrorCode nError = U_ZERO_ERROR;
        UBiDi* pBidi = ubidi_openSized( aText.getLength(), 0, &nError );
        ubidi_setPara( pBidi, reinterpret_cast<const UChar *>(aText.getStr()), aText.getLength(), nFieldDir, nullptr, &nError );
        int32_t nEnd;
        UBiDiLevel nCurrDir;
        ubidi_getLogicalRun( pBidi, 0, &nEnd, &nCurrDir );
        ubidi_close( pBidi );
        const TextFrameIndex nNextDirChg(nEnd);
        m_nNextScriptChg = std::min( m_nNextScriptChg, nNextDirChg );

        // #i89825# change the script type also to CTL
        // if there is no strong LTR char in the LTR run (numbers)
        if (nCurrDir != UBIDI_RTL &&
            (UBIDI_LTR != nFieldDir || i18n::ScriptType::COMPLEX == nScript))
        {
            nCurrDir = UBIDI_RTL;
            for( sal_Int32 nCharIdx = 0; nCharIdx < nEnd; ++nCharIdx )
            {
                UCharDirection nCharDir = u_charDirection ( aText[ nCharIdx ]);
                if ( nCharDir == U_LEFT_TO_RIGHT ||
                     nCharDir == U_LEFT_TO_RIGHT_EMBEDDING ||
                     nCharDir == U_LEFT_TO_RIGHT_OVERRIDE )
                {
                    nCurrDir = UBIDI_LTR;
                    break;
                }
            }
        }

        if (nCurrDir == UBIDI_RTL)
        {
            nTmp = SwFontScript::CTL;
            // If we decided that this range was RTL after all and the
            // previous range was complex but clipped to the start of this
            // range, then extend it to be complex over the additional RTL range
            if (nScript == i18n::ScriptType::COMPLEX)
                m_nNextScriptChg = nNextDirChg;
        }
    }

    // #i98418#
    // keep determined script type for footnote portions as preferred script type.
    // For footnote portions a font can not be created directly - see footnote
    // portion format method.
    if ( IsFootnotePortion() )
    {
        static_cast<SwFootnotePortion*>(this)->SetPreferredScriptType( nTmp );
    }
    else if ( nTmp != nActual )
    {
        if( !m_pFont )
            m_pFont.reset( new SwFont( *rInf.GetFont() ) );
        m_pFont->SetActual( nTmp );
    }

}

bool SwFieldPortion::Format( SwTextFormatInfo &rInf )
{
    // Scope wegen aDiffText::DTOR!
    bool bFull = false;
    bool bEOL = false;
    TextFrameIndex const nTextRest = TextFrameIndex(rInf.GetText().getLength()) - rInf.GetIdx();
    {
        TextFrameIndex nRest;
        SwFieldSlot aDiffText( &rInf, this );
        SwLayoutModeModifier aLayoutModeModifier( *rInf.GetOut() );
        aLayoutModeModifier.SetAuto();

        // Field portion has to be split in several parts if
        // 1. There are script/direction changes inside the field
        // 2. There are portion breaks (tab, break) inside the field:
        const TextFrameIndex nOldFullLen = rInf.GetLen();
        TextFrameIndex nFullLen = rInf.ScanPortionEnd(rInf.GetIdx(), rInf.GetIdx() + nOldFullLen) - rInf.GetIdx();
        if ( m_nNextScriptChg < nFullLen )
        {
            nFullLen = m_nNextScriptChg;
            rInf.SetHookChar( 0 );
        }
        rInf.SetLen( nFullLen );

        if( m_pFont )
            m_pFont->AllocFontCacheId( rInf.GetVsh(), m_pFont->GetActual() );

        SwFontSave aSave( rInf, m_pFont.get() );

        // Length must be 0: the length is set for bFull after format
        // and passed along in nRest. Or else the old length would be
        // retained and be used for nRest!
        SetLen(TextFrameIndex(0));
        TextFrameIndex const nFollow(IsFollow() ? TextFrameIndex(0) : m_nFieldLen);

        // As odd is may seem: the query for GetLen() must return false due
        // to the ExpandPortions _after_ aDiffText (see SoftHyphs), caused
        // by SetFull.
        if( !nFullLen )
        {
            // Don't Init(), as we need height and ascent
            Width(0);
            bFull = rInf.Width() <= rInf.GetPos().X();
        }
        else
        {
            TextFrameIndex const nOldLineStart = rInf.GetLineStart();
            if( IsFollow() )
                rInf.SetLineStart(TextFrameIndex(0));
            rInf.SetNotEOL( nFullLen == nOldFullLen && nTextRest > nFollow );

            // the height depending on the fields font is set,
            // this is required for SwTextGuess::Guess
            Height( rInf.GetTextHeight() + rInf.GetFont()->GetTopBorderSpace() +
                    rInf.GetFont()->GetBottomBorderSpace() );
            // If a kerning portion is inserted after our field portion,
            // the ascent and height must be known
            SetAscent( rInf.GetAscent() + rInf.GetFont()->GetTopBorderSpace() );
            bFull = SwTextPortion::Format( rInf );
            rInf.SetNotEOL( false );
            rInf.SetLineStart( nOldLineStart );
        }
        TextFrameIndex const nTmpLen = GetLen();
        bEOL = !nTmpLen && nFollow && bFull;
        nRest = nOldFullLen - nTmpLen;

        // The char is held in the first position
        // Unconditionally after format!
        SetLen( m_bNoLength ? TextFrameIndex(0) : nFollow );

        if( nRest )
        {
            // aExpand has not yet been shortened; the new Ofst is a
            // result of nRest
            TextFrameIndex nNextOfst = TextFrameIndex(m_aExpand.getLength()) - nRest;

            if ( IsQuoVadisPortion() )
                nNextOfst = nNextOfst + TextFrameIndex(static_cast<SwQuoVadisPortion*>(this)->GetContText().getLength());

            OUString aNew( m_aExpand.copy(sal_Int32(nNextOfst)) );
            m_aExpand = m_aExpand.copy(0, sal_Int32(nNextOfst));

            // These characters should not be contained in the follow
            // field portion. They are handled via the HookChar mechanism.
            const sal_Unicode nNew = !aNew.isEmpty() ? aNew[0] : 0;
            auto IsHook = [](const sal_Unicode cNew, bool const isSpace = false) -> bool
            {
                switch (cNew)
                {
                    case ' '// tdf#159101 this one is not in ScanPortionEnd
                              // but is required for justified text
                        return isSpace;
                    case CH_BREAK:
                    case CH_TAB:
                    case CHAR_HARDHYPHEN: // non-breaking hyphen
                    case CHAR_SOFTHYPHEN:
                    case CHAR_HARDBLANK:
                    case CHAR_ZWSP:
                    case CHAR_WJ:
                    case CH_TXTATR_BREAKWORD:
                    case CH_TXTATR_INWORD:
                    {
                        return true;
                    }
                    default:
                        return false;
                }
            };
            if (IsHook(nNew, true))
            {
                if (nNew == CH_BREAK)
                {
                    bFull = true;
                }
                aNew = aNew.copy(1);
                ++nNextOfst;
            }

            // Even if there is no more text left for a follow field,
            // we have to build a follow field portion (without font),
            // otherwise the HookChar mechanism would not work.
            SwFieldPortion *pField = Clone( aNew );
            if( !aNew.isEmpty() && !pField->GetFont() )
            {
                pField->SetFont( std::make_unique<SwFont>( *rInf.GetFont() ) );
            }
            if (IsFollow() || Compress())
            {   // empty this will be deleted in SwLineLayout::CalcLine()
                // anyway so make sure pField doesn't have a stale flag
                pField->SetFollow( true );
            }
            if (pField->Compress() && !std::all_of(std::u16string_view(aNew).begin(),
                        std::u16string_view(aNew).end(), IsHook))
            {   // empty pField will be deleted in SwLineLayout::CalcLine()
                // anyway so make sure this one doesn't have a stale flag
                SetHasFollow( true );
            }

            // tdf#166044: QuoVadis portions with a continuation notice rely on
            // m_bHasFollow to indicate which portion is responsible for printing
            // the destination page number. Initially, this is the otherwise-empty
            // pField. Sometimes SwLineLayout::CalcLine() will cut this extra
            // field, but that isn't always possible. Set m_bHasFollow on this
            // portion to avoid printing the page number more than once.
            if (IsQuoVadisPortion())
            {
                SetHasFollow(true);
            }

            // For a newly created field, nNextOffset contains the Offset
            // of its start of the original string
            // If a FollowField is created when formatting, this FollowField's
            // Offset is being held in nNextOffset
            m_nNextOffset = m_nNextOffset + nNextOfst;
            pField->SetNextOffset( m_nNextOffset );
            rInf.SetRest( pField );
        }
    }

    if( bEOL && rInf.GetLast() && !rInf.GetUnderflow() )
        rInf.GetLast()->FormatEOL( rInf );
    return bFull;
}

void SwFieldPortion::Paint( const SwTextPaintInfo &rInf ) const
{
    SwFontSave aSave( rInf, m_pFont.get() );

//    OSL_ENSURE(GetLen() <= TextFrameIndex(1), "SwFieldPortion::Paint: rest-portion pollution?");
    if (Width() && !m_bContentControl)
    {
        // A very liberal use of the background
        rInf.DrawViewOpt( *this, PortionType::Field );
        SwExpandPortion::Paint( rInf );
    }
}

bool SwFieldPortion::GetExpText( const SwTextSizeInfo &rInf, OUString &rText ) const
{
    rText = m_aExpand;
    if( rText.isEmpty() && rInf.OnWin() &&
        !rInf.GetOpt().IsPagePreview() && !rInf.GetOpt().IsReadonly() &&
            rInf.GetOpt().IsFieldShadings() &&
            !HasFollow() )
        rText = " ";
    return true;
}

void SwFieldPortion::HandlePortion( SwPortionHandler& rPH ) const
{
    rPH.Special( GetLen(), m_aExpand, GetWhichPor() );
}

void SwFieldPortion::dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText,
                               TextFrameIndex& nOffset) const
{
    (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwFieldPortion"));
    dumpAsXmlAttributes(pWriter, rText, nOffset);
    nOffset += GetLen();

    (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("expand"), BAD_CAST(m_aExpand.toUtf8().getStr()));

    if (m_pFont)
    {
        m_pFont->dumpAsXml(pWriter);
    }

    (void)xmlTextWriterEndElement(pWriter);
}

SwPositiveSize SwFieldPortion::GetTextSize( const SwTextSizeInfo &rInf ) const
{
    SwFontSave aSave( rInf, m_pFont.get() );
    SwPositiveSize aSize( SwExpandPortion::GetTextSize( rInf ) );
    return aSize;
}

SwFieldPortion *SwHiddenPortion::Clone(const OUString &rExpand ) const
{
    std::unique_ptr<SwFont> pNewFnt;
    if( m_pFont )
        pNewFnt.reset(new SwFont( *m_pFont ));
    return new SwHiddenPortion( rExpand, std::move(pNewFnt) );
}

void SwHiddenPortion::Paint( const SwTextPaintInfo &rInf ) const
{
    if( Width() )
    {
        SwFontSave aSave( rInf, m_pFont.get() );
        rInf.DrawViewOpt( *this, PortionType::Hidden );
        SwExpandPortion::Paint( rInf );
    }
}

bool SwHiddenPortion::GetExpText( const SwTextSizeInfo &rInf, OUString &rText ) const
{
    // Do not query for IsHidden()!
    return SwFieldPortion::GetExpText( rInf, rText );
}

SwNumberPortion::SwNumberPortion( const OUString &rExpand,
                                  std::unique_ptr<SwFont> pFont,
                                  const bool bLft,
                                  const bool bCntr, const SwTwips nMinDst,
                                  const bool bLabelAlignmentPosAndSpaceModeActive )
    : SwFieldPortion(rExpand, std::move(pFont), TextFrameIndex(0))
    , m_nFixWidth(0)
    , m_nMinDist(nMinDst)
    , mbLabelAlignmentPosAndSpaceModeActive(bLabelAlignmentPosAndSpaceModeActive)
{
    SetWhichPor( PortionType::Number );
    SetLeft( bLft );
    SetHide( false );
    SetCenter( bCntr );
}

TextFrameIndex SwNumberPortion::GetModelPositionForViewPoint(const SwTwips) const
{
    return TextFrameIndex(0);
}

SwFieldPortion *SwNumberPortion::Clone( const OUString &rExpand ) const
{
    std::unique_ptr<SwFont> pNewFnt;
    if( m_pFont )
        pNewFnt.reset(new SwFont( *m_pFont ));

    return new SwNumberPortion( rExpand, std::move(pNewFnt), IsLeft(), IsCenter(),
                                m_nMinDist, mbLabelAlignmentPosAndSpaceModeActive );
}

/**
 * We can create multiple NumFields
 * Tricky, if one enters enough previous-text in the dialog box
 * to cause the line to overflow
 * We need to keep the Fly's evasion tactics in mind
 */

bool SwNumberPortion::Format( SwTextFormatInfo &rInf )
{
    SetHide( false );
    const bool bFull = SwFieldPortion::Format( rInf );
    SetLen(TextFrameIndex(0));
    // a numbering portion can be contained in a rotated portion!!!
    m_nFixWidth = rInf.IsMulti() ? Height() : Width();
    rInf.SetNumDone( !rInf.GetRest() );
    if( rInf.IsNumDone() )
    {
//        SetAscent( rInf.GetAscent() );
        OSL_ENSURE( Height() && mnAscent, "NumberPortions without Height | Ascent" );

        tools::Long nDiff( 0 );

        if ( !mbLabelAlignmentPosAndSpaceModeActive )
        {
            if ((!rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING) &&
                 // #i32902#
                 !IsFootnoteNumPortion()) ||
                 // tdf#159382
                (IsFootnoteNumPortion() &&
                 rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::NO_GAP_AFTER_NOTE_NUMBER)))
            {
                nDiff = rInf.Left()
                        + rInf.GetTextFrame()
                              ->GetTextNodeForParaProps()
                              ->GetSwAttrSet()
                              .GetFirstLineIndent()
                              .ResolveTextFirstLineOffset({})
                        - rInf.First() + rInf.ForcedLeftMargin();
            }
            else
            {
                nDiff = rInf.Left() - rInf.First() + rInf.ForcedLeftMargin();
            }
        }
        // The text part of the numbering should always at least
        // start at the left margin
        if( nDiff < 0 )
            nDiff = 0;
        else if ( nDiff > rInf.X() )
            nDiff -= rInf.X();
        else
            nDiff = 0;

        if( nDiff < m_nFixWidth + m_nMinDist )
            nDiff = m_nFixWidth + m_nMinDist;

        // Numbering evades the Fly, no nDiff in the second round
        // Tricky special case: FlyFrame is in an Area we're just about to
        // acquire
        // The NumberPortion is marked as hidden
        const bool bFly = rInf.GetFly() ||
            ( rInf.GetLast() && rInf.GetLast()->IsFlyPortion() );
        if( nDiff > rInf.Width() )
        {
            nDiff = rInf.Width();
            if ( bFly )
                SetHide( true );
        }

        // A numbering portion can be inside a SwRotatedPortion. Then the
        // Height has to be changed
        if ( rInf.IsMulti() )
        {
            if ( Height() < nDiff )
                Height( nDiff );
        }
        else if( Width() < nDiff )
            Width( nDiff );
    }
    return bFull;
}


/**
 * A FormatEOL indicates that the subsequent text did not fit onto
 * the line anymore. In order for the Numbering to follow through,
 * we hide this NumberPortion
 */

void SwNumberPortion::FormatEOL( SwTextFormatInfo& )
{

    // This caused trouble with flys anchored as characters.
    // If one of these is numbered but does not fit to the line,
    // it calls this function, causing a loop because both the number
    // portion and the fly portion go to the next line
//    SetHide( true );
}


/**
 * A hidden NumberPortion is not displayed, unless there are TextPortions in
 * this line or there's just one line at all
 */

void SwNumberPortion::Paint( const SwTextPaintInfo &rInf ) const
{
    if ( IsHide() && rInf.GetParaPortion() && rInf.GetParaPortion()->GetNext() )
    {
        SwLinePortion *pTmp = GetNextPortion();
        while ( pTmp && !pTmp->InTextGrp() )
            pTmp = pTmp->GetNextPortion();
        if ( !pTmp )
            return;
    }

    // calculate the width of the number portion, including follows
    const SwTwips nOldWidth = Width();
    SwTwips nSumWidth = 0;
    SwTwips nOffset = 0;

    const SwLinePortion* pTmp = this;
    while ( pTmp && pTmp->InNumberGrp() )
    {
        nSumWidth = nSumWidth + pTmp->Width();
        if ( static_cast<const SwNumberPortion*>(pTmp)->HasFollow() )
            pTmp = pTmp->GetNextPortion();
        else
        {
            nOffset = pTmp->Width() - static_cast<const SwNumberPortion*>(pTmp)->m_nFixWidth;
            break;
        }
    }

    // The master portion takes care for painting the background of the
    // follow field portions
    if ( ! IsFollow() )
    {
        SwNumberPortion *pThis = const_cast<SwNumberPortion*>(this);
        pThis->Width( nSumWidth );
        rInf.DrawViewOpt( *this, PortionType::Number );
        pThis->Width( nOldWidth );
    }

    if( m_aExpand.isEmpty() )
        return;

    const SwFont *pTmpFnt = rInf.GetFont();
    bool bPaintSpace = ( LINESTYLE_NONE != pTmpFnt->GetUnderline() ||
                             LINESTYLE_NONE != pTmpFnt->GetOverline()  ||
                             STRIKEOUT_NONE != pTmpFnt->GetStrikeout() ) &&
                             !pTmpFnt->IsWordLineMode();
    if( bPaintSpace && m_pFont )
        bPaintSpace = ( LINESTYLE_NONE != m_pFont->GetUnderline() ||
                        LINESTYLE_NONE != m_pFont->GetOverline()  ||
                        STRIKEOUT_NONE != m_pFont->GetStrikeout() ) &&
                        !m_pFont->IsWordLineMode();

    SwFontSave aSave( rInf, m_pFont.get() );

    if( m_nFixWidth == Width() && ! HasFollow() )
        SwExpandPortion::Paint( rInf );
    else
    {
        // logical const: reset width
        SwNumberPortion *pThis = const_cast<SwNumberPortion*>(this);
        bPaintSpace = bPaintSpace && m_nFixWidth < nOldWidth;
        SwTwips nSpaceOffs = m_nFixWidth;
        pThis->Width( m_nFixWidth );

        if( ( IsLeft() && ! rInf.GetTextFrame()->IsRightToLeft() ) ||
            ( ! IsLeft() && ! IsCenter() && rInf.GetTextFrame()->IsRightToLeft() ) )
            SwExpandPortion::Paint( rInf );
        else
        {
            SwTextPaintInfo aInf( rInf );
            if( nOffset < m_nMinDist )
                nOffset = 0;
            else
            {
                if( IsCenter() )
                {
                    /* #110778# a / 2 * 2 == a is not a tautology */
                    SwTwips nTmpOffset = nOffset;
                    nOffset /= 2;
                    if( nOffset < m_nMinDist )
                        nOffset = nTmpOffset - m_nMinDist;
                }
                else
                    nOffset = nOffset - m_nMinDist;
            }
            aInf.X( aInf.X() + nOffset );
            SwExpandPortion::Paint( aInf );
            if( bPaintSpace )
                nSpaceOffs = nSpaceOffs + nOffset;
        }
        if( bPaintSpace && nOldWidth > nSpaceOffs )
        {
            SwTextPaintInfo aInf( rInf );
            aInf.X( aInf.X() + nSpaceOffs );

            // #i53199# Adjust position of underline:
            if ( rInf.GetUnderFnt() )
            {
                const Point aNewPos( aInf.GetPos().X(), rInf.GetUnderFnt()->GetPos().Y() );
                rInf.GetUnderFnt()->SetPos( aNewPos );
            }

            pThis->Width( nOldWidth - nSpaceOffs + 12 );
            {
                SwTextSlot aDiffText( &aInf, thistruefalse, u" "_ustr );
                aInf.DrawText( *this, aInf.GetLen(), true );
            }
        }
        pThis->Width( nOldWidth );
    }
}

SwBulletPortion::SwBulletPortion( const sal_UCS4 cBullet,
                                  std::u16string_view rBulletFollowedBy,
                                  std::unique_ptr<SwFont> pFont,
                                  const bool bLft,
                                  const bool bCntr,
                                 const SwTwips nMinDst,
                                  const bool bLabelAlignmentPosAndSpaceModeActive )
    : SwNumberPortion( OUString(&cBullet, 1) + rBulletFollowedBy,
                       std::move(pFont), bLft, bCntr, nMinDst,
                       bLabelAlignmentPosAndSpaceModeActive )
{
    SetWhichPor( PortionType::Bullet );
}

#define GRFNUM_SECURE 10

SwGrfNumPortion::SwGrfNumPortion(
        const OUString& rGraphicFollowedBy,
        const SvxBrushItem* pGrfBrush, OUString const & referer,
        const SwFormatVertOrient* pGrfOrient, const Size& rGrfSize,
        const bool bLft, const bool bCntr, const SwTwips nMinDst,
        const bool bLabelAlignmentPosAndSpaceModeActive ) :
    SwNumberPortion( rGraphicFollowedBy, nullptr, bLft, bCntr, nMinDst,
                     bLabelAlignmentPosAndSpaceModeActive ),
    m_pBrush( new SvxBrushItem(RES_BACKGROUND) ), m_nId( 0 )
{
    SetWhichPor( PortionType::GrfNum );
    SetAnimated( false );
    m_bReplace = false;
    if( pGrfBrush )
    {
        m_pBrush.reset(pGrfBrush->Clone());
        const Graphic* pGraph = pGrfBrush->GetGraphic(referer);
        if( pGraph )
            SetAnimated( pGraph->IsAnimated() );
        else
            m_bReplace = true;
    }
    if( pGrfOrient )
    {
        m_nYPos = pGrfOrient->GetPos();
        m_eOrient = pGrfOrient->GetVertOrient();
    }
    else
    {
        m_nYPos = 0;
        m_eOrient = text::VertOrientation::TOP;
    }
    Width( rGrfSize.Width() + 2 * GRFNUM_SECURE );
    m_nFixWidth = Width();
    m_nGrfHeight = rGrfSize.Height() + 2 * GRFNUM_SECURE;
    Height(m_nGrfHeight);
    m_bNoPaint = false;
}

void SwGrfNumPortion::ImplDestroy()
{
    if ( IsAnimated() )
    {
        Graphic* pGraph = const_cast<Graphic*>(m_pBrush->GetGraphic());
        if (pGraph)
            pGraph->StopAnimation( nullptr, m_nId );
    }
    m_pBrush.reset();
}

SwGrfNumPortion::~SwGrfNumPortion()
{
    suppress_fun_call_w_exception(ImplDestroy());
}

void SwGrfNumPortion::StopAnimation( const OutputDevice* pOut )
{
    if ( IsAnimated() )
    {
        Graphic* pGraph = const_cast<Graphic*>(m_pBrush->GetGraphic());
        if (pGraph)
            pGraph->StopAnimation( pOut, m_nId );
    }
}

bool SwGrfNumPortion::Format( SwTextFormatInfo &rInf )
{
    SetHide( false );
//    Width( nFixWidth );
    SwTwips nFollowedByWidth(0);
    if ( mbLabelAlignmentPosAndSpaceModeActive )
    {
        SwFieldPortion::Format( rInf );
        nFollowedByWidth = Width();
        SetLen(TextFrameIndex(0));
    }
    Width( m_nFixWidth + nFollowedByWidth );
    const bool bFull = rInf.Width() < rInf.X() + Width();
    const bool bFly = rInf.GetFly() ||
        ( rInf.GetLast() && rInf.GetLast()->IsFlyPortion() );
    SetAscent( GetRelPos() > 0 ? GetRelPos() : 0 );
    if( GetAscent() > Height() )
        Height( GetAscent() );

    if( bFull )
    {
        Width( rInf.Width() - rInf.X() );
        if( bFly )
        {
            SetLen(TextFrameIndex(0));
            m_bNoPaint = true;
            rInf.SetNumDone( false );
            return true;
        }
    }
    rInf.SetNumDone( true );
//    long nDiff = rInf.Left() - rInf.First() + rInf.ForcedLeftMargin();
    tools::Long nDiff = mbLabelAlignmentPosAndSpaceModeActive
                 ? 0
                 : rInf.Left() - rInf.First() + rInf.ForcedLeftMargin();
    // The TextPortion should at least always start on the
    // left margin
    if( nDiff < 0 )
        nDiff = 0;
    else if ( nDiff > rInf.X() )
        nDiff -= rInf.X();
    if( nDiff < m_nFixWidth + m_nMinDist )
        nDiff = m_nFixWidth + m_nMinDist;

    // Numbering evades Fly, no nDiff in the second round
    // Tricky special case: FlyFrame is in the Area we were just
    // about to get a hold of.
    // The NumberPortion is marked as hidden
    if( nDiff > rInf.Width() )
    {
        nDiff = rInf.Width();
        if( bFly )
            SetHide( true );
    }

    if( Width() < nDiff )
        Width( nDiff );
    return bFull;
}


/**
 * A hidden NumberPortion is not displayed, unless there are TextPortions in
 * this line or there's only one line at all
 */

void SwGrfNumPortion::Paint( const SwTextPaintInfo &rInf ) const
{
    if( m_bNoPaint )
        return;
    if ( IsHide() && rInf.GetParaPortion() && rInf.GetParaPortion()->GetNext() )
    {
        SwLinePortion *pTmp = GetNextPortion();
        while ( pTmp && !pTmp->InTextGrp() )
            pTmp = pTmp->GetNextPortion();
        if ( !pTmp )
            return;
    }
    Point aPos( rInf.X() + GRFNUM_SECURE, rInf.Y() - GetRelPos() + GRFNUM_SECURE );
    tools::Long nTmpWidth = std::max( tools::Long(0), static_cast<tools::Long>(m_nFixWidth - 2 * GRFNUM_SECURE) );
    Size aSize( nTmpWidth, GetGrfHeight() - 2 * GRFNUM_SECURE );

    const bool bTmpLeft = mbLabelAlignmentPosAndSpaceModeActive ||
                              ( IsLeft() && ! rInf.GetTextFrame()->IsRightToLeft() ) ||
                              ( ! IsLeft() && ! IsCenter() && rInf.GetTextFrame()->IsRightToLeft() );

    if( m_nFixWidth < Width() && !bTmpLeft )
    {
        SwTwips nOffset = Width() - m_nFixWidth;
        if( nOffset < m_nMinDist )
            nOffset = 0;
        else
        {
            if( IsCenter() )
            {
                nOffset /= 2;
                if( nOffset < m_nMinDist )
                    nOffset = Width() - m_nFixWidth - m_nMinDist;
            }
            else
                nOffset = nOffset - m_nMinDist;
        }
        aPos.AdjustX(nOffset );
    }

    if( m_bReplace )
    {
        const tools::Long nTmpH = GetNextPortion() ? GetNextPortion()->GetAscent() : 120;
        aSize = Size( nTmpH, nTmpH );
        aPos.setY( rInf.Y() - nTmpH );
    }
    SwRect aTmp( aPos, aSize );

    bool bDraw = true;

    if ( IsAnimated() )
    {
        bDraw = !rInf.GetOpt().IsGraphic();
        if( !m_nId )
        {
            SetId( reinterpret_cast<sal_IntPtr>( rInf.GetTextFrame() ) );
            rInf.GetTextFrame()->SetAnimation();
        }
        if( aTmp.Overlaps( rInf.GetPaintRect() ) && !bDraw )
        {
            rInf.NoteAnimation();
            const SwViewShell* pViewShell = rInf.GetVsh();

            // virtual device, not pdf export
            if( OUTDEV_VIRDEV == rInf.GetOut()->GetOutDevType() &&
                pViewShell && pViewShell->GetWin()  )
            {
                Graphic* pGraph = const_cast<Graphic*>(m_pBrush->GetGraphic());
                if (pGraph)
                    pGraph->StopAnimation(nullptr,m_nId);
                rInf.GetTextFrame()->getRootFrame()->GetCurrShell()->InvalidateWindows( aTmp );
            }

            else if ( pViewShell &&
                     !pViewShell->GetAccessibilityOptions()->IsStopAnimatedGraphics() &&
                     !pViewShell->IsPreview() &&
                      // #i9684# Stop animation during printing/pdf export.
                      pViewShell->GetWin() )
            {
                Graphic* pGraph = const_cast<Graphic*>(m_pBrush->GetGraphic());
                if (pGraph)
                {
                    const OutputDevice* pOut = rInf.GetOut();
                    assert(pOut);
                    pGraph->StartAnimation(
                        *const_cast<OutputDevice*>(pOut), aPos, aSize, m_nId);
                }
            }

            // pdf export, printing, preview, stop animations...
            else
                bDraw = true;
        }
        if( bDraw )
        {

            Graphic* pGraph = const_cast<Graphic*>(m_pBrush->GetGraphic());
            if (pGraph)
                pGraph->StopAnimation( nullptr, m_nId );
        }
    }

    SwRect aRepaint( rInf.GetPaintRect() );
    const SwTextFrame& rFrame = *rInf.GetTextFrame();
    if( rFrame.IsVertical() )
    {
        rFrame.SwitchHorizontalToVertical( aTmp );
        rFrame.SwitchHorizontalToVertical( aRepaint );
    }

    if( rFrame.IsRightToLeft() )
    {
        rFrame.SwitchLTRtoRTL( aTmp );
        rFrame.SwitchLTRtoRTL( aRepaint );
    }

    if( bDraw && aTmp.HasArea() )
    {
        const OutputDevice* pOut = rInf.GetOut();
        assert(pOut);
        DrawGraphic( m_pBrush.get(), *const_cast<OutputDevice*>(pOut),
            aTmp, aRepaint, m_bReplace ? GRFNUM_REPLACE : GRFNUM_YES );
    }
}

void SwGrfNumPortion::SetBase( tools::Long nLnAscent, tools::Long nLnDescent,
                               tools::Long nFlyAsc, tools::Long nFlyDesc )
{
    if ( GetOrient() == text::VertOrientation::NONE )
        return;

    SetRelPos( 0 );
    if ( GetOrient() == text::VertOrientation::CENTER )
        SetRelPos( GetGrfHeight() / 2 );
    else if ( GetOrient() == text::VertOrientation::TOP )
        SetRelPos( GetGrfHeight() - GRFNUM_SECURE );
    else if ( GetOrient() == text::VertOrientation::BOTTOM )
        ;
    else if ( GetOrient() == text::VertOrientation::CHAR_CENTER )
        SetRelPos( ( GetGrfHeight() + nLnAscent - nLnDescent ) / 2 );
    else if ( GetOrient() == text::VertOrientation::CHAR_TOP )
        SetRelPos( nLnAscent );
    else if ( GetOrient() == text::VertOrientation::CHAR_BOTTOM )
        SetRelPos( GetGrfHeight() - nLnDescent );
    else
    {
        if( GetGrfHeight() >= nFlyAsc + nFlyDesc )
        {
            // If I'm as large as the line, I do not need to adjust
            // at the line; I'll leave the max. ascent unchanged
            SetRelPos( nFlyAsc );
        }
        else if ( GetOrient() == text::VertOrientation::LINE_CENTER )
            SetRelPos( ( GetGrfHeight() + nFlyAsc - nFlyDesc ) / 2 );
        else if ( GetOrient() == text::VertOrientation::LINE_TOP )
            SetRelPos( nFlyAsc );
        else if ( GetOrient() == text::VertOrientation::LINE_BOTTOM )
            SetRelPos( GetGrfHeight() - nFlyDesc );
    }
}

void SwTextFrame::StopAnimation( const OutputDevice* pOut )
{
    OSL_ENSURE( HasAnimation(), "SwTextFrame::StopAnimation: Which Animation?" );
    if( !HasPara() )
        return;

    SwLineLayout *pLine = GetPara();
    while( pLine )
    {
        SwLinePortion *pPor = pLine->GetNextPortion();
        while( pPor )
        {
            if( pPor->IsGrfNumPortion() )
                static_cast<SwGrfNumPortion*>(pPor)->StopAnimation( pOut );
            // The NumberPortion is always at the first char,
            // which means we can cancel as soon as we've reached a portion
            // with a length > 0
            pPor = pPor->GetLen() ? nullptr : pPor->GetNextPortion();
        }
        pLine = pLine->GetLen() ? nullptr : pLine->GetNext();
    }
}

/**
 * Initializes the script array and clears the width array
 */

SwCombinedPortion::SwCombinedPortion( const OUString &rText )
    : SwFieldPortion( rText )
    , m_aWidth{ 0, 0, 0 }
    , m_nUpPos(0)
    , m_nLowPos(0)
    , m_nProportion(55)
{
    SetLen(TextFrameIndex(1));
    SetWhichPor( PortionType::Combined );
    if( m_aExpand.getLength() > 6 )
        m_aExpand = m_aExpand.copy( 0, 6 );

    // Initialization of the scripttype array,
    // the arrays of width and position are filled by the format function
    assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());

    SwFontScript nScr = SW_SCRIPTS;
    for( sal_Int32 i = 0; i < rText.getLength(); ++i )
    {
        switch ( g_pBreakIt->GetBreakIter()->getScriptType( rText, i ) ) {
            case i18n::ScriptType::LATIN : nScr = SwFontScript::Latin; break;
            case i18n::ScriptType::ASIAN : nScr = SwFontScript::CJK; break;
            case i18n::ScriptType::COMPLEX : nScr = SwFontScript::CTL; break;
        }
        m_aScrType[i] = nScr;
    }
}

void SwCombinedPortion::Paint( const SwTextPaintInfo &rInf ) const
{
    OSL_ENSURE(GetLen() <= TextFrameIndex(1), "SwFieldPortion::Paint: rest-portion pollution?");
    if( !Width() )
        return;

    rInf.DrawBackBrush( *this );
    rInf.DrawViewOpt( *this, PortionType::Field );

    // do we have to repaint a post it portion?
    if( rInf.OnWin() && mpNextPortion && !mpNextPortion->Width() )
        mpNextPortion->PrePaint( rInf, this );

    const sal_Int32 nCount = m_aExpand.getLength();
    if( !nCount )
        return;
    OSL_ENSURE( nCount < 7, "Too much combined characters" );

    // the first character of the second row
    const sal_Int32 nTop = ( nCount + 1 ) / 2;

    SwFont aTmpFont( *rInf.GetFont() );
    aTmpFont.SetProportion( m_nProportion );  // a smaller font
    SwFontSave aFontSave( rInf, &aTmpFont );

    Point aOldPos = rInf.GetPos();
    Point aOutPos( aOldPos.X(), aOldPos.Y() - m_nUpPos );// Y of the first row
    for( sal_Int32 i = 0 ; i < nCount; ++i )
    {
        if( i == nTop ) // change the row
            aOutPos.setY( aOldPos.Y() + m_nLowPos );    // Y of the second row
        aOutPos.setX( aOldPos.X() + m_aPos[i] );        // X position
        const SwFontScript nAct = m_aScrType[i];        // script type
        aTmpFont.SetActual( nAct );

        // if there're more than 4 characters to display, we choose fonts
        // with 2/3 of the original font width.
        if( m_aWidth[ nAct ] )
        {
            Size aTmpSz = aTmpFont.GetSize( nAct );
            if( aTmpSz.Width() != m_aWidth[ nAct ] )
            {
                aTmpSz.setWidth( m_aWidth[ nAct ] );
                aTmpFont.SetSize( aTmpSz, nAct );
            }
        }
        const_cast<SwTextPaintInfo&>(rInf).SetPos( aOutPos );
        rInf.DrawText(m_aExpand, *this, TextFrameIndex(i), TextFrameIndex(1));
    }
    // rInf is const, so we have to take back our manipulations
    const_cast<SwTextPaintInfo&>(rInf).SetPos( aOldPos );

}

bool SwCombinedPortion::Format( SwTextFormatInfo &rInf )
{
    const sal_Int32 nCount = m_aExpand.getLength();
    if( !nCount )
    {
        Width( 0 );
        return false;
    }

    OSL_ENSURE( nCount < 7, "Too much combined characters" );

    // If there are leading "weak"-scripttyped characters in this portion,
    // they get the actual scripttype.
    for( sal_Int32 i = 0; i < nCount && SW_SCRIPTS == m_aScrType[i]; ++i )
        m_aScrType[i] = rInf.GetFont()->GetActual();
    if( nCount > 4 )
    {
        // more than four? Ok, then we need the 2/3 font width
        for( sal_Int32 i = 0; i < m_aExpand.getLength(); ++i )
        {
            OSL_ENSURE( m_aScrType[i] < SW_SCRIPTS, "Combined: Script fault" );
            if( !m_aWidth[ m_aScrType[i] ] )
            {
                rInf.GetOut()->SetFont( rInf.GetFont()->GetFnt( m_aScrType[i] ) );
                m_aWidth[ m_aScrType[i] ] =
                        2 * rInf.GetOut()->GetFontMetric().GetFontSize().Width() / 3;
            }
        }
    }

    const sal_Int32 nTop = ( nCount + 1 ) / 2; // the first character of the second line
    SwViewShell *pSh = rInf.GetTextFrame()->getRootFrame()->GetCurrShell();
    SwFont aTmpFont( *rInf.GetFont() );
    SwFontSave aFontSave( rInf, &aTmpFont );
    m_nProportion = 55;
    // In nMainAscent/Descent we store the ascent and descent
    // of the original surrounding font
    SwTwips nMaxDescent, nMaxAscent, nMaxWidth;
    sal_uInt16 nMainDescent = rInf.GetFont()->GetHeight( pSh, *rInf.GetOut() );
    const sal_uInt16 nMainAscent = rInf.GetFont()->GetAscent( pSh, *rInf.GetOut() );
    nMainDescent = nMainDescent - nMainAscent;
    // we start with a 50% font, but if we notice that the combined portion
    // becomes bigger than the surrounding font, we check 45% and maybe 40%.
    do
    {
        m_nProportion -= 5;
        aTmpFont.SetProportion( m_nProportion );
        memset( &m_aPos, 0, sizeof(m_aPos) );
        nMaxDescent = 0;
        nMaxAscent = 0;
        nMaxWidth = 0;
        m_nUpPos = m_nLowPos = 0;

        // Now we get the width of all characters.
        // The ascent and the width of the first line are stored in the
        // ascent member of the portion, the descent in nLowPos.
        // The ascent, descent and width of the second line are stored in the
        // local nMaxAscent, nMaxDescent and nMaxWidth variables.
        for( sal_Int32 i = 0; i < nCount; ++i )
        {
            SwFontScript nScrp = m_aScrType[i];
            aTmpFont.SetActual( nScrp );
            if( m_aWidth[ nScrp ] )
            {
                Size aFontSize( aTmpFont.GetSize( nScrp ) );
                aFontSize.setWidth( m_aWidth[ nScrp ] );
                aTmpFont.SetSize( aFontSize, nScrp );
            }

            SwDrawTextInfo aDrawInf(pSh, *rInf.GetOut(), m_aExpand, i, 1);
            Size aSize = aTmpFont.GetTextSize_( aDrawInf );
            const sal_uInt16 nAsc = aTmpFont.GetAscent( pSh, *rInf.GetOut() );
            m_aPos[i] = aSize.Width();
            if( i == nTop ) // enter the second line
            {
                m_nLowPos = nMaxDescent;
                Height( nMaxDescent + nMaxAscent );
                Width( nMaxWidth );
                SetAscent( nMaxAscent );
                nMaxAscent = 0;
                nMaxDescent = 0;
                nMaxWidth = 0;
            }
            nMaxWidth = nMaxWidth + m_aPos[ i ];
            if( nAsc > nMaxAscent )
                nMaxAscent = nAsc;
            if( aSize.Height() - nAsc > nMaxDescent )
                nMaxDescent = aSize.Height() - nAsc;
        }
        // for one or two characters we double the width of the portion
        if( nCount < 3 )
        {
            nMaxWidth *= 2;
            Width( 2*Width() );
            if( nCount < 2 )
            {
                Height( nMaxAscent + nMaxDescent );
                m_nLowPos = nMaxDescent;
            }
        }
        Height( Height() + nMaxDescent + nMaxAscent );
        m_nUpPos = nMaxAscent;
        SetAscent( Height() - nMaxDescent - m_nLowPos );
    } while( m_nProportion > 40 && ( GetAscent() > nMainAscent ||
                                    Height() - GetAscent() > nMainDescent ) );
    // if the combined portion is smaller than the surrounding text,
    // the portion grows. This looks better, if there's a character background.
    if( GetAscent() < nMainAscent )
    {
        Height( Height() + nMainAscent - GetAscent() );
        SetAscent( nMainAscent );
    }
    if( Height() < nMainAscent + nMainDescent )
        Height( nMainAscent + nMainDescent );

    // We calculate the x positions of the characters in both lines...
    SwTwips nTopDiff = 0;
    SwTwips nBotDiff = 0;
    if( nMaxWidth > Width() )
    {
        nTopDiff = ( nMaxWidth - Width() ) / 2;
        Width( nMaxWidth );
    }
    else
        nBotDiff = ( Width() - nMaxWidth ) / 2;
    switch( nTop)
    {
        case 3: m_aPos[1] = m_aPos[0] + nTopDiff;
            [[fallthrough]];
        case 2: m_aPos[nTop-1] = Width() - m_aPos[nTop-1];
    }
    m_aPos[0] = 0;
    switch( nCount )
    {
        case 5: m_aPos[4] = m_aPos[3] + nBotDiff;
            [[fallthrough]];
        case 3: m_aPos[nTop] = nBotDiff;          break;
        case 6: m_aPos[4] = m_aPos[3] + nBotDiff;
            [[fallthrough]];
        case 4: m_aPos[nTop] = 0;
            [[fallthrough]];
        case 2: m_aPos[nCount-1] = Width() - m_aPos[nCount-1];
    }

    // Does the combined portion fit the line?
    const bool bFull = rInf.Width() < rInf.X() + Width();
    if( bFull )
    {
        if( rInf.GetLineStart() == rInf.GetIdx() && (!rInf.GetLast()->InFieldGrp()
            || !static_cast<SwFieldPortion*>(rInf.GetLast())->IsFollow() ) )
            Width( rInf.Width() - rInf.X() );
        else
        {
            Truncate();
            Width( 0 );
            SetLen(TextFrameIndex(0));
            if( rInf.GetLast() )
                rInf.GetLast()->FormatEOL( rInf );
        }
    }
    return bFull;
}

SwTwips SwCombinedPortion::GetViewWidth(const SwTextSizeInfo& rInf) const
{
    if( !GetLen() ) // for the dummy part at the end of the line, where
        return 0;   // the combined portion doesn't fit.
    return SwFieldPortion::GetViewWidth( rInf );
}

SwFieldPortion *SwFieldFormDropDownPortion::Clone(const OUString &rExpand) const
{
    return new SwFieldFormDropDownPortion(m_pFieldMark, rExpand);
}

void SwFieldFormDropDownPortion::Paint( const SwTextPaintInfo &rInf ) const
{
    SwFieldPortion::Paint( rInf );

    ::sw::mark::DropDownFieldmark* pDropDownField = dynamic_cast< ::sw::mark::DropDownFieldmark* >(m_pFieldMark);
    if(pDropDownField)
    {
        SwRect aPaintArea;
        rInf.CalcRect( *this, &aPaintArea );
        pDropDownField->SetPortionPaintArea(aPaintArea);
    }
}

SwFieldPortion *SwFieldFormDatePortion::Clone(const OUString &/*rExpand*/) const
{
    return new SwFieldFormDatePortion(m_pFieldMark, m_bStart);
}

void SwFieldFormDatePortion::Paint( const SwTextPaintInfo &rInf ) const
{
    SwFieldPortion::Paint( rInf );

    ::sw::mark::DateFieldmark* pDateField = dynamic_cast< ::sw::mark::DateFieldmark* >(m_pFieldMark);
    if(pDateField)
    {
        SwRect aPaintArea;
        rInf.CalcRect( *this, &aPaintArea );
        if(m_bStart)
            pDateField->SetPortionPaintAreaStart(aPaintArea);
        else
            pDateField->SetPortionPaintAreaEnd(aPaintArea);
    }
}

SwFieldPortion* SwJumpFieldPortion::Clone(const OUString& rExpand) const
{
    auto pRet = new SwJumpFieldPortion(*this);
    pRet->m_aExpand = rExpand;
    return pRet;
}

bool SwJumpFieldPortion::DescribePDFControl(const SwTextPaintInfo& rInf) const
{
    auto pPDFExtOutDevData
        = dynamic_cast<vcl::PDFExtOutDevData*>(rInf.GetOut()->GetExtOutDevData());
    if (!pPDFExtOutDevData)
        return false;

    if (!pPDFExtOutDevData->GetIsExportFormFields())
        return false;

    if (m_nFormat != SwJumpEditFormat::JE_FMT_TEXT)
        return false;

    vcl::PDFWriter::EditWidget aDescriptor;

    aDescriptor.Border = true;
    aDescriptor.BorderColor = COL_BLACK;

    SwRect aLocation;
    rInf.CalcRect(*this, &aLocation);
    aDescriptor.Location = aLocation.SVRect();

    // Map the text of the field to the descriptor's text.
    static sal_Unicode constexpr aForbidden[] = { CH_TXTATR_BREAKWORD, 0 };
    aDescriptor.Text = comphelper::string::removeAny(GetExp(), aForbidden);

    // Description for accessibility purposes.
    if (!m_sHelp.isEmpty())
        aDescriptor.Description = m_sHelp;

    pPDFExtOutDevData->WrapBeginStructureElement(vcl::pdf::StructElement::Form);
    pPDFExtOutDevData->CreateControl(aDescriptor);
    pPDFExtOutDevData->EndStructureElement();

    return true;
}

void SwJumpFieldPortion::Paint(const SwTextPaintInfo& rInf) const
{
    if (Width() && DescribePDFControl(rInf))
        return;

    if (rInf.GetOpt().IsShowPlaceHolderFields())
        SwFieldPortion::Paint(rInf);
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Messung V0.5
C=91 H=97 G=93

¤ Dauer der Verarbeitung: 0.23 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.