/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#include <hintids.hxx>

#include <com/sun/star/i18n/ScriptType.hpp>
#include <vcl/outdev.hxx>
#include <editeng/brushitem.hxx>
#include <editeng/wrlmitem.hxx>
#include <editeng/kernitem.hxx>
#include <editeng/cmapitem.hxx>
#include <editeng/langitem.hxx>
#include <editeng/escapementitem.hxx>
#include <editeng/autokernitem.hxx>
#include <editeng/shdditem.hxx>
#include <editeng/charreliefitem.hxx>
#include <editeng/contouritem.hxx>
#include <editeng/colritem.hxx>
#include <editeng/crossedoutitem.hxx>
#include <editeng/udlnitem.hxx>
#include <editeng/wghtitem.hxx>
#include <editeng/postitem.hxx>
#include <editeng/fhgtitem.hxx>
#include <editeng/fontitem.hxx>
#include <editeng/emphasismarkitem.hxx>
#include <editeng/charscaleitem.hxx>
#include <editeng/charrotateitem.hxx>
#include <editeng/twolinesitem.hxx>
#include <editeng/charhiddenitem.hxx>
#include <editeng/boxitem.hxx>
#include <editeng/shaditem.hxx>
#include <vcl/metric.hxx>
#include <IDocumentSettingAccess.hxx>
#include <charatr.hxx>
#include <viewsh.hxx>
#include <swfont.hxx>
#include <fntcache.hxx>
#include <txtfrm.hxx>
#include <scriptinfo.hxx>
#include <swmodule.hxx>

#ifdef DBG_UTIL
// global Variable
SvStatistics g_SvStat;
#endif

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

// set background brush, depending on character formatting
void SwFont::SetBackColor( std::optional<Color> xNewColor )
{
    mxBackColor = xNewColor;
    m_bFontChg = true;
    m_aSub[SwFontScript::Latin].m_nFontCacheId = m_aSub[SwFontScript::CJK].m_nFontCacheId = m_aSub[SwFontScript::CTL].m_nFontCacheId = nullptr;
}

void SwFont::SetTopBorder( const editeng::SvxBorderLine* pTopBorder )
{
    if( pTopBorder )
        m_aTopBorder = *pTopBorder;
    else
    {
        m_aTopBorder.reset();
        m_nTopBorderDist = 0;
    }
    m_bFontChg = true;
    m_aSub[SwFontScript::Latin].m_nFontCacheId = m_aSub[SwFontScript::CJK].m_nFontCacheId = m_aSub[SwFontScript::CTL].m_nFontCacheId = nullptr;
}

void SwFont::SetBottomBorder( const editeng::SvxBorderLine* pBottomBorder )
{
    if( pBottomBorder )
        m_aBottomBorder = *pBottomBorder;
    else
    {
        m_aBottomBorder.reset();
        m_nBottomBorderDist = 0;
    }
    m_bFontChg = true;
    m_aSub[SwFontScript::Latin].m_nFontCacheId = m_aSub[SwFontScript::CJK].m_nFontCacheId = m_aSub[SwFontScript::CTL].m_nFontCacheId = nullptr;
}

void SwFont::SetRightBorder( const editeng::SvxBorderLine* pRightBorder )
{
    if( pRightBorder )
        m_aRightBorder = *pRightBorder;
    else
    {
        m_aRightBorder.reset();
        m_nRightBorderDist = 0;
    }
    m_bFontChg = true;
    m_aSub[SwFontScript::Latin].m_nFontCacheId = m_aSub[SwFontScript::CJK].m_nFontCacheId = m_aSub[SwFontScript::CTL].m_nFontCacheId = nullptr;
}

void SwFont::SetLeftBorder( const editeng::SvxBorderLine* pLeftBorder )
{
    if( pLeftBorder )
        m_aLeftBorder = *pLeftBorder;
    else
    {
        m_aLeftBorder.reset();
        m_nLeftBorderDist = 0;
    }
    m_bFontChg = true;
    m_aSub[SwFontScript::Latin].m_nFontCacheId = m_aSub[SwFontScript::CJK].m_nFontCacheId = m_aSub[SwFontScript::CTL].m_nFontCacheId = nullptr;
}

const std::optional<editeng::SvxBorderLine>&
SwFont::GetAbsTopBorder(const bool bVertLayout, const bool bVertLayoutLRBT) const
{
    switch (GetOrientation(bVertLayout, bVertLayoutLRBT).get())
    {
        case 0 :
            return m_aTopBorder;
        case 900 :
            return m_aRightBorder;
        case 1800 :
            return m_aBottomBorder;
        case 2700 :
            return m_aLeftBorder;
        default :
            assert(false);
            return m_aTopBorder;
    }
}

const std::optional<editeng::SvxBorderLine>&
SwFont::GetAbsBottomBorder(const bool bVertLayout, const bool bVertLayoutLRBT) const
{
    switch (GetOrientation(bVertLayout, bVertLayoutLRBT).get())
    {
        case 0 :
            return m_aBottomBorder;
        case 900 :
            return m_aLeftBorder;
        case 1800 :
            return m_aTopBorder;
        case 2700 :
            return m_aRightBorder;
        default :
            assert(false);
            return m_aBottomBorder;
    }
}

const std::optional<editeng::SvxBorderLine>&
SwFont::GetAbsLeftBorder(const bool bVertLayout, const bool bVertLayoutLRBT) const
{
    switch (GetOrientation(bVertLayout, bVertLayoutLRBT).get())
    {
        case 0 :
            return m_aLeftBorder;
        case 900 :
            return m_aTopBorder;
        case 1800 :
            return m_aRightBorder;
        case 2700 :
            return m_aBottomBorder;
        default :
            assert(false);
            return m_aLeftBorder;
    }
}

const std::optional<editeng::SvxBorderLine>&
SwFont::GetAbsRightBorder(const bool bVertLayout, const bool bVertLayoutLRBT) const
{
    switch (GetOrientation(bVertLayout, bVertLayoutLRBT).get())
    {
        case 0 :
            return m_aRightBorder;
        case 900 :
            return m_aBottomBorder;
        case 1800 :
            return m_aLeftBorder;
        case 2700 :
            return m_aTopBorder;
        default :
            assert(false);
            return m_aRightBorder;
    }
}

SvxShadowLocation SwFont::GetAbsShadowLocation(const bool bVertLayout,
                                               const bool bVertLayoutLRBT) const
{
    SvxShadowLocation aLocation = SvxShadowLocation::NONE;
    switch (GetOrientation(bVertLayout, bVertLayoutLRBT).get())
    {
        case 0:
            aLocation = m_aShadowLocation;
            break;

        case 900:
            switch ( m_aShadowLocation )
            {
                case SvxShadowLocation::TopLeft:
                    aLocation = SvxShadowLocation::BottomLeft;
                    break;
                case SvxShadowLocation::TopRight:
                    aLocation = SvxShadowLocation::TopLeft;
                    break;
                case SvxShadowLocation::BottomLeft:
                    aLocation = SvxShadowLocation::BottomRight;
                    break;
                case SvxShadowLocation::BottomRight:
                    aLocation = SvxShadowLocation::TopRight;
                    break;
                case SvxShadowLocation::NONE:
                case SvxShadowLocation::End:
                    aLocation = m_aShadowLocation;
                    break;
            }
            break;

        case 1800:
            switch ( m_aShadowLocation )
            {
                case SvxShadowLocation::TopLeft:
                    aLocation = SvxShadowLocation::BottomRight;
                    break;
                case SvxShadowLocation::TopRight:
                    aLocation = SvxShadowLocation::BottomLeft;
                    break;
                case SvxShadowLocation::BottomLeft:
                    aLocation = SvxShadowLocation::TopRight;
                    break;
                case SvxShadowLocation::BottomRight:
                    aLocation = SvxShadowLocation::TopLeft;
                    break;
                case SvxShadowLocation::NONE:
                case SvxShadowLocation::End:
                    aLocation = m_aShadowLocation;
                    break;
            }
            break;

        case 2700:
            switch ( m_aShadowLocation )
            {
                case SvxShadowLocation::TopLeft:
                    aLocation = SvxShadowLocation::TopRight;
                    break;
                case SvxShadowLocation::TopRight:
                    aLocation = SvxShadowLocation::BottomRight;
                    break;
                case SvxShadowLocation::BottomLeft:
                    aLocation = SvxShadowLocation::TopLeft;
                    break;
                case SvxShadowLocation::BottomRight:
                    aLocation = SvxShadowLocation::BottomLeft;
                    break;
                case SvxShadowLocation::NONE:
                case SvxShadowLocation::End:
                    aLocation = m_aShadowLocation;
                    break;
            }
            break;

        default:
            assert(false);
            break;
    }
    return aLocation;
}

sal_uInt16 SwFont::CalcShadowSpace(const SvxShadowItemSide nShadow, const bool bVertLayout,
                                   const bool bVertLayoutLRBT, const bool bSkipLeft,
                                   const bool bSkipRight) const
{
    sal_uInt16 nSpace = 0;
    const Degree10 nOrient = GetOrientation(bVertLayout, bVertLayoutLRBT);
    const SvxShadowLocation aLoc = GetAbsShadowLocation(bVertLayout, bVertLayoutLRBT);
    switch( nShadow )
    {
        case SvxShadowItemSide::TOP:
            if(( aLoc == SvxShadowLocation::TopLeft ||
               aLoc == SvxShadowLocation::TopRight ) &&
               ( nOrient == 0_deg10 || nOrient == 1800_deg10 ||
               ( nOrient == 900_deg10 && !bSkipRight ) ||
               ( nOrient == 2700_deg10 && !bSkipLeft )))
            {
                nSpace = m_nShadowWidth;
            }
            break;

        case SvxShadowItemSide::BOTTOM:
            if(( aLoc == SvxShadowLocation::BottomLeft ||
               aLoc == SvxShadowLocation::BottomRight ) &&
               ( nOrient == 0_deg10 || nOrient == 1800_deg10 ||
               ( nOrient == 900_deg10 && !bSkipLeft ) ||
               ( nOrient == 2700_deg10 && !bSkipRight )))
            {
                nSpace = m_nShadowWidth;
            }
            break;

        case SvxShadowItemSide::LEFT:
            if(( aLoc == SvxShadowLocation::TopLeft ||
               aLoc == SvxShadowLocation::BottomLeft ) &&
               ( nOrient == 900_deg10 || nOrient == 2700_deg10 ||
               ( nOrient == 0_deg10 && !bSkipLeft ) ||
               ( nOrient == 1800_deg10 && !bSkipRight )))
            {
                nSpace = m_nShadowWidth;
            }
            break;

         case SvxShadowItemSide::RIGHT:
            if(( aLoc == SvxShadowLocation::TopRight ||
               aLoc == SvxShadowLocation::BottomRight ) &&
               ( nOrient == 900_deg10 || nOrient == 2700_deg10 ||
               ( nOrient == 0_deg10 && !bSkipRight ) ||
               ( nOrient == 1800_deg10 && !bSkipLeft )))
            {
                nSpace = m_nShadowWidth;
            }
            break;
        default:
            assert(false);
            break;
    }

    return nSpace;
}

SvxFontUnitMetrics SwFont::GetFontUnitMetrics() const
{
    // tdf#36709: Metrics conversion should use em and ic values from the bound fonts.
    // Unfortunately, this currently poses a problem due to font substitution: tests
    // abort when a missing font is set on a device.
    // In the interim, use height for all metrics. This is technically not correct, but
    // should be close enough for common fonts.
    return { /*em*/ static_cast<double>(GetHeight(GetActual())),
             /*ic*/ static_cast<double>(GetHeight(SwFontScript::CJK)) };
}

void SwFont::dumpAsXml(xmlTextWriterPtr writer) const
{
    (void)xmlTextWriterStartElement(writer, BAD_CAST("SwFont"));
    (void)xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("ptr"), "%p", this);
    // do not use Color::AsRGBHexString() as that omits the transparency
    (void)xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("color"), "%08" SAL_PRIxUINT32, sal_uInt32(GetColor()));
    (void)xmlTextWriterWriteAttribute(writer, BAD_CAST("height"), BAD_CAST(OString::number(GetSize(GetActual()).Height()).getStr()));
    (void)xmlTextWriterWriteAttribute(writer, BAD_CAST("width"), BAD_CAST(OString::number(GetSize(GetActual()).Width()).getStr()));
    {
        std::stringstream ss;
        ss << GetWeight();
        (void)xmlTextWriterWriteAttribute(writer, BAD_CAST("weight"), BAD_CAST(ss.str().c_str()));
    }
    (void)xmlTextWriterEndElement(writer);
}

// maps directions for vertical layout
static Degree10 MapDirection(Degree10 nDir, const bool bVertFormat, const bool bVertFormatLRBT)
{
    if ( bVertFormat )
    {
        switch ( nDir.get() )
        {
        case 0 :
            if (bVertFormatLRBT)
                nDir = 900_deg10;
            else
                nDir = 2700_deg10;
            break;
        case 900 :
            nDir = 0_deg10;
            break;
        case 2700 :
            nDir = 1800_deg10;
            break;
#if OSL_DEBUG_LEVEL > 0
        default :
            OSL_FAIL( "Unsupported direction" );
            break;
#endif
        }
    }
    return nDir;
}

// maps the absolute direction set at the font to its logical counterpart
// in the rotated environment
Degree10 UnMapDirection(Degree10 nDir, const bool bVertFormat, const bool bVertFormatLRBT)
{
    if (bVertFormatLRBT)
    {
        switch (nDir.get())
        {
            case 900:
                nDir = 0_deg10;
                break;
            default:
                SAL_WARN("sw.core", "unsupported direction for VertLRBT");
                break;
        }
        return nDir;
    }

    if ( bVertFormat )
    {
        switch ( nDir.get() )
        {
        case 0 :
            nDir = 900_deg10;
            break;
        case 1800 :
            nDir = 2700_deg10;
            break;
        case 2700 :
            nDir = 0_deg10;
            break;
#if OSL_DEBUG_LEVEL > 0
        default :
            OSL_FAIL( "Unsupported direction" );
            break;
#endif
        }
    }
    return nDir;
}

Degree10 SwFont::GetOrientation(const bool bVertFormat, const bool bVertFormatLRBT) const
{
    return UnMapDirection(m_aSub[m_nActual].GetOrientation(), bVertFormat, bVertFormatLRBT);
}

void SwFont::SetVertical(Degree10 nDir, const bool bVertFormat, const bool bVertLayoutLRBT)
{
    // map direction if frame has vertical layout
    nDir = MapDirection(nDir, bVertFormat, bVertLayoutLRBT);

    if( nDir != m_aSub[SwFontScript::Latin].GetOrientation() )
    {
        m_bFontChg = true;
        bool bVertical = bVertFormat && !bVertLayoutLRBT;
        m_aSub[SwFontScript::Latin].SetVertical(nDir, bVertical);
        m_aSub[SwFontScript::CJK].SetVertical(nDir, bVertical);
        m_aSub[SwFontScript::CTL].SetVertical(nDir, bVertical);
    }
}

/*
 Escapement:
    frEsc:  Fraction, ratio of Escapements
    Esc = resulting Escapement
    A1 = original Ascent            (nOrgAscent)
    A2 = shrunk Ascent              (nEscAscent)
    Ax = resulting Ascent           (GetAscent())
    H1 = original Height            (nOrgHeight)
    H2 = shrunk Height              (nEscHeight)
    Hx = resulting Height           (GetHeight())
    Bx = resulting Baseline for Text (CalcPos())
         (Attention: Y - A1!)

    Escapement:
        Esc = H1 * frEsc;

    Superscript:
        Ax = A2 + Esc;
        Hx = H2 + Esc;
        Bx = A1 - Esc;

    Subscript:
        Ax = A1;
        Hx = A1 + Esc + (H2 - A2);
        Bx = A1 + Esc;
*/

// nEsc is the percentage
sal_uInt16 SwSubFont::CalcEscAscent( const sal_uInt16 nOldAscent ) const
{
    if( DFLT_ESC_AUTO_SUPER != GetEscapement() &&
        DFLT_ESC_AUTO_SUB != GetEscapement() )
    {
        const tools::Long nAscent = nOldAscent +
                             ( static_cast<tools::Long>(m_nOrgHeight) * GetEscapement() ) / 100;
        if ( nAscent>0 )
            return std::max<sal_uInt16>( nAscent, m_nOrgAscent );
    }
    return m_nOrgAscent;
}

void SwFont::SetDiffFnt( const SfxItemSet *pAttrSet,
                         const IDocumentSettingAccess *pIDocumentSettingAccess )
{
    mxBackColor.reset();

    if( pAttrSet )
    {

        if( const SvxFontItem* pFont = pAttrSet->GetItemIfSet( RES_CHRATR_FONT ) )
        {
            m_aSub[SwFontScript::Latin].SetFamily( pFont->GetFamily() );
            m_aSub[SwFontScript::Latin].Font::SetFamilyName( pFont->GetFamilyName() );
            m_aSub[SwFontScript::Latin].Font::SetStyleName( pFont->GetStyleName() );
            m_aSub[SwFontScript::Latin].Font::SetPitch( pFont->GetPitch() );
            m_aSub[SwFontScript::Latin].Font::SetCharSet( pFont->GetCharSet() );
        }
        if( const SvxFontHeightItem *pHeight = pAttrSet->GetItemIfSet( RES_CHRATR_FONTSIZE ) )
        {
            m_aSub[SwFontScript::Latin].SvxFont::SetPropr( 100 );
            m_aSub[SwFontScript::Latin].m_aSize = m_aSub[SwFontScript::Latin].Font::GetFontSize();
            Size aTmpSize = m_aSub[SwFontScript::Latin].m_aSize;
            aTmpSize.setHeight( pHeight->GetHeight() );
            m_aSub[SwFontScript::Latin].SetSize( aTmpSize );
        }
        if( const SvxPostureItem* pItem = pAttrSet->GetItemIfSet( RES_CHRATR_POSTURE ) )
            m_aSub[SwFontScript::Latin].Font::SetItalic( pItem->GetPosture() );
        if( const SvxWeightItem* pItem = pAttrSet->GetItemIfSet( RES_CHRATR_WEIGHT ) )
            m_aSub[SwFontScript::Latin].Font::SetWeight( pItem->GetWeight() );
        if( const SvxLanguageItem* pItem = pAttrSet->GetItemIfSet( RES_CHRATR_LANGUAGE ) )
            m_aSub[SwFontScript::Latin].SetLanguage( pItem->GetLanguage() );

        if( const SvxFontItem* pFont = pAttrSet->GetItemIfSet( RES_CHRATR_CJK_FONT ) )
        {
            m_aSub[SwFontScript::CJK].SetFamily( pFont->GetFamily() );
            m_aSub[SwFontScript::CJK].Font::SetFamilyName( pFont->GetFamilyName() );
            m_aSub[SwFontScript::CJK].Font::SetStyleName( pFont->GetStyleName() );
            m_aSub[SwFontScript::CJK].Font::SetPitch( pFont->GetPitch() );
            m_aSub[SwFontScript::CJK].Font::SetCharSet( pFont->GetCharSet() );
        }
        if( const SvxFontHeightItem* pHeight = pAttrSet->GetItemIfSet( RES_CHRATR_CJK_FONTSIZE) )
        {
            m_aSub[SwFontScript::CJK].SvxFont::SetPropr( 100 );
            m_aSub[SwFontScript::CJK].m_aSize = m_aSub[SwFontScript::CJK].Font::GetFontSize();
            Size aTmpSize = m_aSub[SwFontScript::CJK].m_aSize;
            aTmpSize.setHeight( pHeight->GetHeight() );
            m_aSub[SwFontScript::CJK].SetSize( aTmpSize );
        }
        if( const SvxPostureItem* pItem = pAttrSet->GetItemIfSet( RES_CHRATR_CJK_POSTURE ) )
            m_aSub[SwFontScript::CJK].Font::SetItalic( pItem->GetPosture() );
        if( const SvxWeightItem* pItem = pAttrSet->GetItemIfSet( RES_CHRATR_CJK_WEIGHT ) )
            m_aSub[SwFontScript::CJK].Font::SetWeight( pItem->GetWeight() );
        if( const SvxLanguageItem* pItem = pAttrSet->GetItemIfSet( RES_CHRATR_CJK_LANGUAGE ) )
        {
            LanguageType eNewLang = pItem->GetLanguage();
            m_aSub[SwFontScript::CJK].SetLanguage( eNewLang );
            m_aSub[SwFontScript::Latin].SetCJKContextLanguage( eNewLang );
            m_aSub[SwFontScript::CJK].SetCJKContextLanguage( eNewLang );
            m_aSub[SwFontScript::CTL].SetCJKContextLanguage( eNewLang );
        }

        if( const SvxFontItem* pFont = pAttrSet->GetItemIfSet( RES_CHRATR_CTL_FONT ) )
        {
            m_aSub[SwFontScript::CTL].SetFamily( pFont->GetFamily() );
            m_aSub[SwFontScript::CTL].Font::SetFamilyName( pFont->GetFamilyName() );
            m_aSub[SwFontScript::CTL].Font::SetStyleName( pFont->GetStyleName() );
            m_aSub[SwFontScript::CTL].Font::SetPitch( pFont->GetPitch() );
            m_aSub[SwFontScript::CTL].Font::SetCharSet( pFont->GetCharSet() );
        }
        if( const SvxFontHeightItem* pHeight = pAttrSet->GetItemIfSet( RES_CHRATR_CTL_FONTSIZE ) )
        {
            m_aSub[SwFontScript::CTL].SvxFont::SetPropr( 100 );
            m_aSub[SwFontScript::CTL].m_aSize = m_aSub[SwFontScript::CTL].Font::GetFontSize();
            Size aTmpSize = m_aSub[SwFontScript::CTL].m_aSize;
            aTmpSize.setHeight( pHeight->GetHeight() );
            m_aSub[SwFontScript::CTL].SetSize( aTmpSize );
        }
        if( const SvxPostureItem* pItem = pAttrSet->GetItemIfSet( RES_CHRATR_CTL_POSTURE ) )
            m_aSub[SwFontScript::CTL].Font::SetItalic(pItem->GetPosture() );
        if( const SvxWeightItem* pItem = pAttrSet->GetItemIfSet( RES_CHRATR_CTL_WEIGHT ) )
            m_aSub[SwFontScript::CTL].Font::SetWeight( pItem->GetWeight() );
        if( const SvxLanguageItem* pItem = pAttrSet->GetItemIfSet( RES_CHRATR_CTL_LANGUAGE ) )
            m_aSub[SwFontScript::CTL].SetLanguage( pItem->GetLanguage() );

        if( const SvxUnderlineItem* pItem = pAttrSet->GetItemIfSet( RES_CHRATR_UNDERLINE ) )
        {
            SetUnderline( pItem->GetLineStyle() );
            SetUnderColor( pItem->GetColor() );
        }
        if( const SvxOverlineItem* pItem = pAttrSet->GetItemIfSet( RES_CHRATR_OVERLINE ) )
        {
            SetOverline( pItem->GetLineStyle() );
            SetOverColor( pItem->GetColor() );
        }
        if( const SvxCrossedOutItem* pItem = pAttrSet->GetItemIfSet( RES_CHRATR_CROSSEDOUT ) )
            SetStrikeout( pItem->GetStrikeout() );
        if( const SvxColorItem* pItem = pAttrSet->GetItemIfSet( RES_CHRATR_COLOR ) )
            SetColor( pItem->GetValue() );
        if( const SvxEmphasisMarkItem* pItem = pAttrSet->GetItemIfSet( RES_CHRATR_EMPHASIS_MARK ))
            SetEmphasisMark( pItem->GetEmphasisMark() );

        SetTransparent( true );
        SetAlign( ALIGN_BASELINE );
        if( const SvxContourItem* pItem = pAttrSet->GetItemIfSet( RES_CHRATR_CONTOUR ) )
            SetOutline( pItem->GetValue() );
        if( const SvxShadowedItem* pItem = pAttrSet->GetItemIfSet( RES_CHRATR_SHADOWED ) )
            SetShadow( pItem->GetValue() );
        if( const SvxCharReliefItem* pItem = pAttrSet->GetItemIfSet( RES_CHRATR_RELIEF ) )
            SetRelief( pItem->GetValue() );
        if( const SvxShadowedItem* pItem = pAttrSet->GetItemIfSet( RES_CHRATR_SHADOWED ))
            SetPropWidth( pItem->GetValue() ? 50 : 100 );
        if( const SvxAutoKernItem* pItem = pAttrSet->GetItemIfSet( RES_CHRATR_AUTOKERN ) )
        {
            if( pItem->GetValue() )
            {
                SetAutoKern( ( !pIDocumentSettingAccess ||
                               !pIDocumentSettingAccess->get(DocumentSettingId::KERN_ASIAN_PUNCTUATION) ) ?
                                FontKerning::FontSpecific :
                                FontKerning::Asian );
            }
            else
                SetAutoKern( FontKerning::NONE );
        }
        if( const SvxWordLineModeItem* pItem = pAttrSet->GetItemIfSet( RES_CHRATR_WORDLINEMODE ) )
            SetWordLineMode( pItem->GetValue() );

        if( const SvxEscapementItem* pEsc = pAttrSet->GetItemIfSet( RES_CHRATR_ESCAPEMENT ) )
        {
            SetEscapement( pEsc->GetEsc() );
            if( m_aSub[SwFontScript::Latin].IsEsc() )
                SetProportion( pEsc->GetProportionalHeight() );
        }
        if( const SvxCaseMapItem* pItem = pAttrSet->GetItemIfSet( RES_CHRATR_CASEMAP ) )
            SetCaseMap( pItem->GetCaseMap() );
        if( const SvxKerningItem* pItem = pAttrSet->GetItemIfSet( RES_CHRATR_KERNING ) )
            SetFixKerning( pItem->GetValue() );
        if( const SvxCharRotateItem* pItem = pAttrSet->GetItemIfSet( RES_CHRATR_ROTATE ) )
            SetVertical( pItem->GetValue() );
        if( const SvxBrushItem* pItem = pAttrSet->GetItemIfSet( RES_CHRATR_BACKGROUND ) )
            mxBackColor = pItem->GetColor();
        if( const SvxBrushItem* pItem = pAttrSet->GetItemIfSet( RES_CHRATR_HIGHLIGHT ) )
            SetHighlightColor(pItem->GetColor());
        if( const SvxBoxItem* pBoxItem = pAttrSet->GetItemIfSet( RES_CHRATR_BOX ) )
        {
            SetTopBorder(pBoxItem->GetTop());
            SetBottomBorder(pBoxItem->GetBottom());
            SetRightBorder(pBoxItem->GetRight());
            SetLeftBorder(pBoxItem->GetLeft());
            SetTopBorderDist(pBoxItem->GetDistance(SvxBoxItemLine::TOP));
            SetBottomBorderDist(pBoxItem->GetDistance(SvxBoxItemLine::BOTTOM));
            SetRightBorderDist(pBoxItem->GetDistance(SvxBoxItemLine::RIGHT));
            SetLeftBorderDist(pBoxItem->GetDistance(SvxBoxItemLine::LEFT));
        }
        if( const SvxShadowItem* pShadowItem = pAttrSet->GetItemIfSet( RES_CHRATR_SHADOW ) )
        {
            SetShadowColor(pShadowItem->GetColor());
            SetShadowWidth(pShadowItem->GetWidth());
            SetShadowLocation(pShadowItem->GetLocation());
        }
        const SvxTwoLinesItem* pTwoLinesItem = pAttrSet->GetItemIfSet( RES_CHRATR_TWO_LINES );
        if( pTwoLinesItem && pTwoLinesItem->GetValue() )
            SetVertical( 0_deg10 );
    }
    else
    {
        Invalidate();
    }
    m_bPaintBlank = false;
    OSL_ENSURE( m_aSub[SwFontScript::Latin].IsTransparent(), "SwFont: Transparent revolution" );
}

SwFont::SwFont( const SwFont &rFont )
    : m_aSub(rFont.m_aSub)
{
    m_nActual = rFont.m_nActual;
    mxBackColor = rFont.mxBackColor;
    m_aHighlightColor = rFont.m_aHighlightColor;
    m_aTopBorder = rFont.m_aTopBorder;
    m_aBottomBorder = rFont.m_aBottomBorder;
    m_aRightBorder = rFont.m_aRightBorder;
    m_aLeftBorder = rFont.m_aLeftBorder;
    m_nTopBorderDist = rFont.m_nTopBorderDist;
    m_nBottomBorderDist = rFont.m_nBottomBorderDist;
    m_nRightBorderDist = rFont.m_nRightBorderDist;
    m_nLeftBorderDist = rFont.m_nLeftBorderDist;
    m_aShadowColor = rFont.m_aShadowColor;
    m_nShadowWidth = rFont.m_nShadowWidth;
    m_aShadowLocation = rFont.m_aShadowLocation;
    m_aUnderColor = rFont.GetUnderColor();
    m_aOverColor  = rFont.GetOverColor();
    m_nToxCount = 0;
    m_nRefCount = 0;
    m_nMetaCount = 0;
    m_nContentControlCount = 0;
    m_nInputFieldCount = 0;
    m_bFontChg = rFont.m_bFontChg;
    m_bOrgChg = rFont.m_bOrgChg;
    m_bPaintBlank = rFont.m_bPaintBlank;
    m_bGreyWave = rFont.m_bGreyWave;
    m_bURL = rFont.m_bURL;
}

SwFont::SwFont( const SwAttrSet* pAttrSet,
                const IDocumentSettingAccess* pIDocumentSettingAccess )
    : m_aSub()
{
    m_nActual = SwFontScript::Latin;
    m_nToxCount = 0;
    m_nRefCount = 0;
    m_nMetaCount = 0;
    m_nContentControlCount = 0;
    m_nInputFieldCount = 0;
    m_bPaintBlank = false;
    m_bGreyWave = false;
    m_bOrgChg = true;
    {
        const SvxFontItem& rFont = pAttrSet->GetFont();
        m_aSub[SwFontScript::Latin].SetFamily( rFont.GetFamily() );
        m_aSub[SwFontScript::Latin].SetFamilyName( rFont.GetFamilyName() );
        m_aSub[SwFontScript::Latin].SetStyleName( rFont.GetStyleName() );
        m_aSub[SwFontScript::Latin].SetPitch( rFont.GetPitch() );
        m_aSub[SwFontScript::Latin].SetCharSet( rFont.GetCharSet() );
        m_aSub[SwFontScript::Latin].SvxFont::SetPropr( 100 ); // 100% of FontSize
        Size aTmpSize = m_aSub[SwFontScript::Latin].m_aSize;
        aTmpSize.setHeight( pAttrSet->GetSize().GetHeight() );
        m_aSub[SwFontScript::Latin].SetSize( aTmpSize );
        m_aSub[SwFontScript::Latin].SetItalic( pAttrSet->GetPosture().GetPosture() );
        m_aSub[SwFontScript::Latin].SetWeight( pAttrSet->GetWeight().GetWeight() );
        m_aSub[SwFontScript::Latin].SetLanguage( pAttrSet->GetLanguage().GetLanguage() );
    }

    {
        const SvxFontItem& rFont = pAttrSet->GetCJKFont();
        m_aSub[SwFontScript::CJK].SetFamily( rFont.GetFamily() );
        m_aSub[SwFontScript::CJK].SetFamilyName( rFont.GetFamilyName() );
        m_aSub[SwFontScript::CJK].SetStyleName( rFont.GetStyleName() );
        m_aSub[SwFontScript::CJK].SetPitch( rFont.GetPitch() );
        m_aSub[SwFontScript::CJK].SetCharSet( rFont.GetCharSet() );
        m_aSub[SwFontScript::CJK].SvxFont::SetPropr( 100 ); // 100% of FontSize
        Size aTmpSize = m_aSub[SwFontScript::CJK].m_aSize;
        aTmpSize.setHeight( pAttrSet->GetCJKSize().GetHeight() );
        m_aSub[SwFontScript::CJK].SetSize( aTmpSize );
        m_aSub[SwFontScript::CJK].SetItalic( pAttrSet->GetCJKPosture().GetPosture() );
        m_aSub[SwFontScript::CJK].SetWeight( pAttrSet->GetCJKWeight().GetWeight() );
        LanguageType eNewLang = pAttrSet->GetCJKLanguage().GetLanguage();
        m_aSub[SwFontScript::CJK].SetLanguage( eNewLang );
        m_aSub[SwFontScript::Latin].SetCJKContextLanguage( eNewLang );
        m_aSub[SwFontScript::CJK].SetCJKContextLanguage( eNewLang );
        m_aSub[SwFontScript::CTL].SetCJKContextLanguage( eNewLang );
    }

    {
        const SvxFontItem& rFont = pAttrSet->GetCTLFont();
        m_aSub[SwFontScript::CTL].SetFamily( rFont.GetFamily() );
        m_aSub[SwFontScript::CTL].SetFamilyName( rFont.GetFamilyName() );
        m_aSub[SwFontScript::CTL].SetStyleName( rFont.GetStyleName() );
        m_aSub[SwFontScript::CTL].SetPitch( rFont.GetPitch() );
        m_aSub[SwFontScript::CTL].SetCharSet( rFont.GetCharSet() );
        m_aSub[SwFontScript::CTL].SvxFont::SetPropr( 100 ); // 100% of FontSize
        Size aTmpSize = m_aSub[SwFontScript::CTL].m_aSize;
        aTmpSize.setHeight( pAttrSet->GetCTLSize().GetHeight() );
        m_aSub[SwFontScript::CTL].SetSize( aTmpSize );
        m_aSub[SwFontScript::CTL].SetItalic( pAttrSet->GetCTLPosture().GetPosture() );
        m_aSub[SwFontScript::CTL].SetWeight( pAttrSet->GetCTLWeight().GetWeight() );
        m_aSub[SwFontScript::CTL].SetLanguage( pAttrSet->GetCTLLanguage().GetLanguage() );
    }
    if ( pAttrSet->GetCharHidden().GetValue() )
        SetUnderline( LINESTYLE_DOTTED );
    else
        SetUnderline( pAttrSet->GetUnderline().GetLineStyle() );
    SetUnderColor( pAttrSet->GetUnderline().GetColor() );
    SetOverline( pAttrSet->GetOverline().GetLineStyle() );
    SetOverColor( pAttrSet->GetOverline().GetColor() );
    SetEmphasisMark( pAttrSet->GetEmphasisMark().GetEmphasisMark() );
    SetStrikeout( pAttrSet->GetCrossedOut().GetStrikeout() );
    SetColor( pAttrSet->GetColor().GetValue() );
    SetTransparent( true );
    SetAlign( ALIGN_BASELINE );
    SetOutline( pAttrSet->GetContour().GetValue() );
    SetShadow( pAttrSet->GetShadowed().GetValue() );
    SetPropWidth( pAttrSet->GetCharScaleW().GetValue() );
    SetRelief( pAttrSet->GetCharRelief().GetValue() );
    if( pAttrSet->GetAutoKern().GetValue() )
    {
        SetAutoKern( ( !pIDocumentSettingAccess ||
                       !pIDocumentSettingAccess->get(DocumentSettingId::KERN_ASIAN_PUNCTUATION) ) ?
                        FontKerning::FontSpecific :
                        FontKerning::Asian );
    }
    else
        SetAutoKern( FontKerning::NONE );
    SetWordLineMode( pAttrSet->GetWordLineMode().GetValue() );
    const SvxEscapementItem &rEsc = pAttrSet->GetEscapement();
    SetEscapement( rEsc.GetEsc() );
    if( m_aSub[SwFontScript::Latin].IsEsc() )
        SetProportion( rEsc.GetProportionalHeight() );
    SetCaseMap( pAttrSet->GetCaseMap().GetCaseMap() );
    SetFixKerning( pAttrSet->GetKerning().GetValue() );
    if( const SvxBrushItem* pItem = pAttrSet->GetItemIfSet( RES_CHRATR_BACKGROUND ) )
        mxBackColor = pItem->GetColor();
    if( const SvxBrushItem* pItem = pAttrSet->GetItemIfSet( RES_CHRATR_HIGHLIGHT ) )
        SetHighlightColor(pItem->GetColor());
    else
        SetHighlightColor(COL_TRANSPARENT);
    if( const SvxBoxItem* pBoxItem = pAttrSet->GetItemIfSet( RES_CHRATR_BOX ) )
    {
        SetTopBorder(pBoxItem->GetTop());
        SetBottomBorder(pBoxItem->GetBottom());
        SetRightBorder(pBoxItem->GetRight());
        SetLeftBorder(pBoxItem->GetLeft());
        SetTopBorderDist(pBoxItem->GetDistance(SvxBoxItemLine::TOP));
        SetBottomBorderDist(pBoxItem->GetDistance(SvxBoxItemLine::BOTTOM));
        SetRightBorderDist(pBoxItem->GetDistance(SvxBoxItemLine::RIGHT));
        SetLeftBorderDist(pBoxItem->GetDistance(SvxBoxItemLine::LEFT));
    }
    else
    {
        SetTopBorder(nullptr);
        SetBottomBorder(nullptr);
        SetRightBorder(nullptr);
        SetLeftBorder(nullptr);
        SetTopBorderDist(0);
        SetBottomBorderDist(0);
        SetRightBorderDist(0);
        SetLeftBorderDist(0);
    }

    if( const SvxShadowItem* pShadowItem = pAttrSet->GetItemIfSet( RES_CHRATR_SHADOW ) )
    {
        SetShadowColor(pShadowItem->GetColor());
        SetShadowWidth(pShadowItem->GetWidth());
        SetShadowLocation(pShadowItem->GetLocation());
    }
    else
    {
        SetShadowColor(COL_TRANSPARENT);
        SetShadowWidth(0);
        SetShadowLocation(SvxShadowLocation::NONE);
    }

    const SvxTwoLinesItem& rTwoLinesItem = pAttrSet->Get2Lines();
    if ( ! rTwoLinesItem.GetValue() )
        SetVertical( pAttrSet->GetCharRotate().GetValue() );
    else
        SetVertical( 0_deg10 );
    if( pIDocumentSettingAccess && pIDocumentSettingAccess->get( DocumentSettingId::SMALL_CAPS_PERCENTAGE_66 ))
    {
        m_aSub[ SwFontScript::Latin ].m_bSmallCapsPercentage66 = true;
        m_aSub[ SwFontScript::CJK ].m_bSmallCapsPercentage66 = true;
        m_aSub[ SwFontScript::CTL ].m_bSmallCapsPercentage66 = true;
    }
}

SwFont::~SwFont()
{
}

SwFont& SwFont::operator=( const SwFont &rFont )
{
    if (this != &rFont)
    {
        m_aSub[SwFontScript::Latin] = rFont.m_aSub[SwFontScript::Latin];
        m_aSub[SwFontScript::CJK] = rFont.m_aSub[SwFontScript::CJK];
        m_aSub[SwFontScript::CTL] = rFont.m_aSub[SwFontScript::CTL];
        m_nActual = rFont.m_nActual;
        mxBackColor = rFont.mxBackColor;
        m_aHighlightColor = rFont.m_aHighlightColor;
        m_aTopBorder = rFont.m_aTopBorder;
        m_aBottomBorder = rFont.m_aBottomBorder;
        m_aRightBorder = rFont.m_aRightBorder;
        m_aLeftBorder = rFont.m_aLeftBorder;
        m_nTopBorderDist = rFont.m_nTopBorderDist;
        m_nBottomBorderDist = rFont.m_nBottomBorderDist;
        m_nRightBorderDist = rFont.m_nRightBorderDist;
        m_nLeftBorderDist = rFont.m_nLeftBorderDist;
        m_aShadowColor = rFont.m_aShadowColor;
        m_nShadowWidth = rFont.m_nShadowWidth;
        m_aShadowLocation = rFont.m_aShadowLocation;
        m_aUnderColor = rFont.GetUnderColor();
        m_aOverColor  = rFont.GetOverColor();
        m_nToxCount = 0;
        m_nRefCount = 0;
        m_nMetaCount = 0;
        m_nContentControlCount = 0;
        m_nInputFieldCount = 0;
        m_bFontChg = rFont.m_bFontChg;
        m_bOrgChg = rFont.m_bOrgChg;
        m_bPaintBlank = rFont.m_bPaintBlank;
        m_bGreyWave = rFont.m_bGreyWave;
        m_bURL = rFont.m_bURL;
    }
    return *this;
}

void SwFont::AllocFontCacheId( SwViewShell const *pSh, SwFontScript nWhich )
{
    SwFntAccess aFntAccess( m_aSub[nWhich].m_nFontCacheId, m_aSub[nWhich].m_nFontIndex,
                            &m_aSub[nWhich], pSh, true );
}

bool SwSubFont::IsSymbol( SwViewShell const *pSh )
{
    SwFntAccess aFntAccess( m_nFontCacheId, m_nFontIndex, this, pSh, false );
    return aFntAccess.Get()->IsSymbol();
}

bool SwSubFont::ChgFnt( SwViewShell const *pSh, OutputDevice& rOut )
{
    if ( pLastFont )
        pLastFont->Unlock();
    SwFntAccess aFntAccess( m_nFontCacheId, m_nFontIndex, this, pSh, true );
    SV_STAT( nChangeFont );

    pLastFont = aFntAccess.Get();

    pLastFont->SetDevFont( pSh, rOut );

    pLastFont->Lock();
    return LINESTYLE_NONE != GetUnderline() ||
           LINESTYLE_NONE != GetOverline()  ||
           STRIKEOUT_NONE != GetStrikeout();
}

void SwFont::ChgPhysFnt( SwViewShell const *pSh, OutputDevice& rOut )
{
    if( m_bOrgChg && m_aSub[m_nActual].IsEsc() )
    {
        const sal_uInt8 nOldProp = m_aSub[m_nActual].GetPropr();
        SetProportion( 100 );
        ChgFnt( pSh, rOut );
        SwFntAccess aFntAccess( m_aSub[m_nActual].m_nFontCacheId, m_aSub[m_nActual].m_nFontIndex,
                                &m_aSub[m_nActual], pSh );
        m_aSub[m_nActual].m_nOrgHeight = aFntAccess.Get()->GetFontHeight( pSh, rOut );
        m_aSub[m_nActual].m_nOrgAscent = aFntAccess.Get()->GetFontAscent( pSh, rOut );
        SetProportion( nOldProp );
        m_bOrgChg = false;
    }

    if( m_bFontChg )
    {
        ChgFnt( pSh, rOut );
        m_bFontChg = m_bOrgChg;
    }
    if( rOut.GetTextLineColor() != m_aUnderColor )
        rOut.SetTextLineColor( m_aUnderColor );
    if( rOut.GetOverlineColor() != m_aOverColor )
        rOut.SetOverlineColor( m_aOverColor );
}

//         Height = MaxAscent + MaxDescent
//      MaxAscent = Max (T1_ascent, T2_ascent + (Esc * T1_height) );
//     MaxDescent = Max (T1_height-T1_ascent,
//                       T2_height-T2_ascent - (Esc * T1_height)
sal_uInt16 SwSubFont::CalcEscHeight( const sal_uInt16 nOldHeight,
                              const sal_uInt16 nOldAscent  ) const
{
    if( DFLT_ESC_AUTO_SUPER != GetEscapement() &&
        DFLT_ESC_AUTO_SUB != GetEscapement() )
    {
        tools::Long nDescent = nOldHeight - nOldAscent -
                             ( static_cast<tools::Long>(m_nOrgHeight) * GetEscapement() ) / 100;
        const sal_uInt16 nDesc = nDescent>0
                ? std::max<sal_uInt16>( nDescent, m_nOrgHeight - m_nOrgAscent)
                : m_nOrgHeight - m_nOrgAscent;
        return ( nDesc + CalcEscAscent( nOldAscent ) );
    }
    return m_nOrgHeight;
}

short SwSubFont::CheckKerning_( )
{
    short nKernx = - short( Font::GetFontSize().Height() / 6 );

    if ( nKernx < GetFixKerning() )
        return GetFixKerning();
    return nKernx;
}

sal_uInt16 SwSubFont::GetAscent( SwViewShell const *pSh, const OutputDevice& rOut )
{
    SwFntAccess aFntAccess( m_nFontCacheId, m_nFontIndex, this, pSh );
    const sal_uInt16 nAscent = aFntAccess.Get()->GetFontAscent( pSh, rOut );
    return GetEscapement() ? CalcEscAscent( nAscent ) : nAscent;
}

sal_uInt16 SwSubFont::GetHeight( SwViewShell const *pSh, const OutputDevice& rOut )
{
    SV_STAT( nGetTextSize );
    SwFntAccess aFntAccess( m_nFontCacheId, m_nFontIndex, this, pSh );
    const sal_uInt16 nHeight = aFntAccess.Get()->GetFontHeight( pSh, rOut );
    if ( GetEscapement() )
    {
        const sal_uInt16 nAscent = aFntAccess.Get()->GetFontAscent( pSh, rOut );
        return CalcEscHeight( nHeight, nAscent ); // + nLeading;
    }
    return nHeight; // + nLeading;
}

sal_uInt16 SwSubFont::GetHangingBaseline( SwViewShell const *pSh, const OutputDevice& rOut )
{
    SwFntAccess aFntAccess( m_nFontCacheId, m_nFontIndex, this, pSh );
    return aFntAccess.Get()->GetFontHangingBaseline( pSh, rOut );
}

Size SwSubFont::GetTextSize_( SwDrawTextInfo& rInf )
{
    // Robust: the font is supposed to be set already, but better safe than
    // sorry...
    if ( !pLastFont || pLastFont->GetOwner() != m_nFontCacheId ||
         !IsSameInstance( rInf.GetpOut()->GetFont() ) )
        ChgFnt( rInf.GetShell(), rInf.GetOut() );

    SwDigitModeModifier aDigitModeModifier(rInf.GetOut(), rInf.GetFont()->GetLanguage(),
                                           SwModule::get()->GetCTLTextNumerals());

    Size aTextSize;
    TextFrameIndex const nLn = rInf.GetLen() == TextFrameIndex(COMPLETE_STRING)
            ? TextFrameIndex(rInf.GetText().getLength())
            : rInf.GetLen();
    rInf.SetLen( nLn );

    if( IsCapital() && nLn )
    {
        if (rInf.GetMeasureLen() != TextFrameIndex(COMPLETE_STRING))
        {
            rInf.SetLen(rInf.GetMeasureLen());
            rInf.SetMeasureLen(TextFrameIndex(COMPLETE_STRING));
        }
        aTextSize = GetCapitalSize( rInf );
    }
    else
    {
        SV_STAT( nGetTextSize );
        tools::Long nOldKern = rInf.GetKern();
        const OUString oldText = rInf.GetText();
        rInf.SetKern( CheckKerning() );
        if ( !IsCaseMap() )
            aTextSize = pLastFont->GetTextSize( rInf );
        else
        {
            const OUString aTmp = CalcCaseMap( rInf.GetText() );
            const OUString oldStr = rInf.GetText();
            bool bCaseMapLengthDiffers(aTmp.getLength() != oldStr.getLength());

            if(bCaseMapLengthDiffers && rInf.GetLen())
            {
                // If the length of the original string and the CaseMapped one
                // are different, it is necessary to handle the given text part as
                // a single snippet since its size may differ, too.
                TextFrameIndex const nOldIdx(rInf.GetIdx());
                TextFrameIndex const nOldLen(rInf.GetLen());
                TextFrameIndex const nOldMeasureLen(rInf.GetMeasureLen());
                const OUString aSnippet(oldStr.copy(sal_Int32(nOldIdx), sal_Int32(nOldLen)));
                const OUString aNewText(CalcCaseMap(aSnippet));

                rInf.SetText( aNewText );
                rInf.SetIdx( TextFrameIndex(0) );
                rInf.SetLen( TextFrameIndex(aNewText.getLength()) );
                if (nOldMeasureLen != TextFrameIndex(COMPLETE_STRING))
                {
                    const OUString aMeasureSnippet(oldStr.copy(sal_Int32(nOldIdx), sal_Int32(nOldMeasureLen)));
                    const OUString aNewMeasureText(CalcCaseMap(aMeasureSnippet));
                    rInf.SetMeasureLen(TextFrameIndex(aNewMeasureText.getLength()));
                }

                aTextSize = pLastFont->GetTextSize( rInf );

                rInf.SetIdx( nOldIdx );
                rInf.SetLen( nOldLen );
                rInf.SetMeasureLen(nOldMeasureLen);
            }
            else
            {
                rInf.SetText( aTmp );
                aTextSize = pLastFont->GetTextSize( rInf );
            }

            rInf.SetText(oldStr);
        }
        rInf.SetKern( nOldKern );
        rInf.SetText(oldText);
        // A word that's longer than one line, with escapement at the line
        // break, must report its effective height.
        if( GetEscapement() )
        {
            const sal_uInt16 nAscent = pLastFont->GetFontAscent( rInf.GetShell(),
                                                             rInf.GetOut() );
            aTextSize.setHeight(
                static_cast<tools::Long>(CalcEscHeight( o3tl::narrowing<sal_uInt16>(aTextSize.Height()), nAscent)) );
        }
    }

    if (TextFrameIndex(1) == rInf.GetLen()
        && CH_TXT_ATR_FIELDSTART == rInf.GetText()[sal_Int32(rInf.GetIdx())])
    {
        SAL_WARN("sw", "this is meant to be dead code");
        TextFrameIndex const nOldIdx(rInf.GetIdx());
        TextFrameIndex const nOldLen(rInf.GetLen());
        OUString aOldText(rInf.GetText());
        const OUString aNewText(CH_TXT_ATR_SUBST_FIELDSTART);
        rInf.SetTextIdxLen(aNewText, TextFrameIndex(0), TextFrameIndex(aNewText.getLength()));
        aTextSize = pLastFont->GetTextSize( rInf );
        rInf.SetTextIdxLen(aOldText, nOldIdx, nOldLen);
    }
    else if (TextFrameIndex(1) == rInf.GetLen()
            && CH_TXT_ATR_FIELDEND == rInf.GetText()[sal_Int32(rInf.GetIdx())])
    {
        SAL_WARN("sw", "this is meant to be dead code");
        TextFrameIndex const nOldIdx(rInf.GetIdx());
        TextFrameIndex const nOldLen(rInf.GetLen());
        OUString aOldText(rInf.GetText());
        const OUString aNewText(CH_TXT_ATR_SUBST_FIELDEND);
        rInf.SetTextIdxLen(aNewText, TextFrameIndex(0), TextFrameIndex(aNewText.getLength()));
        aTextSize = pLastFont->GetTextSize( rInf );
        rInf.SetTextIdxLen(aOldText, nOldIdx, nOldLen);
    }

    return aTextSize;
}

void SwSubFont::DrawText_( SwDrawTextInfo &rInf, const bool bGrey )
{
    rInf.SetGreyWave( bGrey );
    TextFrameIndex const nLn(rInf.GetText().getLength());
    if( !rInf.GetLen() || !nLn )
        return;
    if (TextFrameIndex(COMPLETE_STRING) == rInf.GetLen())
        rInf.SetLen( nLn );

    FontLineStyle nOldUnder = LINESTYLE_NONE;
    SwUnderlineFont* pUnderFnt = nullptr;

    if( rInf.GetUnderFnt() )
    {
        nOldUnder = GetUnderline();
        SetUnderline( LINESTYLE_NONE );
        pUnderFnt = rInf.GetUnderFnt();
    }

    if( !pLastFont || pLastFont->GetOwner() != m_nFontCacheId )
        ChgFnt( rInf.GetShell(), rInf.GetOut() );

    // change glyph scaling, if needed
    if ( GetPropWidth() != rInf.GetScaleWidth() )
    {
        SetPropWidth( rInf.GetScaleWidth() );
        ChgFnt( rInf.GetShell(), rInf.GetOut() );
    }

    SwDigitModeModifier aDigitModeModifier(rInf.GetOut(), rInf.GetFont()->GetLanguage(),
                                           SwModule::get()->GetCTLTextNumerals());

    const Point aOldPos(rInf.GetPos());
    Point aPos( rInf.GetPos() );

    if( GetEscapement() )
        CalcEsc( rInf, aPos );

    rInf.SetPos( aPos );
    rInf.SetKern( CheckKerning() + rInf.GetCharacterSpacing() / SPACING_PRECISION_FACTOR +
                    // modify letter spacing for the actual line
                    // according to the available extra word spacing
                    // to get the desired word spacing
                    rInf.GetLetterSpacing() );

    if( IsCapital() )
        DrawCapital( rInf );
    else
    {
        SV_STAT( nDrawText );
        if ( !IsCaseMap() )
            pLastFont->DrawText( rInf );
        else
        {
            const OUString oldStr = rInf.GetText();
            const OUString aString( CalcCaseMap(oldStr) );
            bool bCaseMapLengthDiffers(aString.getLength() != oldStr.getLength());

            if(bCaseMapLengthDiffers && rInf.GetLen())
            {
                // If the length of the original string and the CaseMapped one
                // are different, it is necessary to handle the given text part as
                // a single snippet since its size may differ, too.
                TextFrameIndex const nOldIdx(rInf.GetIdx());
                TextFrameIndex const nOldLen(rInf.GetLen());
                const OUString aSnippet(oldStr.copy(sal_Int32(nOldIdx), sal_Int32(nOldLen)));
                const OUString aNewText = CalcCaseMap(aSnippet);

                rInf.SetText( aNewText );
                rInf.SetIdx( TextFrameIndex(0) );
                rInf.SetLen( TextFrameIndex(aNewText.getLength()) );

                pLastFont->DrawText( rInf );

                rInf.SetIdx( nOldIdx );
                rInf.SetLen( nOldLen );
            }
            else
            {
                rInf.SetText( aString );
                pLastFont->DrawText( rInf );
            }

            rInf.SetText(oldStr);
        }
    }

    if( pUnderFnt && nOldUnder != LINESTYLE_NONE )
    {
        Size aFontSize = GetTextSize_( rInf );
        const OUString oldStr = rInf.GetText();

        TextFrameIndex const nOldIdx = rInf.GetIdx();
        TextFrameIndex const nOldLen = rInf.GetLen();
        tools::Long nSpace = 0;
        if( rInf.GetSpace() )
        {
            TextFrameIndex nTmpEnd = nOldIdx + nOldLen;
            if (nTmpEnd > TextFrameIndex(oldStr.getLength()))
                nTmpEnd = TextFrameIndex(oldStr.getLength());

            const SwScriptInfo* pSI = rInf.GetScriptInfo();

            const bool bAsianFont =
                ( rInf.GetFont() && SwFontScript::CJK == rInf.GetFont()->GetActual() );
            for (TextFrameIndex nTmp = nOldIdx; nTmp < nTmpEnd; ++nTmp)
            {
                if (CH_BLANK == oldStr[sal_Int32(nTmp)] || bAsianFont ||
                    (nTmp + TextFrameIndex(1) < TextFrameIndex(oldStr.getLength())
                     && pSI
                     && i18n::ScriptType::ASIAN == pSI->ScriptType(nTmp + TextFrameIndex(1))))
                {
                    ++nSpace;
                }
            }

            // if next portion if a hole portion we do not consider any
            // extra space added because the last character was ASIAN
            if ( nSpace && rInf.IsSpaceStop() && bAsianFont )
                 --nSpace;

            nSpace *= rInf.GetSpace() / SPACING_PRECISION_FACTOR;
        }

        rInf.SetWidth( sal_uInt16(aFontSize.Width() + nSpace) );
        rInf.SetTextIdxLen( u"  "_ustr, TextFrameIndex(0), TextFrameIndex(2) );
        SetUnderline( nOldUnder );
        rInf.SetUnderFnt( nullptr );

        // set position for underline font
        rInf.SetPos( pUnderFnt->GetPos() );

        pUnderFnt->GetFont().DrawStretchText_( rInf );

        rInf.SetUnderFnt( pUnderFnt );
        rInf.SetTextIdxLen(oldStr, nOldIdx, nOldLen);
    }

    rInf.SetPos(aOldPos);
}

void SwSubFont::DrawStretchText_( SwDrawTextInfo &rInf )
{
    if( !rInf.GetLen() || !rInf.GetText().getLength() )
        return;

    FontLineStyle nOldUnder = LINESTYLE_NONE;
    SwUnderlineFont* pUnderFnt = nullptr;

    if( rInf.GetUnderFnt() )
    {
        nOldUnder = GetUnderline();
        SetUnderline( LINESTYLE_NONE );
        pUnderFnt = rInf.GetUnderFnt();
    }

    if ( !pLastFont || pLastFont->GetOwner() != m_nFontCacheId )
        ChgFnt( rInf.GetShell(), rInf.GetOut() );

    SwDigitModeModifier aDigitModeModifier(rInf.GetOut(), rInf.GetFont()->GetLanguage(),
                                           SwModule::get()->GetCTLTextNumerals());

    rInf.ApplyAutoColor();

    const Point aOldPos(rInf.GetPos());
    Point aPos( rInf.GetPos() );

    if( GetEscapement() )
        CalcEsc( rInf, aPos );

    rInf.SetKern( CheckKerning() + rInf.GetCharacterSpacing() / SPACING_PRECISION_FACTOR );
    rInf.SetPos( aPos );

    if( IsCapital() )
        DrawStretchCapital( rInf );
    else
    {
        SV_STAT( nDrawStretchText );

        if ( rInf.GetFrame() )
        {
            if ( rInf.GetFrame()->IsRightToLeft() )
                rInf.GetFrame()->SwitchLTRtoRTL( aPos );

            if ( rInf.GetFrame()->IsVertical() )
                rInf.GetFrame()->SwitchHorizontalToVertical( aPos );

            rInf.SetPos( aPos );
        }

        if ( !IsCaseMap() )
            rInf.GetOut().DrawStretchText( aPos, rInf.GetWidth(),
                rInf.GetText(), sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()));
        else
            rInf.GetOut().DrawStretchText( aPos, rInf.GetWidth(),
                    CalcCaseMap(rInf.GetText()),
                    sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()));
    }

    if( pUnderFnt && nOldUnder != LINESTYLE_NONE )
    {
        const OUString oldStr = rInf.GetText();
        TextFrameIndex const nOldIdx = rInf.GetIdx();
        TextFrameIndex const nOldLen = rInf.GetLen();
        rInf.SetTextIdxLen( u"  "_ustr, TextFrameIndex(0), TextFrameIndex(2) );
        SetUnderline( nOldUnder );
        rInf.SetUnderFnt( nullptr );

        // set position for underline font
        rInf.SetPos( pUnderFnt->GetPos() );

        pUnderFnt->GetFont().DrawStretchText_( rInf );

        rInf.SetUnderFnt( pUnderFnt );
        rInf.SetTextIdxLen(oldStr, nOldIdx, nOldLen);
    }

    rInf.SetPos(aOldPos);
}

TextFrameIndex SwSubFont::GetModelPositionForViewPoint_( SwDrawTextInfo& rInf )
{
    if ( !pLastFont || pLastFont->GetOwner() != m_nFontCacheId )
        ChgFnt( rInf.GetShell(), rInf.GetOut() );

    SwDigitModeModifier aDigitModeModifier(rInf.GetOut(), rInf.GetFont()->GetLanguage(),
                                           SwModule::get()->GetCTLTextNumerals());

    TextFrameIndex const nLn = rInf.GetLen() == TextFrameIndex(COMPLETE_STRING)
            ? TextFrameIndex(rInf.GetText().getLength())
            : rInf.GetLen();
    rInf.SetLen( nLn );
    TextFrameIndex nCursor(0);
    if( IsCapital() && nLn )
        nCursor = GetCapitalCursorOfst( rInf );
    else
    {
        const OUString oldText = rInf.GetText();
        tools::Long nOldKern = rInf.GetKern();
        rInf.SetKern( CheckKerning() );
        SV_STAT( nGetTextSize );
        if ( !IsCaseMap() )
            nCursor = pLastFont->GetModelPositionForViewPoint( rInf );
        else
        {
            rInf.SetText( CalcCaseMap( rInf.GetText() ) );
            nCursor = pLastFont->GetModelPositionForViewPoint( rInf );
        }
        rInf.SetKern( nOldKern );
        rInf.SetText(oldText);
    }
    return nCursor;
}

void SwSubFont::CalcEsc( SwDrawTextInfo const & rInf, Point& rPos )
{
    tools::Long nOfst;

    bool bVert = false;
    bool bVertLRBT = false;
    if (rInf.GetFrame())
    {
        bVert = rInf.GetFrame()->IsVertical();
        bVertLRBT = rInf.GetFrame()->IsVertLRBT();
    }
    const Degree10 nDir = UnMapDirection(GetOrientation(), bVert, bVertLRBT);

    switch ( GetEscapement() )
    {
    case DFLT_ESC_AUTO_SUB :
        nOfst = m_nOrgHeight - m_nOrgAscent -
            pLastFont->GetFontHeight( rInf.GetShell(), rInf.GetOut() ) +
            pLastFont->GetFontAscent( rInf.GetShell(), rInf.GetOut() );

        switch ( nDir.get() )
        {
        case 0 :
            rPos.AdjustY(nOfst );
            break;
        case 900 :
            rPos.AdjustX(nOfst );
            break;
        case 2700 :
            rPos.AdjustX( -nOfst );
            break;
        }

        break;
    case DFLT_ESC_AUTO_SUPER :
        nOfst = pLastFont->GetFontAscent( rInf.GetShell(), rInf.GetOut() ) -
                m_nOrgAscent;

        switch ( nDir.get() )
        {
        case 0 :
            rPos.AdjustY(nOfst );
            break;
        case 900 :
            rPos.AdjustX(nOfst );
            break;
        case 2700 :
            rPos.AdjustX( -nOfst );
            break;
        }

        break;
    default :
        nOfst = (static_cast<tools::Long>(m_nOrgHeight) * GetEscapement()) / 100;

        switch ( nDir.get() )
        {
        case 0 :
            rPos.AdjustY( -nOfst );
            break;
        case 900 :
            rPos.AdjustX( -nOfst );
            break;
        case 2700 :
            rPos.AdjustX(nOfst );
            break;
        }
    }
}

// used during painting of small capitals
void SwDrawTextInfo::Shift( Degree10 nDir )
{
#ifdef DBG_UTIL
    OSL_ENSURE( m_bPos, "DrawTextInfo: Undefined Position" );
    OSL_ENSURE( m_bSize, "DrawTextInfo: Undefined Width" );
#endif

    const bool bBidiPor = ( GetFrame() && GetFrame()->IsRightToLeft() ) !=
                          ( vcl::text::ComplexTextLayoutFlags::Default != ( vcl::text::ComplexTextLayoutFlags::BiDiRtl & GetpOut()->GetLayoutMode() ) );

    bool bVert = false;
    bool bVertLRBT = false;
    if (GetFrame())
    {
        bVert = GetFrame()->IsVertical();
        bVertLRBT = GetFrame()->IsVertLRBT();
    }
    nDir = bBidiPor ? 1800_deg10 : UnMapDirection(nDir, bVert, bVertLRBT);

    switch ( nDir.get() )
    {
    case 0 :
        m_aPos.AdjustX(GetSize().Width() );
        break;
    case 900 :
        OSL_ENSURE( m_aPos.Y() >= GetSize().Width(), "Going underground" );
        m_aPos.AdjustY( -(GetSize().Width()) );
        break;
    case 1800 :
        m_aPos.AdjustX( -(GetSize().Width()) );
        break;
    case 2700 :
        m_aPos.AdjustY(GetSize().Width() );
        break;
    }
}

/**
 * @note Used for the "continuous underline" feature.
 **/
SwUnderlineFont::SwUnderlineFont(SwFont& rFnt, TextFrameIndex const nEnd, const Point& rPoint)
        : m_aPos( rPoint ), m_nEnd( nEnd ), m_pFont( &rFnt )
{
};

SwUnderlineFont::~SwUnderlineFont()
{
}

/// Helper for filters to find true lineheight of a font
tools::Long AttrSetToLineHeight( const IDocumentSettingAccess& rIDocumentSettingAccess,
                          const SwAttrSet &rSet,
                          const vcl::RenderContext &rOut, sal_Int16 nScript)
{
    SwFont aFont(&rSet, &rIDocumentSettingAccess);
    SwFontScript nActual;
    switch (nScript)
    {
        default:
        case i18n::ScriptType::LATIN:
            nActual = SwFontScript::Latin;
            break;
        case i18n::ScriptType::ASIAN:
            nActual = SwFontScript::CJK;
            break;
        case i18n::ScriptType::COMPLEX:
            nActual = SwFontScript::CTL;
            break;
    }
    aFont.SetActual(nActual);

    vcl::RenderContext &rMutableOut = const_cast<vcl::RenderContext &>(rOut);
    const vcl::Font aOldFont(rMutableOut.GetFont());

    rMutableOut.SetFont(aFont.GetActualFont());
    tools::Long nHeight = rMutableOut.GetTextHeight();

    rMutableOut.SetFont(aOldFont);
    return nHeight;
}

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