/* -*- 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 #include #include #include #include #include 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(); LanguageTag aLanguageTag(eLang); CharClass aCharClass( aLanguageTag ); 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 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(); LanguageTag aLanguageTag(eLang); CharClass aCharClass( aLanguageTag ); 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 areGetTextWidth( 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 ) ) aTxtSize.AdjustWidth( ( nLen-1 ) * 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, tools::Long* pDXArray ) const { if ( !IsCaseMap() && !IsKern() ) return Size( pOut->GetTextArray( rTxt, pDXArray, nIdx, nLen ), pOut->GetTextHeight() ); Size aTxtSize; aTxtSize.setHeight( pOut->GetTextHeight() ); if ( !IsCaseMap() ) aTxtSize.setWidth( pOut->GetTextArray( rTxt, pDXArray, nIdx, nLen ) ); else aTxtSize.setWidth( pOut->GetTextArray( CalcCaseMap( rTxt ), pDXArray, nIdx, nLen ) ); if( IsKern() && ( nLen > 1 ) ) { aTxtSize.AdjustWidth( ( nLen-1 ) * tools::Long( nKern ) ); if ( pDXArray ) { for ( sal_Int32 i = 0; i < nLen; i++ ) pDXArray[i] += ( (i+1) * tools::Long( nKern ) ); // The last one is a nKern too big: pDXArray[nLen-1] -= 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(rOut))); Size aTxtSize; if( IsCapital() && !rTxt.isEmpty() ) { aTxtSize = GetCapitalSize(&rOut, rTxt, nIdx, nTmp); } else aTxtSize = GetPhysTxtSize(&rOut,rTxt,nIdx,nTmp); const_cast(rOut).SetFont(aOldFont); return aTxtSize; } void SvxFont::QuickDrawText( OutputDevice *pOut, const Point &rPos, const OUString &rTxt, const sal_Int32 nIdx, const sal_Int32 nLen, const tools::Long* pDXArray ) const { // Font has to be selected in OutputDevice... if ( !IsCaseMap() && !IsCapital() && !IsKern() && !IsEsc() ) { pOut->DrawTextArray( 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, "DrawCapital not for TextArray!" ); DrawCapital( pOut, aPos, rTxt, nIdx, nLen ); } else { if ( IsKern() && !pDXArray ) { 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() ) pOut->DrawTextArray( aPos, rTxt, pDXArray, nIdx, nLen ); else pOut->DrawTextArray( 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(_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(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(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(this),pOut,rTxt,nIdx,nLen,rPos,nKern ); DoOnCapitals( aDo ); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */