blob: 86cc9997bd896f43a7676dabd38875c12dfbb00b [file] [log] [blame]
/**************************************************************
*
* 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
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sw.hxx"
#include <hintids.hxx>
#include <vcl/metric.hxx>
#include <vcl/window.hxx>
#include <vcl/svapp.hxx>
#include <paratr.hxx>
#include <txtfrm.hxx> // Format()
#include <charfmt.hxx>
#include <viewopt.hxx> // SwViewOption
#include <viewsh.hxx> // ViewShell
#include <pordrop.hxx>
#include <itrform2.hxx>
#include <txtpaint.hxx> // SwSaveClip
#include <blink.hxx> // pBlink
#include <breakit.hxx>
#include <com/sun/star/i18n/ScriptType.hdl>
#include <com/sun/star/i18n/WordType.hpp>
#include <editeng/langitem.hxx>
#include <charatr.hxx>
#include <editeng/fhgtitem.hxx>
#include <switerator.hxx>
using namespace ::com::sun::star::i18n;
using namespace ::com::sun::star;
/*************************************************************************
* lcl_IsDropFlyInter
*
* 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
*************************************************************************/
sal_Bool lcl_IsDropFlyInter( const SwTxtFormatInfo &rInf,
sal_uInt16 nWidth, sal_uInt16 nHeight )
{
const SwTxtFly *pTxtFly = rInf.GetTxtFly();
if( pTxtFly && pTxtFly->IsOn() )
{
SwRect aRect( rInf.GetTxtFrm()->Frm().Pos(), Size( nWidth, nHeight) );
aRect.Pos() += rInf.GetTxtFrm()->Prt().Pos();
aRect.Pos().X() += rInf.X();
aRect.Pos().Y() = rInf.Y();
aRect = pTxtFly->GetFrm( aRect );
return aRect.HasArea();
}
return sal_False;
}
/*************************************************************************
* class SwDropSave
*************************************************************************/
class SwDropSave
{
SwTxtPaintInfo* pInf;
xub_StrLen nIdx;
xub_StrLen nLen;
long nX;
long nY;
public:
SwDropSave( const SwTxtPaintInfo &rInf );
~SwDropSave();
};
SwDropSave::SwDropSave( const SwTxtPaintInfo &rInf ) :
pInf( ((SwTxtPaintInfo*)&rInf) ), nIdx( rInf.GetIdx() ),
nLen( rInf.GetLen() ), nX( rInf.X() ), nY( rInf.Y() )
{
}
SwDropSave::~SwDropSave()
{
pInf->SetIdx( nIdx );
pInf->SetLen( nLen );
pInf->X( nX );
pInf->Y( nY );
}
/*************************************************************************
* SwDropPortionPart DTor
*************************************************************************/
SwDropPortionPart::~SwDropPortionPart()
{
if ( pFollow )
delete pFollow;
delete pFnt;
}
/*************************************************************************
* SwDropPortion CTor, DTor
*************************************************************************/
SwDropPortion::SwDropPortion( const MSHORT nLineCnt,
const KSHORT nDrpHeight,
const KSHORT nDrpDescent,
const KSHORT nDist )
: pPart( 0 ),
nLines( nLineCnt ),
nDropHeight(nDrpHeight),
nDropDescent(nDrpDescent),
nDistance(nDist),
nFix(0),
nX(0)
{
SetWhichPor( POR_DROP );
}
SwDropPortion::~SwDropPortion()
{
delete pPart;
if( pBlink )
pBlink->Delete( this );
}
sal_Bool SwTxtSizeInfo::_HasHint( const SwTxtNode* pTxtNode, xub_StrLen nPos )
{
return 0 != pTxtNode->GetTxtAttrForCharAt(nPos);
}
/*************************************************************************
* SwTxtNode::GetDropLen()
*
* nWishLen = 0 indicates that we want a whole word
*************************************************************************/
MSHORT SwTxtNode::GetDropLen( MSHORT nWishLen ) const
{
xub_StrLen nEnd = GetTxt().Len();
if( nWishLen && nWishLen < nEnd )
nEnd = nWishLen;
if ( ! nWishLen && pBreakIt->GetBreakIter().is() )
{
// find first word
const SwAttrSet& rAttrSet = GetSwAttrSet();
const sal_uInt16 nTxtScript = pBreakIt->GetRealScriptOfText( GetTxt(), 0 );
LanguageType eLanguage;
switch ( nTxtScript )
{
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 =
pBreakIt->GetBreakIter()->getWordBoundary( GetTxt(), 0,
pBreakIt->GetLocale( eLanguage ), WordType::DICTIONARY_WORD, sal_True );
nEnd = (xub_StrLen)aBound.endPos;
}
xub_StrLen i = 0;
for( ; i < nEnd; ++i )
{
xub_Unicode cChar = GetTxt().GetChar( i );
if( CH_TAB == cChar || CH_BREAK == cChar ||
(( CH_TXTATR_BREAKWORD == cChar || CH_TXTATR_INWORD == cChar )
&& SwTxtSizeInfo::_HasHint( this, i ) ) )
break;
}
return i;
}
/*************************************************************************
* SwTxtNode::GetDropSize()
*
* 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 SwTxtNode::GetDropSize(int& rFontHeight, int& rDropHeight, int& rDropDescent) const
{
rFontHeight = 0;
rDropHeight = 0;
rDropDescent =0;
const SwAttrSet& rSet = GetSwAttrSet();
const SwFmtDrop& 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<SwTxtFrm,SwTxtNode> aIter( *this );
for( SwTxtFrm* pLastFrm = aIter.First(); pLastFrm; pLastFrm = aIter.Next() )
{
// Only (master-) text frames can have a drop cap.
if ( !pLastFrm->IsFollow() )
{
if( !pLastFrm->HasPara() )
pLastFrm->GetFormatted();
if ( !pLastFrm->IsEmpty() )
{
const SwParaPortion* pPara = pLastFrm->GetPara();
ASSERT( 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 = (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 = (SvxFontHeightItem&)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 = (SvxFontHeightItem&)rSet.Get( RES_CHRATR_FONTSIZE );
rFontHeight = rItem.GetHeight();
rDropHeight = nLines * rFontHeight;
rDropDescent = rFontHeight / 5;
return false;
}
return true;
}
/*************************************************************************
* SwDropPortion::PaintTxt()
*************************************************************************/
// Die Breite manipulieren, sonst werden die Buchstaben gestretcht
void SwDropPortion::PaintTxt( const SwTxtPaintInfo &rInf ) const
{
if ( rInf.OnWin() &&
!rInf.GetOpt().IsPagePreview() && !rInf.GetOpt().IsReadonly() && SwViewOption::IsFieldShadings() )
rInf.DrawBackground( *this );
ASSERT( nDropHeight && pPart && nLines != 1, "Drop Portion painted twice" );
const SwDropPortionPart* pCurrPart = GetPart();
const xub_StrLen nOldLen = GetLen();
const SwTwips nBasePosY = rInf.Y();
((SwTxtPaintInfo&)rInf).Y( nBasePosY + nY );
SwDropSave aSave( rInf );
// for text inside drop portions we let vcl handle the text directions
SwLayoutModeModifier aLayoutModeModifier( *rInf.GetOut() );
aLayoutModeModifier.SetAuto();
while ( pCurrPart )
{
((SwDropPortion*)this)->SetLen( pCurrPart->GetLen() );
((SwTxtPaintInfo&)rInf).SetLen( pCurrPart->GetLen() );
SwFontSave aFontSave( rInf, &pCurrPart->GetFont() );
SwTxtPortion::Paint( rInf );
((SwTxtPaintInfo&)rInf).SetIdx( rInf.GetIdx() + pCurrPart->GetLen() );
((SwTxtPaintInfo&)rInf).X( rInf.X() + pCurrPart->GetWidth() );
pCurrPart = pCurrPart->GetFollow();
}
((SwTxtPaintInfo&)rInf).Y( nBasePosY );
((SwDropPortion*)this)->SetLen( nOldLen );
}
/*************************************************************************
* SwDropPortion::Paint()
*************************************************************************/
void SwDropPortion::PaintDrop( const SwTxtPaintInfo &rInf ) const
{
// ganz normale Ausgabe wird w?hrend des normalen Paints erledigt
if( ! nDropHeight || ! pPart || nLines == 1 )
return;
// Luegenwerte einstellen!
const KSHORT nOldHeight = Height();
const KSHORT nOldWidth = Width();
const KSHORT nOldAscent = GetAscent();
const SwTwips nOldPosY = rInf.Y();
const KSHORT nOldPosX = (KSHORT)rInf.X();
const SwParaPortion *pPara = rInf.GetParaPortion();
const Point aOutPos( nOldPosX + nX, nOldPosY - pPara->GetAscent()
- pPara->GetRealHeight() + pPara->Height() );
// Retusche nachholen.
// Set baseline
((SwTxtPaintInfo&)rInf).Y( aOutPos.Y() + nDropHeight );
// for background
((SwDropPortion*)this)->Height( nDropHeight + nDropDescent );
((SwDropPortion*)this)->Width( Width() - nX );
((SwDropPortion*)this)->SetAscent( nDropHeight );
// Clipregion auf uns einstellen!
// Und zwar immer, und nie mit dem bestehenden ClipRect
// verrechnen, weil dies auf die Zeile eingestellt sein koennte.
SwRect aClipRect;
if ( rInf.OnWin() )
{
aClipRect = SwRect( aOutPos, SvLSize() );
aClipRect.Intersection( rInf.GetPaintRect() );
}
SwSaveClip aClip( (OutputDevice*)rInf.GetOut() );
aClip.ChgClip( aClipRect, rInf.GetTxtFrm() );
// Das machen, was man sonst nur macht ...
PaintTxt( rInf );
// Alte Werte sichern
((SwDropPortion*)this)->Height( nOldHeight );
((SwDropPortion*)this)->Width( nOldWidth );
((SwDropPortion*)this)->SetAscent( nOldAscent );
((SwTxtPaintInfo&)rInf).Y( nOldPosY );
}
/*************************************************************************
* virtual SwDropPortion::Paint()
*************************************************************************/
void SwDropPortion::Paint( const SwTxtPaintInfo &rInf ) const
{
// ganz normale Ausgabe wird hier erledigt.
if( ! nDropHeight || ! pPart || 1 == nLines )
{
if ( rInf.OnWin() &&
!rInf.GetOpt().IsPagePreview() && !rInf.GetOpt().IsReadonly() && SwViewOption::IsFieldShadings() )
rInf.DrawBackground( *this );
// make sure that font is not rotated
SwFont* pTmpFont = 0;
if ( rInf.GetFont()->GetOrientation( rInf.GetTxtFrm()->IsVertical() ) )
{
pTmpFont = new SwFont( *rInf.GetFont() );
pTmpFont->SetVertical( 0, rInf.GetTxtFrm()->IsVertical() );
}
SwFontSave aFontSave( rInf, pTmpFont );
// for text inside drop portions we let vcl handle the text directions
SwLayoutModeModifier aLayoutModeModifier( *rInf.GetOut() );
aLayoutModeModifier.SetAuto();
SwTxtPortion::Paint( rInf );
delete pTmpFont;
}
}
/*************************************************************************
* virtual Format()
*************************************************************************/
sal_Bool SwDropPortion::FormatTxt( SwTxtFormatInfo &rInf )
{
const xub_StrLen nOldLen = GetLen();
const xub_StrLen nOldInfLen = rInf.GetLen();
const sal_Bool bFull = SwTxtPortion::Format( rInf );
if( bFull )
{
// sieht zwar Scheisse aus, aber was soll man schon machen?
rInf.SetUnderFlow( 0 );
Truncate();
SetLen( nOldLen );
rInf.SetLen( nOldInfLen );
}
return bFull;
}
/*************************************************************************
* virtual GetTxtSize()
*************************************************************************/
SwPosSize SwDropPortion::GetTxtSize( const SwTxtSizeInfo &rInf ) const
{
sal_uInt16 nMyX = 0;
xub_StrLen 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();
}
xub_StrLen nOldIdx = rInf.GetIdx();
xub_StrLen nOldLen = rInf.GetLen();
((SwTxtSizeInfo&)rInf).SetIdx( nIdx );
((SwTxtSizeInfo&)rInf).SetLen( rInf.GetLen() - nIdx );
// robust
SwFontSave aFontSave( rInf, pCurrPart ? &pCurrPart->GetFont() : 0 );
SwPosSize aPosSize( SwTxtPortion::GetTxtSize( rInf ) );
aPosSize.Width( aPosSize.Width() + nMyX );
((SwTxtSizeInfo&)rInf).SetIdx( nOldIdx );
((SwTxtSizeInfo&)rInf).SetLen( nOldLen );
return aPosSize;
}
/*************************************************************************
* virtual GetCrsrOfst()
*************************************************************************/
xub_StrLen SwDropPortion::GetCrsrOfst( const KSHORT ) const
{
return 0;
}
/*************************************************************************
* SwTxtFormatter::CalcDropHeight()
*************************************************************************/
void SwTxtFormatter::CalcDropHeight( const MSHORT nLines )
{
const SwLinePortion *const pOldCurr = GetCurr();
KSHORT nDropHght = 0;
KSHORT nAscent = 0;
KSHORT nHeight = 0;
KSHORT nDropLns = 0;
sal_Bool bRegisterOld = IsRegisterOn();
bRegisterOn = sal_False;
Top();
while( GetCurr()->IsDummy() )
{
if ( !Next() )
break;
}
// Wenn wir nur eine Zeile haben returnen wir 0
if( GetNext() || GetDropLines() == 1 )
{
for( ; nDropLns < nLines; nDropLns++ )
{
if ( GetCurr()->IsDummy() )
break;
else
{
CalcAscentAndHeight( nAscent, nHeight );
nDropHght = nDropHght + nHeight;
bRegisterOn = bRegisterOld;
}
if ( !Next() )
{
nDropLns++; // Fix: 11356
break;
}
}
// In der letzten Zeile plumpsen wir auf den Zeilenascent!
nDropHght = nDropHght - nHeight;
nDropHght = nDropHght + nAscent;
Top();
}
bRegisterOn = bRegisterOld;
SetDropDescent( nHeight - nAscent );
SetDropHeight( nDropHght );
SetDropLines( nDropLns );
// Alte Stelle wiederfinden!
while( pOldCurr != GetCurr() )
{
if( !Next() )
{
ASSERT( !this, "SwTxtFormatter::_CalcDropHeight: left Toulouse" );
break;
}
}
}
/*************************************************************************
* SwTxtFormatter::GuessDropHeight()
*
* Wir schaetzen mal, dass die Fonthoehe sich nicht aendert und dass
* erst mindestens soviele Zeilen gibt, wie die DropCap-Einstellung angibt.
*
*************************************************************************/
void SwTxtFormatter::GuessDropHeight( const MSHORT nLines )
{
ASSERT( nLines, "GuessDropHeight: Give me more Lines!" );
KSHORT nAscent = 0;
KSHORT nHeight = 0;
SetDropLines( nLines );
if ( GetDropLines() > 1 )
{
CalcRealHeight();
CalcAscentAndHeight( nAscent, nHeight );
}
SetDropDescent( nHeight - nAscent );
SetDropHeight( nHeight * nLines - GetDropDescent() );
}
/*************************************************************************
* SwTxtFormatter::NewDropPortion
*************************************************************************/
SwDropPortion *SwTxtFormatter::NewDropPortion( SwTxtFormatInfo &rInf )
{
if( !pDropFmt )
return 0;
xub_StrLen nPorLen = pDropFmt->GetWholeWord() ? 0 : pDropFmt->GetChars();
nPorLen = pFrm->GetTxtNode()->GetDropLen( nPorLen );
if( !nPorLen )
{
((SwTxtFormatter*)this)->ClearDropFmt();
return 0;
}
SwDropPortion *pDropPor = 0;
// erste oder zweite Runde?
if ( !( GetDropHeight() || IsOnceMore() ) )
{
if ( GetNext() )
CalcDropHeight( pDropFmt->GetLines() );
else
GuessDropHeight( pDropFmt->GetLines() );
}
// the DropPortion
if( GetDropHeight() )
pDropPor = new SwDropPortion( GetDropLines(), GetDropHeight(),
GetDropDescent(), pDropFmt->GetDistance() );
else
pDropPor = new SwDropPortion( 0,0,0,pDropFmt->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 )
{
((SwTxtFormatter*)this)->SetPaintDrop( sal_True );
return pDropPor;
}
// build DropPortionParts:
ASSERT( ! rInf.GetIdx(), "Drop Portion not at 0 position!" );
xub_StrLen nNextChg = 0;
const SwCharFmt* pFmt = pDropFmt->GetCharFmt();
SwDropPortionPart* pCurrPart = 0;
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 ( pFmt )
{
const SwAttrSet& rSet = pFmt->GetAttrSet();
pTmpFnt->SetDiffFnt( &rSet, pFrm->GetTxtNode()->getIDocumentSettingAccess() );
}
// we do not allow a vertical font for the drop portion
pTmpFnt->SetVertical( 0, rInf.GetTxtFrm()->IsVertical() );
// find next attribute change / script change
const xub_StrLen nTmpIdx = nNextChg;
xub_StrLen nNextAttr = Min( GetNextAttr(), rInf.GetTxt().Len() );
nNextChg = pScriptInfo->NextScriptChg( nTmpIdx );
if( nNextChg > nNextAttr )
nNextChg = nNextAttr;
if ( nNextChg > nPorLen )
nNextChg = nPorLen;
SwDropPortionPart* pPart =
new SwDropPortionPart( *pTmpFnt, nNextChg - nTmpIdx );
if ( ! pCurrPart )
pDropPor->SetPart( pPart );
else
pCurrPart->SetFollow( pPart );
pCurrPart = pPart;
}
((SwTxtFormatter*)this)->SetPaintDrop( sal_True );
return pDropPor;
}
/*************************************************************************
* SwTxtPainter::PaintDropPortion()
*************************************************************************/
void SwTxtPainter::PaintDropPortion()
{
const SwDropPortion *pDrop = GetInfo().GetParaPortion()->FindDropPortion();
ASSERT( pDrop, "DrapCop-Portion not available." );
if( !pDrop )
return;
const SwTwips nOldY = GetInfo().Y();
Top();
GetInfo().SetpSpaceAdd( pCurr->GetpLLSpaceAdd() );
GetInfo().ResetSpaceIdx();
GetInfo().SetKanaComp( pCurr->GetpKanaComp() );
GetInfo().ResetKanaIdx();
// 8047: Drops und Dummies
while( !pCurr->GetLen() && Next() )
;
// MarginPortion und Adjustment!
const SwLinePortion *pPor = pCurr->GetFirstPortion();
KSHORT nX = 0;
while( pPor && !pPor->IsDropPortion() )
{
nX = nX + pPor->Width();
pPor = pPor->GetPortion();
}
Point aLineOrigin( GetTopLeft() );
#ifdef NIE
// Retusche nachholen...
if( nX )
{
const Point aPoint( Left(), Y() );
const Size aSize( nX - 1, GetDropHeight()+GetDropDescent() );
SwRect aRetouche( aPoint, aSize );
GetInfo().DrawRect( aRetouche );
}
#endif
aLineOrigin.X() += nX;
KSHORT nTmpAscent, nTmpHeight;
CalcAscentAndHeight( nTmpAscent, nTmpHeight );
aLineOrigin.Y() += nTmpAscent;
GetInfo().SetIdx( GetStart() );
GetInfo().SetPos( aLineOrigin );
GetInfo().SetLen( pDrop->GetLen() );
pDrop->PaintDrop( GetInfo() );
GetInfo().Y( nOldY );
}
/*************************************************************************
* clas SwDropCapCache
*
* Da die Berechnung der Fontgroesse der Initialen ein teures Geschaeft ist,
* wird dies durch einen DropCapCache geschleust.
*************************************************************************/
#define DROP_CACHE_SIZE 10
class SwDropCapCache
{
long aMagicNo[ DROP_CACHE_SIZE ];
XubString aTxt[ DROP_CACHE_SIZE ];
sal_uInt16 aFactor[ DROP_CACHE_SIZE ];
KSHORT aWishedHeight[ DROP_CACHE_SIZE ];
short aDescent[ DROP_CACHE_SIZE ];
MSHORT nIndex;
public:
SwDropCapCache();
~SwDropCapCache(){}
void CalcFontSize( SwDropPortion* pDrop, SwTxtFormatInfo &rInf );
};
/*************************************************************************
* SwDropCapCache Ctor / Dtor
*************************************************************************/
SwDropCapCache::SwDropCapCache() : nIndex( 0 )
{
memset( &aMagicNo, 0, sizeof(aMagicNo) );
memset( &aWishedHeight, 0, sizeof(aWishedHeight) );
}
void SwDropPortion::DeleteDropCapCache()
{
delete pDropCapCache;
}
/*************************************************************************
* SwDropCapCache::CalcFontSize
*************************************************************************/
void SwDropCapCache::CalcFontSize( SwDropPortion* pDrop, SwTxtFormatInfo &rInf )
{
const void* pFntNo = 0;
MSHORT nTmpIdx = 0;
ASSERT( pDrop->GetPart(),"DropPortion without part during font calculation");
SwDropPortionPart* pCurrPart = pDrop->GetPart();
const sal_Bool bUseCache = ! pCurrPart->GetFollow();
xub_StrLen nIdx = rInf.GetIdx();
XubString aStr( rInf.GetTxt(), nIdx, pCurrPart->GetLen() );
long nAscent = 0;
long nDescent = 0;
long nFactor = -1;
if ( bUseCache )
{
SwFont& rFnt = pCurrPart->GetFont();
rFnt.ChkMagic( rInf.GetVsh(), rFnt.GetActual() );
rFnt.GetMagic( pFntNo, nTmpIdx, rFnt.GetActual() );
nTmpIdx = 0;
while( nTmpIdx < DROP_CACHE_SIZE &&
( aTxt[ nTmpIdx ] != aStr || aMagicNo[ nTmpIdx ] != long(pFntNo) ||
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 )
{
++nIndex;
nIndex %= DROP_CACHE_SIZE;
nTmpIdx = nIndex;
long nWishedHeight = pDrop->GetDropHeight();
// find out biggest font size for initial scaling factor
long nMaxFontHeight = 0;
while ( pCurrPart )
{
const SwFont& rFnt = pCurrPart->GetFont();
const long nCurrHeight = rFnt.GetHeight( rFnt.GetActual() );
if ( nCurrHeight > nMaxFontHeight )
nMaxFontHeight = nCurrHeight;
pCurrPart = pCurrPart->GetFollow();
}
nFactor = ( 1000 * nWishedHeight ) / nMaxFontHeight;
if ( bUseCache )
{
// save keys for cache
aMagicNo[ nTmpIdx ] = long(pFntNo);
aTxt[ nTmpIdx ] = aStr;
aWishedHeight[ nTmpIdx ] = KSHORT(nWishedHeight);
// save initial scaling factor
aFactor[ nTmpIdx ] = (sal_uInt16)nFactor;
}
sal_Bool bGrow = ( pDrop->GetLen() != 0 );
// for growing controll
long nMax = KSHRT_MAX;
long nMin = nFactor / 2;
#if OSL_DEBUG_LEVEL > 1
long nGrow = 0;
#endif
sal_Bool bWinUsed = sal_False;
Font aOldFnt;
MapMode aOldMap( MAP_TWIP );
OutputDevice* pOut = rInf.GetOut();
OutputDevice* pWin;
if( rInf.GetVsh() && rInf.GetVsh()->GetWin() )
pWin = rInf.GetVsh()->GetWin();
else
pWin = GetpApp()->GetDefaultDevice();
while( bGrow )
{
// reset pCurrPart to first part
pCurrPart = pDrop->GetPart();
sal_Bool bFirstGlyphRect = sal_True;
sal_Bool bHaveGlyphRect = sal_False;
Rectangle aCommonRect, aRect;
while ( pCurrPart )
{
// current font
SwFont& rFnt = pCurrPart->GetFont();
// Get height including proportion
const sal_uInt16 nCurrHeight =
(sal_uInt16)rFnt.GetHeight( rFnt.GetActual() );
// Get without proportion
const sal_uInt8 nOldProp = rFnt.GetPropr();
rFnt.SetProportion( 100 );
Size aOldSize = Size( 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 );
// Wir besorgen uns das alle Buchstaben umfassende Rechteck:
bHaveGlyphRect = pOut->GetTextBoundRect( aRect, rInf.GetTxt(), 0,
nIdx, 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 = sal_True;
aOldMap = pWin->GetMapMode( );
pWin->SetMapMode( MapMode( MAP_TWIP ) );
aOldFnt = pWin->GetFont();
}
pWin->SetFont( rFnt.GetActualFont() );
bHaveGlyphRect = pWin->GetTextBoundRect( aRect, rInf.GetTxt(), 0,
nIdx, pCurrPart->GetLen() ) &&
! aRect.IsEmpty();
}
if ( bHaveGlyphRect )
{
FontMetric aWinMet( pWin->GetFontMetric() );
nAscent = (KSHORT) aWinMet.GetAscent();
}
else
// We do not have a window or our window could not
// give us glyph boundaries.
aRect = Rectangle( Point( 0, 0 ), Size( 0, nAscent ) );
}
// 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 );
if ( bFirstGlyphRect )
{
aCommonRect = aRect;
bFirstGlyphRect = sal_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.GetTxtFrm()->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 long nHght = nAscent + nDescent;
if ( nHght )
{
if ( nHght > nWishedHeight )
nMax = nFactor;
else
{
if ( bUseCache )
aFactor[ nTmpIdx ] = (sal_uInt16)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 = sal_False;
}
if ( bWinUsed )
{
// reset window if it has been used
pWin->SetMapMode( aOldMap );
pWin->SetFont( aOldFnt );
}
if ( bUseCache )
aDescent[ nTmpIdx ] = -short( nDescent );
}
pCurrPart = pDrop->GetPart();
// did made any new calculations or did we use the cache?
if ( -1 == nFactor )
{
nFactor = aFactor[ nTmpIdx ];
nDescent = 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( (short)nDescent );
}
/*************************************************************************
* virtual Format()
*************************************************************************/
sal_Bool SwDropPortion::Format( SwTxtFormatInfo &rInf )
{
sal_Bool bFull = sal_False;
Fix( (sal_uInt16)rInf.X() );
SwLayoutModeModifier aLayoutModeModifier( *rInf.GetOut() );
aLayoutModeModifier.SetAuto();
if( nDropHeight && pPart && nLines!=1 )
{
if( !pDropCapCache )
pDropCapCache = new SwDropCapCache();
// adjust font sizes to fit into the rectangle
pDropCapCache->CalcFontSize( this, rInf );
const long nOldX = rInf.X();
{
SwDropSave aSave( rInf );
SwDropPortionPart* pCurrPart = pPart;
while ( pCurrPart )
{
rInf.SetLen( pCurrPart->GetLen() );
SwFont& rFnt = pCurrPart->GetFont();
{
SwFontSave aFontSave( rInf, &rFnt );
bFull = FormatTxt( rInf );
if ( bFull )
break;
}
const SwTwips nTmpWidth =
( InSpaceGrp() && rInf.GetSpaceAdd() ) ?
Width() + CalcSpacing( rInf.GetSpaceAdd(), rInf ) :
Width();
// set values
pCurrPart->SetWidth( (sal_uInt16)nTmpWidth );
// Move
rInf.SetIdx( rInf.GetIdx() + pCurrPart->GetLen() );
rInf.X( rInf.X() + nTmpWidth );
pCurrPart = pCurrPart->GetFollow();
}
Width( (sal_uInt16)(rInf.X() - nOldX) );
}
// reset my length
SetLen( rInf.GetLen() );
// 7631, 7633: bei Ueberlappungen mit Flys ist Schluss.
if( ! bFull )
bFull = lcl_IsDropFlyInter( rInf, Width(), nDropHeight );
if( bFull )
{
// Durch FormatTxt kann nHeight auf 0 gesetzt worden sein
if ( !Height() )
Height( rInf.GetTxtHeight() );
// Jetzt noch einmal der ganze Spass
nDropHeight = nLines = 0;
delete pPart;
pPart = NULL;
// meanwhile use normal formatting
bFull = SwTxtPortion::Format( rInf );
}
else
rInf.SetDropInit( sal_True );
Height( rInf.GetTxtHeight() );
SetAscent( rInf.GetAscent() );
}
else
bFull = SwTxtPortion::Format( rInf );
if( bFull )
nDistance = 0;
else
{
const KSHORT nWant = Width() + GetDistance();
const KSHORT nRest = (sal_uInt16)(rInf.Width() - rInf.X());
if( ( nWant > nRest ) ||
lcl_IsDropFlyInter( rInf, Width() + GetDistance(), nDropHeight ) )
nDistance = 0;
Width( Width() + nDistance );
}
return bFull;
}