/* -*- 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 <sal/config.h>

#include <i18nlangtag/mslangid.hxx>
#include <officecfg/Office/Common.hxx>
#include <sal/log.hxx>
#include <tools/stream.hxx>
#include <unotools/configmgr.hxx>
#include <vcl/metric.hxx>
#include <vcl/outdev.hxx>

#include <font/FontSelectPattern.hxx>
#include <font/PhysicalFontFace.hxx>
#include <font/LogicalFontInstance.hxx>
#include <font/FontMetricData.hxx>
#include <sft.hxx>

#include <com/sun/star/uno/Sequence.hxx>
#include <comphelper/sequence.hxx>
#include <hb-ot.h>

using namespace ::com::sun::star;
using namespace ::com::sun::star::uno;
using namespace ::rtl;

FontMetric::FontMetric()
    : mnAscent(0)
    , mnDescent(0)
    , mnIntLeading(0)
    , mnExtLeading(0)
    , mnLineHeight(0)
    , mnSlant(0)
    , mnBulletOffset(0)
    , mnHangingBaseline(0)
    , mdEmSize(0.0)
    , mdHorCJKAdvanceSize(0.0)
    , mdVertCJKAdvanceSize(0.0)
    , mbFullstopCentered(false)
{}

FontMetric::FontMetric( const FontMetric& rFontMetric ) = default;

FontMetric::FontMetric(vcl::font::PhysicalFontFace const& rFace)
    : FontMetric()
{
    SetFamilyName(rFace.GetFamilyName());
    SetStyleName(rFace.GetStyleName());
    SetCharSet(rFace.IsMicrosoftSymbolEncoded() ? RTL_TEXTENCODING_SYMBOL : RTL_TEXTENCODING_UNICODE);
    SetFamily(rFace.GetFamilyType());
    SetPitch(rFace.GetPitch());
    SetWeight(rFace.GetWeight());
    SetItalic(rFace.GetItalic());
    SetAlignment(TextAlign::ALIGN_TOP);
    SetWidthType(rFace.GetWidthType());
    SetQuality(rFace.GetQuality() );
}

FontMetric::~FontMetric()
{
}

FontMetric& FontMetric::operator=(const FontMetric& rFontMetric) = default;

FontMetric& FontMetric::operator=(FontMetric&& rFontMetric) = default;

bool FontMetric::EqualNoBase( const FontMetric& r ) const
{
    if (mbFullstopCentered != r.mbFullstopCentered)
        return false;
    if( mnAscent     != r.mnAscent )
        return false;
    if( mnDescent    != r.mnDescent )
        return false;
    if( mnIntLeading != r.mnIntLeading )
        return false;
    if( mnExtLeading != r.mnExtLeading )
        return false;
    if( mnSlant      != r.mnSlant )
        return false;

    return true;
}

bool FontMetric::operator==( const FontMetric& r ) const
{
    if( Font::operator!=(r) )
        return false;
    return EqualNoBase(r);
}

bool FontMetric::EqualIgnoreColor( const FontMetric& r ) const
{
    if( !Font::EqualIgnoreColor(r) )
        return false;
    return EqualNoBase(r);
}

size_t FontMetric::GetHashValueNoBase() const
{
    size_t hash = 0;
    o3tl::hash_combine( hash, mbFullstopCentered );
    o3tl::hash_combine( hash, mnAscent );
    o3tl::hash_combine( hash, mnDescent );
    o3tl::hash_combine( hash, mnIntLeading );
    o3tl::hash_combine( hash, mnExtLeading );
    o3tl::hash_combine( hash, mnSlant );
    return hash;
}

size_t FontMetric::GetHashValueIgnoreColor() const
{
    size_t hash = GetHashValueNoBase();
    o3tl::hash_combine( hash, Font::GetHashValueIgnoreColor());
    return hash;
}

FontMetricData::FontMetricData( const vcl::font::FontSelectPattern& rFontSelData )
    : FontAttributes( rFontSelData )
    , mnHeight ( rFontSelData.mnHeight )
    , mnWidth ( rFontSelData.mnWidth )
    , mnOrientation( static_cast<short>(rFontSelData.mnOrientation) )
    , mnAscent( 0 )
    , mnDescent( 0 )
    , mnIntLeading( 0 )
    , mnExtLeading( 0 )
    , mnSlant( 0 )
    , mnMinKashida( 0 )
    , mnHangingBaseline( 0 )
    , mdEmSize(0.0)
    , mdHorCJKAdvanceSize(0.0)
    , mdVertCJKAdvanceSize(0.0)
    , mbFullstopCentered( false )
    , mnBulletOffset( 0 )
    , mnUnderlineSize( 0 )
    , mnUnderlineOffset( 0 )
    , mnBUnderlineSize( 0 )
    , mnBUnderlineOffset( 0 )
    , mnDUnderlineSize( 0 )
    , mnDUnderlineOffset1( 0 )
    , mnDUnderlineOffset2( 0 )
    , mnWUnderlineSize( 0 )
    , mnWUnderlineOffset( 0 )
    , mnAboveUnderlineSize( 0 )
    , mnAboveUnderlineOffset( 0 )
    , mnAboveBUnderlineSize( 0 )
    , mnAboveBUnderlineOffset( 0 )
    , mnAboveDUnderlineSize( 0 )
    , mnAboveDUnderlineOffset1( 0 )
    , mnAboveDUnderlineOffset2( 0 )
    , mnAboveWUnderlineSize( 0 )
    , mnAboveWUnderlineOffset( 0 )
    , mnStrikeoutSize( 0 )
    , mnStrikeoutOffset( 0 )
    , mnBStrikeoutSize( 0 )
    , mnBStrikeoutOffset( 0 )
    , mnDStrikeoutSize( 0 )
    , mnDStrikeoutOffset1( 0 )
    , mnDStrikeoutOffset2( 0 )
{
    // initialize the used font name
    sal_Int32 nTokenPos = 0;
    SetFamilyName( OUString(GetNextFontToken( rFontSelData.GetFamilyName(), nTokenPos )) );
    SetStyleName( rFontSelData.GetStyleName() );
}

bool FontMetricData::ShouldNotUseUnderlineMetrics() const
{
    if (comphelper::IsFuzzing())
        return false;

    css::uno::Sequence<OUString> rNoUnderlineMetricsList(
        officecfg::Office::Common::Misc::FontsDontUseUnderlineMetrics::get());
    if (comphelper::findValue(rNoUnderlineMetricsList, GetFamilyName()) != -1)
    {
        SAL_INFO("vcl.gdi.fontmetric", "Not using underline metrics for: " << GetFamilyName());
        return true;
    }
    return false;
}

bool FontMetricData::ImplInitTextLineSizeHarfBuzz(LogicalFontInstance* pFont)
{
    if (ShouldNotUseUnderlineMetrics())
        return false;

    auto* pHbFont = pFont->GetHbFont();

    hb_position_t nUnderlineSize;
    if (!hb_ot_metrics_get_position(pHbFont, HB_OT_METRICS_TAG_UNDERLINE_SIZE, &nUnderlineSize))
        return false;
    hb_position_t nUnderlineOffset;
    if (!hb_ot_metrics_get_position(pHbFont, HB_OT_METRICS_TAG_UNDERLINE_OFFSET, &nUnderlineOffset))
        return false;
    hb_position_t nStrikeoutSize;
    if (!hb_ot_metrics_get_position(pHbFont, HB_OT_METRICS_TAG_STRIKEOUT_SIZE, &nStrikeoutSize))
        return false;
    hb_position_t nStrikeoutOffset;
    if (!hb_ot_metrics_get_position(pHbFont, HB_OT_METRICS_TAG_STRIKEOUT_OFFSET, &nStrikeoutOffset))
        return false;

    double fScale = 0;
    pFont->GetScale(nullptr, &fScale);

    double nOffset = -nUnderlineOffset * fScale;
    double nSize = nUnderlineSize * fScale;
    double nSize2 = nSize / 2.;
    double nBSize = nSize * 2.;
    double n2Size = nBSize / 3.;

    mnUnderlineSize = std::ceil(nSize);
    mnUnderlineOffset = std::ceil(nOffset);

    mnBUnderlineSize = std::ceil(nBSize);
    mnBUnderlineOffset = std::ceil(nOffset - nSize2);

    mnDUnderlineSize = std::ceil(n2Size);
    mnDUnderlineOffset1 = mnBUnderlineOffset;
    mnDUnderlineOffset2 = mnBUnderlineOffset + mnDUnderlineSize * 2;

    mnWUnderlineSize = mnBUnderlineSize;
    mnWUnderlineOffset = std::ceil(nOffset + nSize);

    nOffset = -nStrikeoutOffset * fScale;
    nSize = nStrikeoutSize * fScale;
    nSize2 = nSize / 2.;
    nBSize = nSize * 2.;
    n2Size = nBSize / 3.;

    mnStrikeoutSize = std::ceil(nSize);
    mnStrikeoutOffset = std::ceil(nOffset);

    mnBStrikeoutSize = std::ceil(nBSize);
    mnBStrikeoutOffset = std::round(nOffset - nSize2);

    mnDStrikeoutSize = std::ceil(n2Size);
    mnDStrikeoutOffset1 = mnBStrikeoutOffset;
    mnDStrikeoutOffset2 = mnBStrikeoutOffset + mnDStrikeoutSize * 2;

    return true;
}

void FontMetricData::ImplInitTextLineSize( const OutputDevice* pDev )
{
    mnBulletOffset = ( pDev->GetTextWidth( OUString( u' ' ) ) - pDev->GetTextWidth( OUString( u'\x00b7' ) ) ) >> 1 ;

    if (ImplInitTextLineSizeHarfBuzz(const_cast<LogicalFontInstance*>(pDev->GetFontInstance())))
        return;

    tools::Long nDescent = mnDescent;
    if ( nDescent <= 0 )
    {
        nDescent = mnAscent / 10;
        if ( !nDescent )
            nDescent = 1;
    }

    // #i55341# for some fonts it is not a good idea to calculate
    // their text line metrics from the real font descent
    // => work around this problem just for these fonts
    if( 3*nDescent > mnAscent )
        nDescent = mnAscent / 3;

    tools::Long nLineHeight = ((nDescent*25)+50) / 100;
    if ( !nLineHeight )
        nLineHeight = 1;
    tools::Long nLineHeight2 = nLineHeight / 2;
    if ( !nLineHeight2 )
        nLineHeight2 = 1;

    tools::Long nBLineHeight = ((nDescent*50)+50) / 100;
    if ( nBLineHeight == nLineHeight )
        nBLineHeight++;
    tools::Long nBLineHeight2 = nBLineHeight/2;
    if ( !nBLineHeight2 )
        nBLineHeight2 = 1;

    tools::Long n2LineHeight = ((nDescent*16)+50) / 100;
    if ( !n2LineHeight )
        n2LineHeight = 1;
    tools::Long n2LineDY = n2LineHeight;
     /* #117909#
      * add some pixels to minimum double line distance on higher resolution devices
      */
    tools::Long nMin2LineDY = 1 + pDev->GetDPIY()/150;
    if ( n2LineDY < nMin2LineDY )
        n2LineDY = nMin2LineDY;
    tools::Long n2LineDY2 = n2LineDY/2;
    if ( !n2LineDY2 )
        n2LineDY2 = 1;

    const vcl::Font& rFont ( pDev->GetFont() );
    bool bCJKVertical = MsLangId::isCJK(rFont.GetLanguage()) && rFont.IsVertical();
    tools::Long nUnderlineOffset = bCJKVertical ? mnDescent : (mnDescent/2 + 1);
    tools::Long nStrikeoutOffset = rFont.IsVertical() ? -((mnAscent - mnDescent) / 2) : -((mnAscent - mnIntLeading) / 3);

    mnUnderlineSize        = nLineHeight;
    mnUnderlineOffset      = nUnderlineOffset - nLineHeight2;

    mnBUnderlineSize       = nBLineHeight;
    mnBUnderlineOffset     = nUnderlineOffset - nBLineHeight2;

    mnDUnderlineSize       = n2LineHeight;
    mnDUnderlineOffset1    = nUnderlineOffset - n2LineDY2 - n2LineHeight;
    mnDUnderlineOffset2    = mnDUnderlineOffset1 + n2LineDY + n2LineHeight;

    tools::Long nWCalcSize = mnDescent;
    if ( nWCalcSize < 6 )
    {
        if ( (nWCalcSize == 1) || (nWCalcSize == 2) )
            mnWUnderlineSize = nWCalcSize;
        else
            mnWUnderlineSize = 3;
    }
    else
        mnWUnderlineSize = ((nWCalcSize*50)+50) / 100;


    // Don't assume that wavelines are never placed below the descent, because for most fonts the waveline
    // is drawn into the text
    mnWUnderlineOffset     = nUnderlineOffset;

    mnStrikeoutSize        = nLineHeight;
    mnStrikeoutOffset      = nStrikeoutOffset - nLineHeight2;

    mnBStrikeoutSize       = nBLineHeight;
    mnBStrikeoutOffset     = nStrikeoutOffset - nBLineHeight2;

    mnDStrikeoutSize       = n2LineHeight;
    mnDStrikeoutOffset1    = nStrikeoutOffset - n2LineDY2 - n2LineHeight;
    mnDStrikeoutOffset2    = mnDStrikeoutOffset1 + n2LineDY + n2LineHeight;

}


void FontMetricData::ImplInitAboveTextLineSize( const OutputDevice* pDev )
{
    ImplInitTextLineSize(pDev);

    tools::Long nIntLeading = mnIntLeading;
    // TODO: assess usage of nLeading below (changed in extleading CWS)
    // if no leading is available, we assume 15% of the ascent
    if ( nIntLeading <= 0 )
    {
        nIntLeading = mnAscent*15/100;
        if ( !nIntLeading )
            nIntLeading = 1;
    }

    tools::Long nCeiling = -mnAscent;

    mnAboveUnderlineSize       = mnUnderlineSize;
    mnAboveUnderlineOffset     = nCeiling + (nIntLeading - mnUnderlineSize + 1) / 2;

    mnAboveBUnderlineSize      = mnBUnderlineSize;
    mnAboveBUnderlineOffset    = nCeiling + (nIntLeading - mnBUnderlineSize + 1) / 2;

    mnAboveDUnderlineSize      = mnDUnderlineSize;
    mnAboveDUnderlineOffset1   = nCeiling + (nIntLeading - 3*mnDUnderlineSize + 1) / 2;
    mnAboveDUnderlineOffset2   = nCeiling + (nIntLeading +   mnDUnderlineSize + 1) / 2;

    mnAboveWUnderlineSize = mnWUnderlineSize;
    mnAboveWUnderlineOffset = nCeiling + (nIntLeading + 1) / 2;
}

void FontMetricData::ImplInitFlags( const OutputDevice* pDev )
{
    const vcl::Font& rFont ( pDev->GetFont() );
    bool bCentered = true;
    if (MsLangId::isCJK(rFont.GetLanguage()))
    {
        tools::Rectangle aRect;
        pDev->GetTextBoundRect( aRect, u"\x3001"_ustr ); // Fullwidth fullstop
        const auto nH = rFont.GetFontSize().Height();
        const auto nB = aRect.Left();
        // Use 18.75% as a threshold to define a centered fullwidth fullstop.
        // In general, nB/nH < 5% for most Japanese fonts.
        bCentered = nB > (((nH >> 1)+nH)>>3);
    }
    SetFullstopCenteredFlag( bCentered );
}

bool FontMetricData::ShouldUseWinMetrics(int nAscent, int nDescent, int nTypoAscent,
                                             int nTypoDescent, int nWinAscent,
                                             int nWinDescent) const
{
    if (comphelper::IsFuzzing())
        return false;

    OUString aFontIdentifier(
        GetFamilyName() + ","
        + OUString::number(nAscent) + "," + OUString::number(nDescent) + ","
        + OUString::number(nTypoAscent) + "," + OUString::number(nTypoDescent) + ","
        + OUString::number(nWinAscent) + "," + OUString::number(nWinDescent));

    css::uno::Sequence<OUString> rWinMetricFontList(
        officecfg::Office::Common::Misc::FontsUseWinMetrics::get());
    if (comphelper::findValue(rWinMetricFontList, aFontIdentifier) != -1)
    {
        SAL_INFO("vcl.gdi.fontmetric", "Using win metrics for: " << aFontIdentifier);
        return true;
    }
    return false;
}

// These are “private” HarfBuzz metrics tags, they are supported by not exposed
// in the public header. They are safe to use, HarfBuzz just does not want to
// advertise them.
constexpr auto ASCENT_OS2 = static_cast<hb_ot_metrics_tag_t>(HB_TAG('O', 'a', 's', 'c'));
constexpr auto DESCENT_OS2 = static_cast<hb_ot_metrics_tag_t>(HB_TAG('O', 'd', 's', 'c'));
constexpr auto LINEGAP_OS2 = static_cast<hb_ot_metrics_tag_t>(HB_TAG('O', 'l', 'g', 'p'));
constexpr auto ASCENT_HHEA = static_cast<hb_ot_metrics_tag_t>(HB_TAG('H', 'a', 's', 'c'));
constexpr auto DESCENT_HHEA = static_cast<hb_ot_metrics_tag_t>(HB_TAG('H', 'd', 's', 'c'));
constexpr auto LINEGAP_HHEA = static_cast<hb_ot_metrics_tag_t>(HB_TAG('H', 'l', 'g', 'p'));

void FontMetricData::ImplCalcLineSpacing(LogicalFontInstance* pFontInstance)
{
    mnAscent = mnDescent = mnExtLeading = mnIntLeading = 0;
    auto* pFace = pFontInstance->GetFontFace();
    auto* pHbFont = pFontInstance->GetHbFont();

    double fScale = 0;
    pFontInstance->GetScale(nullptr, &fScale);
    double fAscent = 0, fDescent = 0, fExtLeading = 0;

    auto aFvar(pFace->GetRawFontData(HB_TAG('f', 'v', 'a', 'r')));
    if (!aFvar.empty())
    {
        // This is a variable font, trust HarfBuzz to give us the right metrics
        // and apply variations to them.
        hb_position_t nAscent, nDescent, nLineGap;
        if (hb_ot_metrics_get_position(pHbFont, HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER, &nAscent)
            && hb_ot_metrics_get_position(pHbFont, HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER,
                                          &nDescent)
            && hb_ot_metrics_get_position(pHbFont, HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP,
                                          &nLineGap))
        {
            fAscent = nAscent * fScale;
            fDescent = -nDescent * fScale;
            fExtLeading = nLineGap * fScale;
        }
    }
    else
    {
        // This is not a variable font, we try to choose the best metrics
        // ourselves for backward comparability:
        //
        // - hhea metrics should be used, since hhea is a mandatory font table
        //   and should always be present.
        // - But if OS/2 is present, it should be used since it is mandatory in
        //   Windows.
        //   OS/2 has Typo and Win metrics, but the later was meant to control
        //   text clipping not line spacing and can be ridiculously large.
        //   Unfortunately many Windows application incorrectly use the Win
        //   metrics (thanks to GDI’s TEXTMETRIC) and old fonts might be
        //   designed with this in mind, so OpenType introduced a flag for
        //   fonts to indicate that they really want to use Typo metrics. So
        //   for best backward compatibility:
        //   - Use Win metrics if available.
        //   - Unless USE_TYPO_METRICS flag is set, in which case use Typo
        //     metrics.

        // Try hhea table first.
        hb_position_t nAscent = 0, nDescent = 0, nLineGap = 0;
        if (hb_ot_metrics_get_position(pHbFont, ASCENT_HHEA, &nAscent)
            && hb_ot_metrics_get_position(pHbFont, DESCENT_HHEA, &nDescent)
            && hb_ot_metrics_get_position(pHbFont, LINEGAP_HHEA, &nLineGap))
        {
            // tdf#107605: Some fonts have weird values here, so check that
            // ascender is +ve and descender is -ve as they normally should.
            if (nAscent >= 0 && nDescent <= 0)
            {
                fAscent = nAscent * fScale;
                fDescent = -nDescent * fScale;
                fExtLeading = nLineGap * fScale;
            }
        }

        // But if OS/2 is present, prefer it.
        hb_position_t nTypoAscent, nTypoDescent, nTypoLineGap, nWinAscent, nWinDescent;
        if (hb_ot_metrics_get_position(pHbFont, ASCENT_OS2, &nTypoAscent)
            && hb_ot_metrics_get_position(pHbFont, DESCENT_OS2, &nTypoDescent)
            && hb_ot_metrics_get_position(pHbFont, LINEGAP_OS2, &nTypoLineGap)
            && hb_ot_metrics_get_position(pHbFont, HB_OT_METRICS_TAG_HORIZONTAL_CLIPPING_ASCENT,
                                          &nWinAscent)
            && hb_ot_metrics_get_position(pHbFont, HB_OT_METRICS_TAG_HORIZONTAL_CLIPPING_DESCENT,
                                          &nWinDescent))
        {
            if ((fAscent == 0.0 && fDescent == 0.0)
                || ShouldUseWinMetrics(nAscent, nDescent, nTypoAscent, nTypoDescent, nWinAscent,
                                       nWinDescent))
            {
                fAscent = nWinAscent * fScale;
                fDescent = nWinDescent * fScale;
                fExtLeading = 0;
            }

            bool bUseTypoMetrics = false;
            {
                // TODO: Use HarfBuzz API instead of raw access
                // https://github.com/harfbuzz/harfbuzz/issues/1920
                sal_uInt16 fsSelection = 0;
                auto aOS2(pFace->GetRawFontData(HB_TAG('O', 'S', '/', '2')));
                SvMemoryStream aStream(const_cast<uint8_t*>(aOS2.data()), aOS2.size(),
                                       StreamMode::READ);
                // Font data are big endian.
                aStream.SetEndian(SvStreamEndian::BIG);
                if (aStream.Seek(vcl::OS2_fsSelection_offset) == vcl::OS2_fsSelection_offset)
                    aStream.ReadUInt16(fsSelection);
                bUseTypoMetrics = fsSelection & (1 << 7);
            }
            if (bUseTypoMetrics && nTypoAscent >= 0 && nTypoDescent <= 0)
            {
                fAscent = nTypoAscent * fScale;
                fDescent = -nTypoDescent * fScale;
                fExtLeading = nTypoLineGap * fScale;
            }
        }
    }

    mnAscent = round(fAscent);
    mnDescent = round(fDescent);
    mnExtLeading = round(fExtLeading);

    if (mnAscent || mnDescent)
        mnIntLeading = mnAscent + mnDescent - mnHeight;

    // tdf#36709: Additional font metrics are needed for font-relative indentation
    mdEmSize = static_cast<double>(mnHeight);

    // The ic is defined as the advance of CJK UNIFIED IDEOGRAPH-6C34 (水).
    // If this character does not exist, it defaults to em.
    mdHorCJKAdvanceSize = mdEmSize;
    mdVertCJKAdvanceSize = mdEmSize;

    hb_codepoint_t nIcGlyph;
    if (hb_font_get_glyph(pHbFont, 0x6C34, /*variation selector*/ 0, &nIcGlyph))
    {
        auto nIcHAdvance = hb_font_get_glyph_h_advance(pHbFont, nIcGlyph);
        mdHorCJKAdvanceSize = static_cast<double>(nIcHAdvance) * fScale;

        auto nIcVAdvance = hb_font_get_glyph_v_advance(pHbFont, nIcGlyph);
        mdVertCJKAdvanceSize = static_cast<double>(nIcVAdvance) * fScale;
    }
}

void FontMetricData::ImplInitBaselines(LogicalFontInstance *pFontInstance)
{
    hb_font_t* pHbFont = pFontInstance->GetHbFont();
    double fScale = 0;
    pFontInstance->GetScale(nullptr, &fScale);
    hb_position_t nBaseline = 0;

    if (hb_ot_layout_get_baseline(pHbFont,
             HB_OT_LAYOUT_BASELINE_TAG_HANGING,
                                HB_DIRECTION_INVALID,
                                HB_SCRIPT_UNKNOWN,
                                HB_TAG_NONE,
                                &nBaseline))
    {
        mnHangingBaseline = nBaseline * fScale;
    }
    else
    {
        mnHangingBaseline = 0;
    }
}

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