/* -*- 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 #include #include #include #include #include // Format() #include #include // SwViewOption #include // ViewShell #include #include #include // SwSaveClip #include // pBlink #include #include #include #include #include #include #include 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 *************************************************************************/ static 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() { 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().getLength(); if( nWishLen && nWishLen < nEnd ) nEnd = nWishLen; if ( ! nWishLen && g_pBreakIt->GetBreakIter().is() ) { // find first word const SwAttrSet& rAttrSet = GetSwAttrSet(); const sal_uInt16 nTxtScript = g_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 = g_pBreakIt->GetBreakIter()->getWordBoundary( GetTxt(), 0, g_pBreakIt->GetLocale( eLanguage ), WordType::DICTIONARY_WORD, sal_True ); nEnd = (xub_StrLen)aBound.endPos; } xub_StrLen i = 0; for( ; i < nEnd; ++i ) { sal_Unicode const cChar = GetTxt()[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 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(); 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 = (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 ); OSL_ENSURE( 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() ) { OSL_ENSURE( !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 ) { OSL_ENSURE( 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: OSL_ENSURE( ! 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; sal_Int32 nNextAttr = std::min( static_cast(GetNextAttr()), rInf.GetTxt().getLength() ); 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(); OSL_ENSURE( 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() ); 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; OSL_ENSURE( pDrop->GetPart(),"DropPortion without part during font calculation"); SwDropPortionPart* pCurrPart = pDrop->GetPart(); const 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 ] != sal_IntPtr(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 ] = sal_IntPtr(pFntNo); aTxt[ nTmpIdx ] = aStr; aWishedHeight[ nTmpIdx ] = KSHORT(nWishedHeight); // save initial scaling factor aFactor[ nTmpIdx ] = (sal_uInt16)nFactor; } 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 bool bWinUsed = 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(); bool bFirstGlyphRect = true; bool bHaveGlyphRect = 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 = 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 = 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 = 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; } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */