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 34 kB image not shown  

Quelle  txtdrop.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 <vcl/svapp.hxx>
#include <paratr.hxx>
#include <txtfrm.hxx>
#include <charfmt.hxx>
#include <viewopt.hxx>
#include <viewsh.hxx>
#include "pordrop.hxx"
#include "itrform2.hxx"
#include "txtpaint.hxx"
#include <breakit.hxx>
#include <com/sun/star/i18n/ScriptType.hpp>
#include <com/sun/star/i18n/WordType.hpp>
#include <com/sun/star/i18n/XBreakIterator.hpp>
#include <editeng/langitem.hxx>
#include <charatr.hxx>
#include <editeng/fhgtitem.hxx>
#include <calbck.hxx>
#include <doc.hxx>
#include <IDocumentSettingAccess.hxx>

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

/**
 * Calculates if a drop caps portion intersects with a fly
 * The width and height of the drop caps portion are passed as arguments,
 * the position is calculated from the values in rInf
 */

static bool lcl_IsDropFlyInter(const SwTextFormatInfo& rInf, SwTwips nWidth, sal_uInt16 nHeight)
{
    const SwTextFly& rTextFly = rInf.GetTextFly();
    if( rTextFly.IsOn() )
    {
        SwRect aRect( rInf.GetTextFrame()->getFrameArea().Pos(), Size( nWidth, nHeight) );
        aRect.Pos() += rInf.GetTextFrame()->getFramePrintArea().Pos();
        aRect.Pos().AdjustX(rInf.X() );
        aRect.Pos().setY( rInf.Y() );
        aRect = rTextFly.GetFrame( aRect );
        return aRect.HasArea();
    }

    return false;
}

namespace {

class SwDropSave
{
    SwTextPaintInfo* pInf;
    sal_Int32 nIdx;
    sal_Int32 nLen;
    tools::Long nX;
    tools::Long nY;

public:
    explicit SwDropSave( const SwTextPaintInfo &rInf );
    ~SwDropSave();
};

}

SwDropSave::SwDropSave( const SwTextPaintInfo &rInf ) :
        pInf( const_cast<SwTextPaintInfo*>(&rInf) ), nIdx( rInf.GetIdx() ),
        nLen( rInf.GetLen() ), nX( rInf.X() ), nY( rInf.Y() )
{
}

SwDropSave::~SwDropSave()
{
    pInf->SetIdx(TextFrameIndex(nIdx));
    pInf->SetLen(TextFrameIndex(nLen));
    pInf->X( nX );
    pInf->Y( nY );
}

/// SwDropPortionPart DTor
SwDropPortionPart::~SwDropPortionPart()
{
    m_pFollow.reset();
    m_pFnt.reset();
}

/// SwDropPortion CTor, DTor
SwDropPortion::SwDropPortion( const sal_uInt16 nLineCnt,
                              const SwTwips nDrpHeight,
                              const SwTwips nDrpDescent,
                              const SwTwips nDist )
  : m_nLines( nLineCnt ),
    m_nDropHeight(nDrpHeight),
    m_nDropDescent(nDrpDescent),
    m_nDistance(nDist),
    m_nFix(0),
    m_nY(0)
{
    SetWhichPor( PortionType::Drop );
}

SwDropPortion::~SwDropPortion()
{
    m_pPart.reset();
}

/// nWishLen = 0 indicates that we want a whole word
sal_Int32 SwTextNode::GetDropLen( sal_Int32 nWishLen ) const
{
    sal_Int32 nEnd = GetText().getLength();
    if( nWishLen && nWishLen < nEnd )
        nEnd = nWishLen;

    if (! nWishLen)
    {
        // find first word
        const SwAttrSet& rAttrSet = GetSwAttrSet();
        const sal_uInt16 nTextScript = g_pBreakIt->GetRealScriptOfText( GetText(), 0 );

        LanguageType eLanguage;

        switch ( nTextScript )
        {
        case i18n::ScriptType::ASIAN :
            eLanguage = rAttrSet.GetCJKLanguage().GetLanguage();
            break;
        case i18n::ScriptType::COMPLEX :
            eLanguage = rAttrSet.GetCTLLanguage().GetLanguage();
            break;
        default :
            eLanguage = rAttrSet.GetLanguage().GetLanguage();
            break;
        }

        Boundary aBound =
            g_pBreakIt->GetBreakIter()->getWordBoundary( GetText(), 0,
            g_pBreakIt->GetLocale( eLanguage ), WordType::DICTIONARY_WORD, true );

        nEnd = aBound.endPos;
    }

    sal_Int32 i = 0;
    for( ; i < nEnd; ++i )
    {
        sal_Unicode const cChar = GetText()[i];
        if( CH_TAB == cChar || CH_BREAK == cChar ||
            (( CH_TXTATR_BREAKWORD == cChar || CH_TXTATR_INWORD == cChar )
                && GetTextAttrForCharAt(i)) )
            break;
    }
    return i;
}

/// nWishLen = 0 indicates that we want a whole word
TextFrameIndex SwTextFrame::GetDropLen(TextFrameIndex const nWishLen) const
{
    TextFrameIndex nEnd(GetText().getLength());
    if (nWishLen && nWishLen < nEnd)
        nEnd = nWishLen;

    if (! nWishLen)
    {
        // find first word
        const SwAttrSet& rAttrSet = GetTextNodeForParaProps()->GetSwAttrSet();
        const sal_uInt16 nTextScript = g_pBreakIt->GetRealScriptOfText(GetText(), 0);

        LanguageType eLanguage;

        switch ( nTextScript )
        {
        case i18n::ScriptType::ASIAN :
            eLanguage = rAttrSet.GetCJKLanguage().GetLanguage();
            break;
        case i18n::ScriptType::COMPLEX :
            eLanguage = rAttrSet.GetCTLLanguage().GetLanguage();
            break;
        default :
            eLanguage = rAttrSet.GetLanguage().GetLanguage();
            break;
        }

        Boundary aBound = g_pBreakIt->GetBreakIter()->getWordBoundary(
            GetText(), 0, g_pBreakIt->GetLocale(eLanguage),
            WordType::DICTIONARY_WORD, true );

        nEnd = TextFrameIndex(aBound.endPos);
    }

    TextFrameIndex i(0);
    for ( ; i < nEnd; ++i)
    {
        sal_Unicode const cChar = GetText()[sal_Int32(i)];
        if (CH_TAB == cChar || CH_BREAK == cChar ||
            CH_TXTATR_BREAKWORD == cChar || CH_TXTATR_INWORD == cChar)
        {
#ifndef NDEBUG
            if (CH_TXTATR_BREAKWORD == cChar || CH_TXTATR_INWORD == cChar)
            {
                std::pair<SwTextNode const*, sal_Int32> const pos(MapViewToModel(i));
                assert(pos.first->GetTextAttrForCharAt(pos.second) != nullptr);
            }
#endif
            break;
        }
    }
    return i;
}

/**
 * If a dropcap is found the return value is true otherwise false. The
 * drop cap sizes passed back by reference are font height, drop height
 * and drop descent.
 */

bool SwTextNode::GetDropSize(int& rFontHeight, int& rDropHeight, int& rDropDescent) const
{
    rFontHeight = 0;
    rDropHeight = 0;
    rDropDescent =0;

    const SwAttrSet& rSet = GetSwAttrSet();
    const SwFormatDrop& rDrop = rSet.GetDrop();

    // Return (0,0) if there is no drop cap at this paragraph
    if( 1 >= rDrop.GetLines() ||
        ( !rDrop.GetChars() && !rDrop.GetWholeWord() ) )
    {
        return false;
    }

    // get text frame
    SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*this);
    for( SwTextFrame* pLastFrame = aIter.First(); pLastFrame; pLastFrame = aIter.Next() )
    {
        // Only (master-) text frames can have a drop cap.
        if (!pLastFrame->IsFollow() &&
            pLastFrame->GetTextNodeForFirstText() == this)
        {

            if( !pLastFrame->HasPara() )
                pLastFrame->GetFormatted();

            if ( !pLastFrame->IsEmpty() )
            {
                const SwParaPortion* pPara = pLastFrame->GetPara();
                OSL_ENSURE( pPara, "GetDropSize could not find the ParaPortion, I'll guess the drop cap size" );

                if ( pPara )
                {
                    const SwLinePortion* pFirstPor = pPara->GetFirstPortion();
                    if (pFirstPor && pFirstPor->IsDropPortion())
                    {
                        const SwDropPortion* pDrop = static_cast<const SwDropPortion*>(pFirstPor);
                        rDropHeight = pDrop->GetDropHeight();
                        rDropDescent = pDrop->GetDropDescent();
                        if (const SwFont *pFont = pDrop->GetFnt())
                            rFontHeight = pFont->GetSize(pFont->GetActual()).Height();
                        else
                        {
                            const SvxFontHeightItem& rItem = rSet.Get(RES_CHRATR_FONTSIZE);
                            rFontHeight = rItem.GetHeight();
                        }
                    }
                }
            }
            break;
        }
    }

    if (rFontHeight==0 && rDropHeight==0 && rDropDescent==0)
    {
        const sal_uInt16 nLines = rDrop.GetLines();

        const SvxFontHeightItem& rItem = rSet.Get( RES_CHRATR_FONTSIZE );
        rFontHeight = rItem.GetHeight();
        rDropHeight = nLines * rFontHeight;
        rDropDescent = rFontHeight / 5;
        return false;
    }

    return true;
}

/// Manipulate the width, otherwise the chars are being stretched
void SwDropPortion::PaintText( const SwTextPaintInfo &rInf ) const
{
    OSL_ENSURE( m_nDropHeight && m_pPart && m_nLines != 1, "Drop Portion painted twice" );

    const SwDropPortionPart* pCurrPart = GetPart();
    const TextFrameIndex nOldLen = GetLen();
    const SwTwips nOldWidth = Width();
    const SwTwips nOldAscent = GetAscent();

    const SwTwips nBasePosY  = rInf.Y();
    const_cast<SwTextPaintInfo&>(rInf).Y( nBasePosY + m_nY );
    const_cast<SwDropPortion*>(this)->SetAscent( nOldAscent + m_nY );
    SwDropSave aSave( rInf );
    // for text inside drop portions we let vcl handle the text directions
    SwLayoutModeModifier aLayoutModeModifier( *rInf.GetOut() );
    aLayoutModeModifier.SetAuto();

    while ( pCurrPart )
    {
        const_cast<SwDropPortion*>(this)->SetLen( pCurrPart->GetLen() );
        const_cast<SwDropPortion*>(this)->Width( pCurrPart->GetWidth() );
        const_cast<SwTextPaintInfo&>(rInf).SetLen( pCurrPart->GetLen() );
        SwFontSave aFontSave( rInf, &pCurrPart->GetFont() );
        const_cast<SwDropPortion*>(this)->SetJoinBorderWithNext(pCurrPart->GetJoinBorderWithNext());
        const_cast<SwDropPortion*>(this)->SetJoinBorderWithPrev(pCurrPart->GetJoinBorderWithPrev());

        if ( rInf.OnWin() &&
            !rInf.GetOpt().IsPagePreview() && !rInf.GetOpt().IsReadonly() && rInf.GetOpt().IsFieldShadings() &&
            (!pCurrPart->GetFont().GetBackColor() || *pCurrPart->GetFont().GetBackColor() == COL_TRANSPARENT) )
        {
            rInf.DrawBackground( *this );
        }

        SwTextPortion::Paint( rInf );

        const_cast<SwTextPaintInfo&>(rInf).SetIdx( rInf.GetIdx() + pCurrPart->GetLen() );
        const_cast<SwTextPaintInfo&>(rInf).X( rInf.X() + pCurrPart->GetWidth() );
        pCurrPart = pCurrPart->GetFollow();
    }

    const_cast<SwTextPaintInfo&>(rInf).Y( nBasePosY );
    const_cast<SwDropPortion*>(this)->Width( nOldWidth );
    const_cast<SwDropPortion*>(this)->SetLen( nOldLen );
    const_cast<SwDropPortion*>(this)->SetAscent( nOldAscent );
    const_cast<SwDropPortion*>(this)->SetJoinBorderWithNext(false);
    const_cast<SwDropPortion*>(this)->SetJoinBorderWithPrev(false);
}

void SwDropPortion::PaintDrop( const SwTextPaintInfo &rInf ) const
{
    // normal output is being done during the normal painting
    if( ! m_nDropHeight || ! m_pPart || m_nLines == 1 )
        return;

    // set the lying values
    const SwTwips nOldHeight = Height();
    const SwTwips nOldWidth = Width();
    const SwTwips nOldAscent = GetAscent();
    const SwTwips nOldPosY  = rInf.Y();
    const SwTwips nOldPosX  = rInf.X();
    const SwParaPortion *pPara = rInf.GetParaPortion();
    const Point aOutPos( nOldPosX, nOldPosY - pPara->GetAscent()
                         - pPara->GetRealHeight() + pPara->Height() );
    // make good for retouching

    // Set baseline
    const_cast<SwTextPaintInfo&>(rInf).Y( aOutPos.Y() + m_nDropHeight );

    // for background
    const_cast<SwDropPortion*>(this)->Height( m_nDropHeight + m_nDropDescent );
    const_cast<SwDropPortion*>(this)->SetAscent( m_nDropHeight );

    // Always adapt Clipregion to us, never set it off using the existing ClipRect
    // as that could be set for the line
    SwRect aClipRect;
    if ( rInf.OnWin() )
    {
        aClipRect = SwRect( aOutPos, SvLSize() );
        aClipRect.Intersection( rInf.GetPaintRect() );
    }
    SwSaveClip aClip( const_cast<OutputDevice*>(rInf.GetOut()) );
    aClip.ChgClip( aClipRect, rInf.GetTextFrame() );

    // Just do, what we always do ...
    PaintText( rInf );

    // save old values
    const_cast<SwDropPortion*>(this)->Height( nOldHeight );
    const_cast<SwDropPortion*>(this)->Width( nOldWidth );
    const_cast<SwDropPortion*>(this)->SetAscent( nOldAscent );
    const_cast<SwTextPaintInfo&>(rInf).Y( nOldPosY );
}

void SwDropPortion::Paint( const SwTextPaintInfo &rInf ) const
{
    // normal output is being done here
    if( !(! m_nDropHeight || ! m_pPart || 1 == m_nLines) )
        return;

    if ( rInf.OnWin() &&
        !rInf.GetOpt().IsPagePreview() && !rInf.GetOpt().IsReadonly() && rInf.GetOpt().IsFieldShadings()       )
        rInf.DrawBackground( *this );

    // make sure that font is not rotated
    std::unique_ptr<SwFont> pTmpFont;
    if ( rInf.GetFont()->GetOrientation( rInf.GetTextFrame()->IsVertical() ) )
    {
        pTmpFont.reset(new SwFont( *rInf.GetFont() ));
        pTmpFont->SetVertical( 0_deg10, rInf.GetTextFrame()->IsVertical() );
    }

    SwFontSave aFontSave( rInf, pTmpFont.get() );
    // for text inside drop portions we let vcl handle the text directions
    SwLayoutModeModifier aLayoutModeModifier( *rInf.GetOut() );
    aLayoutModeModifier.SetAuto();

    SwTextPortion::Paint( rInf );
}

bool SwDropPortion::FormatText( SwTextFormatInfo &rInf )
{
    const TextFrameIndex nOldLen = GetLen();
    const TextFrameIndex nOldInfLen = rInf.GetLen();
    if (!SwTextPortion::Format( rInf ))
        return false;

    // looks like shit, but what can we do?
    rInf.SetUnderflow( nullptr );
    Truncate();
    SetLen( nOldLen );
    rInf.SetLen( nOldInfLen );

    return true;
}

SwPositiveSize SwDropPortion::GetTextSize( const SwTextSizeInfo &rInf ) const
{
    SwTwips nMyX = 0;
    TextFrameIndex nIdx(0);

    const SwDropPortionPart* pCurrPart = GetPart();

    // skip parts
    while ( pCurrPart && nIdx + pCurrPart->GetLen() < rInf.GetLen() )
    {
        nMyX = nMyX + pCurrPart->GetWidth();
        nIdx = nIdx + pCurrPart->GetLen();
        pCurrPart = pCurrPart->GetFollow();
    }

    TextFrameIndex const nOldIdx = rInf.GetIdx();
    TextFrameIndex const nOldLen = rInf.GetLen();

    const_cast<SwTextSizeInfo&>(rInf).SetIdx( nIdx );
    const_cast<SwTextSizeInfo&>(rInf).SetLen( rInf.GetLen() - nIdx );

    if( pCurrPart )
    {
        const_cast<SwDropPortion*>(this)->SetJoinBorderWithNext(pCurrPart->GetJoinBorderWithNext());
        const_cast<SwDropPortion*>(this)->SetJoinBorderWithPrev(pCurrPart->GetJoinBorderWithPrev());
    }

    // robust
    SwFontSave aFontSave( rInf, pCurrPart ? &pCurrPart->GetFont() : nullptr );
    SwPositiveSize aPosSize( SwTextPortion::GetTextSize( rInf ) );
    aPosSize.Width( aPosSize.Width() + nMyX );

    const_cast<SwTextSizeInfo&>(rInf).SetIdx( nOldIdx );
    const_cast<SwTextSizeInfo&>(rInf).SetLen( nOldLen );
    if( pCurrPart )
    {
        const_cast<SwDropPortion*>(this)->SetJoinBorderWithNext(false);
        const_cast<SwDropPortion*>(this)->SetJoinBorderWithPrev(false);
    }

    return aPosSize;
}

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

void SwTextFormatter::CalcDropHeight( const sal_uInt16 nLines )
{
    const SwLinePortion *const pOldCurr = GetCurr();
    SwTwips nDropHght = 0;
    SwTwips nAscent = 0;
    SwTwips nHeight = 0;
    sal_uInt16 nDropLns = 0;
    const bool bRegisterOld = IsRegisterOn();
    m_bRegisterOn = false;

    Top();

    while( GetCurr()->IsDummy() )
    {
        if ( !Next() )
            break;
    }

    // If we have only one line we return 0
    if( GetNext() || GetDropLines() == 1 )
    {
        for( ; nDropLns < nLines; nDropLns++ )
        {
            if ( GetCurr()->IsDummy() )
                break;
            else
            {
                CalcAscentAndHeight( nAscent, nHeight );
                nDropHght = nDropHght + nHeight;
                m_bRegisterOn = bRegisterOld;
            }
            if ( !Next() )
            {
                nDropLns++;
                break;
            }
        }

        // We hit the line ascent when reaching the last line!
        nDropHght = nDropHght - nHeight;
        nDropHght = nDropHght + nAscent;
        Top();
    }
    m_bRegisterOn = bRegisterOld;
    SetDropDescent( nHeight - nAscent );
    SetDropHeight( nDropHght );
    SetDropLines( nDropLns );
    // Find old position!
    while( pOldCurr != GetCurr() )
    {
        if( !Next() )
        {
            OSL_ENSURE( false"SwTextFormatter::_CalcDropHeight: left Toulouse" );
            break;
        }
    }
}

/**
 * We assume that the font height doesn't change and that at first there
 * are at least as many lines, as the DropCap-setting claims
 */

void SwTextFormatter::GuessDropHeight( const sal_uInt16 nLines )
{
    OSL_ENSURE( nLines, "GuessDropHeight: Give me more Lines!" );
    SwTwips nAscent = 0;
    SwTwips nHeight = 0;
    SetDropLines( nLines );
    if ( GetDropLines() > 1 )
    {
        CalcRealHeight();
        CalcAscentAndHeight( nAscent, nHeight );
    }
    SetDropDescent( nHeight - nAscent );
    SetDropHeight( nHeight * nLines - GetDropDescent() );
}

SwDropPortion *SwTextFormatter::NewDropPortion( SwTextFormatInfo &rInf )
{
    if( !m_pDropFormat )
        return nullptr;

    TextFrameIndex nPorLen(m_pDropFormat->GetWholeWord() ? 0 : m_pDropFormat->GetChars());
    nPorLen = m_pFrame->GetDropLen( nPorLen );
    if( !nPorLen )
    {
        ClearDropFormat();
        return nullptr;
    }

    SwDropPortion *pDropPor = nullptr;

    // first or second round?
    if ( !( GetDropHeight() || IsOnceMore() ) )
    {
        if ( GetNext() )
            CalcDropHeight( m_pDropFormat->GetLines() );
        else
            GuessDropHeight( m_pDropFormat->GetLines() );
    }

    // the DropPortion
    if( GetDropHeight() )
        pDropPor = new SwDropPortion( GetDropLines(), GetDropHeight(),
                                      GetDropDescent(), m_pDropFormat->GetDistance() );
    else
       pDropPor = new SwDropPortion( 0,0,0,m_pDropFormat->GetDistance() );

    pDropPor->SetLen( nPorLen );

    // If it was not possible to create a proper drop cap portion
    // due to avoiding endless loops. We return a drop cap portion
    // with an empty SwDropCapPart. For these portions the current
    // font is used.
    if ( GetDropLines() < 2 )
    {
        SetPaintDrop( true );
        return pDropPor;
    }

    // build DropPortionParts:
    OSL_ENSURE( ! rInf.GetIdx(), "Drop Portion not at 0 position!" );
    TextFrameIndex nNextChg(0);
    const SwCharFormat* pFormat = m_pDropFormat->GetCharFormat();
    SwDropPortionPart* pCurrPart = nullptr;

    while ( nNextChg  < nPorLen )
    {
        // check for attribute changes and if the portion has to split:
        Seek( nNextChg );

        // the font is deleted in the destructor of the drop portion part
        SwFont* pTmpFnt = new SwFont( *rInf.GetFont() );
        if ( pFormat )
        {
            const SwAttrSet& rSet = pFormat->GetAttrSet();
            pTmpFnt->SetDiffFnt(&rSet, &m_pFrame->GetDoc().getIDocumentSettingAccess());
        }

        // we do not allow a vertical font for the drop portion
        pTmpFnt->SetVertical( 0_deg10, rInf.GetTextFrame()->IsVertical() );

        // find next attribute change / script change
        const TextFrameIndex nTmpIdx = nNextChg;
        TextFrameIndex nNextAttr = GetNextAttr();
        nNextChg = m_pScriptInfo->NextScriptChg( nTmpIdx );
        if( nNextChg > nNextAttr )
            nNextChg = nNextAttr;
        if ( nNextChg > nPorLen )
            nNextChg = nPorLen;

        std::unique_ptr<SwDropPortionPart> pPart(
                new SwDropPortionPart( *pTmpFnt, nNextChg - nTmpIdx ) );
        auto pPartTemp = pPart.get();

        if ( ! pCurrPart )
            pDropPor->SetPart( std::move(pPart) );
        else
            pCurrPart->SetFollow( std::move(pPart) );

        pCurrPart = pPartTemp;
    }

    SetPaintDrop( true );
    return pDropPor;
}

void SwTextPainter::PaintDropPortion()
{
    const SwDropPortion *pDrop = GetInfo().GetParaPortion()->FindDropPortion();
    OSL_ENSURE( pDrop, "DrapCop-Portion not available." );
    if( !pDrop )
        return;

    const SwTwips nOldY = GetInfo().Y();

    Top();

    GetInfo().SetpSpaceAdd( m_pCurr->GetpLLSpaceAdd() );
    GetInfo().ResetSpaceIdx();
    GetInfo().SetKanaComp( m_pCurr->GetpKanaComp() );
    GetInfo().ResetKanaIdx();

    // 8047: Drops and Dummies
    while( !m_pCurr->GetLen() && Next() )
        ;

    // MarginPortion and Adjustment!
    const SwLinePortion *pPor = m_pCurr->GetFirstPortion();
    tools::Long nX = 0;
    while( pPor && !pPor->IsDropPortion() )
    {
        nX = nX + pPor->Width();
        pPor = pPor->GetNextPortion();
    }
    Point aLineOrigin( GetTopLeft() );

    aLineOrigin.AdjustX(nX );
    SwTwips nTmpAscent, nTmpHeight;
    CalcAscentAndHeight( nTmpAscent, nTmpHeight );
    aLineOrigin.AdjustY(nTmpAscent );
    GetInfo().SetIdx( GetStart() );
    GetInfo().SetPos( aLineOrigin );
    GetInfo().SetLen( pDrop->GetLen() );

    pDrop->PaintDrop( GetInfo() );

    GetInfo().Y( nOldY );
}

// Since the calculation of the font size is expensive, this is being
// channeled through a DropCapCache
#define DROP_CACHE_SIZE 10

class SwDropCapCache
{
    const void* m_aFontCacheId[ DROP_CACHE_SIZE ] = {};
    OUString m_aText[ DROP_CACHE_SIZE ];
    tools::Long m_aFactor[ DROP_CACHE_SIZE ];
    SwTwips m_aWishedHeight[DROP_CACHE_SIZE] = {};
    SwTwips m_aDescent[DROP_CACHE_SIZE];
    sal_uInt16 m_nIndex = 0;
public:
    SwDropCapCache() = default;
    void CalcFontSize( SwDropPortion* pDrop, SwTextFormatInfo &rInf );
};

void SwDropPortion::DeleteDropCapCache()
{
    delete pDropCapCache;
}

void SwDropCapCache::CalcFontSize( SwDropPortion* pDrop, SwTextFormatInfo &rInf )
{
    const void* nFntCacheId = nullptr;
    sal_uInt16 nTmpIdx = 0;

    OSL_ENSURE( pDrop->GetPart(),"DropPortion without part during font calculation");

    SwDropPortionPart* pCurrPart = pDrop->GetPart();
    const bool bUseCache = ! pCurrPart->GetFollow() && !pCurrPart->GetFont().HasBorder();
    TextFrameIndex nIdx = rInf.GetIdx();
    OUString aStr(rInf.GetText().copy(sal_Int32(nIdx), sal_Int32(pCurrPart->GetLen())));

    tools::Long nDescent = 0;
    tools::Long nFactor = -1;

    if ( bUseCache )
    {
        SwFont& rFnt = pCurrPart->GetFont();
        rFnt.CheckFontCacheId( rInf.GetVsh(), rFnt.GetActual() );
        rFnt.GetFontCacheId( nFntCacheId, nTmpIdx, rFnt.GetActual() );

        nTmpIdx = 0;

        while( nTmpIdx < DROP_CACHE_SIZE &&
            ( m_aText[ nTmpIdx ] != aStr || m_aFontCacheId[ nTmpIdx ] != nFntCacheId ||
            m_aWishedHeight[ nTmpIdx ] != pDrop->GetDropHeight() ) )
            ++nTmpIdx;
    }

    // we have to calculate a new font scaling factor if
    // 1. we did not find a scaling factor in the cache or
    // 2. we are not allowed to use the cache because the drop portion
    //    consists of more than one part
    if( nTmpIdx >= DROP_CACHE_SIZE || ! bUseCache )
    {
        ++m_nIndex;
        m_nIndex %= DROP_CACHE_SIZE;
        nTmpIdx = m_nIndex;

        SwTwips nWishedHeight = pDrop->GetDropHeight();
        tools::Long nAscent = 0;

        // find out biggest font size for initial scaling factor
        tools::Long nMaxFontHeight = 1;
        while ( pCurrPart )
        {
            const SwFont& rFnt = pCurrPart->GetFont();
            const tools::Long nCurrHeight = rFnt.GetHeight( rFnt.GetActual() );
            if ( nCurrHeight > nMaxFontHeight )
                nMaxFontHeight = nCurrHeight;

            pCurrPart = pCurrPart->GetFollow();
        }

        nFactor = ( 1000 * nWishedHeight ) / nMaxFontHeight;

        if ( bUseCache )
        {
            // save keys for cache
            m_aFontCacheId[ nTmpIdx ] = nFntCacheId;
            m_aText[ nTmpIdx ] = aStr;
            m_aWishedHeight[ nTmpIdx ] = nWishedHeight;
            // save initial scaling factor
            m_aFactor[ nTmpIdx ] = nFactor;
        }

        bool bGrow = (pDrop->GetLen() != TextFrameIndex(0));

        // for growing control
        tools::Long nMax = std::numeric_limits<tools::Long>::max();
        tools::Long nMin = 0;
#if OSL_DEBUG_LEVEL > 1
        tools::Long nGrow = 0;
#endif

        bool bWinUsed = false;
        vcl::Font aOldFnt;
        MapMode aOldMap( MapUnit::MapTwip );
        OutputDevice* pOut = rInf.GetOut();
        OutputDevice* pWin;
        if( rInf.GetVsh() && rInf.GetVsh()->GetWin() )
            pWin = rInf.GetVsh()->GetWin()->GetOutDev();
        else
            pWin = Application::GetDefaultDevice();

        // adjust punctuation?
        bool bKeepBaseline = rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess()
            .get(DocumentSettingId::DROP_CAP_PUNCTUATION) &&
            !rInf.GetDropFormat()->GetWholeWord(); // && rInf.GetDropFormat()->GetChars() == 1;

        while( bGrow )
        {
            // reset pCurrPart to first part
            pCurrPart = pDrop->GetPart();
            bool bFirstGlyphRect = true;
            tools::Rectangle aCommonRect, aRect;

            while ( pCurrPart )
            {
                // current font
                SwFont& rFnt = pCurrPart->GetFont();

                // Get height including proportion
                const tools::Long nCurrHeight = rFnt.GetHeight( rFnt.GetActual() );

                // Get without proportion
                const sal_uInt8 nOldProp = rFnt.GetPropr();
                rFnt.SetProportion( 100 );
                Size aOldSize( 0, rFnt.GetHeight( rFnt.GetActual() ) );

                Size aNewSize( 0, ( nFactor * nCurrHeight ) / 1000 );
                rFnt.SetSize( aNewSize, rFnt.GetActual() );
                rFnt.ChgPhysFnt( rInf.GetVsh(), *pOut );

                nAscent = rFnt.GetAscent( rInf.GetVsh(), *pOut );

                // we get the rectangle that covers all chars
                bool bHaveGlyphRect = pOut->GetTextBoundRect( aRect, rInf.GetText(), 0,
                            sal_Int32(nIdx), sal_Int32(pCurrPart->GetLen()))
                    && ! aRect.IsEmpty();

                if ( ! bHaveGlyphRect )
                {
                    // getting glyph boundaries failed for some reason,
                    // we take the window for calculating sizes
                    if ( pWin )
                    {
                        if ( ! bWinUsed )
                        {
                            bWinUsed = true;
                            aOldMap = pWin->GetMapMode( );
                            pWin->SetMapMode( MapMode( MapUnit::MapTwip ) );
                            aOldFnt = pWin->GetFont();
                        }
                        pWin->SetFont( rFnt.GetActualFont() );

                        bHaveGlyphRect = pWin->GetTextBoundRect( aRect, rInf.GetText(), 0,
                                sal_Int32(nIdx), sal_Int32(pCurrPart->GetLen()))
                            && ! aRect.IsEmpty();
                    }
                    if (!bHaveGlyphRect)
                    {
                        // We do not have a window or our window could not
                        // give us glyph boundaries.
                        aRect = tools::Rectangle( Point( 0, 0 ), Size( 0, nAscent ) );
                    }
                }

                // extend rectangle to the baseline to avoid of giant dashes,
                // quotation marks, bullet, asterisks etc.
                if ( bKeepBaseline && aRect.Top() < 0 )
                {
                    aRect.SetBottom(0);
                    aRect.SetTop(aRect.Top() - nAscent/60);
                }

                // Now we (hopefully) have a bounding rectangle for the
                // glyphs of the current portion and the ascent of the current
                // font

                // reset font size and proportion
                rFnt.SetSize( aOldSize, rFnt.GetActual() );
                rFnt.SetProportion( nOldProp );

                // Modify the bounding rectangle with the borders
                // Robust: If the padding is so big as drop cap letter has no enough space than
                // remove all padding.
                if( rFnt.GetTopBorderSpace() + rFnt.GetBottomBorderSpace() >= nWishedHeight )
                {
                    rFnt.SetTopBorderDist(0);
                    rFnt.SetBottomBorderDist(0);
                    rFnt.SetRightBorderDist(0);
                    rFnt.SetLeftBorderDist(0);
                }

                if( rFnt.GetTopBorder() )
                {
                    aRect.setHeight(aRect.GetHeight() + rFnt.GetTopBorderSpace());
                    aRect.SetPosY(aRect.Top() - rFnt.GetTopBorderSpace());
                }

                if( rFnt.GetBottomBorder() )
                {
                    aRect.setHeight(aRect.GetHeight() + rFnt.GetBottomBorderSpace());
                }

                if ( bFirstGlyphRect )
                {
                    aCommonRect = aRect;
                    bFirstGlyphRect = false;
                }
                else
                    aCommonRect.Union( aRect );

                nIdx = nIdx + pCurrPart->GetLen();
                pCurrPart = pCurrPart->GetFollow();
            }

            // now we have a union ( aCommonRect ) of all glyphs with
            // respect to a common baseline : 0

            // get descent and ascent from union
            if ( rInf.GetTextFrame()->IsVertical() )
            {
                nDescent = aCommonRect.Left();
                nAscent = aCommonRect.Right();

                if ( nDescent < 0 )
                    nDescent = -nDescent;
            }
            else
            {
                nDescent = aCommonRect.Bottom();
                nAscent = aCommonRect.Top();
            }
            if ( nAscent < 0 )
                nAscent = -nAscent;

            const tools::Long nHght = nAscent + nDescent;
            if ( nHght )
            {
                if ( nHght > nWishedHeight )
                    nMax = nFactor;
                else
                {
                    if ( bUseCache )
                        m_aFactor[ nTmpIdx ] = nFactor;
                    nMin = nFactor;
                }

                nFactor = ( nFactor * nWishedHeight ) / nHght;
                bGrow = ( nFactor > nMin ) && ( nFactor < nMax );
#if OSL_DEBUG_LEVEL > 1
                if ( bGrow )
                    nGrow++;
#endif
                nIdx = rInf.GetIdx();
            }
            else
                bGrow = false;
        }

        if ( bWinUsed )
        {
            // reset window if it has been used
            pWin->SetMapMode( aOldMap );
            pWin->SetFont( aOldFnt );
        }

        if ( bUseCache )
            m_aDescent[ nTmpIdx ] = -short( nDescent );
    }

    pCurrPart = pDrop->GetPart();

    // did made any new calculations or did we use the cache?
    if ( -1 == nFactor )
    {
        nFactor = m_aFactor[ nTmpIdx ];
        nDescent = m_aDescent[ nTmpIdx ];
    }
    else
        nDescent = -nDescent;

    while ( pCurrPart )
    {
        // scale current font
        SwFont& rFnt = pCurrPart->GetFont();
        Size aNewSize( 0, ( nFactor * rFnt.GetHeight( rFnt.GetActual() ) ) / 1000 );

        const sal_uInt8 nOldProp = rFnt.GetPropr();
        rFnt.SetProportion( 100 );
        rFnt.SetSize( aNewSize, rFnt.GetActual() );
        rFnt.SetProportion( nOldProp );

        pCurrPart = pCurrPart->GetFollow();
    }
    pDrop->SetY( static_cast<short>(nDescent) );
}

bool SwDropPortion::Format( SwTextFormatInfo &rInf )
{
    bool bFull = false;
    m_nFix = rInf.X();

    SwLayoutModeModifier aLayoutModeModifier( *rInf.GetOut() );
    aLayoutModeModifier.SetAuto();

    if( m_nDropHeight && m_pPart && m_nLines!=1 )
    {
        if( !pDropCapCache )
            pDropCapCache = new SwDropCapCache;

        // adjust font sizes to fit into the rectangle
        pDropCapCache->CalcFontSize( this, rInf );

        const SwTwips nOldX = rInf.X();
        {
            SwDropSave aSave( rInf );
            SwDropPortionPart* pCurrPart = m_pPart.get();

            while ( pCurrPart )
            {
                rInf.SetLen( pCurrPart->GetLen() );
                SwFont& rFnt = pCurrPart->GetFont();
                {
                    SwFontSave aFontSave( rInf, &rFnt );
                    SetJoinBorderWithNext(pCurrPart->GetJoinBorderWithNext());
                    SetJoinBorderWithPrev(pCurrPart->GetJoinBorderWithPrev());
                    bFull = FormatText( rInf );

                    if ( bFull )
                        break;
                }

                const SwTwips nTmpWidth =
                        ( InSpaceGrp() && rInf.GetSpaceAdd() ) ?
                        Width() + CalcSpacing( rInf.GetSpaceAdd(), rInf ) :
                        Width();

                // set values
                pCurrPart->SetWidth(nTmpWidth);

                // Move
                rInf.SetIdx( rInf.GetIdx() + pCurrPart->GetLen() );
                rInf.X( rInf.X() + nTmpWidth );
                pCurrPart = pCurrPart->GetFollow();
            }
            SetJoinBorderWithNext(false);
            SetJoinBorderWithPrev(false);
            Width(rInf.X() - nOldX);
        }

        // reset my length
        SetLen( rInf.GetLen() );

        // Quit when Flys are overlapping
        if( ! bFull )
            bFull = lcl_IsDropFlyInter( rInf, Width(), m_nDropHeight );

        if( bFull )
        {
            // FormatText could have caused nHeight to be 0
            if ( !Height() )
                Height( rInf.GetTextHeight() );

            // And now for another round
            m_nDropHeight = m_nLines = 0;
            m_pPart.reset();

            // Meanwhile use normal formatting
            bFull = SwTextPortion::Format( rInf );
        }
        else
            rInf.SetDropInit( true );

        Height( rInf.GetTextHeight() );
        SetAscent( rInf.GetAscent() );
    }
    else
        bFull = SwTextPortion::Format( rInf );

    if( bFull )
        m_nDistance = 0;
    else
    {
        const SwTwips nWant = Width() + GetDistance();
        const SwTwips nRest = rInf.Width() - rInf.X();
        if( ( nWant > nRest ) ||
            lcl_IsDropFlyInter( rInf, nWant, m_nDropHeight ) )
            m_nDistance = 0;

        Width( Width() + m_nDistance );
    }
    return bFull;
}

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

Messung V0.5
C=96 H=95 G=95

¤ Dauer der Verarbeitung: 0.16 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.