/* -*- 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 <memory>
#include <unistd.h>
#include <osl/thread.h>
 
#include <unx/fontmanager.hxx>
#include <impfontcharmap.hxx>
#include <unotools/syslocaleoptions.hxx>
#include <unx/gendata.hxx>
#include <unx/helper.hxx>
#include <vcl/fontcharmap.hxx>
 
#include <tools/urlobj.hxx>
 
#include <o3tl/string_view.hxx>
#include <osl/file.hxx>
 
#include <rtl/ustrbuf.hxx>
#include <rtl/strbuf.hxx>
 
#include <sal/macros.h>
#include <sal/log.hxx>
 
#include <i18nlangtag/applelangid.hxx>
 
#include <sft.hxx>
 
#if OSL_DEBUG_LEVEL > 1
#include <sys/times.h>
#include <stdio.h>
#endif
 
#include <algorithm>
#include <set>
 
#ifdef CALLGRIND_COMPILE
#include <valgrind/callgrind.h>
#endif
 
#include <com/sun/star/beans/XMaterialHolder.hpp>
 
using namespace vcl;
using namespace psp;
using namespace com::sun::star::uno;
using namespace com::sun::star::lang;
 
/*
 *  static helpers
 */
 
static sal_uInt16 getUInt16BE( const sal_uInt8*& pBuffer )
{
    sal_uInt16 nRet = static_cast<sal_uInt16>(pBuffer[1]) |
        (static_cast<sal_uInt16>(pBuffer[0]) << 8);
    pBuffer+=2;
    return nRet;
}
 
/*
 *  PrintFont implementations
 */
PrintFontManager::PrintFont::PrintFont()
:   m_nDirectory(0)
,   m_nCollectionEntry(0)
,   m_nVariationEntry(0)
{
}
 
/*
 *  one instance only
 */
PrintFontManager& PrintFontManager::get()
{
    GenericUnixSalData* const pSalData(GetGenericUnixSalData());
    assert(pSalData);
    return *pSalData->GetPrintFontManager();
}
 
/*
 *  the PrintFontManager
 */
 
PrintFontManager::PrintFontManager()
    : m_nNextFontID( 1 )
    , m_nNextDirAtom( 1 )
    , m_aFontInstallerTimer("PrintFontManager m_aFontInstallerTimer")
{
    m_aFontInstallerTimer.SetInvokeHandler(LINK(this, PrintFontManager, autoInstallFontLangSupport));
    m_aFontInstallerTimer.SetTimeout(5000);
}
 
PrintFontManager::~PrintFontManager()
{
    m_aFontInstallerTimer.Stop();
    deinitFontconfig();
}
 
OString PrintFontManager::getDirectory( int nAtom ) const
{
    std::unordered_map< int, OString >::const_iterator it( m_aAtomToDir.find( nAtom ) );
    return it != m_aAtomToDir.end() ? it->second : OString();
}
 
int PrintFontManager::getDirectoryAtom( const OString& rDirectory )
{
    int nAtom = 0;
    std::unordered_map< OString, int >::const_iterator it
          ( m_aDirToAtom.find( rDirectory ) );
    if( it != m_aDirToAtom.end() )
        nAtom = it->second;
    else
    {
        nAtom = m_nNextDirAtom++;
        m_aDirToAtom[ rDirectory ] = nAtom;
        m_aAtomToDir[ nAtom ] = rDirectory;
    }
    return nAtom;
}
 
std::vector<fontID> PrintFontManager::addFontFile( std::u16string_view rFileUrl )
{
    rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
    INetURLObject aPath( rFileUrl );
    OString aName(OUStringToOString(aPath.GetLastName(INetURLObject::DecodeMechanism::WithCharset, aEncoding), aEncoding));
    OString aDir( OUStringToOString(
        INetURLObject::decode( aPath.GetPath(), INetURLObject::DecodeMechanism::WithCharset, aEncoding ), aEncoding ) );
 
    int nDirID = getDirectoryAtom( aDir );
    std::vector<fontID> aFontIds = findFontFileIDs( nDirID, aName );
    if( aFontIds.empty() )
    {
        addFontconfigFile(OUStringToOString(aPath.GetFull(), osl_getThreadTextEncoding()));
 
        std::vector<PrintFont> aNewFonts = analyzeFontFile(nDirID, aName);
        for (auto & font : aNewFonts)
        {
            fontID nFontId = m_nNextFontID++;
            m_aFonts[nFontId] = std::move(font);
            m_aFontFileToFontID[ aName ].insert( nFontId );
            aFontIds.push_back(nFontId);
        }
    }
    return aFontIds;
}
 
std::vector<PrintFontManager::PrintFont> PrintFontManager::analyzeFontFile( int nDirID, const OString& rFontFile) const
{
    std::vector<PrintFontManager::PrintFont> aNewFonts;
 
    OString aDir( getDirectory( nDirID ) );
 
    OString aFullPath = aDir + "/" + rFontFile;
 
    bool bSupported;
    int nFD;
    int n;
    if (sscanf(aFullPath.getStr(), "/:FD:/%d%n", &nFD, &n) == 1 && aFullPath.getStr()[n] == '\0')
    {
        // Hack, pathname that actually means we will use a pre-opened file descriptor
        bSupported = true;
    }
    else
    {
        // #i1872# reject unreadable files
        if( access( aFullPath.getStr(), R_OK ) )
            return aNewFonts;
 
        bSupported = false;
        OString aExt( rFontFile.copy( rFontFile.lastIndexOf( '.' )+1 ) );
        if( aExt.equalsIgnoreAsciiCase("ttf")
             ||  aExt.equalsIgnoreAsciiCase("ttc")
             ||  aExt.equalsIgnoreAsciiCase("tte")   // #i33947# for Gaiji support
             ||  aExt.equalsIgnoreAsciiCase("otf") ) // check for TTF- and PS-OpenType too
            bSupported = true;
    }
 
    if (bSupported)
    {
        // get number of ttc entries
        int nLength = CountTTCFonts( aFullPath.getStr() );
        if (nLength > 0)
        {
            SAL_INFO("vcl.fonts", "ttc: " << aFullPath << " contains " << nLength << " fonts");
 
            for( int i = 0; i < nLength; i++ )
            {
                PrintFont aFont;
                aFont.m_nDirectory         = nDirID;
                aFont.m_aFontFile          = rFontFile;
                aFont.m_nCollectionEntry   = i;
                if (analyzeSfntFile(aFont))
                    aNewFonts.push_back(aFont);
            }
        }
        else
        {
            PrintFont aFont;
            aFont.m_nDirectory         = nDirID;
            aFont.m_aFontFile          = rFontFile;
            aFont.m_nCollectionEntry   = 0;
 
            // need to read the font anyway to get aliases inside the font file
            if (analyzeSfntFile(aFont))
                aNewFonts.push_back(aFont);
        }
    }
    return aNewFonts;
}
 
fontID PrintFontManager::findFontFileID(int nDirID, const OString& rFontFile, int nFaceIndex, int nVariationIndex) const
{
    fontID nID = 0;
 
    auto set_it = m_aFontFileToFontID.find( rFontFile );
    if( set_it == m_aFontFileToFontID.end() )
        return nID;
 
    for (fontID elem : set_it->second)
    {
        auto it = m_aFonts.find(elem);
        if( it == m_aFonts.end() )
            continue;
        const PrintFont& rFont = (*it).second;
        if (rFont.m_nDirectory == nDirID &&
            rFont.m_aFontFile == rFontFile &&
            rFont.m_nCollectionEntry == nFaceIndex &&
            rFont.m_nVariationEntry == nVariationIndex)
        {
            nID = it->first;
            if (nID)
                break;
        }
    }
 
    return nID;
}
 
std::vector<fontID> PrintFontManager::findFontFileIDs( int nDirID, const OString& rFontFile ) const
{
    std::vector<fontID> aIds;
 
    auto set_it = m_aFontFileToFontID.find( rFontFile );
    if( set_it == m_aFontFileToFontID.end() )
        return aIds;
 
    for (auto const& elem : set_it->second)
    {
        auto it = m_aFonts.find(elem);
        if( it == m_aFonts.end() )
            continue;
        const PrintFont& rFont = (*it).second;
        if (rFont.m_nDirectory == nDirID &&
            rFont.m_aFontFile == rFontFile)
            aIds.push_back(it->first);
    }
 
    return aIds;
}
 
namespace {
 
OUString convertSfntName( const NameRecord& rNameRecord )
{
    OUString aValue;
    if(
       ( rNameRecord.platformID == 3 && ( rNameRecord.encodingID == 0 || rNameRecord.encodingID == 1 ) )  // MS, Unicode
       ||
       ( rNameRecord.platformID == 0 ) // Apple, Unicode
       )
    {
        OUStringBuffer aName( rNameRecord.sptr.size()/2 );
        const sal_uInt8* pNameBuffer = rNameRecord.sptr.data();
        for(size_t n = 0; n < rNameRecord.sptr.size()/2; n++ )
            aName.append( static_cast<sal_Unicode>(getUInt16BE( pNameBuffer )) );
        aValue = aName.makeStringAndClear();
    }
    else if( rNameRecord.platformID == 3 )
    {
        if( rNameRecord.encodingID >= 2 && rNameRecord.encodingID <= 6 )
        {
            /*
             *  and now for a special kind of madness:
             *  some fonts encode their byte value string as BE uint16
             *  (leading to stray zero bytes in the string)
             *  while others code two bytes as a uint16 and swap to BE
             */
            OStringBuffer aName;
            const sal_uInt8* pNameBuffer = rNameRecord.sptr.data();
            for(size_t n = 0; n < rNameRecord.sptr.size()/2; n++ )
            {
                sal_Unicode aCode = static_cast<sal_Unicode>(getUInt16BE( pNameBuffer ));
                char aChar = aCode >> 8;
                if( aChar )
                    aName.append( aChar );
                aChar = aCode & 0x00ff;
                if( aChar )
                    aName.append( aChar );
            }
            switch( rNameRecord.encodingID )
            {
                case 2:
                    aValue = OStringToOUString( aName, RTL_TEXTENCODING_MS_932 );
                    break;
                case 3:
                    aValue = OStringToOUString( aName, RTL_TEXTENCODING_MS_936 );
                    break;
                case 4:
                    aValue = OStringToOUString( aName, RTL_TEXTENCODING_MS_950 );
                    break;
                case 5:
                    aValue = OStringToOUString( aName, RTL_TEXTENCODING_MS_949 );
                    break;
                case 6:
                    aValue = OStringToOUString( aName, RTL_TEXTENCODING_MS_1361 );
                    break;
            }
        }
    }
    else if( rNameRecord.platformID == 1 )
    {
        std::string_view aName(reinterpret_cast<const char*>(rNameRecord.sptr.data()), rNameRecord.sptr.size());
        rtl_TextEncoding eEncoding = RTL_TEXTENCODING_DONTKNOW;
        switch (rNameRecord.encodingID)
        {
            case 0:
                eEncoding = RTL_TEXTENCODING_APPLE_ROMAN;
                break;
            case 1:
                eEncoding = RTL_TEXTENCODING_APPLE_JAPANESE;
                break;
            case 2:
                eEncoding = RTL_TEXTENCODING_APPLE_CHINTRAD;
                break;
            case 3:
                eEncoding = RTL_TEXTENCODING_APPLE_KOREAN;
                break;
            case 4:
                eEncoding = RTL_TEXTENCODING_APPLE_ARABIC;
                break;
            case 5:
                eEncoding = RTL_TEXTENCODING_APPLE_HEBREW;
                break;
            case 6:
                eEncoding = RTL_TEXTENCODING_APPLE_GREEK;
                break;
            case 7:
                eEncoding = RTL_TEXTENCODING_APPLE_CYRILLIC;
                break;
            case 9:
                eEncoding = RTL_TEXTENCODING_APPLE_DEVANAGARI;
                break;
            case 10:
                eEncoding = RTL_TEXTENCODING_APPLE_GURMUKHI;
                break;
            case 11:
                eEncoding = RTL_TEXTENCODING_APPLE_GUJARATI;
                break;
            case 21:
                eEncoding = RTL_TEXTENCODING_APPLE_THAI;
                break;
            case 25:
                eEncoding = RTL_TEXTENCODING_APPLE_CHINSIMP;
                break;
            case 29:
                eEncoding = RTL_TEXTENCODING_APPLE_CENTEURO;
                break;
            case 32:    //Uninterpreted
                eEncoding = RTL_TEXTENCODING_UTF8;
                break;
            default:
                if (o3tl::starts_with(aName, "Khmer OS") || // encoding '20' (Khmer) isn't implemented
                    o3tl::starts_with(aName, "YoavKtav")) // tdf#152278
                {
                    eEncoding = RTL_TEXTENCODING_UTF8;
                }
                SAL_WARN_IF(eEncoding == RTL_TEXTENCODING_DONTKNOW, "vcl.fonts", "mac encoding " <<
                            rNameRecord.encodingID << " in font '" << aName << "'" <<
                            (rNameRecord.encodingID > 32 ? " is invalid" : " has unimplemented conversion"));
                break;
        }
        if (eEncoding != RTL_TEXTENCODING_DONTKNOW)
            aValue = OStringToOUString(aName, eEncoding);
    }
 
    return aValue;
}
 
OUString analyzeSfntFamilyName(void const * pTTFont)
{
    OUString aFamily;
 
    std::vector<NameRecord> aNameRecords;
    GetTTNameRecords( static_cast<TrueTypeFont const *>(pTTFont), aNameRecords );
    if( !aNameRecords.empty() )
    {
        LanguageTag aUILang(SvtSysLocaleOptions().GetRealUILanguageTag());
        LanguageType eLang = aUILang.getLanguageType();
        int nLastMatch = -1;
        for( size_t i = 0; i < aNameRecords.size(); i++ )
        {
            if( aNameRecords[i].nameID != 1 || aNameRecords[i].sptr.empty() )
                continue;
            int nMatch = -1;
            if( aNameRecords[i].platformID == 0 ) // Unicode
                nMatch = 4000;
            else if( aNameRecords[i].platformID == 3 )
            {
                // this bases on the LanguageType actually being a Win LCID
                if (aNameRecords[i].languageID == eLang)
                    nMatch = 8000;
                else if( aNameRecords[i].languageID == LANGUAGE_ENGLISH_US )
                    nMatch = 2000;
                else if( aNameRecords[i].languageID == LANGUAGE_ENGLISH ||
                         aNameRecords[i].languageID == LANGUAGE_ENGLISH_UK )
                    nMatch = 1500;
                else
                    nMatch = 1000;
            }
            else if (aNameRecords[i].platformID == 1)
            {
                AppleLanguageId aAppleId = static_cast<AppleLanguageId>(static_cast<sal_uInt16>(aNameRecords[i].languageID));
                LanguageTag aApple(makeLanguageTagFromAppleLanguageId(aAppleId));
                if (aApple == aUILang)
                    nMatch = 8000;
                else if (aAppleId == AppleLanguageId::ENGLISH)
                    nMatch = 2000;
                else
                    nMatch = 1000;
            }
            OUString aName = convertSfntName( aNameRecords[i] );
            if (!(aName.isEmpty()) && nMatch > nLastMatch)
            {
                nLastMatch = nMatch;
                aFamily = aName;
            }
        }
    }
 
    return aFamily;
}
 
}
 
bool PrintFontManager::analyzeSfntFile( PrintFont& rFont ) const
{
    bool bSuccess = false;
    rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
    OString aFile = getFontFile( rFont );
    TrueTypeFont* pTTFont = nullptr;
 
    auto& rDFA = rFont.m_aFontAttributes;
    rDFA.SetQuality(512);
 
    auto const e = OpenTTFontFile( aFile.getStr(), rFont.m_nCollectionEntry, &pTTFont );
    if( e == SFErrCodes::Ok )
    {
        TTGlobalFontInfo aInfo;
        GetTTGlobalFontInfo( pTTFont, & aInfo );
 
        if (rDFA.GetFamilyName().isEmpty())
        {
            OUString aFamily = analyzeSfntFamilyName(pTTFont);
            if (aFamily.isEmpty())
            {
                 // poor font does not have a family name
                 // name it to file name minus the extension
                 sal_Int32 dotIndex = rFont.m_aFontFile.lastIndexOf('.');
                 if ( dotIndex == -1 )
                     dotIndex = rFont.m_aFontFile.getLength();
                 aFamily = OStringToOUString(rFont.m_aFontFile.subView(0, dotIndex), aEncoding);
            }
 
            rDFA.SetFamilyName(aFamily);
        }
 
        if( !aInfo.usubfamily.isEmpty() )
            rDFA.SetStyleName(aInfo.usubfamily);
 
        rDFA.SetFamilyType(matchFamilyName(rDFA.GetFamilyName()));
 
        switch( aInfo.weight )
        {
            case FW_THIN:       rDFA.SetWeight(WEIGHT_THIN); break;
            case FW_EXTRALIGHT: rDFA.SetWeight(WEIGHT_ULTRALIGHT); break;
            case FW_LIGHT:      rDFA.SetWeight(WEIGHT_LIGHT); break;
            case FW_MEDIUM:     rDFA.SetWeight(WEIGHT_MEDIUM); break;
            case FW_SEMIBOLD:   rDFA.SetWeight(WEIGHT_SEMIBOLD); break;
            case FW_BOLD:       rDFA.SetWeight(WEIGHT_BOLD); break;
            case FW_EXTRABOLD:  rDFA.SetWeight(WEIGHT_ULTRABOLD); break;
            case FW_BLACK:      rDFA.SetWeight(WEIGHT_BLACK); break;
 
            case FW_NORMAL:
            default:            rDFA.SetWeight(WEIGHT_NORMAL); break;
        }
 
        switch( aInfo.width )
        {
            case FWIDTH_ULTRA_CONDENSED:    rDFA.SetWidthType(WIDTH_ULTRA_CONDENSED); break;
            case FWIDTH_EXTRA_CONDENSED:    rDFA.SetWidthType(WIDTH_EXTRA_CONDENSED); break;
            case FWIDTH_CONDENSED:          rDFA.SetWidthType(WIDTH_CONDENSED); break;
            case FWIDTH_SEMI_CONDENSED:     rDFA.SetWidthType(WIDTH_SEMI_CONDENSED); break;
            case FWIDTH_SEMI_EXPANDED:      rDFA.SetWidthType(WIDTH_SEMI_EXPANDED); break;
            case FWIDTH_EXPANDED:           rDFA.SetWidthType(WIDTH_EXPANDED); break;
            case FWIDTH_EXTRA_EXPANDED:     rDFA.SetWidthType(WIDTH_EXTRA_EXPANDED); break;
            case FWIDTH_ULTRA_EXPANDED:     rDFA.SetWidthType(WIDTH_ULTRA_EXPANDED); break;
 
            case FWIDTH_NORMAL:
            default:                        rDFA.SetWidthType(WIDTH_NORMAL); break;
        }
 
        rDFA.SetPitch(aInfo.pitch ? PITCH_FIXED : PITCH_VARIABLE);
        rDFA.SetItalic(aInfo.italicAngle == 0 ? ITALIC_NONE : (aInfo.italicAngle < 0 ? ITALIC_NORMAL : ITALIC_OBLIQUE));
        // #104264# there are fonts that set italic angle 0 although they are
        // italic; use macstyle bit here
        if( aInfo.italicAngle == 0 && (aInfo.macStyle & 2) )
            rDFA.SetItalic(ITALIC_NORMAL);
 
        rDFA.SetMicrosoftSymbolEncoded(aInfo.microsoftSymbolEncoded);
 
        CloseTTFont( pTTFont );
        bSuccess = true;
    }
    else
        SAL_WARN("vcl.fonts", "Could not OpenTTFont \"" << aFile << "\": " << int(e));
 
    return bSuccess;
}
 
void PrintFontManager::initialize()
{
    #ifdef CALLGRIND_COMPILE
    CALLGRIND_TOGGLE_COLLECT();
    CALLGRIND_ZERO_STATS();
    #endif
 
    // initialize can be called more than once, e.g.
    // gtk-fontconfig-timestamp changes to reflect new font installed and
    // PrintFontManager::initialize called again
    {
        m_nNextFontID = 1;
        m_aFonts.clear();
    }
#if OSL_DEBUG_LEVEL > 1
    clock_t aStart;
    clock_t aStep1;
    clock_t aStep2;
 
    struct tms tms;
 
    aStart = times( &tms );
#endif
 
    // first try fontconfig
    initFontconfig();
 
    // part one - look for downloadable fonts
    rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
    const OUString &rSalPrivatePath = psp::getFontPath();
 
    // search for the fonts in SAL_PRIVATE_FONTPATH first; those are
    // the fonts installed with the office
    if( !rSalPrivatePath.isEmpty() )
    {
        OString aPath = OUStringToOString( rSalPrivatePath, aEncoding );
        sal_Int32 nIndex = 0;
        do
        {
            OString aToken = aPath.getToken( 0, ';', nIndex );
            normPath( aToken );
            if (!aToken.isEmpty())
                addFontconfigDir(aToken);
        } while( nIndex >= 0 );
    }
 
    countFontconfigFonts();
 
#if OSL_DEBUG_LEVEL > 1
    aStep1 = times( &tms );
 
    aStep2 = times( &tms );
    SAL_INFO("vcl.fonts", "PrintFontManager::initialize: collected "
            << m_aFonts.size()
            << " fonts.");
    double fTick = (double)sysconf( _SC_CLK_TCK );
    SAL_INFO("vcl.fonts", "Step 1 took "
            << ((double)(aStep1 - aStart)/fTick)
            << " seconds.");
    SAL_INFO("vcl.fonts", "Step 2 took "
            << ((double)(aStep2 - aStep1)/fTick)
            << " seconds.");
#endif
 
    #ifdef CALLGRIND_COMPILE
    CALLGRIND_DUMP_STATS();
    CALLGRIND_TOGGLE_COLLECT();
    #endif
}
 
void PrintFontManager::getFontList( ::std::vector< fontID >& rFontIDs )
{
    rFontIDs.clear();
 
    for (auto const& font : m_aFonts)
        rFontIDs.push_back(font.first);
}
 
int PrintFontManager::getFontFaceNumber( fontID nFontID ) const
{
    int nRet = 0;
    const PrintFont* pFont = getFont( nFontID );
    if (pFont)
    {
        nRet = pFont->m_nCollectionEntry;
        if (nRet < 0)
            nRet = 0;
    }
    return nRet;
}
 
int PrintFontManager::getFontFaceVariation( fontID nFontID ) const
{
    int nRet = 0;
    const PrintFont* pFont = getFont( nFontID );
    if (pFont)
    {
        nRet = pFont->m_nVariationEntry;
        if (nRet < 0)
            nRet = 0;
    }
    return nRet;
}
 
FontFamily PrintFontManager::matchFamilyName( std::u16string_view rFamily )
{
    struct family_t {
        const char*  mpName;
        sal_uInt16   mnLength;
        FontFamily   meType;
    };
 
#define InitializeClass( p, a ) p, sizeof(p) - 1, a
    static const family_t pFamilyMatch[] =  {
        { InitializeClass( "arial",                  FAMILY_SWISS )  },
        { InitializeClass( "arioso",                 FAMILY_SCRIPT ) },
        { InitializeClass( "avant garde",            FAMILY_SWISS )  },
        { InitializeClass( "avantgarde",             FAMILY_SWISS )  },
        { InitializeClass( "bembo",                  FAMILY_ROMAN )  },
        { InitializeClass( "bookman",                FAMILY_ROMAN )  },
        { InitializeClass( "conga",                  FAMILY_ROMAN )  },
        { InitializeClass( "courier",                FAMILY_MODERN ) },
        { InitializeClass( "curl",                   FAMILY_SCRIPT ) },
        { InitializeClass( "fixed",                  FAMILY_MODERN ) },
        { InitializeClass( "gill",                   FAMILY_SWISS )  },
        { InitializeClass( "helmet",                 FAMILY_MODERN ) },
        { InitializeClass( "helvetica",              FAMILY_SWISS )  },
        { InitializeClass( "international",          FAMILY_MODERN ) },
        { InitializeClass( "lucida",                 FAMILY_SWISS )  },
        { InitializeClass( "new century schoolbook", FAMILY_ROMAN )  },
        { InitializeClass( "palatino",               FAMILY_ROMAN )  },
        { InitializeClass( "roman",                  FAMILY_ROMAN )  },
        { InitializeClass( "sans serif",             FAMILY_SWISS )  },
        { InitializeClass( "sansserif",              FAMILY_SWISS )  },
        { InitializeClass( "serf",                   FAMILY_ROMAN )  },
        { InitializeClass( "serif",                  FAMILY_ROMAN )  },
        { InitializeClass( "times",                  FAMILY_ROMAN )  },
        { InitializeClass( "utopia",                 FAMILY_ROMAN )  },
        { InitializeClass( "zapf chancery",          FAMILY_SCRIPT ) },
        { InitializeClass( "zapfchancery",           FAMILY_SCRIPT ) }
    };
 
    OString aFamily = OUStringToOString( rFamily, RTL_TEXTENCODING_ASCII_US );
    sal_uInt32 nLower = 0;
    sal_uInt32 nUpper = SAL_N_ELEMENTS(pFamilyMatch);
 
    while( nLower < nUpper )
    {
        sal_uInt32 nCurrent = (nLower + nUpper) / 2;
        const family_t* pHaystack = pFamilyMatch + nCurrent;
        sal_Int32  nComparison =
            rtl_str_compareIgnoreAsciiCase_WithLength
            (
             aFamily.getStr(), aFamily.getLength(),
             pHaystack->mpName, pHaystack->mnLength
             );
 
        if( nComparison < 0 )
            nUpper = nCurrent;
        else
            if( nComparison > 0 )
                nLower = nCurrent + 1;
            else
                return pHaystack->meType;
    }
 
    return FAMILY_DONTKNOW;
}
 
OString PrintFontManager::getFontFile(const PrintFont& rFont) const
{
    std::unordered_map< int, OString >::const_iterator it = m_aAtomToDir.find(rFont.m_nDirectory);
    assert(it != m_aAtomToDir.end());
    OString aPath = it->second + "/" + rFont.m_aFontFile;
    return aPath;
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.