diff options
Diffstat (limited to 'editeng/source/items/svxfont.cxx')
-rw-r--r-- | editeng/source/items/svxfont.cxx | 860 |
1 files changed, 860 insertions, 0 deletions
diff --git a/editeng/source/items/svxfont.cxx b/editeng/source/items/svxfont.cxx new file mode 100644 index 000000000..ca8c5f3fd --- /dev/null +++ b/editeng/source/items/svxfont.cxx @@ -0,0 +1,860 @@ +/* -*- 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 <editeng/svxfont.hxx> + +#include <vcl/glyphitemcache.hxx> +#include <vcl/metric.hxx> +#include <vcl/outdev.hxx> +#include <vcl/print.hxx> +#include <tools/debug.hxx> +#include <tools/gen.hxx> +#include <tools/poly.hxx> +#include <unotools/charclass.hxx> +#include <com/sun/star/i18n/KCharacterType.hpp> +#include <editeng/escapementitem.hxx> +#include <sal/log.hxx> +#include <limits> + +static tools::Long GetTextArray( const OutputDevice* pOut, const OUString& rStr, std::vector<sal_Int32>* pDXAry, + sal_Int32 nIndex, sal_Int32 nLen ) + +{ + const SalLayoutGlyphs* layoutGlyphs = SalLayoutGlyphsCache::self()->GetLayoutGlyphs(pOut, rStr, nIndex, nLen); + return pOut->GetTextArray( rStr, pDXAry, nIndex, nLen, nullptr, layoutGlyphs); +} + +SvxFont::SvxFont() +{ + nKern = nEsc = 0; + nPropr = 100; + eCaseMap = SvxCaseMap::NotMapped; + SetLanguage(LANGUAGE_SYSTEM); +} + +SvxFont::SvxFont( const vcl::Font &rFont ) + : Font( rFont ) +{ + nKern = nEsc = 0; + nPropr = 100; + eCaseMap = SvxCaseMap::NotMapped; + SetLanguage(LANGUAGE_SYSTEM); +} + +SvxFont::SvxFont( const SvxFont &rFont ) + : Font( rFont ) +{ + nKern = rFont.GetFixKerning(); + nEsc = rFont.GetEscapement(); + nPropr = rFont.GetPropr(); + eCaseMap = rFont.GetCaseMap(); + SetLanguage(rFont.GetLanguage()); +} + +void SvxFont::SetNonAutoEscapement(short nNewEsc, const OutputDevice* pOutDev) +{ + nEsc = nNewEsc; + if ( abs(nEsc) == DFLT_ESC_AUTO_SUPER ) + { + double fAutoAscent = .8; + double fAutoDescent = .2; + if ( pOutDev ) + { + const FontMetric& rFontMetric = pOutDev->GetFontMetric(); + double fFontHeight = rFontMetric.GetAscent() + rFontMetric.GetDescent(); + if ( fFontHeight ) + { + fAutoAscent = rFontMetric.GetAscent() / fFontHeight; + fAutoDescent = rFontMetric.GetDescent() / fFontHeight; + } + } + + if ( nEsc == DFLT_ESC_AUTO_SUPER ) + nEsc = fAutoAscent * (100 - nPropr); + else //DFLT_ESC_AUTO_SUB + nEsc = fAutoDescent * -(100 - nPropr); + } + + if ( nEsc > MAX_ESC_POS ) + nEsc = MAX_ESC_POS; + else if ( nEsc < -MAX_ESC_POS ) + nEsc = -MAX_ESC_POS; +} + +tools::Polygon SvxFont::DrawArrow( OutputDevice &rOut, const tools::Rectangle& rRect, + const Size& rSize, const Color& rCol, bool bLeftOrTop, bool bVertical ) +{ + tools::Polygon aPoly; + Point aTmp; + Point aNxt; + if (bVertical) + { + tools::Long nLeft = ((rRect.Left() + rRect.Right()) / 2) - (rSize.Height() / 2); + tools::Long nRight = ((rRect.Left() + rRect.Right()) / 2) + (rSize.Height() / 2); + tools::Long nMid = (rRect.Left() + rRect.Right()) / 2; + tools::Long nTop = ((rRect.Top() + rRect.Bottom()) / 2) - (rSize.Height() / 2); + tools::Long nBottom = nTop + rSize.Height(); + if (nTop < rRect.Top()) + { + if (bLeftOrTop) + { + nTop = rRect.Top(); + nBottom = rRect.Bottom(); + } + else + { + nTop = rRect.Bottom(); + nBottom = rRect.Bottom() - (rSize.Height() / 2); + } + } + aTmp.setX(nRight); + aTmp.setY(nBottom); + aNxt.setX(nMid); + aNxt.setY(nTop); + aPoly.Insert(0, aTmp); + aPoly.Insert(0, aNxt); + aTmp.setX(nLeft); + aPoly.Insert(0, aTmp); + } + else + { + tools::Long nLeft = (rRect.Left() + rRect.Right() - rSize.Width()) / 2; + tools::Long nRight = nLeft + rSize.Width(); + tools::Long nMid = (rRect.Top() + rRect.Bottom()) / 2; + tools::Long nTop = nMid - rSize.Height() / 2; + tools::Long nBottom = nTop + rSize.Height(); + if (nLeft < rRect.Left()) + { + nLeft = rRect.Left(); + nRight = rRect.Right(); + } + aTmp.setX(bLeftOrTop ? nLeft : nRight); + aTmp.setY(nMid); + aNxt.setX(bLeftOrTop ? nRight : nLeft); + aNxt.setY(nTop); + aPoly.Insert(0, aTmp); + aPoly.Insert(0, aNxt); + aNxt.setY(nBottom); + aPoly.Insert(0, aNxt); + } + Color aOldLineColor = rOut.GetLineColor(); + Color aOldFillColor = rOut.GetFillColor(); + rOut.SetFillColor( rCol ); + rOut.SetLineColor( COL_BLACK ); + rOut.DrawPolygon( aPoly ); + rOut.DrawLine( aTmp, aNxt ); + rOut.SetLineColor( aOldLineColor ); + rOut.SetFillColor( aOldFillColor ); + return aPoly; +} + +OUString SvxFont::CalcCaseMap(const OUString &rTxt) const +{ + if (!IsCaseMap() || rTxt.isEmpty()) + return rTxt; + OUString aTxt(rTxt); + // I still have to get the language + const LanguageType eLang = LANGUAGE_DONTKNOW == GetLanguage() + ? LANGUAGE_SYSTEM : GetLanguage(); + + CharClass aCharClass(( LanguageTag(eLang) )); + + switch( eCaseMap ) + { + case SvxCaseMap::SmallCaps: + case SvxCaseMap::Uppercase: + { + aTxt = aCharClass.uppercase( aTxt ); + break; + } + + case SvxCaseMap::Lowercase: + { + aTxt = aCharClass.lowercase( aTxt ); + break; + } + case SvxCaseMap::Capitalize: + { + // Every beginning of a word is capitalized, the rest of the word + // is taken over as is. + // Bug: if the attribute starts in the middle of the word. + bool bBlank = true; + + for (sal_Int32 i = 0; i < aTxt.getLength(); ++i) + { + if( aTxt[i] == ' ' || aTxt[i] == '\t') + bBlank = true; + else + { + if (bBlank) + { + OUString sTitle(aCharClass.uppercase(OUString(aTxt[i]))); + aTxt = aTxt.replaceAt(i, 1, sTitle); + } + bBlank = false; + } + } + break; + } + default: + { + SAL_WARN( "editeng", "SvxFont::CaseMapTxt: unknown casemap"); + break; + } + } + return aTxt; +} + +/************************************************************************* + * class SvxDoCapitals + * The virtual Method Do si called by SvxFont::DoOnCapitals alternately + * the uppercase and lowercase parts. The derivate of SvxDoCapitals fills + * this method with life. + *************************************************************************/ + +class SvxDoCapitals +{ +protected: + VclPtr<OutputDevice> pOut; + const OUString &rTxt; + const sal_Int32 nIdx; + const sal_Int32 nLen; + +public: + SvxDoCapitals( OutputDevice *_pOut, const OUString &_rTxt, + const sal_Int32 _nIdx, const sal_Int32 _nLen ) + : pOut(_pOut), rTxt(_rTxt), nIdx(_nIdx), nLen(_nLen) + { } + + virtual ~SvxDoCapitals() {} + + virtual void DoSpace( const bool bDraw ); + virtual void SetSpace(); + virtual void Do( const OUString &rTxt, + const sal_Int32 nIdx, const sal_Int32 nLen, + const bool bUpper ) = 0; + + const OUString &GetTxt() const { return rTxt; } + sal_Int32 GetIdx() const { return nIdx; } + sal_Int32 GetLen() const { return nLen; } +}; + +void SvxDoCapitals::DoSpace( const bool /*bDraw*/ ) { } + +void SvxDoCapitals::SetSpace() { } + +/************************************************************************* + * SvxFont::DoOnCapitals() const + * Decomposes the String into uppercase and lowercase letters and then + * calls the method SvxDoCapitals::Do( ). + *************************************************************************/ + +void SvxFont::DoOnCapitals(SvxDoCapitals &rDo) const +{ + const OUString &rTxt = rDo.GetTxt(); + const sal_Int32 nIdx = rDo.GetIdx(); + const sal_Int32 nLen = rDo.GetLen(); + + const OUString aTxt( CalcCaseMap( rTxt ) ); + const sal_Int32 nTxtLen = std::min( rTxt.getLength(), nLen ); + sal_Int32 nPos = 0; + sal_Int32 nOldPos = nPos; + + // Test if string length differ between original and CaseMapped + bool bCaseMapLengthDiffers(aTxt.getLength() != rTxt.getLength()); + + const LanguageType eLang = LANGUAGE_DONTKNOW == GetLanguage() + ? LANGUAGE_SYSTEM : GetLanguage(); + + CharClass aCharClass(( LanguageTag(eLang) )); + OUString aCharString; + + while( nPos < nTxtLen ) + { + // first in turn are the uppercase letters + + // There are characters that are both upper- and lower-case L (eg blank) + // Such ambiguities lead to chaos, this is why these characters are + // allocated to the lowercase characters! + + while( nPos < nTxtLen ) + { + aCharString = rTxt.copy( nPos + nIdx, 1 ); + sal_Int32 nCharacterType = aCharClass.getCharacterType( aCharString, 0 ); + if ( nCharacterType & css::i18n::KCharacterType::LOWER ) + break; + if ( ! ( nCharacterType & css::i18n::KCharacterType::UPPER ) ) + break; + ++nPos; + } + if( nOldPos != nPos ) + { + if(bCaseMapLengthDiffers) + { + // If strings differ work preparing the necessary snippet to address that + // potential difference + const OUString aSnippet = rTxt.copy(nIdx + nOldPos, nPos-nOldPos); + OUString aNewText = CalcCaseMap(aSnippet); + + rDo.Do( aNewText, 0, aNewText.getLength(), true ); + } + else + { + rDo.Do( aTxt, nIdx + nOldPos, nPos-nOldPos, true ); + } + + nOldPos = nPos; + } + // Now the lowercase are processed (without blanks) + while( nPos < nTxtLen ) + { + sal_uInt32 nCharacterType = aCharClass.getCharacterType( aCharString, 0 ); + if ( nCharacterType & css::i18n::KCharacterType::UPPER ) + break; + if ( aCharString == " " ) + break; + if( ++nPos < nTxtLen ) + aCharString = rTxt.copy( nPos + nIdx, 1 ); + } + if( nOldPos != nPos ) + { + if(bCaseMapLengthDiffers) + { + // If strings differ work preparing the necessary snippet to address that + // potential difference + const OUString aSnippet = rTxt.copy(nIdx + nOldPos, nPos - nOldPos); + OUString aNewText = CalcCaseMap(aSnippet); + + rDo.Do( aNewText, 0, aNewText.getLength(), false ); + } + else + { + rDo.Do( aTxt, nIdx + nOldPos, nPos-nOldPos, false ); + } + + nOldPos = nPos; + } + // Now the blanks are<processed + while( nPos < nTxtLen && aCharString == " " && ++nPos < nTxtLen ) + aCharString = rTxt.copy( nPos + nIdx, 1 ); + + if( nOldPos != nPos ) + { + rDo.DoSpace( false ); + + if(bCaseMapLengthDiffers) + { + // If strings differ work preparing the necessary snippet to address that + // potential difference + const OUString aSnippet = rTxt.copy(nIdx + nOldPos, nPos - nOldPos); + OUString aNewText = CalcCaseMap(aSnippet); + + rDo.Do( aNewText, 0, aNewText.getLength(), false ); + } + else + { + rDo.Do( aTxt, nIdx + nOldPos, nPos - nOldPos, false ); + } + + nOldPos = nPos; + rDo.SetSpace(); + } + } + rDo.DoSpace( true ); +} + + +void SvxFont::SetPhysFont(OutputDevice& rOut) const +{ + const vcl::Font& rCurrentFont = rOut.GetFont(); + if ( nPropr == 100 ) + { + if ( !rCurrentFont.IsSameInstance( *this ) ) + rOut.SetFont( *this ); + } + else + { + Font aNewFont( *this ); + Size aSize( aNewFont.GetFontSize() ); + aNewFont.SetFontSize( Size( aSize.Width() * nPropr / 100, + aSize.Height() * nPropr / 100 ) ); + if ( !rCurrentFont.IsSameInstance( aNewFont ) ) + rOut.SetFont( aNewFont ); + } +} + +vcl::Font SvxFont::ChgPhysFont(OutputDevice& rOut) const +{ + vcl::Font aOldFont(rOut.GetFont()); + SetPhysFont(rOut); + return aOldFont; +} + +Size SvxFont::GetPhysTxtSize( const OutputDevice *pOut, const OUString &rTxt, + const sal_Int32 nIdx, const sal_Int32 nLen ) const +{ + if ( !IsCaseMap() && !IsKern() ) + return Size( pOut->GetTextWidth( rTxt, nIdx, nLen ), + pOut->GetTextHeight() ); + + Size aTxtSize; + aTxtSize.setHeight( pOut->GetTextHeight() ); + if ( !IsCaseMap() ) + aTxtSize.setWidth( pOut->GetTextWidth( rTxt, nIdx, nLen ) ); + else + { + const OUString aNewText = CalcCaseMap(rTxt); + bool bCaseMapLengthDiffers(aNewText.getLength() != rTxt.getLength()); + sal_Int32 nWidth(0); + + if(bCaseMapLengthDiffers) + { + // If strings differ work preparing the necessary snippet to address that + // potential difference + const OUString aSnippet = rTxt.copy(nIdx, nLen); + OUString _aNewText = CalcCaseMap(aSnippet); + nWidth = pOut->GetTextWidth( _aNewText, 0, _aNewText.getLength() ); + } + else + { + nWidth = pOut->GetTextWidth( aNewText, nIdx, nLen ); + } + + aTxtSize.setWidth(nWidth); + } + + if( IsKern() && ( nLen > 1 ) ) + { + std::vector<sal_Int32> aDXArray(nLen); + GetTextArray(pOut, rTxt, &aDXArray, nIdx, nLen); + tools::Long nOldValue = aDXArray[0]; + sal_Int32 nSpaceCount = 0; + for(sal_Int32 i = 1; i < nLen; ++i) + { + if (aDXArray[i] != nOldValue) + { + nOldValue = aDXArray[i]; + ++nSpaceCount; + } + } + aTxtSize.AdjustWidth( nSpaceCount * tools::Long( nKern ) ); + } + + return aTxtSize; +} + +Size SvxFont::GetPhysTxtSize( const OutputDevice *pOut ) +{ + if ( !IsCaseMap() && !IsKern() ) + return Size( pOut->GetTextWidth( "" ), pOut->GetTextHeight() ); + + Size aTxtSize; + aTxtSize.setHeight( pOut->GetTextHeight() ); + if ( !IsCaseMap() ) + aTxtSize.setWidth( pOut->GetTextWidth( "" ) ); + else + aTxtSize.setWidth( pOut->GetTextWidth( CalcCaseMap( "" ) ) ); + + return aTxtSize; +} + +Size SvxFont::QuickGetTextSize( const OutputDevice *pOut, const OUString &rTxt, + const sal_Int32 nIdx, const sal_Int32 nLen, std::vector<sal_Int32>* pDXArray ) const +{ + if ( !IsCaseMap() && !IsKern() ) + return Size( GetTextArray( pOut, rTxt, pDXArray, nIdx, nLen ), + pOut->GetTextHeight() ); + + std::vector<sal_Int32> aDXArray; + + // We always need pDXArray to count the number of kern spaces + if (!pDXArray && IsKern() && nLen > 1) + { + pDXArray = &aDXArray; + aDXArray.reserve(nLen); + } + + Size aTxtSize; + aTxtSize.setHeight( pOut->GetTextHeight() ); + if ( !IsCaseMap() ) + aTxtSize.setWidth( GetTextArray( pOut, rTxt, pDXArray, nIdx, nLen ) ); + else + aTxtSize.setWidth( GetTextArray( pOut, CalcCaseMap( rTxt ), + pDXArray, nIdx, nLen ) ); + + if( IsKern() && ( nLen > 1 ) ) + { + tools::Long nOldValue = (*pDXArray)[0]; + tools::Long nSpaceSum = nKern; + (*pDXArray)[0] += nSpaceSum; + + for ( sal_Int32 i = 1; i < nLen; i++ ) + { + if ( (*pDXArray)[i] != nOldValue ) + { + nOldValue = (*pDXArray)[i]; + nSpaceSum += nKern; + } + (*pDXArray)[i] += nSpaceSum; + } + + // The last one is a nKern too big: + nOldValue = (*pDXArray)[nLen - 1]; + tools::Long nNewValue = nOldValue - nKern; + for ( sal_Int32 i = nLen - 1; i >= 0 && (*pDXArray)[i] == nOldValue; --i) + (*pDXArray)[i] = nNewValue; + + aTxtSize.AdjustWidth(nSpaceSum - nKern); + } + + return aTxtSize; +} + +Size SvxFont::GetTextSize(const OutputDevice& rOut, const OUString &rTxt, + const sal_Int32 nIdx, const sal_Int32 nLen) const +{ + sal_Int32 nTmp = nLen; + if ( nTmp == SAL_MAX_INT32 ) // already initialized? + nTmp = rTxt.getLength(); + Font aOldFont( ChgPhysFont(const_cast<OutputDevice&>(rOut))); + Size aTxtSize; + if( IsCapital() && !rTxt.isEmpty() ) + { + aTxtSize = GetCapitalSize(&rOut, rTxt, nIdx, nTmp); + } + else aTxtSize = GetPhysTxtSize(&rOut,rTxt,nIdx,nTmp); + const_cast<OutputDevice&>(rOut).SetFont(aOldFont); + return aTxtSize; +} + +static void DrawTextArray( OutputDevice* pOut, const Point& rStartPt, const OUString& rStr, + o3tl::span<const sal_Int32> pDXAry, + sal_Int32 nIndex, sal_Int32 nLen ) +{ + const SalLayoutGlyphs* layoutGlyphs = SalLayoutGlyphsCache::self()->GetLayoutGlyphs(pOut, rStr, nIndex, nLen); + pOut->DrawTextArray(rStartPt, rStr, pDXAry, nIndex, nLen, SalLayoutFlags::NONE, layoutGlyphs); +} + +void SvxFont::QuickDrawText( OutputDevice *pOut, + const Point &rPos, const OUString &rTxt, + const sal_Int32 nIdx, const sal_Int32 nLen, o3tl::span<const sal_Int32> pDXArray ) const +{ + + // Font has to be selected in OutputDevice... + if ( !IsCaseMap() && !IsCapital() && !IsKern() && !IsEsc() ) + { + DrawTextArray( pOut, rPos, rTxt, pDXArray, nIdx, nLen ); + return; + } + + Point aPos( rPos ); + + if ( nEsc ) + { + tools::Long nDiff = GetFontSize().Height(); + nDiff *= nEsc; + nDiff /= 100; + + if ( !IsVertical() ) + aPos.AdjustY( -nDiff ); + else + aPos.AdjustX(nDiff ); + } + + if( IsCapital() ) + { + DBG_ASSERT( pDXArray.empty(), "DrawCapital not for TextArray!" ); + DrawCapital( pOut, aPos, rTxt, nIdx, nLen ); + } + else + { + if ( IsKern() && pDXArray.empty() ) + { + Size aSize = GetPhysTxtSize( pOut, rTxt, nIdx, nLen ); + + if ( !IsCaseMap() ) + pOut->DrawStretchText( aPos, aSize.Width(), rTxt, nIdx, nLen ); + else + pOut->DrawStretchText( aPos, aSize.Width(), CalcCaseMap( rTxt ), nIdx, nLen ); + } + else + { + if ( !IsCaseMap() ) + DrawTextArray( pOut, aPos, rTxt, pDXArray, nIdx, nLen ); + else + DrawTextArray( pOut, aPos, CalcCaseMap( rTxt ), pDXArray, nIdx, nLen ); + } + } +} + + +void SvxFont::DrawPrev( OutputDevice *pOut, Printer* pPrinter, + const Point &rPos, const OUString &rTxt, + const sal_Int32 nIdx, const sal_Int32 nLen ) const +{ + if ( !nLen || rTxt.isEmpty() ) + return; + sal_Int32 nTmp = nLen; + + if ( nTmp == SAL_MAX_INT32 ) // already initialized? + nTmp = rTxt.getLength(); + Point aPos( rPos ); + + if ( nEsc ) + { + short nTmpEsc; + if( DFLT_ESC_AUTO_SUPER == nEsc ) + { + nTmpEsc = .8 * (100 - nPropr); + assert (nTmpEsc == DFLT_ESC_SUPER && "I'm sure this formula needs to be changed, but how to confirm that???"); + nTmpEsc = DFLT_ESC_SUPER; + } + else if( DFLT_ESC_AUTO_SUB == nEsc ) + { + nTmpEsc = .2 * -(100 - nPropr); + assert (nTmpEsc == -20 && "I'm sure this formula needs to be changed, but how to confirm that???"); + nTmpEsc = -20; + } + else + nTmpEsc = nEsc; + Size aSize = GetFontSize(); + aPos.AdjustY( -(( nTmpEsc * aSize.Height() ) / 100) ); + } + Font aOldFont( ChgPhysFont(*pOut) ); + Font aOldPrnFont( ChgPhysFont(*pPrinter) ); + + if ( IsCapital() ) + DrawCapital( pOut, aPos, rTxt, nIdx, nTmp ); + else + { + Size aSize = GetPhysTxtSize( pPrinter, rTxt, nIdx, nTmp ); + + if ( !IsCaseMap() ) + pOut->DrawStretchText( aPos, aSize.Width(), rTxt, nIdx, nTmp ); + else + { + const OUString aNewText = CalcCaseMap(rTxt); + bool bCaseMapLengthDiffers(aNewText.getLength() != rTxt.getLength()); + + if(bCaseMapLengthDiffers) + { + // If strings differ work preparing the necessary snippet to address that + // potential difference + const OUString aSnippet(rTxt.copy( nIdx, nTmp)); + OUString _aNewText = CalcCaseMap(aSnippet); + + pOut->DrawStretchText( aPos, aSize.Width(), _aNewText, 0, _aNewText.getLength() ); + } + else + { + pOut->DrawStretchText( aPos, aSize.Width(), CalcCaseMap( rTxt ), nIdx, nTmp ); + } + } + } + pOut->SetFont(aOldFont); + pPrinter->SetFont( aOldPrnFont ); +} + + +SvxFont& SvxFont::operator=( const vcl::Font& rFont ) +{ + Font::operator=( rFont ); + return *this; +} + +SvxFont& SvxFont::operator=( const SvxFont& rFont ) +{ + Font::operator=( rFont ); + eCaseMap = rFont.eCaseMap; + nEsc = rFont.nEsc; + nPropr = rFont.nPropr; + nKern = rFont.nKern; + return *this; +} + +namespace { + +class SvxDoGetCapitalSize : public SvxDoCapitals +{ +protected: + SvxFont* pFont; + Size aTxtSize; + short nKern; +public: + SvxDoGetCapitalSize( SvxFont *_pFnt, const OutputDevice *_pOut, + const OUString &_rTxt, const sal_Int32 _nIdx, + const sal_Int32 _nLen, const short _nKrn ) + : SvxDoCapitals( const_cast<OutputDevice*>(_pOut), _rTxt, _nIdx, _nLen ), + pFont( _pFnt ), + nKern( _nKrn ) + { } + + virtual void Do( const OUString &rTxt, const sal_Int32 nIdx, + const sal_Int32 nLen, const bool bUpper ) override; + + const Size &GetSize() const { return aTxtSize; }; +}; + +} + +void SvxDoGetCapitalSize::Do( const OUString &_rTxt, const sal_Int32 _nIdx, + const sal_Int32 _nLen, const bool bUpper ) +{ + Size aPartSize; + if ( !bUpper ) + { + sal_uInt8 nProp = pFont->GetPropr(); + pFont->SetProprRel( SMALL_CAPS_PERCENTAGE ); + pFont->SetPhysFont( *pOut ); + aPartSize.setWidth( pOut->GetTextWidth( _rTxt, _nIdx, _nLen ) ); + aPartSize.setHeight( pOut->GetTextHeight() ); + aTxtSize.setHeight( aPartSize.Height() ); + pFont->SetPropr( nProp ); + pFont->SetPhysFont( *pOut ); + } + else + { + aPartSize.setWidth( pOut->GetTextWidth( _rTxt, _nIdx, _nLen ) ); + aPartSize.setHeight( pOut->GetTextHeight() ); + } + aTxtSize.AdjustWidth(aPartSize.Width() ); + aTxtSize.AdjustWidth( _nLen * tools::Long( nKern ) ); +} + +Size SvxFont::GetCapitalSize( const OutputDevice *pOut, const OUString &rTxt, + const sal_Int32 nIdx, const sal_Int32 nLen) const +{ + // Start: + SvxDoGetCapitalSize aDo( const_cast<SvxFont *>(this), pOut, rTxt, nIdx, nLen, nKern ); + DoOnCapitals( aDo ); + Size aTxtSize( aDo.GetSize() ); + + // End: + if( !aTxtSize.Height() ) + { + aTxtSize.setWidth( 0 ); + aTxtSize.setHeight( pOut->GetTextHeight() ); + } + return aTxtSize; +} + +namespace { + +class SvxDoDrawCapital : public SvxDoCapitals +{ +protected: + SvxFont *pFont; + Point aPos; + Point aSpacePos; + short nKern; +public: + SvxDoDrawCapital( SvxFont *pFnt, OutputDevice *_pOut, const OUString &_rTxt, + const sal_Int32 _nIdx, const sal_Int32 _nLen, + const Point &rPos, const short nKrn ) + : SvxDoCapitals( _pOut, _rTxt, _nIdx, _nLen ), + pFont( pFnt ), + aPos( rPos ), + aSpacePos( rPos ), + nKern( nKrn ) + { } + virtual void DoSpace( const bool bDraw ) override; + virtual void SetSpace() override; + virtual void Do( const OUString &rTxt, const sal_Int32 nIdx, + const sal_Int32 nLen, const bool bUpper ) override; +}; + +} + +void SvxDoDrawCapital::DoSpace( const bool bDraw ) +{ + if ( !(bDraw || pFont->IsWordLineMode()) ) + return; + + sal_uLong nDiff = static_cast<sal_uLong>(aPos.X() - aSpacePos.X()); + if ( nDiff ) + { + bool bWordWise = pFont->IsWordLineMode(); + bool bTrans = pFont->IsTransparent(); + pFont->SetWordLineMode( false ); + pFont->SetTransparent( true ); + pFont->SetPhysFont(*pOut); + pOut->DrawStretchText( aSpacePos, nDiff, " ", 0, 2 ); + pFont->SetWordLineMode( bWordWise ); + pFont->SetTransparent( bTrans ); + pFont->SetPhysFont(*pOut); + } +} + +void SvxDoDrawCapital::SetSpace() +{ + if ( pFont->IsWordLineMode() ) + aSpacePos.setX( aPos.X() ); +} + +void SvxDoDrawCapital::Do( const OUString &_rTxt, const sal_Int32 _nIdx, + const sal_Int32 _nLen, const bool bUpper) +{ + sal_uInt8 nProp = 0; + Size aPartSize; + + // Set the desired font + FontLineStyle eUnder = pFont->GetUnderline(); + FontStrikeout eStrike = pFont->GetStrikeout(); + pFont->SetUnderline( LINESTYLE_NONE ); + pFont->SetStrikeout( STRIKEOUT_NONE ); + if ( !bUpper ) + { + nProp = pFont->GetPropr(); + pFont->SetProprRel( SMALL_CAPS_PERCENTAGE ); + } + pFont->SetPhysFont(*pOut); + + aPartSize.setWidth( pOut->GetTextWidth( _rTxt, _nIdx, _nLen ) ); + aPartSize.setHeight( pOut->GetTextHeight() ); + tools::Long nWidth = aPartSize.Width(); + if ( nKern ) + { + aPos.AdjustX(nKern/2); + if ( _nLen ) nWidth += (_nLen*tools::Long(nKern)); + } + pOut->DrawStretchText(aPos,nWidth-nKern,_rTxt,_nIdx,_nLen); + + // Restore Font + pFont->SetUnderline( eUnder ); + pFont->SetStrikeout( eStrike ); + if ( !bUpper ) + pFont->SetPropr( nProp ); + pFont->SetPhysFont(*pOut); + + aPos.AdjustX(nWidth-(nKern/2) ); +} + +/************************************************************************* + * SvxFont::DrawCapital() draws the uppercase letter. + *************************************************************************/ + +void SvxFont::DrawCapital( OutputDevice *pOut, + const Point &rPos, const OUString &rTxt, + const sal_Int32 nIdx, const sal_Int32 nLen ) const +{ + SvxDoDrawCapital aDo( const_cast<SvxFont *>(this),pOut,rTxt,nIdx,nLen,rPos,nKern ); + DoOnCapitals( aDo ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |