/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 * 
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_vcl.hxx"

#include <unistd.h>
#include <sys/stat.h>
#include <dirent.h>
#include <stdlib.h>
#include <osl/thread.h>

#include "unotools/atom.hxx"

#include "vcl/fontmanager.hxx"
#include "vcl/fontcache.hxx"
#include "vcl/fontcache.hxx"
#include "vcl/fontsubset.hxx"
#include "vcl/impfont.hxx"
#include "vcl/strhelper.hxx"
#include "vcl/ppdparser.hxx"
#include "vcl/svdata.hxx"
#include "vcl/salinst.hxx"

#include "tools/urlobj.hxx"
#include "tools/stream.hxx"
#include "tools/debug.hxx"
#include "tools/config.hxx"

#include "osl/file.hxx"
#include "osl/process.h"

#include "rtl/tencinfo.h"
#include "rtl/ustrbuf.hxx"
#include "rtl/strbuf.hxx"

#include <sal/macros.h>

#include "i18npool/mslangid.hxx"


#include "parseAFM.hxx"
#include "sft.hxx"

#if OSL_DEBUG_LEVEL > 1
#include <sys/times.h>
#include <stdio.h>
#endif

#include "sal/alloca.h"

#include <set>
#include <boost/unordered_set.hpp>
#include <algorithm>

#include "adobeenc.tab" // get encoding table for AFM metrics

#ifdef CALLGRIND_COMPILE
#include <valgrind/callgrind.h>
#endif

#include "comphelper/processfactory.hxx"
#include "com/sun/star/beans/XMaterialHolder.hpp"
#include "com/sun/star/beans/NamedValue.hpp"

#define PRINTER_METRICDIR "fontmetric"

namespace {

namespace css = com::sun::star;

}

using namespace vcl;
using namespace utl;
using namespace psp;
using namespace osl;
using namespace com::sun::star::uno;
using namespace com::sun::star::beans;
using namespace com::sun::star::lang;

using ::rtl::OUString;
using ::rtl::OString;
using ::rtl::OStringHash;
using ::rtl::OStringBuffer;
using ::rtl::OUStringBuffer;
using ::rtl::OUStringHash;
using ::rtl::OStringToOUString;
using ::rtl::OUStringToOString;

/*
 *  static helpers
 */

inline sal_uInt16 getUInt16BE( const sal_uInt8*& pBuffer )
{
    sal_uInt16 nRet = (sal_uInt16)pBuffer[1] |
        (((sal_uInt16)pBuffer[0]) << 8);
    pBuffer+=2;
    return nRet;
}

inline sal_uInt32 getUInt32BE( const sal_uInt8*& pBuffer )
{
    sal_uInt32 nRet = (((sal_uInt32)pBuffer[0]) << 24) |
                      (((sal_uInt32)pBuffer[1]) << 16) |
                      (((sal_uInt32)pBuffer[2]) << 8)  |
                      (((sal_uInt32)pBuffer[3]) );
    pBuffer += 4;
    return nRet;
}

static italic::type parseItalic( const ByteString& rItalic )
{
    italic::type eItalic = italic::Unknown;
    if( rItalic.EqualsIgnoreCaseAscii( "i" ) )
        eItalic = italic::Italic;
    else if( rItalic.EqualsIgnoreCaseAscii( "o" ) )
        eItalic = italic::Oblique;
    else
        eItalic = italic::Upright;
    return eItalic;
}

// -------------------------------------------------------------------------

static weight::type parseWeight( const ByteString& rWeight )
{
    weight::type eWeight = weight::Unknown;
    if( rWeight.Search( "bold" ) != STRING_NOTFOUND )
    {
        if( rWeight.Search( "emi" ) != STRING_NOTFOUND ) // semi, demi
            eWeight = weight::SemiBold;
        else if( rWeight.Search( "ultra" ) != STRING_NOTFOUND )
            eWeight = weight::UltraBold;
        else
            eWeight = weight::Bold;
    }
    else if( rWeight.Search( "heavy" ) != STRING_NOTFOUND )
        eWeight = weight::Bold;
    else if( rWeight.Search( "light" ) != STRING_NOTFOUND )
    {
        if( rWeight.Search( "emi" ) != STRING_NOTFOUND ) // semi, demi
            eWeight = weight::SemiLight;
        else if( rWeight.Search( "ultra" ) != STRING_NOTFOUND )
            eWeight = weight::UltraLight;
        else
            eWeight = weight::Light;
    }
    else if( rWeight.Search( "black" ) != STRING_NOTFOUND )
        eWeight = weight::Black;
    else if( rWeight.Equals( "demi" ) )
        eWeight = weight::SemiBold;
    else if( rWeight.Equals( "book" ) ||
             rWeight.Equals( "semicondensed" ) )
        eWeight = weight::Light;
    else if( rWeight.Equals( "medium" ) || rWeight.Equals( "roman" ) )
        eWeight = weight::Medium;
    else
        eWeight = weight::Normal;
    return eWeight;
}

// -------------------------------------------------------------------------

static width::type parseWidth( const ByteString& rWidth )
{
    width::type eWidth = width::Unknown;
    if( rWidth.Equals( "bold" ) ||
        rWidth.Equals( "semiexpanded" ) )
        eWidth = width::SemiExpanded;
    else if( rWidth.Equals( "condensed" ) ||
             rWidth.Equals( "narrow" ) )
        eWidth = width::Condensed;
    else if( rWidth.Equals( "double wide" ) ||
             rWidth.Equals( "extraexpanded" ) ||
             rWidth.Equals( "ultraexpanded" ) )
        eWidth = width::UltraExpanded;
    else if( rWidth.Equals( "expanded" ) ||
             rWidth.Equals( "wide" ) )
        eWidth = width::Expanded;
    else if( rWidth.Equals( "extracondensed" ) )
        eWidth = width::ExtraCondensed;
    else if( rWidth.Equals( "semicondensed" ) )
        eWidth = width::SemiCondensed;
    else if( rWidth.Equals( "ultracondensed" ) )
        eWidth = width::UltraCondensed;
    else
        eWidth = width::Normal;

    return eWidth;
}

// -------------------------------------------------------------------------
bool PrintFontManager::XLFDEntry::operator<(const PrintFontManager::XLFDEntry& rRight) const
{
    sal_Int32 nCmp = 0;
    if( (nMask & MaskFamily) && (rRight.nMask & MaskFamily) )
    {
        nCmp =  rtl_str_compareIgnoreAsciiCase_WithLength( aFamily.pData->buffer,
                                                           aFamily.pData->length,
                                                           rRight.aFamily.pData->buffer,
                                                           rRight.aFamily.pData->length );
        if( nCmp != 0 )
            return nCmp < 0;
    }

    if( (nMask & MaskFoundry) && (rRight.nMask & MaskFoundry) )
    {
        nCmp =  rtl_str_compareIgnoreAsciiCase_WithLength( aFoundry.pData->buffer,
                                                           aFoundry.pData->length,
                                                           rRight.aFoundry.pData->buffer,
                                                           rRight.aFoundry.pData->length );
        if( nCmp != 0 )
            return nCmp < 0;
    }

    if( (nMask & MaskItalic) && (rRight.nMask & MaskItalic) )
    {
        if( eItalic != rRight.eItalic )
            return (int)eItalic < (int)rRight.eItalic;
    }

    if( (nMask & MaskWeight) && (rRight.nMask & MaskWeight) )
    {
        if( eWeight != rRight.eWeight )
            return (int)eWeight < (int)rRight.eWeight;
    }

    if( (nMask & MaskWidth) && (rRight.nMask & MaskWidth) )
    {
        if( eWidth != rRight.eWidth )
            return (int)eWidth < (int)rRight.eWidth;
    }

    if( (nMask & MaskPitch) && (rRight.nMask & MaskPitch) )
    {
        if( ePitch != rRight.ePitch )
            return (int)ePitch < (int)rRight.ePitch;
    }

    if( (nMask & MaskAddStyle) && (rRight.nMask & MaskAddStyle) )
    {
        nCmp =  rtl_str_compareIgnoreAsciiCase_WithLength( aAddStyle.pData->buffer,
                                                           aAddStyle.pData->length,
                                                           rRight.aAddStyle.pData->buffer,
                                                           rRight.aAddStyle.pData->length );
        if( nCmp != 0 )
            return nCmp < 0;
    }

    if( (nMask & MaskEncoding) && (rRight.nMask & MaskEncoding) )
    {
        if( aEncoding != rRight.aEncoding )
            return aEncoding < rRight.aEncoding;
    }

    return false;
}

bool PrintFontManager::XLFDEntry::operator==(const PrintFontManager::XLFDEntry& rRight) const
{
    sal_Int32 nCmp = 0;
    if( (nMask & MaskFamily) && (rRight.nMask & MaskFamily) )
    {
        nCmp =  rtl_str_compareIgnoreAsciiCase_WithLength( aFamily.pData->buffer,
                                                           aFamily.pData->length,
                                                           rRight.aFamily.pData->buffer,
                                                           rRight.aFamily.pData->length );
        if( nCmp != 0 )
            return false;
    }

    if( (nMask & MaskFoundry) && (rRight.nMask & MaskFoundry) )
    {
        nCmp =  rtl_str_compareIgnoreAsciiCase_WithLength( aFoundry.pData->buffer,
                                                           aFoundry.pData->length,
                                                           rRight.aFoundry.pData->buffer,
                                                           rRight.aFoundry.pData->length );
        if( nCmp != 0 )
            return false;
    }

    if( (nMask & MaskItalic) && (rRight.nMask & MaskItalic) )
    {
        if( eItalic != rRight.eItalic )
            return false;
    }

    if( (nMask & MaskWeight) && (rRight.nMask & MaskWeight) )
    {
        if( eWeight != rRight.eWeight )
            return false;
    }

    if( (nMask & MaskWidth) && (rRight.nMask & MaskWidth) )
    {
        if( eWidth != rRight.eWidth )
            return false;
    }

    if( (nMask & MaskPitch) && (rRight.nMask & MaskPitch) )
    {
        if( ePitch != rRight.ePitch )
            return false;
    }

    if( (nMask & MaskAddStyle) && (rRight.nMask & MaskAddStyle) )
    {
        nCmp =  rtl_str_compareIgnoreAsciiCase_WithLength( aAddStyle.pData->buffer,
                                                           aAddStyle.pData->length,
                                                           rRight.aAddStyle.pData->buffer,
                                                           rRight.aAddStyle.pData->length );
        if( nCmp != 0 )
            return false;
    }

    if( (nMask & MaskEncoding) && (rRight.nMask & MaskEncoding) )
    {
        if( aEncoding != rRight.aEncoding )
            return false;
    }

    return true;
}

/*
 *  PrintFont implementations
 */
PrintFontManager::PrintFont::PrintFont( fonttype::type eType ) :
        m_eType( eType ),
        m_nFamilyName( 0 ),
        m_nPSName( 0 ),
        m_eItalic( italic::Unknown ),
        m_eWidth( width::Unknown ),
        m_eWeight( weight::Unknown ),
        m_ePitch( pitch::Unknown ),
        m_aEncoding( RTL_TEXTENCODING_DONTKNOW ),
        m_bFontEncodingOnly( false ),
        m_pMetrics( NULL ),
        m_nAscend( 0 ),
        m_nDescend( 0 ),
        m_nLeading( 0 ),
        m_nXMin( 0 ),
        m_nYMin( 0 ),
        m_nXMax( 0 ),
        m_nYMax( 0 ),
        m_bHaveVerticalSubstitutedGlyphs( false ),
        m_bUserOverride( false )
{
}

// -------------------------------------------------------------------------

PrintFontManager::PrintFont::~PrintFont()
{
    if( m_pMetrics )
        delete m_pMetrics;
}

// -------------------------------------------------------------------------

PrintFontManager::Type1FontFile::~Type1FontFile()
{
}

// -------------------------------------------------------------------------

PrintFontManager::TrueTypeFontFile::TrueTypeFontFile()
:	PrintFont( fonttype::TrueType )
,	m_nDirectory( 0 )
,	m_nCollectionEntry(-1)
,	m_nTypeFlags( TYPEFLAG_INVALID )
{}

// -------------------------------------------------------------------------

PrintFontManager::TrueTypeFontFile::~TrueTypeFontFile()
{
}

// -------------------------------------------------------------------------

PrintFontManager::BuiltinFont::~BuiltinFont()
{
}

// -------------------------------------------------------------------------

bool PrintFontManager::Type1FontFile::queryMetricPage( int /*nPage*/, MultiAtomProvider* pProvider )
{
    return readAfmMetrics( PrintFontManager::get().getAfmFile( this ), pProvider, false, false );
}

// -------------------------------------------------------------------------

bool PrintFontManager::BuiltinFont::queryMetricPage( int /*nPage*/, MultiAtomProvider* pProvider )
{
    return readAfmMetrics( PrintFontManager::get().getAfmFile( this ), pProvider, false, false );
}

// -------------------------------------------------------------------------

bool PrintFontManager::TrueTypeFontFile::queryMetricPage( int nPage, MultiAtomProvider* pProvider )
{
    bool bSuccess = false;

    ByteString aFile( PrintFontManager::get().getFontFile( this ) );

    TrueTypeFont* pTTFont = NULL;

    if( OpenTTFontFile( aFile.GetBuffer(), m_nCollectionEntry < 0 ? 0 : m_nCollectionEntry, &pTTFont ) == SF_OK )
    {
        if( ! m_pMetrics )
        {
            m_pMetrics = new PrintFontMetrics;
            memset (m_pMetrics->m_aPages, 0, sizeof(m_pMetrics->m_aPages));
        }
        m_pMetrics->m_aPages[ nPage/8 ] |= (1 << ( nPage & 7 ));
        int i;
        sal_uInt16 table[256], table_vert[256];

        for( i = 0; i < 256; i++ )
            table[ i ] = 256*nPage + i;

        int nCharacters = nPage < 255 ? 256 : 254;
        MapString( pTTFont, table, nCharacters, NULL, 0 );
        TTSimpleGlyphMetrics* pMetrics = GetTTSimpleCharMetrics( pTTFont, nPage*256, nCharacters, 0 );
        if( pMetrics )
        {
            for( i = 0; i < nCharacters; i++ )
            {
                if( table[i] )
                {
                    CharacterMetric& rChar = m_pMetrics->m_aMetrics[ nPage*256 + i ];
                    rChar.width = pMetrics[ i ].adv;
                    rChar.height = m_aGlobalMetricX.height;
                }
            }

            free( pMetrics );
        }

        for( i = 0; i < 256; i++ )
            table_vert[ i ] = 256*nPage + i;
        MapString( pTTFont, table_vert, nCharacters, NULL, 1 );
        pMetrics = GetTTSimpleCharMetrics( pTTFont, nPage*256, nCharacters, 1 );
        if( pMetrics )
        {
            for( i = 0; i < nCharacters; i++ )
            {
                if( table_vert[i] )
                {
                    CharacterMetric& rChar = m_pMetrics->m_aMetrics[ nPage*256 + i + ( 1 << 16 ) ];
                    rChar.width = m_aGlobalMetricY.width;
                    rChar.height = pMetrics[ i ].adv;
                    if( table_vert[i] != table[i] )
                        m_pMetrics->m_bVerticalSubstitutions[ nPage*256 + i ] = 1;
                }
            }
            free( pMetrics );
        }

        if( ! m_pMetrics->m_bKernPairsQueried )
        {
            m_pMetrics->m_bKernPairsQueried = true;
            // this is really a hack
            // in future MapString/KernGlyphs should be used
            // but vcl is not in a state where that could be used
            // so currently we get kernpairs by accessing the raw data
            struct _TrueTypeFont* pImplTTFont = (struct _TrueTypeFont*)pTTFont;

            //-----------------------------------------------------------------
            // Kerning:  KT_MICROSOFT
            //-----------------------------------------------------------------
            if( pImplTTFont->nkern && pImplTTFont->kerntype == KT_MICROSOFT )
            {
                // create a glyph -> character mapping
                ::boost::unordered_map< sal_uInt16, sal_Unicode > aGlyphMap;
                ::boost::unordered_map< sal_uInt16, sal_Unicode >::iterator left, right;
                for( i = 21; i < 0xfffd; i++ )
                {
                    sal_uInt16 nGlyph = MapChar( pTTFont, (sal_Unicode)i, 0 ); // kerning for horz only
                    if( nGlyph != 0 )
                        aGlyphMap[ nGlyph ] = (sal_Unicode)i;
                }


                KernPair aPair;
                for( i = 0; i < (int)pImplTTFont->nkern; i++ )
                {
                    const sal_uInt8* pTable = pImplTTFont->kerntables[i];

                    /*sal_uInt16 nVersion     =*/ getUInt16BE( pTable );
                    /*sal_uInt16 nLength      =*/ getUInt16BE( pTable );
                    sal_uInt16 nCoverage    = getUInt16BE( pTable );

                    aPair.kern_x    = 0;
                    aPair.kern_y    = 0;
                    switch( nCoverage >> 8 )
                    {
                        case 0:
                        {
                            sal_uInt16 nPairs = getUInt16BE( pTable );
                            pTable += 6;
                            for( int n = 0; n < nPairs; n++ )
                            {
                                sal_uInt16 nLeftGlyph   = getUInt16BE( pTable );
                                sal_uInt16 nRightGlyph  = getUInt16BE( pTable );
                                sal_Int16 nKern         = (sal_Int16)getUInt16BE( pTable );

                                left = aGlyphMap.find( nLeftGlyph );
                                right = aGlyphMap.find( nRightGlyph );
                                if( left != aGlyphMap.end() && right != aGlyphMap.end() )
                                {
                                    aPair.first     = left->second;
                                    aPair.second    = right->second;
                                    switch( nCoverage & 1 )
                                    {
                                        case 1:
                                            aPair.kern_x = (int)nKern * 1000 / pImplTTFont->unitsPerEm;
                                            m_pMetrics->m_aXKernPairs.push_back( aPair );
                                            break;
                                        case 0:
                                            aPair.kern_y = (int)nKern * 1000 / pImplTTFont->unitsPerEm;
                                            m_pMetrics->m_aYKernPairs.push_back( aPair );
                                            break;
                                    }
                                }
                            }
                        }
                        break;

                        case 2:
                        {
                            const sal_uInt8* pSubTable = pTable;
                            /*sal_uInt16 nRowWidth    =*/ getUInt16BE( pTable );
                            sal_uInt16 nOfLeft      = getUInt16BE( pTable );
                            sal_uInt16 nOfRight     = getUInt16BE( pTable );
                            /*sal_uInt16 nOfArray     =*/ getUInt16BE( pTable );
                            const sal_uInt8* pTmp = pSubTable + nOfLeft;
                            sal_uInt16 nFirstLeft   = getUInt16BE( pTmp );
                            sal_uInt16 nLastLeft    = getUInt16BE( pTmp ) + nFirstLeft - 1;
                            pTmp = pSubTable + nOfRight;
                            sal_uInt16 nFirstRight  = getUInt16BE( pTmp );
                            sal_uInt16 nLastRight   = getUInt16BE( pTmp ) + nFirstRight -1;

                            // int nPairs = (int)(nLastLeft-nFirstLeft+1)*(int)(nLastRight-nFirstRight+1);
                            for( aPair.first = nFirstLeft; aPair.first < nLastLeft; aPair.first++ )
                            {
                                for( aPair.second = 0; aPair.second < nLastRight; aPair.second++ )
                                {
                                    sal_Int16 nKern = (sal_Int16)getUInt16BE( pTmp );
                                    switch( nCoverage & 1 )
                                    {
                                        case 1:
                                            aPair.kern_x = (int)nKern * 1000 / pImplTTFont->unitsPerEm;
                                            m_pMetrics->m_aXKernPairs.push_back( aPair );
                                            break;
                                        case 0:
                                            aPair.kern_y = (int)nKern * 1000 / pImplTTFont->unitsPerEm;
                                            m_pMetrics->m_aYKernPairs.push_back( aPair );
                                            break;
                                    }
                                }
                            }
                        }
                        break;
                    }
                }
            }

            //-----------------------------------------------------------------
            // Kerning:  KT_APPLE_NEW
            //-----------------------------------------------------------------
            if( pImplTTFont->nkern && pImplTTFont->kerntype == KT_APPLE_NEW )
            {
                // create a glyph -> character mapping
                ::boost::unordered_map< sal_uInt16, sal_Unicode > aGlyphMap;
                ::boost::unordered_map< sal_uInt16, sal_Unicode >::iterator left, right;
                for( i = 21; i < 0xfffd; i++ )
                {
                    sal_uInt16 nGlyph = MapChar( pTTFont, (sal_Unicode)i, 0 ); // kerning for horz only
                    if( nGlyph != 0 )
                        aGlyphMap[ nGlyph ] = (sal_Unicode)i;
                }
                
                // Loop through each of the 'kern' subtables
                KernPair aPair;
                for( i = 0; (unsigned int)i < pImplTTFont->nkern; i++ )
                {
                    const sal_uInt8* pTable = pImplTTFont->kerntables[i];
                    
                    /*sal_uInt32 nLength      =*/ getUInt32BE( pTable );
                    sal_uInt16 nCoverage    = getUInt16BE( pTable );
                    /*sal_uInt16 nTupleIndex  =*/ getUInt16BE( pTable );

                    // Get kerning type
                    // sal_Bool bKernVertical     = nCoverage & 0x8000;
                    // sal_Bool bKernCrossStream  = nCoverage & 0x4000;
                    // sal_Bool bKernVariation    = nCoverage & 0x2000;

                    // Kerning sub-table format, 0 through 3
                    sal_uInt8 nSubTableFormat  = nCoverage & 0x00FF;
 
                    aPair.kern_x    = 0;
                    aPair.kern_y    = 0;
                    switch( nSubTableFormat )
                    {
                        case 0:
                        {
                            // Grab the # of kern pairs but skip over the:
                            //   searchRange
                            //   entrySelector
                            //   rangeShift
                            sal_uInt16 nPairs = getUInt16BE( pTable );
                            pTable += 6;

                            for( int n = 0; n < nPairs; n++ )
                            {
                                sal_uInt16 nLeftGlyph   = getUInt16BE( pTable );
                                sal_uInt16 nRightGlyph  = getUInt16BE( pTable );
                                sal_Int16  nKern         = (sal_Int16)getUInt16BE( pTable );

                                left = aGlyphMap.find( nLeftGlyph );
                                right = aGlyphMap.find( nRightGlyph );
                                if( left != aGlyphMap.end() && right != aGlyphMap.end() )
                                {
                                    aPair.first     = left->second;
                                    aPair.second    = right->second;

                                    // Only support horizontal kerning for now
                                    aPair.kern_x = (int)nKern * 1000 / pImplTTFont->unitsPerEm;
                                    aPair.kern_y = 0;
                                    m_pMetrics->m_aXKernPairs.push_back( aPair );

/*                                  switch( nCoverage & 1 )
                                    {
                                        case 1:
                                            aPair.kern_x = (int)nKern * 1000 / pImplTTFont->unitsPerEm;
                                            m_pMetrics->m_aXKernPairs.push_back( aPair );
                                            break;
                                        case 0:
                                            aPair.kern_y = (int)nKern * 1000 / pImplTTFont->unitsPerEm;
                                            m_pMetrics->m_aYKernPairs.push_back( aPair );
                                            break;                                          
                                    }
*/
                                }
                            }
                        }
                        break;

                        case 2:
                        {
                            const sal_uInt8* pSubTable = pTable;
                            /*sal_uInt16 nRowWidth    =*/ getUInt16BE( pTable );
                            sal_uInt16 nOfLeft      = getUInt16BE( pTable );
                            sal_uInt16 nOfRight     = getUInt16BE( pTable );
                            /*sal_uInt16 nOfArray     =*/ getUInt16BE( pTable );
                            const sal_uInt8* pTmp = pSubTable + nOfLeft;
                            sal_uInt16 nFirstLeft   = getUInt16BE( pTmp );
                            sal_uInt16 nLastLeft    = getUInt16BE( pTmp ) + nFirstLeft - 1;
                            pTmp = pSubTable + nOfRight;
                            sal_uInt16 nFirstRight  = getUInt16BE( pTmp );
                            sal_uInt16 nLastRight   = getUInt16BE( pTmp ) + nFirstRight -1;

                            for( aPair.first = nFirstLeft; aPair.first < nLastLeft; aPair.first++ )
                            {
                                for( aPair.second = 0; aPair.second < nLastRight; aPair.second++ )
                                {
                                    sal_Int16 nKern = (sal_Int16)getUInt16BE( pTmp );
                                    switch( nCoverage & 1 )
                                    {
                                        case 1:
                                            aPair.kern_x = (int)nKern * 1000 / pImplTTFont->unitsPerEm;
                                            m_pMetrics->m_aXKernPairs.push_back( aPair );
                                            break;
                                        case 0:
                                            aPair.kern_y = (int)nKern * 1000 / pImplTTFont->unitsPerEm;
                                            m_pMetrics->m_aYKernPairs.push_back( aPair );
                                            break;                                          
                                    }
                                }
                            }
                        }
                        break;

                        default:
                            fprintf( stderr, "Found unsupported Apple-style kern subtable type %d.\n", nSubTableFormat );
                            break;
                    }
                }
            }

#if OSL_DEBUG_LEVEL > 1
            fprintf( stderr, "found %" SAL_PRI_SIZET "u/%" SAL_PRI_SIZET "u kern pairs for %s\n",
                     m_pMetrics->m_aXKernPairs.size(),
                     m_pMetrics->m_aYKernPairs.size(),
                     OUStringToOString( pProvider->getString( ATOM_FAMILYNAME, m_nFamilyName ), RTL_TEXTENCODING_MS_1252 ).getStr() );
#else
            (void) pProvider; /* avoid warnings */
#endif
        }

        CloseTTFont( pTTFont );
        bSuccess = true;
    }
    return bSuccess;
}

// -------------------------------------------------------------------------

/* #i73387# There seem to be fonts with a rather unwell chosen family name
*  consider e.g. "Helvetica Narrow" which defines its family as "Helvetica"
*  It can really only be distinguished by its PSName and FullName. Both of
*  which are not user presentable in OOo. So replace it by something sensible.
*
*  If other fonts feature this behaviour, insert them to the map.
*/
static bool familyNameOverride( const OUString& i_rPSname, OUString& o_rFamilyName )
{
    static boost::unordered_map< OUString, OUString, OUStringHash > aPSNameToFamily( 16 );
    if( aPSNameToFamily.empty() ) // initialization
    {
        aPSNameToFamily[ OUString( RTL_CONSTASCII_USTRINGPARAM( "Helvetica-Narrow" ) ) ] =
                         OUString( RTL_CONSTASCII_USTRINGPARAM( "Helvetica Narrow" ) );
        aPSNameToFamily[ OUString( RTL_CONSTASCII_USTRINGPARAM( "Helvetica-Narrow-Bold" ) ) ] =
                         OUString( RTL_CONSTASCII_USTRINGPARAM( "Helvetica Narrow" ) );
        aPSNameToFamily[ OUString( RTL_CONSTASCII_USTRINGPARAM( "Helvetica-Narrow-BoldOblique" ) ) ] =
                         OUString( RTL_CONSTASCII_USTRINGPARAM( "Helvetica Narrow" ) );
        aPSNameToFamily[ OUString( RTL_CONSTASCII_USTRINGPARAM( "Helvetica-Narrow-Oblique" ) ) ] =
                         OUString( RTL_CONSTASCII_USTRINGPARAM( "Helvetica Narrow" ) );
    }
    boost::unordered_map<OUString,OUString,OUStringHash>::const_iterator it =
       aPSNameToFamily.find( i_rPSname );
    bool bReplaced = (it != aPSNameToFamily.end() );
    if( bReplaced )
        o_rFamilyName = it->second;
    return bReplaced;
};

bool PrintFontManager::PrintFont::readAfmMetrics( const OString& rFileName, MultiAtomProvider* pProvider, bool bFillEncodingvector, bool bOnlyGlobalAttributes )
{
    PrintFontManager& rManager( PrintFontManager::get() );

    FontInfo* pInfo = NULL;
    parseFile( rFileName.getStr(), &pInfo, P_ALL );
    if( ! pInfo || ! pInfo->numOfChars )
    {
        if( pInfo )
            freeFontInfo( pInfo );
        return false;
    }

    m_aEncodingVector.clear();
    // fill in global info

    // PSName
    OUString aPSName( OStringToOUString( pInfo->gfi->fontName, RTL_TEXTENCODING_ISO_8859_1 ) );
    m_nPSName = pProvider->getAtom( ATOM_PSNAME, aPSName, sal_True );

    // family name (if not already set)
    OUString aFamily;
    if( ! m_nFamilyName )
    {
        aFamily = OStringToOUString( pInfo->gfi->familyName, RTL_TEXTENCODING_ISO_8859_1 );
        if( ! aFamily.getLength() )
        {
            aFamily = OStringToOUString( pInfo->gfi->fontName, RTL_TEXTENCODING_ISO_8859_1 );
            sal_Int32 nIndex  = 0;
            aFamily = aFamily.getToken( 0, '-', nIndex );
        }
        familyNameOverride( aPSName, aFamily );
        m_nFamilyName = pProvider->getAtom( ATOM_FAMILYNAME, aFamily, sal_True );
    }
    else
        aFamily = pProvider->getString( ATOM_FAMILYNAME, m_nFamilyName );
    
    // style name: if fullname begins with family name
    // interpret the rest of fullname as style
    if( ! m_aStyleName.getLength() && pInfo->gfi->fullName && *pInfo->gfi->fullName )
    {
        OUString aFullName( OStringToOUString( pInfo->gfi->fullName, RTL_TEXTENCODING_ISO_8859_1 ) );
        if( aFullName.indexOf( aFamily ) == 0 )
            m_aStyleName = WhitespaceToSpace( aFullName.copy( aFamily.getLength() ) );
    }

    // italic
    if( pInfo->gfi->italicAngle > 0 )
        m_eItalic = italic::Oblique;
    else if( pInfo->gfi->italicAngle < 0 )
        m_eItalic = italic::Italic;
    else
        m_eItalic = italic::Upright;

    // weight
    ByteString aLowerWeight( pInfo->gfi->weight );
    aLowerWeight.ToLowerAscii();
    m_eWeight = parseWeight( aLowerWeight );

    // pitch
    m_ePitch = pInfo->gfi->isFixedPitch ? pitch::Fixed : pitch::Variable;

    // encoding - only set if unknown
    int nAdobeEncoding = 0;
    if( pInfo->gfi->encodingScheme )
    {
        if( !strcmp( pInfo->gfi->encodingScheme, "AdobeStandardEncoding" ) )
            nAdobeEncoding = 1;
        else if( !strcmp( pInfo->gfi->encodingScheme, "ISO10646-1" ) )
        {
            nAdobeEncoding = 1;
            m_aEncoding = RTL_TEXTENCODING_UNICODE;
        }
        else if( !strcmp( pInfo->gfi->encodingScheme, "Symbol") )
            nAdobeEncoding = 2;
        else if( !strcmp( pInfo->gfi->encodingScheme, "FontSpecific") )
            nAdobeEncoding = 3;

        if( m_aEncoding == RTL_TEXTENCODING_DONTKNOW )
            m_aEncoding = nAdobeEncoding == 1 ?
                RTL_TEXTENCODING_ADOBE_STANDARD : RTL_TEXTENCODING_SYMBOL;        
    }
    else if( m_aEncoding == RTL_TEXTENCODING_DONTKNOW )
        m_aEncoding = RTL_TEXTENCODING_ADOBE_STANDARD;

    // try to parse the font name and decide wether it might be a
    // japanese font. Who invented this PITA ?
    OUString aPSNameLastToken( aPSName.copy( aPSName.lastIndexOf( '-' )+1 ) );
    if( ! aPSNameLastToken.compareToAscii( "H" )    ||
        ! aPSNameLastToken.compareToAscii( "V" )  )
    {
        static const char* pEncs[] =
            {
                "EUC",
                "RKSJ",
                "SJ"
            };
        static const rtl_TextEncoding aEncs[] =
            {
                RTL_TEXTENCODING_EUC_JP,
                RTL_TEXTENCODING_SHIFT_JIS,
                RTL_TEXTENCODING_JIS_X_0208
            };

        for( unsigned int enc = 0; enc < SAL_N_ELEMENTS( aEncs ) && m_aEncoding == RTL_TEXTENCODING_DONTKNOW; enc++ )
        {
            sal_Int32 nIndex = 0, nOffset = 1;
            do
            {
                OUString aToken( aPSName.getToken( nOffset, '-', nIndex ) );
                if( nIndex == -1 )
                    break;
                nOffset = 0;
                if( ! aToken.compareToAscii( pEncs[enc] ) )
                {
                    m_aEncoding = aEncs[ enc ];
                    m_bFontEncodingOnly = true;
                }
            } while( nIndex != -1 );
        }

        // default is jis
        if( m_aEncoding == RTL_TEXTENCODING_DONTKNOW )
            m_aEncoding = RTL_TEXTENCODING_JIS_X_0208;
#if OSL_DEBUG_LEVEL > 1
        fprintf( stderr, "Encoding %d for %s\n", m_aEncoding, pInfo->gfi->fontName );
#endif
    }

    // hack for GB encoded builtin fonts posing as FontSpecific
    if( m_eType == fonttype::Builtin && ( nAdobeEncoding == 3 || nAdobeEncoding == 0 ) )
    {
        int nLen = aFamily.getLength();
        if( nLen > 2 &&
            aFamily.getStr()[ nLen-2 ] == 'G' &&
            aFamily.getStr()[ nLen-1 ] == 'B' &&
            pInfo->numOfChars > 255 )
        {
            m_aEncoding = RTL_TEXTENCODING_GBK;
            m_bFontEncodingOnly = true;
#if OSL_DEBUG_LEVEL > 1
            fprintf( stderr, "found builtin font %s with GBK encoding\n", pInfo->gfi->fontName );
#endif
        }
    }

    // #i37313# check if Fontspecific is not rather some character encoding
    if( nAdobeEncoding == 3 && m_aEncoding == RTL_TEXTENCODING_SYMBOL )
    {
        bool bYFound = false;
        bool bQFound = false;
        CharMetricInfo* pChar = pInfo->cmi;
        for( int j = 0; j < pInfo->numOfChars && ! (bYFound && bQFound); j++ )
        {
            if( pChar[j].name )
            {
                if( pChar[j].name[0] == 'Y' && pChar[j].name[1] == 0 )
                    bYFound = true;
                else if( pChar[j].name[0] == 'Q' && pChar[j].name[1] == 0 )
                    bQFound = true;
            }
        }
        if( bQFound && bYFound )
        {
            #if OSL_DEBUG_LEVEL > 1
            fprintf( stderr, "setting FontSpecific font %s (file %s) to unicode\n",
                     pInfo->gfi->fontName,
                     rFileName.getStr()
                     );
            #endif
            nAdobeEncoding = 4;
            m_aEncoding = RTL_TEXTENCODING_UNICODE;
            bFillEncodingvector = false; // will be filled anyway, don't do the work twice
        }
    }
    
    // ascend
    m_nAscend = pInfo->gfi->fontBBox.ury;

    // descend
    // descends have opposite sign of our definition
    m_nDescend = -pInfo->gfi->fontBBox.lly;

    // fallback to ascender, descender
    // interesting: the BBox seems to describe Ascender and Descender better
    // as we understand it
    if( m_nAscend == 0 )
        m_nAscend = pInfo->gfi->ascender;
    if( m_nDescend == 0)
        m_nDescend = -pInfo->gfi->descender;

    m_nLeading = m_nAscend + m_nDescend - 1000;

    if( m_pMetrics )
        delete m_pMetrics;
    m_pMetrics = new PrintFontMetrics;
    // mark all pages as queried (or clear if only global font info queiried)
    memset( m_pMetrics->m_aPages, bOnlyGlobalAttributes ? 0 : 0xff, sizeof( m_pMetrics->m_aPages ) );

    m_aGlobalMetricX.width = m_aGlobalMetricY.width =
        pInfo->gfi->charwidth ? pInfo->gfi->charwidth : pInfo->gfi->fontBBox.urx;
    m_aGlobalMetricX.height = m_aGlobalMetricY.height =
        pInfo->gfi->capHeight ? pInfo->gfi->capHeight : pInfo->gfi->fontBBox.ury;

    m_nXMin = pInfo->gfi->fontBBox.llx;
    m_nYMin = pInfo->gfi->fontBBox.lly;
    m_nXMax = pInfo->gfi->fontBBox.urx;
    m_nYMax = pInfo->gfi->fontBBox.ury;

    if( bFillEncodingvector || !bOnlyGlobalAttributes )
    {
        // fill in character metrics
    
        // first transform the character codes to unicode
        // note: this only works with single byte encodings
        sal_Unicode* pUnicodes = (sal_Unicode*)alloca( pInfo->numOfChars * sizeof(sal_Unicode));
        CharMetricInfo* pChar = pInfo->cmi;
        int i;
    
        for( i = 0; i < pInfo->numOfChars; i++, pChar++ )
        {
            if( nAdobeEncoding == 4 )
            {
                if( pChar->name )
                {
                    pUnicodes[i] = 0;
                    std::list< sal_Unicode > aCodes = rManager.getUnicodeFromAdobeName( pChar->name );
                    for( std::list< sal_Unicode >::const_iterator it = aCodes.begin(); it != aCodes.end(); ++it )
                    {
                        if( *it != 0 )
                        {
                            m_aEncodingVector[ *it ] = pChar->code;
                            if( pChar->code == -1 )
                                m_aNonEncoded[ *it ] = pChar->name;
                            if( ! pUnicodes[i] ) // map the first
                                pUnicodes[i] = *it;
                        }
                    }
                }
            }
            else if( pChar->code != -1 )
            {
                if( nAdobeEncoding == 3 && m_aEncoding == RTL_TEXTENCODING_SYMBOL )
                {
                    pUnicodes[i] = pChar->code + 0xf000;
                    if( bFillEncodingvector )
                        m_aEncodingVector[ pUnicodes[i] ] = pChar->code;
                    continue;
                }
    
                if( m_aEncoding == RTL_TEXTENCODING_UNICODE )
                {
                    pUnicodes[i] = (sal_Unicode)pChar->code;
                    continue;
                }
    
                ByteString aTranslate;
                if( pChar->code & 0xff000000 )
                    aTranslate += (char)(pChar->code >> 24 );
                if( pChar->code & 0xffff0000 )
                    aTranslate += (char)((pChar->code & 0x00ff0000) >> 16 );
                if( pChar->code & 0xffffff00 )
                    aTranslate += (char)((pChar->code & 0x0000ff00) >> 8 );
                aTranslate += (char)(pChar->code & 0xff);
                String aUni( aTranslate, m_aEncoding );
                pUnicodes[i] = *aUni.GetBuffer();
            }
            else
                pUnicodes[i] = 0;
        }
    
        // now fill in the character metrics
        // parseAFM.cxx effectively only supports direction 0 (horizontal)
        pChar = pInfo->cmi;
        CharacterMetric aMetric;
        for( i = 0; i < pInfo->numOfChars; i++, pChar++ )
        {
            if( pChar->code == -1 && ! pChar->name )
                continue;
    
            if( bFillEncodingvector && pChar->name )
            {
                std::list< sal_Unicode > aCodes = rManager.getUnicodeFromAdobeName( pChar->name );
                for( std::list< sal_Unicode >::const_iterator it = aCodes.begin(); it != aCodes.end(); ++it )
                {
                    if( *it != 0 )
                    {
                        m_aEncodingVector[ *it ] = pChar->code;
                        if( pChar->code == -1 )
                            m_aNonEncoded[ *it ] = pChar->name;
                    }
                }
            }
            
            aMetric.width   = pChar->wx ? pChar->wx : pChar->charBBox.urx;
            aMetric.height  = pChar->wy ? pChar->wy : pChar->charBBox.ury - pChar->charBBox.lly;
            if( aMetric.width == 0 && aMetric.height == 0 )
                // guess something for e.g. space
                aMetric.width = m_aGlobalMetricX.width/4;
    
            if( ( nAdobeEncoding == 0 ) ||
                ( ( nAdobeEncoding == 3 ) && ( m_aEncoding != RTL_TEXTENCODING_SYMBOL ) ) )
            {
                if( pChar->code != -1 )
                {
                    m_pMetrics->m_aMetrics[ pUnicodes[i] ] = aMetric;
                    if( bFillEncodingvector )
                        m_aEncodingVector[ pUnicodes[i] ] = pChar->code;
                }
                else if( pChar->name )
                {
                    std::list< sal_Unicode > aCodes = rManager.getUnicodeFromAdobeName( pChar->name );
                    for( std::list< sal_Unicode >::const_iterator it = aCodes.begin(); it != aCodes.end(); ++it )
                    {
                        if( *it != 0 )
                            m_pMetrics->m_aMetrics[ *it ] = aMetric;
                    }
                }
            }
            else if( nAdobeEncoding == 1 || nAdobeEncoding == 2 || nAdobeEncoding == 4)
            {
                if( pChar->name )
                {
                    std::list< sal_Unicode > aCodes = rManager.getUnicodeFromAdobeName( pChar->name );
                    for( std::list< sal_Unicode >::const_iterator it = aCodes.begin(); it != aCodes.end(); ++it )
                    {
                        if( *it != 0 )
                            m_pMetrics->m_aMetrics[ *it ] = aMetric;
                    }
                }
                else if( pChar->code != -1 )
                {
                    ::std::pair< ::boost::unordered_multimap< sal_uInt8, sal_Unicode >::const_iterator,
                          ::boost::unordered_multimap< sal_uInt8, sal_Unicode >::const_iterator >
                          aCodes = rManager.getUnicodeFromAdobeCode( pChar->code );
                    while( aCodes.first != aCodes.second )
                    {
                        if( (*aCodes.first).second != 0 )
                        {
                            m_pMetrics->m_aMetrics[ (*aCodes.first).second ] = aMetric;
                            if( bFillEncodingvector )
                                m_aEncodingVector[ (*aCodes.first).second ] = pChar->code;
                        }
                        ++aCodes.first;
                    }
                }
            }
            else if( nAdobeEncoding == 3 )
            {
                if( pChar->code != -1 )
                {
                    sal_Unicode code = 0xf000 + pChar->code;
                    m_pMetrics->m_aMetrics[ code ] = aMetric;
                    // maybe should try to find the name in the convtabs ?
                    if( bFillEncodingvector )
                        m_aEncodingVector[ code ] = pChar->code;
                }
            }
        }
    
        m_pMetrics->m_aXKernPairs.clear();
        m_pMetrics->m_aYKernPairs.clear();
    
        // now fill in the kern pairs
        // parseAFM.cxx effectively only supports direction 0 (horizontal)
        PairKernData* pKern = pInfo->pkd;
        KernPair aPair;
        for( i = 0; i < pInfo->numOfPairs; i++, pKern++ )
        {
            // #i37703# broken kern table
            if( ! pKern->name1 || ! pKern->name2 )
                continue;
            
            aPair.first = 0;
            aPair.second = 0;
            // currently we have to find the adobe character names
            // in the already parsed character metrics to find
            // the corresponding UCS2 code which is a bit dangerous
            // since the character names are not required
            // in the metric descriptions
            pChar = pInfo->cmi;
            for( int j = 0;
                 j < pInfo->numOfChars && ( aPair.first == 0 || aPair.second == 0 );
                 j++, pChar++ )
            {
                if( pChar->code != -1 )
                {
                    if( ! strcmp( pKern->name1, pChar->name ? pChar->name : "" ) )
                        aPair.first = pUnicodes[ j ];
                    if( ! strcmp( pKern->name2, pChar->name ? pChar->name : "" ) )
                        aPair.second = pUnicodes[ j ];
                }
            }
            if( aPair.first && aPair.second )
            {
                aPair.kern_x = pKern->xamt;
                aPair.kern_y = pKern->yamt;
                m_pMetrics->m_aXKernPairs.push_back( aPair );
            }
        }
        m_pMetrics->m_bKernPairsQueried = true;
    }

    freeFontInfo( pInfo );
    return true;
}

// -------------------------------------------------------------------------

OString PrintFontManager::s_aEmptyOString;

/*
 *  one instance only
 */
PrintFontManager& PrintFontManager::get()
{
    static PrintFontManager* pManager = NULL;
    if( ! pManager )
    {
        static PrintFontManager theManager;
        pManager = &theManager;
        pManager->initialize();
    }
    return *pManager;
}

// -------------------------------------------------------------------------

/*
 *  the PrintFontManager
 */

PrintFontManager::PrintFontManager() :
        m_nNextFontID( 1 ),
        m_pAtoms( new MultiAtomProvider() ),
        m_nNextDirAtom( 1 ),
        m_pFontCache( NULL ),
        m_bFontconfigSuccess( false )
{
    for( unsigned int i = 0; i < SAL_N_ELEMENTS( aAdobeCodes ); i++ )
    {
        m_aUnicodeToAdobename.insert( ::boost::unordered_multimap< sal_Unicode, ::rtl::OString >::value_type( aAdobeCodes[i].aUnicode, aAdobeCodes[i].pAdobename ) );
        m_aAdobenameToUnicode.insert( ::boost::unordered_multimap< ::rtl::OString, sal_Unicode, ::rtl::OStringHash >::value_type( aAdobeCodes[i].pAdobename, aAdobeCodes[i].aUnicode ) );
        if( aAdobeCodes[i].aAdobeStandardCode )
        {
            m_aUnicodeToAdobecode.insert( ::boost::unordered_multimap< sal_Unicode, sal_uInt8 >::value_type( aAdobeCodes[i].aUnicode, aAdobeCodes[i].aAdobeStandardCode ) );
            m_aAdobecodeToUnicode.insert( ::boost::unordered_multimap< sal_uInt8, sal_Unicode >::value_type( aAdobeCodes[i].aAdobeStandardCode, aAdobeCodes[i].aUnicode ) );
        }
    }
}

// -------------------------------------------------------------------------

PrintFontManager::~PrintFontManager()
{
    deinitFontconfig();
    for( ::boost::unordered_map< fontID, PrintFont* >::const_iterator it = m_aFonts.begin(); it != m_aFonts.end(); ++it )
        delete (*it).second;
    delete m_pAtoms;
    if( m_pFontCache )
        delete m_pFontCache;
}

// -------------------------------------------------------------------------

const OString& PrintFontManager::getDirectory( int nAtom ) const
{
    ::boost::unordered_map< int, OString >::const_iterator it( m_aAtomToDir.find( nAtom ) );
    return it != m_aAtomToDir.end() ? it->second : s_aEmptyOString;
}

// -------------------------------------------------------------------------

int PrintFontManager::getDirectoryAtom( const OString& rDirectory, bool bCreate )
{
    int nAtom = 0;
    ::boost::unordered_map< OString, int, OStringHash >::const_iterator it
          ( m_aDirToAtom.find( rDirectory ) );
    if( it != m_aDirToAtom.end() )
        nAtom = it->second;
    else if( bCreate )
    {
        nAtom = m_nNextDirAtom++;
        m_aDirToAtom[ rDirectory ] = nAtom;
        m_aAtomToDir[ nAtom ] = rDirectory;
    }
    return nAtom;
}

// -------------------------------------------------------------------------

int PrintFontManager::addFontFile( const ::rtl::OString& rFileName, int /*nFaceNum*/ )
{
    rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
    INetURLObject aPath( OStringToOUString( rFileName, aEncoding ), INET_PROT_FILE, INetURLObject::ENCODE_ALL );
    OString aName( OUStringToOString( aPath.GetName(), aEncoding ) );
    OString aDir( OUStringToOString( aPath.GetPath(), aEncoding ) );

    int nDirID = getDirectoryAtom( aDir, true );
    fontID nFontId = findFontFileID( nDirID, aName );
    if( !nFontId )
    {
        ::std::list< PrintFont* > aNewFonts;
        if( analyzeFontFile( nDirID, aName, ::std::list<OString>(), aNewFonts ) )
        {
            for( ::std::list< PrintFont* >::iterator it = aNewFonts.begin();
                 it != aNewFonts.end(); ++it )
            {
                m_aFonts[ nFontId = m_nNextFontID++ ] = *it;
                m_aFontFileToFontID[ aName ].insert( nFontId );
                m_pFontCache->updateFontCacheEntry( *it, true );
            }
        }
    }
    return nFontId;
}

// -------------------------------------------------------------------------

bool PrintFontManager::analyzeFontFile( int nDirID, const OString& rFontFile, const ::std::list<OString>& rXLFDs, ::std::list< PrintFontManager::PrintFont* >& rNewFonts ) const
{
    rNewFonts.clear();

    OString aDir( getDirectory( nDirID ) );

    OString aFullPath( aDir );
    aFullPath += "/";
    aFullPath += rFontFile;

    // #i1872# reject unreadable files
    if( access( aFullPath.getStr(), R_OK ) )
        return false;

    ByteString aExt( rFontFile.copy( rFontFile.lastIndexOf( '.' )+1 ) );
    if( aExt.EqualsIgnoreCaseAscii( "pfb" ) || aExt.EqualsIgnoreCaseAscii( "pfa" ) )
    {
        // check for corresponding afm metric
        // first look for an adjacent file
        static const char* pSuffix[] = { ".afm", ".AFM" };
        
        for( unsigned int i = 0; i < SAL_N_ELEMENTS(pSuffix); i++ )
        {
            ByteString aName( rFontFile );
            aName.Erase( aName.Len()-4 );
            aName.Append( pSuffix[i] );
            
            ByteString aFilePath( aDir );
            aFilePath.Append( '/' );
            aFilePath.Append( aName );

            ByteString aAfmFile;
            if( access( aFilePath.GetBuffer(), R_OK ) )
            {
                // try in subdirectory afm instead
                aFilePath = aDir;
                aFilePath.Append( "/afm/" );
                aFilePath.Append( aName );
            
                if( ! access( aFilePath.GetBuffer(), R_OK ) )
                {
                    aAfmFile = "afm/";
                    aAfmFile += aName;
                }
            }
            else
                aAfmFile = aName;

            if( aAfmFile.Len() )
            {
                Type1FontFile* pFont = new Type1FontFile();
                pFont->m_nDirectory     = nDirID;
                
                pFont->m_aFontFile      = rFontFile;
                pFont->m_aMetricFile    = aAfmFile;
                
                if( ! pFont->readAfmMetrics( getAfmFile( pFont ), m_pAtoms, false, true ) )
                {
                    delete pFont;
                    pFont = NULL;
                }
                if( pFont && rXLFDs.size() )
                    getFontAttributesFromXLFD( pFont, rXLFDs );
                if( pFont )
                    rNewFonts.push_back( pFont );
                break;
            }
        }
    }
    else if( aExt.EqualsIgnoreCaseAscii( "afm" ) )
    {
        ByteString aFilePath( aDir );
        aFilePath.Append( '/' );
        aFilePath.Append( ByteString( rFontFile ) );
        BuiltinFont* pFont = new BuiltinFont();
        pFont->m_nDirectory		= nDirID;
        pFont->m_aMetricFile	= rFontFile;
        if( pFont->readAfmMetrics( aFilePath, m_pAtoms, false, true ) )
            rNewFonts.push_back( pFont );
        else
            delete pFont;
    }
    else if( aExt.EqualsIgnoreCaseAscii( "ttf" )
         ||  aExt.EqualsIgnoreCaseAscii( "tte" )   // #i33947# for Gaiji support
         ||  aExt.EqualsIgnoreCaseAscii( "otf" ) ) // check for TTF- and PS-OpenType too
    {
        TrueTypeFontFile* pFont     = new TrueTypeFontFile();
        pFont->m_nDirectory         = nDirID;
        pFont->m_aFontFile          = rFontFile;
        pFont->m_nCollectionEntry   = -1;

        if( rXLFDs.size() )
            getFontAttributesFromXLFD( pFont, rXLFDs );
        // need to read the font anyway to get aliases inside the font file
        if( ! analyzeTrueTypeFile( pFont ) )
        {
            delete pFont;
            pFont = NULL;
        }
        else
            rNewFonts.push_back( pFont );
    }
    else if( aExt.EqualsIgnoreCaseAscii( "ttc" ) )
    {
        // get number of ttc entries
        int nLength = CountTTCFonts( aFullPath.getStr() );
        if( nLength )
        {
#if OSL_DEBUG_LEVEL > 1
            fprintf( stderr, "%s contains %d fonts\n", aFullPath.getStr(), nLength );
#endif
            for( int i = 0; i < nLength; i++ )
            {
                TrueTypeFontFile* pFont     = new TrueTypeFontFile();
                pFont->m_nDirectory         = nDirID;
                pFont->m_aFontFile          = rFontFile;
                pFont->m_nCollectionEntry   = i;
                if( nLength == 1 )
                    getFontAttributesFromXLFD( pFont, rXLFDs );
                if( ! analyzeTrueTypeFile( pFont ) )
                {
                    delete pFont;
                    pFont = NULL;
                }
                else
                    rNewFonts.push_back( pFont );
            }
        }
#if OSL_DEBUG_LEVEL > 1
        else
            fprintf( stderr, "CountTTCFonts( \"%s/%s\" ) failed\n", getDirectory(nDirID).getStr(), rFontFile.getStr() );
#endif
    }
    return ! rNewFonts.empty();
}

// -------------------------------------------------------------------------

fontID PrintFontManager::findFontBuiltinID( int nPSNameAtom ) const
{
    fontID nID = 0;
    ::boost::unordered_map< fontID, PrintFont* >::const_iterator it;
    for( it = m_aFonts.begin(); nID == 0 && it != m_aFonts.end(); ++it )
    {
        if( it->second->m_eType == fonttype::Builtin &&
            it->second->m_nPSName == nPSNameAtom )
            nID = it->first;
    }
    return nID;
}

// -------------------------------------------------------------------------

fontID PrintFontManager::findFontFileID( int nDirID, const OString& rFontFile ) const
{
    fontID nID = 0;

    ::boost::unordered_map< OString, ::std::set< fontID >, OStringHash >::const_iterator set_it = m_aFontFileToFontID.find( rFontFile );
    if( set_it != m_aFontFileToFontID.end() )
    {
        for( ::std::set< fontID >::const_iterator font_it = set_it->second.begin(); font_it != set_it->second.end() && ! nID; ++font_it )
        {
            ::boost::unordered_map< fontID, PrintFont* >::const_iterator it = m_aFonts.find( *font_it );
            if( it != m_aFonts.end() )
            {
                switch( it->second->m_eType )
                {
                    case fonttype::Type1:
                    {
                        Type1FontFile* const pFont = static_cast< Type1FontFile* const >((*it).second);
                        if( pFont->m_nDirectory == nDirID &&
                            pFont->m_aFontFile == rFontFile )
                            nID = it->first;
                    }
                    break;
                    case fonttype::TrueType:
                    {
                        TrueTypeFontFile* const pFont = static_cast< TrueTypeFontFile* const >((*it).second);
                        if( pFont->m_nDirectory == nDirID &&
                            pFont->m_aFontFile == rFontFile )
                            nID = it->first;
                    }
                    break;
                    case fonttype::Builtin:
                        if( static_cast<const BuiltinFont*>((*it).second)->m_nDirectory == nDirID &&
                            static_cast<const BuiltinFont*>((*it).second)->m_aMetricFile == rFontFile )
                            nID = it->first;
                        break;
                    default:
                        break;
                }
            }
        }
    }
    return nID;
}

// -------------------------------------------------------------------------

bool PrintFontManager::parseXLFD( const OString& rXLFD, XLFDEntry& rEntry )
{
    sal_Int32 nIndex = 0;
    OString aFoundry		= WhitespaceToSpace( rXLFD.getToken( 1, '-', nIndex ) );
    if( nIndex < 0 ) return false;
    OString aFamilyXLFD		= WhitespaceToSpace( rXLFD.getToken( 0, '-', nIndex ) );
    if( nIndex < 0 ) return false;
    OString aWeight			= rXLFD.getToken( 0, '-', nIndex ).toAsciiLowerCase();
    if( nIndex < 0 ) return false;
    OString aSlant			= rXLFD.getToken( 0, '-', nIndex ).toAsciiLowerCase();
    if( nIndex < 0 ) return false;
    OString aWidth			= rXLFD.getToken( 0, '-', nIndex ).toAsciiLowerCase();
    if( nIndex < 0 ) return false;
    OString aAddStyle		= rXLFD.getToken( 0, '-', nIndex ).toAsciiLowerCase();
    if( nIndex < 0 ) return false;
    OString aPitch			= rXLFD.getToken( 4, '-', nIndex ).toAsciiLowerCase();
    if( nIndex < 0 ) return false;
    OString aRegEnc			= WhitespaceToSpace( rXLFD.getToken( 1, '-', nIndex ).toAsciiLowerCase() );
    if( nIndex < 0 ) return false;
    OString aEnc			= WhitespaceToSpace( rXLFD.getToken( 0, '-', nIndex ).toAsciiLowerCase() );

    // capitalize words
    sal_Int32 nFamIndex = 0;
    OStringBuffer aFamilyName;
    while( nFamIndex >= 0 )
    {
        OString aToken = aFamilyXLFD.getToken( 0, ' ', nFamIndex );
        sal_Char aFirst = aToken.toChar();
        if( aFirst >= 'a' && aFirst <= 'z' )
            aFirst = aFirst - 'a' + 'A';
        OStringBuffer aNewToken( aToken.getLength() );
        aNewToken.append( aToken );
        aNewToken.setCharAt( 0, aFirst );
        if( aFamilyName.getLength() > 0 )
            aFamilyName.append( ' ' );
        aFamilyName.append( aNewToken.makeStringAndClear() );
    }

    rEntry.aFoundry		= aFoundry;
    rEntry.aFamily		= aFamilyName.makeStringAndClear();
    rEntry.aAddStyle	= aAddStyle;
    // evaluate weight
    rEntry.eWeight = parseWeight( aWeight );
    // evaluate slant
    rEntry.eItalic = parseItalic( aSlant );
    // evaluate width
    rEntry.eWidth = parseWidth( aWidth );
    
    // evaluate pitch
    if( aPitch.toChar() == 'c' || aPitch.toChar() == 'm' )
        rEntry.ePitch = pitch::Fixed;
    else
        rEntry.ePitch = pitch::Variable;
    
    OString aToken = aEnc.toAsciiLowerCase();
    // get encoding
    if( aAddStyle.indexOf( "symbol" ) != -1 )
        rEntry.aEncoding = RTL_TEXTENCODING_SYMBOL;
    else
    {
        if( aToken.equals( "symbol" ) )
            rEntry.aEncoding = RTL_TEXTENCODING_SYMBOL;
        else
        {
            OStringBuffer aCharset( aRegEnc.getLength() + aEnc.getLength() + 1 );
            aCharset.append( aRegEnc );
            aCharset.append( '-' );
            aCharset.append( aEnc );
            rEntry.aEncoding = rtl_getTextEncodingFromUnixCharset( aCharset.getStr() );
        }
    }
    
    // set correct mask flags
    rEntry.nMask = 0;
    if( rEntry.aFoundry != "*" )		rEntry.nMask |= XLFDEntry::MaskFoundry;
    if( rEntry.aFamily != "*" )			rEntry.nMask |= XLFDEntry::MaskFamily;
    if( rEntry.aAddStyle != "*" )		rEntry.nMask |= XLFDEntry::MaskAddStyle;
    if( aWeight != "*" )				rEntry.nMask |= XLFDEntry::MaskWeight;
    if( aSlant != "*" )					rEntry.nMask |= XLFDEntry::MaskItalic;
    if( aWidth != "*" )					rEntry.nMask |= XLFDEntry::MaskWidth;
    if( aPitch != "*" )					rEntry.nMask |= XLFDEntry::MaskPitch;
    if( aRegEnc != "*" && aEnc != "*" )	rEntry.nMask |= XLFDEntry::MaskEncoding;

    return true;
}

// -------------------------------------------------------------------------

void PrintFontManager::parseXLFD_appendAliases( const std::list< OString >& rXLFDs, std::list< XLFDEntry >& rEntries ) const
{
    for( std::list< OString >::const_iterator it = rXLFDs.begin(); it != rXLFDs.end(); ++it )
    {
        XLFDEntry aEntry;
        if( ! parseXLFD(*it, aEntry) )
            continue;
        rEntries.push_back( aEntry );
        std::map< XLFDEntry, std::list< XLFDEntry > >::const_iterator alias_it =
            m_aXLFD_Aliases.find( aEntry );
        if( alias_it != m_aXLFD_Aliases.end() )
        {
            rEntries.insert( rEntries.end(), alias_it->second.begin(), alias_it->second.end() );
        }
    }
}

// -------------------------------------------------------------------------

void PrintFontManager::getFontAttributesFromXLFD( PrintFont* pFont, const std::list< OString >& rXLFDs ) const
{
    bool bFamilyName = false;

    std::list< XLFDEntry > aXLFDs;

    parseXLFD_appendAliases( rXLFDs, aXLFDs );
    
    for( std::list< XLFDEntry >::const_iterator it = aXLFDs.begin();
         it != aXLFDs.end(); ++it )
    {
        // set family name or alias
        int nFam = 
            m_pAtoms->getAtom( ATOM_FAMILYNAME,
                               OStringToOUString( it->aFamily, it->aAddStyle.indexOf( "utf8" ) != -1 ? RTL_TEXTENCODING_UTF8 : RTL_TEXTENCODING_ISO_8859_1 ),
                               sal_True );
        if( ! bFamilyName )
        {
            bFamilyName = true;
            pFont->m_nFamilyName = nFam;
            switch( pFont->m_eType )
            {
                case fonttype::Type1:
                    static_cast<Type1FontFile*>(pFont)->m_aXLFD = rXLFDs.front();
                    break;
                case fonttype::TrueType:
                    static_cast<TrueTypeFontFile*>(pFont)->m_aXLFD = rXLFDs.front();
                    break;
                default:
                    break;
            }
        }
        else
        {
            // make sure that aliases are unique
            if( nFam != pFont->m_nFamilyName )
            {
                std::list< int >::const_iterator al_it;
                for( al_it = pFont->m_aAliases.begin(); al_it != pFont->m_aAliases.end() && *al_it != nFam; ++al_it )
                    ;
                if( al_it == pFont->m_aAliases.end() )
                    pFont->m_aAliases.push_back( nFam );
                
            }
            // for the rest of the attributes there can only be one value;
            // we'll trust the first one
            continue;
        }

        // fill in weight
        pFont->m_eWeight	= it->eWeight;
        // fill in slant
        pFont->m_eItalic	= it->eItalic;
        // fill in width
        pFont->m_eWidth		= it->eWidth;
        // fill in pitch
        pFont->m_ePitch		= it->ePitch;
        // fill in encoding
        pFont->m_aEncoding	= it->aEncoding;
    }

    // handle iso8859-1 as ms1252 to fill the "gap" starting at 0x80
    if( pFont->m_aEncoding == RTL_TEXTENCODING_ISO_8859_1 )
        pFont->m_aEncoding = RTL_TEXTENCODING_MS_1252;
    if( rXLFDs.begin() != rXLFDs.end() )
    {
        switch( pFont->m_eType )
        {
            case fonttype::Type1:
                static_cast<Type1FontFile*>(pFont)->m_aXLFD = rXLFDs.front();
                break;
            case fonttype::TrueType:
                static_cast<TrueTypeFontFile*>(pFont)->m_aXLFD = rXLFDs.front();
                break;
            default: break;
        }
    }
}

// -------------------------------------------------------------------------

OString PrintFontManager::getXLFD( PrintFont* pFont ) const
{
    if( pFont->m_eType == fonttype::Type1 )
    {
        if( static_cast<Type1FontFile*>(pFont)->m_aXLFD.getLength() )
            return static_cast<Type1FontFile*>(pFont)->m_aXLFD;
    }
    if( pFont->m_eType == fonttype::TrueType )
    {
        if( static_cast<TrueTypeFontFile*>(pFont)->m_aXLFD.getLength() )
            return static_cast<TrueTypeFontFile*>(pFont)->m_aXLFD;
    }

    OStringBuffer aXLFD( 128 );

    aXLFD.append( "-misc-" );
    ByteString aFamily( String( m_pAtoms->getString( ATOM_FAMILYNAME, pFont->m_nFamilyName ) ), RTL_TEXTENCODING_UTF8 );
    aFamily.SearchAndReplaceAll( '-',' ' );
    aFamily.SearchAndReplaceAll( '?',' ' );
    aFamily.SearchAndReplaceAll( '*',' ' );
    aXLFD.append( OString( aFamily ) );
    aXLFD.append( '-' );
    switch( pFont->m_eWeight )
    {
        case weight::Thin:          aXLFD.append("thin");break;
        case weight::UltraLight:    aXLFD.append("ultralight");break;
        case weight::Light:         aXLFD.append("light");break;
        case weight::SemiLight:     aXLFD.append("semilight");break;
        case weight::Normal:        aXLFD.append("normal");break;
        case weight::Medium:        aXLFD.append("medium");break;
        case weight::SemiBold:      aXLFD.append("semibold");break;
        case weight::Bold:          aXLFD.append("bold");break;
        case weight::UltraBold:     aXLFD.append("ultrabold");break;
        case weight::Black:         aXLFD.append("black");break;
        default: break;
    }
    aXLFD.append('-');
    switch( pFont->m_eItalic )
    {
        case italic::Upright:       aXLFD.append('r');break;
        case italic::Oblique:       aXLFD.append('o');break;
        case italic::Italic:        aXLFD.append('i');break;
        default: break;
    }
    aXLFD.append('-');
    switch( pFont->m_eWidth )
    {
        case width::UltraCondensed: aXLFD.append("ultracondensed");break;
        case width::ExtraCondensed: aXLFD.append("extracondensed");break;
        case width::Condensed:      aXLFD.append("condensed");break;
        case width::SemiCondensed:  aXLFD.append("semicondensed");break;
        case width::Normal:         aXLFD.append("normal");break;
        case width::SemiExpanded:   aXLFD.append("semiexpanded");break;
        case width::Expanded:       aXLFD.append("expanded");break;
        case width::ExtraExpanded:  aXLFD.append("extraexpanded");break;
        case width::UltraExpanded:  aXLFD.append("ultraexpanded");break;
        default: break;
    }
    aXLFD.append("-utf8-0-0-0-0-");
    aXLFD.append( pFont->m_ePitch == pitch::Fixed ? "m" : "p" );
    aXLFD.append("-0-");
    const char* pEnc = rtl_getBestUnixCharsetFromTextEncoding( pFont->m_aEncoding );
    if( ! pEnc )
    {
        if( pFont->m_aEncoding == RTL_TEXTENCODING_ADOBE_STANDARD )
            pEnc = "adobe-standard";
        else
            pEnc = "iso8859-1";
    }
    aXLFD .append( pEnc );

    return aXLFD.makeStringAndClear();
}

// -------------------------------------------------------------------------

OUString PrintFontManager::convertTrueTypeName( void* pRecord ) const
{
    NameRecord* pNameRecord = (NameRecord*)pRecord;
    OUString aValue;
    if(
       ( pNameRecord->platformID == 3 && ( pNameRecord->encodingID == 0 || pNameRecord->encodingID == 1 ) )  // MS, Unicode
       ||
       ( pNameRecord->platformID == 0 ) // Apple, Unicode
       )
    {
        OUStringBuffer aName( pNameRecord->slen/2 );
        const sal_uInt8* pNameBuffer = pNameRecord->sptr;
        for(int n = 0; n < pNameRecord->slen/2; n++ )
            aName.append( (sal_Unicode)getUInt16BE( pNameBuffer ) );
        aValue = aName.makeStringAndClear();
    }
    else if( pNameRecord->platformID == 3 )
    {
        if( pNameRecord->encodingID >= 2 && pNameRecord->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 = pNameRecord->sptr;
            for(int n = 0; n < pNameRecord->slen/2; n++ )
            {
                sal_Unicode aCode = (sal_Unicode)getUInt16BE( pNameBuffer );
                sal_Char aChar = aCode >> 8;
                if( aChar )
                    aName.append( aChar );
                aChar = aCode & 0x00ff;
                if( aChar )
                    aName.append( aChar );
            }
            switch( pNameRecord->encodingID )
            {
                case 2:
                    aValue = OStringToOUString( aName.makeStringAndClear(), RTL_TEXTENCODING_MS_932 );
                    break;
                case 3:
                    aValue = OStringToOUString( aName.makeStringAndClear(), RTL_TEXTENCODING_MS_936 );
                    break;
                case 4:
                    aValue = OStringToOUString( aName.makeStringAndClear(), RTL_TEXTENCODING_MS_950 );
                    break;
                case 5:
                    aValue = OStringToOUString( aName.makeStringAndClear(), RTL_TEXTENCODING_MS_949 );
                    break;
                case 6:
                    aValue = OStringToOUString( aName.makeStringAndClear(), RTL_TEXTENCODING_MS_1361 );
                    break;
            }
        }
    }
    return aValue;
}

//fdo#33349.There exists an archaic Berling Antiqua font which has a "Times New
//Roman" name field in it. We don't want the "Times New Roman" name to take
//precedence in this case. We take Berling Antiqua as a higher priority name,
//and erase the "Times New Roman" name
namespace
{
    bool isBadTNR(const OUString &rName, ::std::set< OUString >& rSet)
    {
        bool bRet = false;
        if (rName.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("Berling Antiqua")))
        {
            ::std::set< OUString >::iterator aEnd = rSet.end();
            ::std::set< OUString >::iterator aI = rSet.find(OUString(RTL_CONSTASCII_USTRINGPARAM("Times New Roman")));
            if (aI != aEnd)
            {
                bRet = true;
                rSet.erase(aI);
            }
        }
        return bRet;
    }
}

// -------------------------------------------------------------------------

void PrintFontManager::analyzeTrueTypeFamilyName( void* pTTFont, ::std::list< OUString >& rNames ) const
{
    OUString aFamily;

    rNames.clear();
    ::std::set< OUString > aSet;

    NameRecord* pNameRecords = NULL;
    int nNameRecords = GetTTNameRecords( (TrueTypeFont*)pTTFont, &pNameRecords );
    if( nNameRecords && pNameRecords )
    {
        LanguageType aLang = MsLangId::getSystemLanguage();
        int nLastMatch = -1;
        for( int i = 0; i < nNameRecords; i++ )
        {
            if( pNameRecords[i].nameID != 1 || pNameRecords[i].sptr == NULL )
                continue;
            int nMatch = -1;
            if( pNameRecords[i].platformID == 0 ) // Unicode
                nMatch = 4000;
            else if( pNameRecords[i].platformID == 3 )
            {
                // this bases on the LanguageType actually being a Win LCID
                if( pNameRecords[i].languageID == aLang )
                    nMatch = 8000;
                else if( pNameRecords[i].languageID == LANGUAGE_ENGLISH_US )
                    nMatch = 2000;
                else if( pNameRecords[i].languageID == LANGUAGE_ENGLISH ||
                         pNameRecords[i].languageID == LANGUAGE_ENGLISH_UK )
                    nMatch = 1500;
                else
                    nMatch = 1000;
            }
            OUString aName = convertTrueTypeName( pNameRecords + i );
            aSet.insert( aName );
            if( nMatch > nLastMatch || isBadTNR(aName, aSet) )
            {
                nLastMatch = nMatch;
                aFamily = aName;
            }
        }
        DisposeNameRecords( pNameRecords, nNameRecords );
    }
    if( aFamily.getLength() )
    {
        rNames.push_front( aFamily );
        for( ::std::set< OUString >::const_iterator it = aSet.begin(); it != aSet.end(); ++it )
            if( *it != aFamily )
                rNames.push_back( *it );
    }
    return;
}

// -------------------------------------------------------------------------

bool PrintFontManager::analyzeTrueTypeFile( PrintFont* pFont ) const
{
    bool bSuccess = false;
    rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
    ByteString aFile = getFontFile( pFont );
    TrueTypeFont* pTTFont = NULL;

    TrueTypeFontFile* pTTFontFile = static_cast< TrueTypeFontFile* >(pFont);
    if( OpenTTFontFile( aFile.GetBuffer(), pTTFontFile->m_nCollectionEntry < 0 ? 0 : pTTFontFile->m_nCollectionEntry, &pTTFont ) == SF_OK )
    {
        TTGlobalFontInfo aInfo;
        GetTTGlobalFontInfo( pTTFont, & aInfo );

        ::std::list< OUString > aNames;
        analyzeTrueTypeFamilyName( pTTFont, aNames );

        // set family name from XLFD if possible
        if( ! pFont->m_nFamilyName )
        {
            if( aNames.begin() != aNames.end() )
            {
                pFont->m_nFamilyName = m_pAtoms->getAtom( ATOM_FAMILYNAME, aNames.front(), sal_True );
                aNames.pop_front();
            }
            else
            {
                 sal_Int32   dotIndex;

                 // poor font does not have a family name
                 // name it to file name minus the extension
                 dotIndex = pTTFontFile->m_aFontFile.lastIndexOf( '.' );
                 if ( dotIndex == -1 )
                     dotIndex = pTTFontFile->m_aFontFile.getLength();

                 pFont->m_nFamilyName = m_pAtoms->getAtom( ATOM_FAMILYNAME, OStringToOUString( pTTFontFile->m_aFontFile.copy( 0, dotIndex ), aEncoding ), sal_True );
            }
        }
        for( ::std::list< OUString >::iterator it = aNames.begin(); it != aNames.end(); ++it )
        {
            if( it->getLength() )
            {
                int nAlias = m_pAtoms->getAtom( ATOM_FAMILYNAME, *it, sal_True );
                if( nAlias != pFont->m_nFamilyName )
                {
                    std::list< int >::const_iterator al_it;
                    for( al_it = pFont->m_aAliases.begin(); al_it != pFont->m_aAliases.end() && *al_it != nAlias; ++al_it )
                        ;
                    if( al_it == pFont->m_aAliases.end() )
                        pFont->m_aAliases.push_back( nAlias );
                }
            }
        }

        if( aInfo.usubfamily )
            pFont->m_aStyleName = OUString( aInfo.usubfamily );

        pFont->m_nPSName = m_pAtoms->getAtom( ATOM_PSNAME, String( ByteString( aInfo.psname ), aEncoding ), sal_True );
        switch( aInfo.weight )
        {
            case FW_THIN:           pFont->m_eWeight = weight::Thin; break;
            case FW_EXTRALIGHT: pFont->m_eWeight = weight::UltraLight; break;
            case FW_LIGHT:          pFont->m_eWeight = weight::Light; break;
            case FW_MEDIUM:     pFont->m_eWeight = weight::Medium; break;
            case FW_SEMIBOLD:       pFont->m_eWeight = weight::SemiBold; break;
            case FW_BOLD:           pFont->m_eWeight = weight::Bold; break;
            case FW_EXTRABOLD:      pFont->m_eWeight = weight::UltraBold; break;
            case FW_BLACK:          pFont->m_eWeight = weight::Black; break;

            case FW_NORMAL:
            default:        pFont->m_eWeight = weight::Normal; break;
        }

        switch( aInfo.width )
        {
            case FWIDTH_ULTRA_CONDENSED:    pFont->m_eWidth = width::UltraCondensed; break;
            case FWIDTH_EXTRA_CONDENSED:    pFont->m_eWidth = width::ExtraCondensed; break;
            case FWIDTH_CONDENSED:          pFont->m_eWidth = width::Condensed; break;
            case FWIDTH_SEMI_CONDENSED: pFont->m_eWidth = width::SemiCondensed; break;
            case FWIDTH_SEMI_EXPANDED:      pFont->m_eWidth = width::SemiExpanded; break;
            case FWIDTH_EXPANDED:           pFont->m_eWidth = width::Expanded; break;
            case FWIDTH_EXTRA_EXPANDED: pFont->m_eWidth = width::ExtraExpanded; break;
            case FWIDTH_ULTRA_EXPANDED: pFont->m_eWidth = width::UltraExpanded; break;

            case FWIDTH_NORMAL:
            default:                        pFont->m_eWidth = width::Normal; break;
        }

        pFont->m_ePitch = aInfo.pitch ? pitch::Fixed : pitch::Variable;
        pFont->m_eItalic = aInfo.italicAngle == 0 ? italic::Upright : ( aInfo.italicAngle < 0 ? italic::Italic : 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) )
            pFont->m_eItalic = italic::Italic;

        pFont->m_aEncoding = aInfo.symbolEncoded ? RTL_TEXTENCODING_SYMBOL : RTL_TEXTENCODING_UCS2;

        pFont->m_aGlobalMetricY.width = pFont->m_aGlobalMetricX.width = aInfo.xMax - aInfo.xMin;
        pFont->m_aGlobalMetricY.height = pFont->m_aGlobalMetricX.height = aInfo.yMax - aInfo.yMin;

        if( aInfo.winAscent && aInfo.winDescent )
        {
            pFont->m_nAscend    = aInfo.winAscent;
            pFont->m_nDescend   = aInfo.winDescent;
            pFont->m_nLeading   = pFont->m_nAscend + pFont->m_nDescend - 1000;
        }
        else if( aInfo.typoAscender && aInfo.typoDescender )
        {
            pFont->m_nLeading   = aInfo.typoLineGap;
            pFont->m_nAscend    = aInfo.typoAscender;
            pFont->m_nDescend   = -aInfo.typoDescender;
        }
        else
        {
            pFont->m_nLeading   = aInfo.linegap;
            pFont->m_nAscend    = aInfo.ascender;
            pFont->m_nDescend   = -aInfo.descender;
        }

        // last try: font bounding box
        if( pFont->m_nAscend == 0 )
            pFont->m_nAscend = aInfo.yMax;
        if( pFont->m_nDescend == 0 )
            pFont->m_nDescend = -aInfo.yMin;
        if( pFont->m_nLeading == 0 )
            pFont->m_nLeading = 15 * (pFont->m_nAscend+pFont->m_nDescend) / 100;

        if( pFont->m_nAscend )
            pFont->m_aGlobalMetricX.height = pFont->m_aGlobalMetricY.height = pFont->m_nAscend + pFont->m_nDescend;

        // get bounding box
        pFont->m_nXMin = aInfo.xMin;
        pFont->m_nYMin = aInfo.yMin;
        pFont->m_nXMax = aInfo.xMax;
        pFont->m_nYMax = aInfo.yMax;

        // get type flags
        pTTFontFile->m_nTypeFlags = (unsigned int)aInfo.typeFlags;

        // get vertical substitutions flag
        pFont->m_bHaveVerticalSubstitutedGlyphs = DoesVerticalSubstitution( pTTFont, 1 );

        CloseTTFont( pTTFont );
        bSuccess = true;
    }
#if OSL_DEBUG_LEVEL > 1
    else
        fprintf( stderr, "could not OpenTTFont \"%s\"\n", aFile.GetBuffer() );
#endif

    return bSuccess;
}

// -------------------------------------------------------------------------

void PrintFontManager::initFontsAlias()
{
    m_aXLFD_Aliases.clear();
    rtl_TextEncoding aEnc = osl_getThreadTextEncoding();
    for( std::list< OString >::const_iterator dir_it = m_aFontDirectories.begin();
         dir_it != m_aFontDirectories.end(); ++dir_it )
    {
        OStringBuffer aDirName(512);
        aDirName.append( *dir_it );
        aDirName.append( "/fonts.alias" );
        SvFileStream aStream( OStringToOUString( aDirName.makeStringAndClear(), aEnc ), STREAM_READ );
        if( ! aStream.IsOpen() )
            continue;

        do
        {
            ByteString aLine;
            aStream.ReadLine( aLine );

            // get the alias and the pattern it gets translated to
            ByteString aAlias	= GetCommandLineToken( 0, aLine );
            ByteString aMap		= GetCommandLineToken( 1, aLine );
            
            // remove eventual quotes
            aAlias.EraseLeadingChars( '"' );
            aAlias.EraseTrailingChars( '"' );
            aMap.EraseLeadingChars( '"' );
            aMap.EraseTrailingChars( '"' );
            
            XLFDEntry aAliasEntry, aMapEntry;
            parseXLFD( aAlias, aAliasEntry );
            parseXLFD( aMap, aMapEntry );           

            if( aAliasEntry.nMask && aMapEntry.nMask )
                m_aXLFD_Aliases[ aMapEntry ].push_back( aAliasEntry );
        } while( ! aStream.IsEof() );
    }
}

// code stolen from vcl's RegisterFontSubstitutors()
// TODO: use that method once psprint gets merged into vcl
static bool AreFCSubstitutionsEnabled()
{
    // init font substitution defaults
    int nDisableBits = 0;
#ifdef SOLARIS
    // TODO: check the OS version and fc-data maintenance level
    nDisableBits = 1; // disable "font fallback" here on default
#endif
    // apply the environment variable if any
    const char* pEnvStr = ::getenv( "SAL_DISABLE_FC_SUBST" );
    if( pEnvStr )
    {
        //
        if( (*pEnvStr >= '0') && (*pEnvStr <= '9') )
            nDisableBits = (*pEnvStr - '0'); 
        else
            nDisableBits = ~0U; // no specific bits set: disable all
    }

    return ((nDisableBits & 3) == 0);
}

void PrintFontManager::initialize()
{
    #ifdef CALLGRIND_COMPILE
    CALLGRIND_TOGGLE_COLLECT();
    CALLGRIND_ZERO_STATS();
    #endif
    
    long aDirEntBuffer[ (sizeof(struct dirent)+_PC_NAME_MAX)+1 ];

    if( ! m_pFontCache )
    {
#if OSL_DEBUG_LEVEL > 1
        fprintf( stderr, "creating font cache ... " );
        clock_t aStart;
        struct tms tms;
        aStart = times( &tms );
#endif
        m_pFontCache = new FontCache();
#if OSL_DEBUG_LEVEL > 1
        clock_t aStop = times( &tms );
        fprintf( stderr, "done in %lf s\n", (double)(aStop - aStart)/(double)sysconf( _SC_CLK_TCK ) );
#endif
    }

    // initialize may be called twice in the future
    {
        for( ::boost::unordered_map< fontID, PrintFont* >::const_iterator it = m_aFonts.begin(); it != m_aFonts.end(); ++it )
            delete (*it).second;
        m_nNextFontID = 1;
        m_aFonts.clear();
        m_aFontDirectories.clear();
        m_aPrivateFontDirectories.clear();
        m_aOverrideFonts.clear();
    }

#if OSL_DEBUG_LEVEL > 1
    clock_t aStart;
    clock_t aStep1;
    clock_t aStep2;
    clock_t aStep3;
    int nBuiltinFonts = 0;
    int nCached = 0;
    
    struct tms tms;

    aStart = times( &tms );
#endif

    // first try fontconfig
    m_bFontconfigSuccess = initFontconfig();

    // part one - look for downloadable fonts
    rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
    const ::rtl::OUString &rSalPrivatePath = psp::getFontPath();

    // search for the fonts in SAL_PRIVATE_FONTPATH first; those are
    // the fonts installed with the office
    if( rSalPrivatePath.getLength() )
    {
        OString aPath = rtl::OUStringToOString( rSalPrivatePath, aEncoding );
        const bool bAreFCSubstitutionsEnabled = AreFCSubstitutionsEnabled();
        sal_Int32 nIndex = 0;
        do
        {
            OString aToken = aPath.getToken( 0, ';', nIndex );
            normPath( aToken );
            // if registering an app-specific fontdir with fontconfig fails
            // and fontconfig-based substitutions are enabled
            // then trying to use these app-specific fonts doesn't make sense 
            if( m_bFontconfigSuccess && !addFontconfigDir( aToken ) )
                if( bAreFCSubstitutionsEnabled )
                    continue;
            m_aFontDirectories.push_back( aToken );
            m_aPrivateFontDirectories.push_back( getDirectoryAtom( aToken, true ) );
        } while( nIndex >= 0 );
    }

    // protect against duplicate paths
    boost::unordered_map< OString, int, OStringHash > visited_dirs;

    // now that all global and local font dirs are known to fontconfig
    // check that there are fonts actually managed by fontconfig
    // also don't search directories that fontconfig already did
    if( m_bFontconfigSuccess )
        m_bFontconfigSuccess = (countFontconfigFonts( visited_dirs ) > 0);

    // don't search through many directories fontconfig already told us about
    if( ! m_bFontconfigSuccess )
        ImplGetSVData()->mpDefInst->FillFontPathList( m_aFontDirectories );

    // fill XLFD aliases from fonts.alias files
    initFontsAlias();

    // search for font files in each path
    std::list< OString >::iterator dir_it;
    for( dir_it = m_aFontDirectories.begin(); dir_it != m_aFontDirectories.end(); ++dir_it )
    {
        OString aPath( *dir_it );
        // see if we were here already
        if( visited_dirs.find( aPath ) != visited_dirs.end() )
            continue;
        visited_dirs[ aPath ] = 1;

        // there may be ":unscaled" directories (see XFree86)
        // it should be safe to ignore them since they should not
        // contain any of our recognizeable fonts

        // ask the font cache whether it handles this directory
        std::list< PrintFont* > aCacheFonts;
        if( m_pFontCache->listDirectory( aPath, aCacheFonts ) )
        {
#if OSL_DEBUG_LEVEL > 1
            fprintf( stderr, "adding cache directory: %s\n", aPath.getStr() );
#endif
            for( ::std::list< PrintFont* >::iterator it = aCacheFonts.begin(); it != aCacheFonts.end(); ++it )
            {
                fontID aFont = m_nNextFontID++;
                m_aFonts[ aFont ] = *it;
                if( (*it)->m_eType == fonttype::Type1 )
                    m_aFontFileToFontID[ static_cast<Type1FontFile*>(*it)->m_aFontFile ].insert( aFont );
                else if( (*it)->m_eType == fonttype::TrueType )
                    m_aFontFileToFontID[ static_cast<TrueTypeFontFile*>(*it)->m_aFontFile ].insert( aFont );
                else if( (*it)->m_eType == fonttype::Builtin )
                    m_aFontFileToFontID[ static_cast<BuiltinFont*>(*it)->m_aMetricFile ].insert( aFont );
#if OSL_DEBUG_LEVEL > 1
                if( (*it)->m_eType == fonttype::Builtin )
                    nBuiltinFonts++;
                nCached++;
#if OSL_DEBUG_LEVEL > 2
                fprintf( stderr, "adding cached font %d: \"%s\" from %s\n", aFont,
                         OUStringToOString( getFontFamily( aFont ), RTL_TEXTENCODING_MS_1252 ).getStr(),
                         getFontFileSysPath( aFont ).getStr() );
#endif
#endif
            }
            if( ! m_pFontCache->scanAdditionalFiles( aPath ) )
                continue;
        }

        DIR* pDIR = opendir( aPath.getStr() );
        struct dirent* pEntry = (struct dirent*)aDirEntBuffer;
        if( pDIR )
        {
            // read fonts.dir if possible
            ::boost::unordered_map< OString, ::std::list<OString>, OStringHash > aFontsDir;
            int nDirID = getDirectoryAtom( aPath, true );
            // #i38367# no fonts.dir in our own directories anymore
            std::list< int >::const_iterator priv_dir;
            for( priv_dir = m_aPrivateFontDirectories.begin();
                 priv_dir != m_aPrivateFontDirectories.end() && *priv_dir != nDirID;
                 ++priv_dir )
                 ;
                 
            if( priv_dir == m_aPrivateFontDirectories.end() )
            {
                ByteString aGccDummy( aPath );
                String aFontsDirPath( aGccDummy, aEncoding );
                aFontsDirPath.AppendAscii( "/fonts.dir" );
                SvFileStream aStream( aFontsDirPath, STREAM_READ );
                if( aStream.IsOpen() )
                {
                    ByteString aLine;
                    while( ! aStream.IsEof() )
                    {
                        aStream.ReadLine( aLine );
                        ByteString aFileName( GetCommandLineToken( 0, aLine ) );
                        ByteString aXLFD( aLine.Copy( aFileName.Len() ) );
                        if( aFileName.Len() && aXLFD.Len() )
                            aFontsDir[ aFileName ].push_back(aXLFD);
                    }
                }
            }
        
            int nDirFonts = 0;
            while( ! readdir_r( pDIR, (struct dirent*)aDirEntBuffer, &pEntry ) && pEntry )
            {
                OString aFileName( pEntry->d_name );
                // ignore .afm files here
                if( aFileName.getLength() > 3 &&
                    aFileName.lastIndexOf( ".afm" ) == aFileName.getLength()-4 )
                    continue;

                struct stat aStat;
                ByteString aFilePath( aPath );
                aFilePath.Append( '/' );
                aFilePath.Append( ByteString( aFileName ) );
                if( ! stat( aFilePath.GetBuffer(), &aStat )     &&
                    S_ISREG( aStat.st_mode ) )
                {
                    if( findFontFileID( nDirID, aFileName ) == 0 )
                    {
                        ::std::list<OString> aXLFDs;
                        ::boost::unordered_map< OString, ::std::list<OString>, OStringHash >::const_iterator it =
                              aFontsDir.find( aFileName );
                        if( it != aFontsDir.end() )
                            aXLFDs = (*it).second;

                        // fill in font attributes from XLFD rather
                        // than reading every file
                        ::std::list< PrintFont* > aNewFonts;
                        if( analyzeFontFile( nDirID, aFileName, aXLFDs, aNewFonts ) )
                        {
                            for( ::std::list< PrintFont* >::iterator font_it = aNewFonts.begin(); font_it != aNewFonts.end(); ++font_it )
                            {
                                fontID aFont = m_nNextFontID++;
                                m_aFonts[ aFont ] = *font_it;
                                m_aFontFileToFontID[ aFileName ].insert( aFont );
                                m_pFontCache->updateFontCacheEntry( *font_it, false );
                                nDirFonts++;
#if OSL_DEBUG_LEVEL > 2
                                fprintf( stderr, "adding font %d: \"%s\" from %s\n", aFont,
                                         OUStringToOString( getFontFamily( aFont ), RTL_TEXTENCODING_MS_1252 ).getStr(),
                                         getFontFileSysPath( aFont ).getStr() );
#endif
                            }
                        }
                    }
                }
            }
            closedir( pDIR );
            m_pFontCache->updateDirTimestamp( nDirID );
            if( ! nDirFonts )
                m_pFontCache->markEmptyDir( nDirID );
        }
    }

#if OSL_DEBUG_LEVEL > 1
    aStep1 = times( &tms );
#endif

    // part two - look for metrics for builtin printer fonts
    std::list< OUString > aMetricDirs;
    psp::getPrinterPathList( aMetricDirs, PRINTER_METRICDIR );

    std::list< OString > aEmptyFontsDir;
    for( std::list< OUString >::const_iterator met_dir_it = aMetricDirs.begin(); met_dir_it != aMetricDirs.end(); ++met_dir_it )
    {
        OString aDir = OUStringToOString( *met_dir_it, aEncoding );

        // ask the font cache whether it handles this directory
        std::list< PrintFont* > aCacheFonts;
        
        if( m_pFontCache->listDirectory( aDir, aCacheFonts ) )
        {
#if OSL_DEBUG_LEVEL > 1
            fprintf( stderr, "adding cache directory: %s\n", aDir.getStr() );
#endif
            for( ::std::list< PrintFont* >::iterator it = aCacheFonts.begin(); it != aCacheFonts.end(); ++it )
            {
                fontID aFont = m_nNextFontID++;
                m_aFonts[ aFont ] = *it;
                if( (*it)->m_eType == fonttype::Type1 )
                    m_aFontFileToFontID[ static_cast<Type1FontFile*>(*it)->m_aFontFile ].insert( aFont );
                else if( (*it)->m_eType == fonttype::TrueType )
                    m_aFontFileToFontID[ static_cast<TrueTypeFontFile*>(*it)->m_aFontFile ].insert( aFont );
                else if( (*it)->m_eType == fonttype::Builtin )
                    m_aFontFileToFontID[ static_cast<BuiltinFont*>(*it)->m_aMetricFile ].insert( aFont );
#if OSL_DEBUG_LEVEL > 1
                if( (*it)->m_eType == fonttype::Builtin )
                    nBuiltinFonts++;
                nCached++;
#if OSL_DEBUG_LEVEL > 2
                fprintf( stderr, "adding cached font %d: \"%s\" from %s\n", aFont,
                         OUStringToOString( getFontFamily( aFont ), RTL_TEXTENCODING_MS_1252 ).getStr(),
                         getFontFileSysPath( aFont ).getStr() );
#endif
#endif
            }
            continue;
        }

        DIR* pDIR = opendir( aDir.getStr() );
        if( pDIR )
        {
            struct dirent* pDirEntry = (struct dirent*)aDirEntBuffer;
            int nDirID = getDirectoryAtom( aDir, true );
            int nDirFonts = 0;

            while( ! readdir_r( pDIR, (struct dirent*)aDirEntBuffer, &pDirEntry ) && pDirEntry )
            {
                ByteString aFile( aDir );
                aFile += '/';
                aFile += pDirEntry->d_name;
                struct stat aStat;
                if( ! stat( aFile.GetBuffer(), &aStat )
                    && S_ISREG( aStat.st_mode )
                    )
                {
                    OString aFileName( pDirEntry->d_name, strlen( pDirEntry->d_name ) );
                    OString aExt( aFileName.copy( aFileName.lastIndexOf( '.' )+1 ) );
                    if( aExt.equalsIgnoreAsciiCase( "afm" ) )
                    {
                        ::std::list< PrintFont* > aNewFonts;

                        analyzeFontFile( nDirID, aFileName, aEmptyFontsDir, aNewFonts );
                        for( ::std::list< PrintFont* >::iterator it = aNewFonts.begin(); it != aNewFonts.end(); ++it )
                        {
                            if( findFontBuiltinID( (*it)->m_nPSName ) == 0 )
                            {
                                m_aFontFileToFontID[ aFileName ].insert( m_nNextFontID );
                                m_aFonts[ m_nNextFontID++ ] = *it;
                                m_pFontCache->updateFontCacheEntry( *it, false );
#if OSL_DEBUG_LEVEL > 2
                                nBuiltinFonts++;
#endif
                            }
                            else
                                delete *it;
                        }
                    }
                }
            }
            closedir( pDIR );
            if( ! nDirFonts )
                m_pFontCache->markEmptyDir( nDirID );
        }
    }

#if OSL_DEBUG_LEVEL > 1
    aStep2 = times( &tms );
#endif

    // part three - fill in family styles
    ::boost::unordered_map< fontID, PrintFont* >::iterator font_it;
    for (font_it = m_aFonts.begin(); font_it != m_aFonts.end(); ++font_it)
    {
        ::boost::unordered_map< int, family::type >::const_iterator it =
              m_aFamilyTypes.find( font_it->second->m_nFamilyName );
        if (it != m_aFamilyTypes.end())
            continue;
        const ::rtl::OUString& rFamily =
            m_pAtoms->getString( ATOM_FAMILYNAME, font_it->second->m_nFamilyName);
        family::type eType = matchFamilyName( rFamily );
        m_aFamilyTypes[ font_it->second->m_nFamilyName ] = eType;
    }

#if OSL_DEBUG_LEVEL > 1
    aStep3 = times( &tms );
    fprintf( stderr, "PrintFontManager::initialize: collected %" SAL_PRI_SIZET "u fonts (%d builtin, %d cached)\n", m_aFonts.size(), nBuiltinFonts, nCached );
    double fTick = (double)sysconf( _SC_CLK_TCK );
    fprintf( stderr, "Step 1 took %lf seconds\n", (double)(aStep1 - aStart)/fTick );
    fprintf( stderr, "Step 2 took %lf seconds\n", (double)(aStep2 - aStep1)/fTick );
    fprintf( stderr, "Step 3 took %lf seconds\n", (double)(aStep3 - aStep2)/fTick );
#endif

    m_pFontCache->flush();
    
    #ifdef CALLGRIND_COMPILE
    CALLGRIND_DUMP_STATS();
    CALLGRIND_TOGGLE_COLLECT();
    #endif
}

// -------------------------------------------------------------------------
inline bool
equalPitch (psp::pitch::type from, psp::pitch::type to)
{
    return from == to;
}

inline bool
equalWeight (psp::weight::type from, psp::weight::type to)
{
    return from > to ? (from - to) <= 3 : (to - from) <= 3;
}

inline bool
equalItalic (psp::italic::type from, psp::italic::type to)
{
    if ( (from == psp::italic::Italic) || (from == psp::italic::Oblique) )
        return (to == psp::italic::Italic) || (to == psp::italic::Oblique);
    return to == from;
}
inline bool
equalEncoding (rtl_TextEncoding from, rtl_TextEncoding to)
{
    if ((from == RTL_TEXTENCODING_ISO_8859_1) || (from == RTL_TEXTENCODING_MS_1252))
        return (to == RTL_TEXTENCODING_ISO_8859_1) || (to == RTL_TEXTENCODING_MS_1252);
    return from == to;
}

namespace {
    struct BuiltinFontIdentifier
    {
        OUString            aFamily;
        italic::type        eItalic;
        weight::type        eWeight;
        pitch::type         ePitch;
        rtl_TextEncoding    aEncoding;
        
        BuiltinFontIdentifier( const OUString& rFam,
                               italic::type eIt,
                               weight::type eWg,
                               pitch::type ePt,
                               rtl_TextEncoding enc ) :
            aFamily( rFam ),
            eItalic( eIt ),
            eWeight( eWg ),
            ePitch( ePt ),
            aEncoding( enc )
        {}
        
        bool operator==( const BuiltinFontIdentifier& rRight ) const
        {
            return equalItalic( eItalic, rRight.eItalic ) &&
                   equalWeight( eWeight, rRight.eWeight ) &&
                   equalPitch( ePitch, rRight.ePitch ) &&
                   equalEncoding( aEncoding, rRight.aEncoding ) &&
                   aFamily.equalsIgnoreAsciiCase( rRight.aFamily );
        }
    };
    
    struct BuiltinFontIdentifierHash
    {
        size_t operator()( const BuiltinFontIdentifier& rFont ) const
        {
            return rFont.aFamily.hashCode() ^ rFont.eItalic ^ rFont.eWeight ^ rFont.ePitch ^ rFont.aEncoding;
        }
    };
}

void PrintFontManager::getFontList( ::std::list< fontID >& rFontIDs, const PPDParser* pParser, bool bUseOverrideMetrics )
{
    rFontIDs.clear();
    boost::unordered_map< fontID, PrintFont* >::const_iterator it;
    
    /*
    * Note: there are two easy steps making this faster:
    * first: insert the printer builtins first, then the not builtins,
    * if they do not match.
    * drawback: this would change the sequence of fonts; this could have
    * subtle, unknown consequences in vcl font matching
    * second: instead of comparing attributes to see whether a softfont
    * is duplicate to a builtin one could simply compare the PSName (which is
    * supposed to be unique), which at this point is just an int.
    * drawback: this could change which fonts are listed; especially TrueType
    * fonts often have a rather dubious PSName, so this could change the
    * font list not so subtle.
    * Until getFontList for a printer becomes a performance issue (which is
    * currently not the case), best stay with the current algorithm.
    */
    
    // fill sets of printer supported fonts
    if( pParser )
    {
        std::set<int> aBuiltinPSNames;
        boost::unordered_set< BuiltinFontIdentifier,
                       BuiltinFontIdentifierHash
                       > aBuiltinFonts;

        std::map<int, fontID > aOverridePSNames;
        if( bUseOverrideMetrics )
        {
            readOverrideMetrics();
            for( std::vector<fontID>::const_iterator over = m_aOverrideFonts.begin();
                 over != m_aOverrideFonts.end(); ++over )
            {
                boost::unordered_map<fontID,PrintFont*>::const_iterator font_it = m_aFonts.find( *over );
                DBG_ASSERT( font_it != m_aFonts.end(), "override to nonexistant font" );
                if( font_it != m_aFonts.end() )
                    aOverridePSNames[ font_it->second->m_nPSName ] = *over;
            }
        }
        
        int nFonts = pParser->getFonts();
        for( int i = 0; i < nFonts; i++ )
            aBuiltinPSNames.insert( m_pAtoms->getAtom( ATOM_PSNAME, pParser->getFont( i ) ) );
        for( it = m_aFonts.begin(); it != m_aFonts.end(); ++it )
        {
            PrintFont* pFont = it->second;
            if( it->second->m_eType == fonttype::Builtin &&
                aBuiltinPSNames.find( pFont->m_nPSName ) != aBuiltinPSNames.end() )
            {
                bool bInsert = true;
                if( bUseOverrideMetrics )
                {
                    // in override case only use the override fonts, not their counterparts
                    std::map<int,fontID>::const_iterator over = aOverridePSNames.find( pFont->m_nPSName );
                    if( over != aOverridePSNames.end() && over->second != it->first )
                        bInsert = false;
                }
                else
                {
                    // do not insert override fonts in non override case
                    if( std::find( m_aOverrideFonts.begin(), m_aOverrideFonts.end(), it->first ) != m_aOverrideFonts.end() )
                        bInsert = false;
                }
                if( bInsert )
                {
                    aBuiltinFonts.insert( BuiltinFontIdentifier(
                        m_pAtoms->getString( ATOM_FAMILYNAME, pFont->m_nFamilyName ),
                        pFont->m_eItalic,
                        pFont->m_eWeight,
                        pFont->m_ePitch,
                        pFont->m_aEncoding
                        ) );
                }
            }
        }
        for( it = m_aFonts.begin(); it != m_aFonts.end(); ++it )
        {
            PrintFont* pFont = it->second;
            if( it->second->m_eType == fonttype::Builtin )
            {
                if( aBuiltinPSNames.find( pFont->m_nPSName ) != aBuiltinPSNames.end() )
                {
                    bool bInsert = true;
                    if( bUseOverrideMetrics )
                    {
                        // in override case only use the override fonts, not their counterparts
                        std::map<int,fontID>::const_iterator over = aOverridePSNames.find( pFont->m_nPSName );
                        if( over != aOverridePSNames.end() && over->second != it->first )
                            bInsert = false;
                    }
                    else
                    {
                        // do not insert override fonts in non override case
                        if( std::find( m_aOverrideFonts.begin(), m_aOverrideFonts.end(), it->first ) != m_aOverrideFonts.end() )
                            bInsert = false;
                    }
                    if( bInsert )
                        rFontIDs.push_back( it->first );
                }
            }
            else if( aBuiltinFonts.find( BuiltinFontIdentifier(
                m_pAtoms->getString( ATOM_FAMILYNAME, pFont->m_nFamilyName ),
                pFont->m_eItalic,
                pFont->m_eWeight,
                pFont->m_ePitch,
                pFont->m_aEncoding
                ) ) == aBuiltinFonts.end() )
            {
                rFontIDs.push_back( it->first );
            }
        }
    }
    else // no specific printer
    {
        for( it = m_aFonts.begin(); it != m_aFonts.end(); ++it )
            rFontIDs.push_back( it->first );
    }
}

// -------------------------------------------------------------------------

void PrintFontManager::fillPrintFontInfo( PrintFont* pFont, FastPrintFontInfo& rInfo ) const
{
    ::boost::unordered_map< int, family::type >::const_iterator style_it =
          m_aFamilyTypes.find( pFont->m_nFamilyName );
    rInfo.m_eType           = pFont->m_eType;
    rInfo.m_aFamilyName     = m_pAtoms->getString( ATOM_FAMILYNAME, pFont->m_nFamilyName );
    rInfo.m_aStyleName      = pFont->m_aStyleName;
    rInfo.m_eFamilyStyle    = style_it != m_aFamilyTypes.end() ? style_it->second : family::Unknown;
    rInfo.m_eItalic         = pFont->m_eItalic;
    rInfo.m_eWidth          = pFont->m_eWidth;
    rInfo.m_eWeight         = pFont->m_eWeight;
    rInfo.m_ePitch          = pFont->m_ePitch;
    rInfo.m_aEncoding       = pFont->m_aEncoding;

    rInfo.m_bEmbeddable  = (pFont->m_eType == fonttype::Type1);
    rInfo.m_bSubsettable = (pFont->m_eType == fonttype::TrueType); // TODO: rename to SfntType

    rInfo.m_aAliases.clear();
    for( ::std::list< int >::iterator it = pFont->m_aAliases.begin(); it != pFont->m_aAliases.end(); ++it )
        rInfo.m_aAliases.push_back( m_pAtoms->getString( ATOM_FAMILYNAME, *it ) );
}

// -------------------------------------------------------------------------

void PrintFontManager::fillPrintFontInfo( PrintFont* pFont, PrintFontInfo& rInfo ) const
{
    if( ( pFont->m_nAscend == 0 && pFont->m_nDescend == 0 ) ||
        ! pFont->m_pMetrics || pFont->m_pMetrics->isEmpty()
        )
    {
        // might be a truetype font not analyzed or type1 without metrics read
        if( pFont->m_eType == fonttype::Type1 )
            pFont->readAfmMetrics( getAfmFile( pFont ), m_pAtoms, false, false );
        else if( pFont->m_eType == fonttype::TrueType )
            analyzeTrueTypeFile( pFont );
    }

    fillPrintFontInfo( pFont, static_cast< FastPrintFontInfo& >( rInfo ) );

    rInfo.m_nAscend         = pFont->m_nAscend;
    rInfo.m_nDescend        = pFont->m_nDescend;
    rInfo.m_nLeading        = pFont->m_nLeading;
    rInfo.m_nWidth          = pFont->m_aGlobalMetricX.width < pFont->m_aGlobalMetricY.width ? pFont->m_aGlobalMetricY.width : pFont->m_aGlobalMetricX.width;
}

// -------------------------------------------------------------------------

void PrintFontManager::getFontListWithInfo( ::std::list< PrintFontInfo >& rFonts, const PPDParser* pParser, bool bUseOverrideMetrics )
{
    rFonts.clear();
    ::std::list< fontID > aFontList;
    getFontList( aFontList, pParser, bUseOverrideMetrics );

    ::std::list< fontID >::iterator it;
    for( it = aFontList.begin(); it != aFontList.end(); ++it )
    {
        PrintFontInfo aInfo;
        aInfo.m_nID = *it;
        fillPrintFontInfo( getFont( *it ), aInfo );
        rFonts.push_back( aInfo );
    }
}

// -------------------------------------------------------------------------

void PrintFontManager::getFontListWithFastInfo( ::std::list< FastPrintFontInfo >& rFonts, const PPDParser* pParser, bool bUseOverrideMetrics )
{
    rFonts.clear();
    ::std::list< fontID > aFontList;
    getFontList( aFontList, pParser, bUseOverrideMetrics );

    ::std::list< fontID >::iterator it;
    for( it = aFontList.begin(); it != aFontList.end(); ++it )
    {
        FastPrintFontInfo aInfo;
        aInfo.m_nID = *it;
        fillPrintFontInfo( getFont( *it ), aInfo );
        rFonts.push_back( aInfo );
    }
}

// -------------------------------------------------------------------------

bool PrintFontManager::getFontInfo( fontID nFontID, PrintFontInfo& rInfo ) const
{
    PrintFont* pFont = getFont( nFontID );
    if( pFont )
    {
        rInfo.m_nID = nFontID;
        fillPrintFontInfo( pFont, rInfo );
    }
    return pFont ? true : false;
}

// -------------------------------------------------------------------------

bool PrintFontManager::getFontFastInfo( fontID nFontID, FastPrintFontInfo& rInfo ) const
{
    PrintFont* pFont = getFont( nFontID );
    if( pFont )
    {
        rInfo.m_nID = nFontID;
        fillPrintFontInfo( pFont, rInfo );
    }
    return pFont ? true : false;
}

// -------------------------------------------------------------------------

bool PrintFontManager::getFontBoundingBox( fontID nFontID, int& xMin, int& yMin, int& xMax, int& yMax )
{
    bool bSuccess = false;
    PrintFont* pFont = getFont( nFontID );
    if( pFont )
    {
        if( pFont->m_nXMin == 0 && pFont->m_nYMin == 0 && pFont->m_nXMax == 0 && pFont->m_nYMax == 0 )
        {
            // might be a truetype font not analyzed or type1 without metrics read
            if( pFont->m_eType == fonttype::Type1 || pFont->m_eType == fonttype::Builtin )
                pFont->readAfmMetrics( getAfmFile( pFont ), m_pAtoms, false, true );
            else if( pFont->m_eType == fonttype::TrueType )
                analyzeTrueTypeFile( pFont );
        }
        bSuccess = true;
        xMin = pFont->m_nXMin;
        yMin = pFont->m_nYMin;
        xMax = pFont->m_nXMax;
        yMax = pFont->m_nYMax;
    }
    return bSuccess;
}

// -------------------------------------------------------------------------

int PrintFontManager::getFontFaceNumber( fontID nFontID ) const
{
    int nRet = -1;
    PrintFont* pFont = getFont( nFontID );
    if( pFont && pFont->m_eType == fonttype::TrueType )
        nRet = static_cast< TrueTypeFontFile* >(pFont)->m_nCollectionEntry;
    return nRet;
}

// -------------------------------------------------------------------------


family::type PrintFontManager::matchFamilyName( const ::rtl::OUString& rFamily ) const
{
    typedef struct {
        const char*  mpName;
        sal_uInt16   mnLength;
        family::type meType;
    } family_t;

#define InitializeClass( p, a ) p, sizeof(p) - 1, a
    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 ) }
    };

    rtl::OString aFamily = rtl::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::Unknown;
}

// -------------------------------------------------------------------------

family::type PrintFontManager::getFontFamilyType( fontID nFontID ) const
{
    PrintFont* pFont = getFont( nFontID );
    if( !pFont )
        return family::Unknown;

    ::boost::unordered_map< int, family::type >::const_iterator it =
          m_aFamilyTypes.find( pFont->m_nFamilyName );
    return (it != m_aFamilyTypes.end()) ? it->second : family::Unknown;
}


// -------------------------------------------------------------------------

const ::rtl::OUString& PrintFontManager::getFontFamily( fontID nFontID ) const
{
    PrintFont* pFont = getFont( nFontID );
    return m_pAtoms->getString( ATOM_FAMILYNAME, pFont ? pFont->m_nFamilyName : INVALID_ATOM );
}

// -------------------------------------------------------------------------

OString PrintFontManager::getAfmFile( PrintFont* pFont ) const
{
    OString aMetricPath;
    if( pFont )
    {
        switch( pFont->m_eType )
        {
            case fonttype::Type1:
            {
                Type1FontFile* pPSFont = static_cast< Type1FontFile* >(pFont);
                aMetricPath = getDirectory( pPSFont->m_nDirectory );
                aMetricPath += "/";
                aMetricPath += pPSFont->m_aMetricFile;
            }
            break;
            case fonttype::Builtin:
            {
                BuiltinFont* pBuiltinFont = static_cast< BuiltinFont* >(pFont);
                aMetricPath = getDirectory( pBuiltinFont->m_nDirectory );
                aMetricPath += "/";
                aMetricPath += pBuiltinFont->m_aMetricFile;
            }
            break;
            default: break;
        }
    }
    return aMetricPath;
}

// -------------------------------------------------------------------------

OString PrintFontManager::getFontFile( PrintFont* pFont ) const
{
    OString aPath;

    if( pFont && pFont->m_eType == fonttype::Type1 )
    {
        Type1FontFile* pPSFont = static_cast< Type1FontFile* >(pFont);
        ::boost::unordered_map< int, OString >::const_iterator it = m_aAtomToDir.find( pPSFont->m_nDirectory );
        aPath = it->second;
        aPath += "/";
        aPath += pPSFont->m_aFontFile;
    }
    else if( pFont && pFont->m_eType == fonttype::TrueType )
    {
        TrueTypeFontFile* pTTFont = static_cast< TrueTypeFontFile* >(pFont);
        ::boost::unordered_map< int, OString >::const_iterator it = m_aAtomToDir.find( pTTFont->m_nDirectory );
        aPath = it->second;
        aPath += "/";
        aPath += pTTFont->m_aFontFile;
    }
    return aPath;
}

// -------------------------------------------------------------------------

const ::rtl::OUString& PrintFontManager::getPSName( fontID nFontID ) const
{
    PrintFont* pFont = getFont( nFontID );
    if( pFont && pFont->m_nPSName == 0 )
    {
        if( pFont->m_eType == fonttype::TrueType )
            analyzeTrueTypeFile( pFont );
    }

    return m_pAtoms->getString( ATOM_PSNAME, pFont ? pFont->m_nPSName : INVALID_ATOM );
}

// -------------------------------------------------------------------------

const CharacterMetric& PrintFontManager::getGlobalFontMetric( fontID nFontID, bool bHorizontal ) const
{
    static CharacterMetric aMetric;
    PrintFont* pFont = getFont( nFontID );
    return pFont ? ( bHorizontal ? pFont->m_aGlobalMetricX : pFont->m_aGlobalMetricY ) : aMetric;
}

// -------------------------------------------------------------------------

int PrintFontManager::getFontAscend( fontID nFontID ) const
{
    PrintFont* pFont = getFont( nFontID );
    if( pFont->m_nAscend == 0 && pFont->m_nDescend == 0 )
    {
        // might be a truetype font not yet analyzed
        if( pFont->m_eType == fonttype::TrueType )
            analyzeTrueTypeFile( pFont );
        else if( pFont->m_eType == fonttype::Type1 || pFont->m_eType == fonttype::Builtin )
            pFont->readAfmMetrics( getAfmFile( pFont ), m_pAtoms, false, true );
    }
    return pFont->m_nAscend;
}

// -------------------------------------------------------------------------

int PrintFontManager::getFontDescend( fontID nFontID ) const
{
    PrintFont* pFont = getFont( nFontID );
    if( pFont->m_nAscend == 0 && pFont->m_nDescend == 0 )
    {
        // might be a truetype font not yet analyzed
        if( pFont->m_eType == fonttype::TrueType )
            analyzeTrueTypeFile( pFont );
        else if( pFont->m_eType == fonttype::Type1 || pFont->m_eType == fonttype::Builtin )
            pFont->readAfmMetrics( getAfmFile( pFont ), m_pAtoms, false, true );
    }
    return pFont->m_nDescend;
}

// -------------------------------------------------------------------------

int PrintFontManager::getFontLeading( fontID nFontID ) const
{
    PrintFont* pFont = getFont( nFontID );
    if( pFont->m_nAscend == 0 && pFont->m_nDescend == 0 )
    {
        // might be a truetype font not yet analyzed
        if( pFont->m_eType == fonttype::TrueType )
            analyzeTrueTypeFile( pFont );
    }
    return pFont->m_nLeading;
}

// -------------------------------------------------------------------------

bool PrintFontManager::hasVerticalSubstitutions( fontID nFontID ) const
{
    PrintFont* pFont = getFont( nFontID );
    if( pFont->m_nAscend == 0 && pFont->m_nDescend == 0 )
    {
        // might be a truetype font not yet analyzed
        if( pFont->m_eType == fonttype::TrueType )
            analyzeTrueTypeFile( pFont );
    }
    return pFont->m_bHaveVerticalSubstitutedGlyphs;
}

// -------------------------------------------------------------------------

void PrintFontManager::hasVerticalSubstitutions( fontID nFontID,
    const sal_Unicode* pCharacters, int nCharacters, bool* pHasSubst ) const
{
    PrintFont* pFont = getFont( nFontID );
    if( pFont->m_nAscend == 0 && pFont->m_nDescend == 0 )
    {
        // might be a truetype font not yet analyzed
        if( pFont->m_eType == fonttype::TrueType )
            analyzeTrueTypeFile( pFont );
    }

    if( ! pFont->m_bHaveVerticalSubstitutedGlyphs )
        memset( pHasSubst, 0, sizeof(bool)*nCharacters );
    else
    {
        for( int i = 0; i < nCharacters; i++ )
        {
            sal_Unicode code = pCharacters[i];
            if( ! pFont->m_pMetrics ||
                ! ( pFont->m_pMetrics->m_aPages[ code >> 11 ] & ( 1 << ( ( code >> 8 ) & 7 ) ) ) )
                pFont->queryMetricPage( code >> 8, m_pAtoms );
            ::boost::unordered_map< sal_Unicode, bool >::const_iterator it = pFont->m_pMetrics->m_bVerticalSubstitutions.find( code );
            pHasSubst[i] = it != pFont->m_pMetrics->m_bVerticalSubstitutions.end();
        }
    }
}

// -------------------------------------------------------------------------

OUString PrintFontManager::getFontXLFD( fontID nFontID ) const
{
    PrintFont* pFont = getFont( nFontID );
    OUString aRet;
    if( pFont )
    {
        ByteString aXLFD( getXLFD( pFont ) );
        rtl_TextEncoding aEncoding = aXLFD.GetToken( 6, '-' ).Search( "utf8" ) != STRING_NOTFOUND ? RTL_TEXTENCODING_UTF8 : RTL_TEXTENCODING_ISO_8859_1;
        aRet = OStringToOUString( aXLFD, aEncoding );
    }
    return aRet;
}

// -------------------------------------------------------------------------

const ::std::list< KernPair >& PrintFontManager::getKernPairs( fontID nFontID, bool bVertical ) const
{
    static ::std::list< KernPair > aEmpty;

    PrintFont* pFont = getFont( nFontID );
    if( ! pFont )
        return aEmpty;

    if( ! pFont->m_pMetrics || ! pFont->m_pMetrics->m_bKernPairsQueried )
        pFont->queryMetricPage( 0, m_pAtoms );
    if( ! pFont->m_pMetrics || ! pFont->m_pMetrics->m_bKernPairsQueried )
        return aEmpty;
    return bVertical ? pFont->m_pMetrics->m_aYKernPairs : pFont->m_pMetrics->m_aXKernPairs;
}

// -------------------------------------------------------------------------

bool PrintFontManager::isFontDownloadingAllowed( fontID nFont ) const
{
    static const char* pEnable = getenv( "PSPRINT_ENABLE_TTF_COPYRIGHTAWARENESS" );
    bool bRet = true;

    if( pEnable && *pEnable )
    {
        PrintFont* pFont = getFont( nFont );
        if( pFont && pFont->m_eType == fonttype::TrueType )
        {
            TrueTypeFontFile* pTTFontFile = static_cast<TrueTypeFontFile*>(pFont);
            if( pTTFontFile->m_nTypeFlags & TYPEFLAG_INVALID )
            {
                TrueTypeFont* pTTFont = NULL;
                ByteString aFile = getFontFile( pFont );
                if( OpenTTFontFile( aFile.GetBuffer(), pTTFontFile->m_nCollectionEntry < 0 ? 0 : pTTFontFile->m_nCollectionEntry, &pTTFont ) == SF_OK )
                {
                    // get type flags
                    TTGlobalFontInfo aInfo;
                    GetTTGlobalFontInfo( pTTFont, & aInfo );
                    pTTFontFile->m_nTypeFlags = (unsigned int)aInfo.typeFlags;
                    CloseTTFont( pTTFont );
                }
            }

            unsigned int nCopyrightFlags = pTTFontFile->m_nTypeFlags & TYPEFLAG_COPYRIGHT_MASK;

            // font embedding is allowed if either
            //   no restriction at all (bit 1 clear)
            //   printing allowed (bit 1 set, bit 2 set )
            bRet = ! ( nCopyrightFlags & 0x02 ) || ( nCopyrightFlags & 0x04 );
        }
    }
    return bRet;
}

// -------------------------------------------------------------------------

bool PrintFontManager::getMetrics( fontID nFontID, const sal_Unicode* pString, int nLen, CharacterMetric* pArray, bool bVertical ) const
{
    PrintFont* pFont = getFont( nFontID );
    if( ! pFont )
        return false;

    if( ( pFont->m_nAscend == 0 && pFont->m_nDescend == 0 )
        || ! pFont->m_pMetrics || pFont->m_pMetrics->isEmpty()
        )
    {
        // might be a font not yet analyzed
        if( pFont->m_eType == fonttype::Type1 || pFont->m_eType == fonttype::Builtin )
            pFont->readAfmMetrics( getAfmFile( pFont ), m_pAtoms, false, false );
        else if( pFont->m_eType == fonttype::TrueType )
            analyzeTrueTypeFile( pFont );
    }

    for( int i = 0; i < nLen; i++ )
    {
        if( ! pFont->m_pMetrics ||
            ! ( pFont->m_pMetrics->m_aPages[ pString[i] >> 11 ] & ( 1 << ( ( pString[i] >> 8 ) & 7 ) ) ) )
            pFont->queryMetricPage( pString[i] >> 8, m_pAtoms );
        pArray[i].width = pArray[i].height = -1;
        if( pFont->m_pMetrics )
        {
            int effectiveCode = pString[i];
            effectiveCode |= bVertical ? 1 << 16 : 0;
            ::boost::unordered_map< int, CharacterMetric >::const_iterator it =
                  pFont->m_pMetrics->m_aMetrics.find( effectiveCode );
        // if no vertical metrics are available assume rotated horizontal metrics
        if( bVertical && (it == pFont->m_pMetrics->m_aMetrics.end()) )
                  it = pFont->m_pMetrics->m_aMetrics.find( pString[i] );
        // the character metrics are in it->second
            if( it != pFont->m_pMetrics->m_aMetrics.end() )
                pArray[ i ] = it->second;
        }
    }

    return true;
}

// -------------------------------------------------------------------------

bool PrintFontManager::getMetrics( fontID nFontID, sal_Unicode minCharacter, sal_Unicode maxCharacter, CharacterMetric* pArray, bool bVertical ) const
{
    PrintFont* pFont = getFont( nFontID );
    if( ! pFont )
        return false;

    if( ( pFont->m_nAscend == 0 && pFont->m_nDescend == 0 )
        || ! pFont->m_pMetrics || pFont->m_pMetrics->isEmpty()
        )
    {
        // might be a font not yet analyzed
        if( pFont->m_eType == fonttype::Type1 || pFont->m_eType == fonttype::Builtin )
            pFont->readAfmMetrics( getAfmFile( pFont ), m_pAtoms, false, false );
        else if( pFont->m_eType == fonttype::TrueType )
            analyzeTrueTypeFile( pFont );
    }

    sal_Unicode code = minCharacter;
    do
    {
        if( ! pFont->m_pMetrics ||
            ! ( pFont->m_pMetrics->m_aPages[ code >> 11 ] & ( 1 << ( ( code >> 8 ) & 7 ) ) ) )
            pFont->queryMetricPage( code >> 8, m_pAtoms );
        pArray[ code - minCharacter ].width     = -1;
        pArray[ code - minCharacter ].height    = -1;
        if( pFont->m_pMetrics )
        {
            int effectiveCode = code;
            effectiveCode |= bVertical ? 1 << 16 : 0;
            ::boost::unordered_map< int, CharacterMetric >::const_iterator it =
                  pFont->m_pMetrics->m_aMetrics.find( effectiveCode );
            // if no vertical metrics are available assume rotated horizontal metrics
            if( bVertical && (it == pFont->m_pMetrics->m_aMetrics.end()) )
                it = pFont->m_pMetrics->m_aMetrics.find( code );
            // the character metrics are in it->second
            if( it != pFont->m_pMetrics->m_aMetrics.end() )
                pArray[ code - minCharacter ] = it->second;
        }
    } while( code++ != maxCharacter );
    
    return true;
}

// -------------------------------------------------------------------------

static bool createWriteablePath( const ByteString& rPath )
{
    bool bSuccess = false;

    if( access( rPath.GetBuffer(), W_OK ) )
    {
        int nPos = rPath.SearchBackward( '/' );
        if( nPos != STRING_NOTFOUND )
            while( nPos > 0 && rPath.GetChar( nPos ) == '/' )
                nPos--;

        if( nPos != STRING_NOTFOUND && nPos != 0 && createWriteablePath( rPath.Copy( 0, nPos+1 ) ) )
        {
            bSuccess = mkdir( rPath.GetBuffer(), 0777 ) ? false : true;
        }
    }
    else
        bSuccess = true;

    return bSuccess;
}


// -------------------------------------------------------------------------

int PrintFontManager::importFonts( const ::std::list< OString >& rFiles, bool bLinkOnly, ImportFontCallback* pCallback )
{
    int nSuccess = 0;

    // find a directory with write access
    rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
    bool bCanWrite = false;
    int nDirID = 0;
    INetURLObject aDir;
    for( ::std::list< int >::const_iterator dir_it = m_aPrivateFontDirectories.begin();
         ! bCanWrite && dir_it != m_aPrivateFontDirectories.end(); ++dir_it )
    {
        // check if we can create files in that directory
        ByteString aDirPath = getDirectory( *dir_it );
        if( createWriteablePath( aDirPath ) )
        {
            aDir = INetURLObject( OStringToOUString( aDirPath, aEncoding ), INET_PROT_FILE, INetURLObject::ENCODE_ALL );
            nDirID = *dir_it;
            bCanWrite = true;
        }
    }
    if( bCanWrite )
    {
        for( ::std::list< OString >::const_iterator font_it = rFiles.begin();
             font_it != rFiles.end(); ++font_it )
        {
            INetURLObject aFrom( OStringToOUString( *font_it, aEncoding ), INET_PROT_FILE, INetURLObject::ENCODE_ALL );
            INetURLObject aTo( aDir );
            aTo.Append( aFrom.GetName() );

            if( pCallback )
                pCallback->progress( aTo.PathToFileName() );

            if( pCallback && pCallback->isCanceled() )
                break;

            if( ! access( ByteString( String(aTo.PathToFileName()), aEncoding ).GetBuffer(), F_OK ) )
            {
                if( ! ( pCallback ? pCallback->queryOverwriteFile( aTo.PathToFileName() ) : false ) )
                    continue;
            }
            // look for afm if necessary
            OUString aAfmCopied;
            FileBase::RC nError;
            if( aFrom.getExtension().equalsIgnoreAsciiCaseAscii( "pfa" ) ||
                aFrom.getExtension().equalsIgnoreAsciiCaseAscii( "pfb" ) )
            {
                INetURLObject aFromAfm( aFrom );
                aFromAfm.setExtension( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "afm" ) ) );
                if( access( ByteString( String(aFromAfm.PathToFileName()), aEncoding ).GetBuffer(), F_OK ) )
                {
                    aFromAfm.setExtension( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "AFM" ) ) );
                    if( access( ByteString( String(aFromAfm.PathToFileName()), aEncoding ).GetBuffer(), F_OK ) )
                    {
                        aFromAfm.removeSegment();
                        aFromAfm.Append( String( RTL_CONSTASCII_USTRINGPARAM( "afm" ) ) );
                        aFromAfm.Append( aTo.GetName() );
                        aFromAfm.setExtension( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "afm" ) ) );
                        if( access( ByteString( String(aFromAfm.PathToFileName()), aEncoding ).GetBuffer(), F_OK ) )
                        {
                            aFromAfm.setExtension( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "AFM" ) ) );
                            if( access( ByteString( String(aFromAfm.PathToFileName()), aEncoding ).GetBuffer(), F_OK ) )
                            {
                                // give up
                                if( pCallback )
                                    pCallback->importFontFailed( aTo.PathToFileName(), ImportFontCallback::NoAfmMetric );
                                continue;
                            }
                        }
                    }
                }
                INetURLObject aToAfm( aTo );
                aToAfm.setExtension( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "afm" ) ) );
                OUString aFromPath, aToPath;
                if( bLinkOnly )
                {
                    ByteString aLinkFromPath( String(aFromAfm.PathToFileName()), 
                        aEncoding );
                    ByteString aLinkToPath( String(aToAfm.PathToFileName()), 
                        aEncoding );
                    nError = (FileBase::RC)symlink( aLinkFromPath.GetBuffer(), aLinkToPath.GetBuffer() );
                }
                else
                    nError = File::copy( aFromAfm.GetMainURL(INetURLObject::DECODE_TO_IURI), aToAfm.GetMainURL(INetURLObject::DECODE_TO_IURI) );
                if( nError )
                {
                    if( pCallback )
                        pCallback->importFontFailed( aTo.PathToFileName(), ImportFontCallback::AfmCopyFailed );
                    continue;
                }
                aAfmCopied = aToPath;
            }
            if( bLinkOnly )
            {
                ByteString aFromPath( String(aFrom.PathToFileName()), 
                    aEncoding );
                ByteString aToPath( String(aTo.PathToFileName()), aEncoding );
                nError = (FileBase::RC)symlink( aFromPath.GetBuffer(), 
                    aToPath.GetBuffer() );
            }
            else
                nError = File::copy( aFrom.GetMainURL(INetURLObject::DECODE_TO_IURI), aTo.GetMainURL(INetURLObject::DECODE_TO_IURI) );
            // copy font file
            if( nError )
            {
                if( aAfmCopied.getLength() )
                    File::remove( aAfmCopied );
                if( pCallback )
                    pCallback->importFontFailed( aTo.PathToFileName(), ImportFontCallback::FontCopyFailed );
                continue;
            }

            ::std::list< PrintFont* > aNewFonts;
            ::std::list< PrintFont* >::iterator it;
            if( analyzeFontFile( nDirID, OUStringToOString( aTo.GetName(), aEncoding ), ::std::list<OString>(), aNewFonts ) )
            {
                // remove all fonts for the same file
                // discarding their font ids
                ::boost::unordered_map< fontID, PrintFont* >::iterator current, next;
                current = m_aFonts.begin();
                OString aFileName( OUStringToOString( aTo.GetName(), aEncoding ) );
                while( current != m_aFonts.end() )
                {
                    bool bRemove = false;
                    switch( current->second->m_eType )
                    {
                        case fonttype::Type1:
                            if( static_cast<Type1FontFile*>(current->second)->m_aFontFile == aFileName )
                                bRemove = true;
                            break;
                        case fonttype::TrueType:
                            if( static_cast<TrueTypeFontFile*>(current->second)->m_aFontFile == aFileName )
                                bRemove = true;
                            break;
                        default: break;
                    }
                    if( bRemove )
                    {
                        next = current;
                        ++next;
                        m_aFontFileToFontID[ aFileName ].erase( current->first );
                        delete current->second;
                        m_aFonts.erase( current );
                        current = next;
                    }
                    else
                        ++current;
                }

                DBG_ASSERT( !findFontFileID( nDirID, aFileName ), "not all fonts removed for file" );

                nSuccess++;
                for( it = aNewFonts.begin(); it != aNewFonts.end(); ++it )
                {
                    m_aFontFileToFontID[ aFileName ].insert( m_nNextFontID );
                    m_aFonts[ m_nNextFontID++ ] = *it;
                    m_pFontCache->updateFontCacheEntry( *it, false );
                }
            }
        }

        m_pFontCache->updateDirTimestamp( nDirID );
        m_pFontCache->flush();
    }
    else if( pCallback )
        pCallback->importFontsFailed( ImportFontCallback::NoWritableDirectory );

    return nSuccess;
}

// -------------------------------------------------------------------------

bool PrintFontManager::checkImportPossible() const
{
    bool bSuccess = false;

    // find a directory with write access
    ByteString aDir;
    for( std::list< int >::const_iterator dir_it = m_aPrivateFontDirectories.begin();
         dir_it != m_aPrivateFontDirectories.end(); ++dir_it )
    {
        aDir = getDirectory( *dir_it );
        if( createWriteablePath( aDir ) )
        {
            bSuccess = true;
            break;
        }
    }

#if OSL_DEBUG_LEVEL > 1
    if( bSuccess )
        fprintf( stderr, "found writable %s\n", aDir.GetBuffer() );
#endif

    return bSuccess;
}

// -------------------------------------------------------------------------

bool PrintFontManager::checkChangeFontPropertiesPossible( fontID /*nFontID*/ ) const
{
    // since font properties are changed in the font cache file only nowadays
    // they can always be changed
    return true;
}

// -------------------------------------------------------------------------

bool PrintFontManager::changeFontProperties( fontID nFontID, const ::rtl::OUString& rXLFD )
{
    ByteString aXLFD( OUStringToOString( rXLFD, RTL_TEXTENCODING_UTF8 ) );
    ByteString aAddStyle = aXLFD.GetToken( '-', 6 );
    if( aAddStyle.Search( "utf8" ) == STRING_NOTFOUND )
    {
        aAddStyle.Append( aAddStyle.Len() ? ";utf8" : "utf8" );
        aXLFD.SetToken( 6, ';', aAddStyle );
    }
    PrintFont* pFont = getFont( nFontID );
    std::list< OString > aDummyList;
    aDummyList.push_back( aXLFD );
    getFontAttributesFromXLFD( pFont, aDummyList );
    pFont->m_bUserOverride = true;
    m_pFontCache->updateFontCacheEntry( pFont, true );
    
    return true;
}

// -------------------------------------------------------------------------

bool PrintFontManager::
getImportableFontProperties(
                            const OString& rFile,
                            ::std::list< FastPrintFontInfo >& rFontProps
                            )
{
    rFontProps.clear();
    int nIndex = rFile.lastIndexOf( '/' );
    OString aDir, aFile( rFile.copy( nIndex+1 ) );
    if( nIndex != -1 )
        aDir = rFile.copy( 0, nIndex );
    int nDirID = getDirectoryAtom( aDir, true );
    ::std::list< PrintFont* > aFonts;
    bool bRet = analyzeFontFile( nDirID, aFile, ::std::list<OString>(), aFonts );
    while( aFonts.begin() != aFonts.end() )
    {
        PrintFont* pFont = aFonts.front();
        aFonts.pop_front();
        FastPrintFontInfo aInfo;
        fillPrintFontInfo( pFont, aInfo );
        rFontProps.push_back( aInfo );
        delete pFont;
    }
    return bRet;
}

// -------------------------------------------------------------------------

bool PrintFontManager::getFileDuplicates( fontID nFont, ::std::list< fontID >& rFonts ) const
{
    bool bRet = false;

    rFonts.clear();

    PrintFont* pSearchFont = getFont( nFont );
    if( ! pSearchFont ||
        pSearchFont->m_eType != fonttype::TrueType ||
        static_cast<TrueTypeFontFile*>(pSearchFont)->m_nCollectionEntry == -1
        )
        return false;

    OString aFile( getFontFileSysPath( nFont ) );
    if( ! aFile.getLength() )
        return false;

    for( ::boost::unordered_map< fontID, PrintFont* >::const_iterator it = m_aFonts.begin(); it != m_aFonts.end(); ++it )
    {
        if( nFont != it->first )
        {
            OString aCompFile( getFontFile( it->second ) );
            if( aCompFile == aFile )
            {
                rFonts.push_back( it->first );
                bRet = true;
            }
        }
    }
    return bRet;
}

// -------------------------------------------------------------------------

bool PrintFontManager::removeFonts( const ::std::list< fontID >& rFonts )
{
    bool bRet = true;
    ::std::list< fontID > aDuplicates;
    for( ::std::list< fontID >::const_iterator it = rFonts.begin(); it != rFonts.end(); ++it )
    {
        ::boost::unordered_map< fontID, PrintFont* >::const_iterator haveFont = m_aFonts.find( *it );
        if( haveFont == m_aFonts.end() )
            continue;

        PrintFont* pFont = haveFont->second;
        bool bRemoveDuplicates = getFileDuplicates( *it, aDuplicates );
        ByteString aFile( getFontFile( pFont ) );
        if( aFile.Len() )
        {
#if OSL_DEBUG_LEVEL > 1
            fprintf( stderr, "try unlink( \"%s\" ) ... ", aFile.GetBuffer() );
#endif
            if( unlink( aFile.GetBuffer() ) )
            {
                bRet = false;
#if OSL_DEBUG_LEVEL > 1
                fprintf( stderr, "failed\n" );
#endif
                continue;
            }
#if OSL_DEBUG_LEVEL > 1
            fprintf( stderr, "succeeded\n" );
#endif
            OString aAfm( getAfmFile( pFont ) );
            if( aAfm.getLength() )
            {
#if OSL_DEBUG_LEVEL > 1
                fprintf( stderr, "unlink( \"%s\" )\n", aAfm.getStr() );
#endif
                unlink( aAfm.getStr() );
            }
            m_aFonts.erase( *it );
            delete pFont;
            if( bRemoveDuplicates )
            {
                for( ::std::list< fontID >::iterator dup = aDuplicates.begin(); dup != aDuplicates.end(); ++dup )
                {
                    m_aFontFileToFontID[ aFile ].erase( *dup );
                    PrintFont* pDup = m_aFonts[ *dup ];
                    m_aFonts.erase( *dup );
                    delete pDup;
                }
            }
        }
    }
    return bRet;
}

// -------------------------------------------------------------------------

bool PrintFontManager::isPrivateFontFile( fontID nFont ) const
{
    bool bRet = false;
    int nDirID = -1;
    PrintFont* pFont = getFont( nFont );
    if( pFont )
    {
        switch( pFont->m_eType )
        {
            case fonttype::Type1: nDirID = static_cast< Type1FontFile* >(pFont)->m_nDirectory;break;
            case fonttype::TrueType: nDirID = static_cast< TrueTypeFontFile* >(pFont)->m_nDirectory;break;
            default: break;
        }
    }
    if( nDirID != -1 )
    {
        for( ::std::list< int >::const_iterator it = m_aPrivateFontDirectories.begin(); it != m_aPrivateFontDirectories.end(); ++it )
        {
            if( nDirID == *it )
            {
                bRet = true;
                break;
            }
        }
    }
    return bRet;
}

// -------------------------------------------------------------------------

bool PrintFontManager::getAlternativeFamilyNames( fontID nFont, ::std::list< OUString >& rNames ) const
{
    rNames.clear();

    PrintFont* pFont = getFont( nFont );
    if( pFont && pFont->m_eType == fonttype::TrueType )
    {
        TrueTypeFontFile* pTTFontFile = static_cast< TrueTypeFontFile* >(pFont);
        ByteString aFile( getFontFile( pFont ) );
        TrueTypeFont* pTTFont;
        if( OpenTTFontFile( aFile.GetBuffer(), pTTFontFile->m_nCollectionEntry < 0 ? 0 : pTTFontFile->m_nCollectionEntry, &pTTFont ) == SF_OK )
        {
            NameRecord* pNameRecords = NULL;
            int nNameRecords = GetTTNameRecords( pTTFont, &pNameRecords );
            for( int i = 0; i < nNameRecords; i++ )
            {
                if( pNameRecords[i].nameID != 1 ) // family name
                    continue;

                OUString aFamily( convertTrueTypeName( pNameRecords+i ) );
                if( aFamily.getLength()
                    &&
                    m_pAtoms->getAtom( ATOM_FAMILYNAME, aFamily, sal_True ) != pFont->m_nFamilyName
                    )
                {
                    rNames.push_back( aFamily );
                }
            }

            if( nNameRecords )
                DisposeNameRecords( pNameRecords, nNameRecords );
            CloseTTFont( pTTFont );
        }
    }
    return rNames.begin() != rNames.end();
}

// -------------------------------------------------------------------------

// TODO: move most of this stuff into the central font-subsetting code
bool PrintFontManager::createFontSubset(
                                        FontSubsetInfo& rInfo,
                                        fontID nFont,
                                        const OUString& rOutFile,
                                        sal_Int32* pGlyphIDs,
                                        sal_uInt8* pNewEncoding,
                                        sal_Int32* pWidths,
                                        int nGlyphs,
                                        bool bVertical
                                        )
{
    PrintFont* pFont = getFont( nFont );
    if( !pFont )
        return false;

    switch( pFont->m_eType )
    {
        case psp::fonttype::TrueType: rInfo.m_nFontType = FontSubsetInfo::SFNT_TTF; break;
        case psp::fonttype::Type1: rInfo.m_nFontType = FontSubsetInfo::ANY_TYPE1; break;
        default:
            return false;
    }
    // TODO: remove when Type1 subsetting gets implemented
    if( pFont->m_eType != fonttype::TrueType )
        return false;

    // reshuffle array of requested glyphs to make sure glyph0==notdef
    sal_uInt8  pEnc[256];
    sal_uInt16 pGID[256];
    sal_uInt8  pOldIndex[256];
    memset( pEnc, 0, sizeof( pEnc ) );
    memset( pGID, 0, sizeof( pGID ) );
    memset( pOldIndex, 0, sizeof( pOldIndex ) );
    if( nGlyphs > 256 )
        return false;
    int nChar = 1;
    for( int i = 0; i < nGlyphs; i++ )
    {
        if( pNewEncoding[i] == 0 )
        {
            pOldIndex[ 0 ] = i;
        }
        else
        {
            DBG_ASSERT( !(pGlyphIDs[i] & 0x007f0000), "overlong glyph id" );
            DBG_ASSERT( (int)pNewEncoding[i] < nGlyphs, "encoding wrong" );
            DBG_ASSERT( pEnc[pNewEncoding[i]] == 0 && pGID[pNewEncoding[i]] == 0, "duplicate encoded glyph" );
            pEnc[ pNewEncoding[i] ] = pNewEncoding[i];
            pGID[ pNewEncoding[i] ] = (sal_uInt16)pGlyphIDs[ i ];
            pOldIndex[ pNewEncoding[i] ] = i;
            nChar++;
        }
    }
    nGlyphs = nChar; // either input value or increased by one

    // prepare system name for read access for subset source file
    // TODO: since this file is usually already mmapped there is no need to open it again
    const ByteString aFromFile = getFontFile( pFont );

    TrueTypeFont* pTTFont = NULL; // TODO: rename to SfntFont
    TrueTypeFontFile* pTTFontFile = static_cast< TrueTypeFontFile* >(pFont);
    if( OpenTTFontFile( aFromFile.GetBuffer(), pTTFontFile->m_nCollectionEntry < 0 ? 0 : pTTFontFile->m_nCollectionEntry, &pTTFont ) != SF_OK )
        return false;

    // prepare system name for write access for subset file target
    OUString aSysPath;
    if( osl_File_E_None != osl_getSystemPathFromFileURL( rOutFile.pData, &aSysPath.pData ) )
        return false;
    const rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
    const ByteString aToFile( OUStringToOString( aSysPath, aEncoding ) );

    // do CFF subsetting if possible
    int nCffLength = 0;
    const sal_uInt8* pCffBytes = NULL;
    if( GetSfntTable( pTTFont, O_CFF, &pCffBytes, &nCffLength ) )
    {
        rInfo.LoadFont( FontSubsetInfo::CFF_FONT, pCffBytes, nCffLength );
#if 1 // TODO: remove 16bit->long conversion when related methods handle non-16bit glyphids
        long aRequestedGlyphs[256];
        for( int i = 0; i < nGlyphs; ++i )
            aRequestedGlyphs[i] = pGID[i];
#endif
        // create subset file at requested path
        FILE* pOutFile = fopen( aToFile.GetBuffer(), "wb" );
        // create font subset
        const char* pGlyphSetName = NULL; // TODO: better name?
        const bool bOK = rInfo.CreateFontSubset(
            FontSubsetInfo::TYPE1_PFB,
            pOutFile, pGlyphSetName,
            aRequestedGlyphs, pEnc, nGlyphs, pWidths );
        fclose( pOutFile );
        // cleanup before early return
        CloseTTFont( pTTFont );
        return bOK;
    }

    // do TTF->Type42 or Type3 subsetting
    // fill in font info
    psp::PrintFontInfo aFontInfo;
    if( ! getFontInfo( nFont, aFontInfo ) )
        return false;

    rInfo.m_nAscent     = aFontInfo.m_nAscend;
    rInfo.m_nDescent    = aFontInfo.m_nDescend;
    rInfo.m_aPSName     = getPSName( nFont );

    int xMin, yMin, xMax, yMax;
    getFontBoundingBox( nFont, xMin, yMin, xMax, yMax );
    rInfo.m_aFontBBox	= Rectangle( Point( xMin, yMin ), Size( xMax-xMin, yMax-yMin ) );
    rInfo.m_nCapHeight	= yMax; // Well ...

    // fill in glyph advance widths
    TTSimpleGlyphMetrics* pMetrics = GetTTSimpleGlyphMetrics( pTTFont,
                                                              pGID,
                                                              nGlyphs,
                                                              bVertical ? 1 : 0 );
    if( pMetrics )
    {
        for( int i = 0; i < nGlyphs; i++ )
            pWidths[pOldIndex[i]] = pMetrics[i].adv;
        free( pMetrics );
    }
    else
    {
        CloseTTFont( pTTFont );
        return false;
    }

    bool bSuccess = ( SF_OK == CreateTTFromTTGlyphs( pTTFont,
                                                     aToFile.GetBuffer(),
                                                     pGID,
                                                     pEnc,
                                                     nGlyphs,
                                                     0,
                                                     NULL,
                                                     0 ) );
    CloseTTFont( pTTFont );

    return bSuccess;
}

void PrintFontManager::getGlyphWidths( fontID nFont,
                                       bool bVertical,
                                       std::vector< sal_Int32 >& rWidths,
                                       std::map< sal_Unicode, sal_uInt32 >& rUnicodeEnc )
{
    PrintFont* pFont = getFont( nFont );
    if( !pFont ||
        (pFont->m_eType != fonttype::TrueType && pFont->m_eType != fonttype::Type1) )
        return;
    if( pFont->m_eType == fonttype::TrueType )
    {
        TrueTypeFont* pTTFont = NULL;
        TrueTypeFontFile* pTTFontFile = static_cast< TrueTypeFontFile* >(pFont);
        ByteString aFromFile = getFontFile( pFont );
        if( OpenTTFontFile( aFromFile.GetBuffer(), pTTFontFile->m_nCollectionEntry < 0 ? 0 : pTTFontFile->m_nCollectionEntry, &pTTFont ) != SF_OK )
            return;
        int nGlyphs = GetTTGlyphCount( pTTFont );
        if( nGlyphs > 0 )
        {
            rWidths.resize(nGlyphs);
            std::vector<sal_uInt16> aGlyphIds(nGlyphs);
            for( int i = 0; i < nGlyphs; i++ )
                aGlyphIds[i] = sal_uInt16(i);
            TTSimpleGlyphMetrics* pMetrics = GetTTSimpleGlyphMetrics( pTTFont,
                                                                      &aGlyphIds[0],
                                                                      nGlyphs,
                                                                      bVertical ? 1 : 0 );
            if( pMetrics )
            {
                for( int i = 0; i< nGlyphs; i++ )
                    rWidths[i] = pMetrics[i].adv;
                free( pMetrics );
                rUnicodeEnc.clear();
            }

            // fill the unicode map
            // TODO: isn't this map already available elsewhere in the fontmanager?
            const sal_uInt8* pCmapData = NULL;
            int nCmapSize = 0;
            if( GetSfntTable( pTTFont, O_cmap, &pCmapData, &nCmapSize ) )
            {
                CmapResult aCmapResult;
                if( ParseCMAP( pCmapData, nCmapSize, aCmapResult ) )
                {
                    const ImplFontCharMap aCharMap( aCmapResult );
                    for( sal_uInt32 cOld = 0;;)
                    {
                        // get next unicode covered by font
                        const sal_uInt32 c = aCharMap.GetNextChar( cOld );
                        if( c == cOld )
                            break;
                        cOld = c;
#if 1 // TODO: remove when sal_Unicode covers all of unicode
                        if( c > (sal_Unicode)~0 )
                            break;
#endif
                        // get the matching glyph index
                        const sal_uInt32 nGlyphId = aCharMap.GetGlyphIndex( c );
                        // update the requested map
                        rUnicodeEnc[ (sal_Unicode)c ] = nGlyphId;
                    }
                }
            }
        }
        CloseTTFont( pTTFont );
    }
    else if( pFont->m_eType == fonttype::Type1 )
    {
        if( ! pFont->m_aEncodingVector.size() )
            pFont->readAfmMetrics( getAfmFile( pFont ), m_pAtoms, true, true );
        if( pFont->m_pMetrics )
        {
            rUnicodeEnc.clear();
            rWidths.clear();
            rWidths.reserve( pFont->m_pMetrics->m_aMetrics.size() );
            for( boost::unordered_map< int, CharacterMetric >::const_iterator it = 
                 pFont->m_pMetrics->m_aMetrics.begin();
                 it != pFont->m_pMetrics->m_aMetrics.end(); ++it )
            {
                if( (it->first & 0x00010000) == 0 || bVertical )
                {
                    rUnicodeEnc[ sal_Unicode(it->first & 0x0000ffff) ] = sal_uInt32(rWidths.size());
                    rWidths.push_back( it->second.width );
                }
            }
        }
    }
}

// -------------------------------------------------------------------------

const std::map< sal_Unicode, sal_Int32 >* PrintFontManager::getEncodingMap( fontID nFont, const std::map< sal_Unicode, rtl::OString >** pNonEncoded ) const
{
    PrintFont* pFont = getFont( nFont );
    if( !pFont ||
        (pFont->m_eType != fonttype::Type1 && pFont->m_eType != fonttype::Builtin)
        )
        return NULL;

    if( ! pFont->m_aEncodingVector.size() )
        pFont->readAfmMetrics( getAfmFile( pFont ), m_pAtoms, true, true );

    if( pNonEncoded )
        *pNonEncoded = pFont->m_aNonEncoded.size() ? &pFont->m_aNonEncoded : NULL;

    return pFont->m_aEncodingVector.size() ? &pFont->m_aEncodingVector : NULL;
}

// -------------------------------------------------------------------------

std::list< OString > PrintFontManager::getAdobeNameFromUnicode( sal_Unicode aChar ) const
{
    std::pair< boost::unordered_multimap< sal_Unicode, rtl::OString >::const_iterator,
        boost::unordered_multimap< sal_Unicode, rtl::OString >::const_iterator > range
        =  m_aUnicodeToAdobename.equal_range( aChar );

    std::list< OString > aRet;
    for( ; range.first != range.second; ++range.first )
        aRet.push_back( range.first->second );

    if( aRet.begin() == aRet.end() && aChar != 0 )
    {
        sal_Char aBuf[8];
        sal_Int32 nChars = snprintf( (char*)aBuf, sizeof( aBuf ), "uni%.4hX", aChar );
        aRet.push_back( OString( aBuf, nChars ) );
    }

    return aRet;
}

// -------------------------------------------------------------------------
std::list< sal_Unicode >  PrintFontManager::getUnicodeFromAdobeName( const rtl::OString& rName ) const
{
    std::pair< boost::unordered_multimap< rtl::OString, sal_Unicode, rtl::OStringHash >::const_iterator,
        boost::unordered_multimap< rtl::OString, sal_Unicode, rtl::OStringHash >::const_iterator > range
        =  m_aAdobenameToUnicode.equal_range( rName );

    std::list< sal_Unicode > aRet;
    for( ; range.first != range.second; ++range.first )
        aRet.push_back( range.first->second );

    if( aRet.begin() == aRet.end() )
    {
        if( rName.getLength() == 7 && rName.indexOf( "uni" ) == 0 )
        {
            sal_Unicode aCode = (sal_Unicode)rName.copy( 3 ).toInt32( 16 );
            aRet.push_back( aCode );
        }
    }

    return aRet;
}

// -------------------------------------------------------------------------
namespace
{
    OUString getString( const Any& rAny )
    {
        OUString aStr;
        rAny >>= aStr;
        return aStr;
    }
    bool getBool( const Any& rAny )
    {
        sal_Bool bBool = sal_False;
        rAny >>= bBool;
        return static_cast<bool>(bBool);
    }
    sal_Int32 getInt( const Any& rAny )
    {
        sal_Int32 n = 0;
        rAny >>= n;
        return n;
    }
}
bool PrintFontManager::readOverrideMetrics()
{
    if( ! m_aOverrideFonts.empty() )
        return false;

    css::uno::Reference< XMultiServiceFactory > xFact( comphelper::getProcessServiceFactory() );
    if( !xFact.is() )
        return false;
    css::uno::Reference< XMaterialHolder > xMat(
                xFact->createInstance( OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.psprint.CompatMetricOverride" ) ) ),
                UNO_QUERY );
    if( !xMat.is() )
        return false;
    
    Any aAny( xMat->getMaterial() );
    Sequence< Any > aOverrideFonts;
    if( ! (aAny >>= aOverrideFonts ) )
        return false;
    sal_Int32 nFonts = aOverrideFonts.getLength();
    for( sal_Int32 i = 0; i < nFonts; i++ )
    {
        Sequence< NamedValue > aMetrics;
        if( ! (aOverrideFonts.getConstArray()[i] >>= aMetrics) )
            continue;
        BuiltinFont* pFont = new BuiltinFont();
        pFont->m_nDirectory = 0;
        pFont->m_bUserOverride = false;
        pFont->m_pMetrics = new PrintFontMetrics;
        memset( pFont->m_pMetrics->m_aPages, 0xff, sizeof( pFont->m_pMetrics->m_aPages ) );
        pFont->m_pMetrics->m_bKernPairsQueried = true;
        sal_Int32 nProps = aMetrics.getLength();
        const NamedValue* pProps = aMetrics.getConstArray();
        for( sal_Int32 n = 0; n < nProps; n++ )
        {
            if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "FamilyName" ) ) )
                pFont->m_nFamilyName = m_pAtoms->getAtom( ATOM_FAMILYNAME,
                                                          getString(pProps[n].Value),
                                                          sal_True );
            else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "PSName" ) ) )
                pFont->m_nPSName = m_pAtoms->getAtom( ATOM_PSNAME,
                                                      getString(pProps[n].Value),
                                                      sal_True );
            else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "StyleName" ) ) )
                pFont->m_aStyleName = getString(pProps[n].Value);
            else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "Italic" ) ) )
                pFont->m_eItalic = static_cast<italic::type>(getInt(pProps[n].Value));
            else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "Width" ) ) )
                pFont->m_eWidth = static_cast<width::type>(getInt(pProps[n].Value));
            else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "Weight" ) ) )
                pFont->m_eWeight = static_cast<weight::type>(getInt(pProps[n].Value));
            else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "Pitch" ) ) )
                pFont->m_ePitch = static_cast<pitch::type>(getInt(pProps[n].Value));
            else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "Encoding" ) ) )
                pFont->m_aEncoding = static_cast<rtl_TextEncoding>(getInt(pProps[n].Value));
            else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "FontEncodingOnly" ) ) )
                pFont->m_bFontEncodingOnly = getBool(pProps[n].Value);
            else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "GlobalMetricXWidth" ) ) )
                pFont->m_aGlobalMetricX.width = getInt(pProps[n].Value);
            else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "GlobalMetricXHeight" ) ) )
                pFont->m_aGlobalMetricX.height = getInt(pProps[n].Value);
            else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "GlobalMetricYWidth" ) ) )
                pFont->m_aGlobalMetricY.width = getInt(pProps[n].Value);
            else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "GlobalMetricYHeight" ) ) )
                pFont->m_aGlobalMetricY.height = getInt(pProps[n].Value);
            else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "Ascend" ) ) )
                pFont->m_nAscend = getInt(pProps[n].Value);
            else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "Descend" ) ) )
                pFont->m_nDescend = getInt(pProps[n].Value);
            else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "Leading" ) ) )
                pFont->m_nLeading = getInt(pProps[n].Value);
            else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "XMin" ) ) )
                pFont->m_nXMin = getInt(pProps[n].Value);
            else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "YMin" ) ) )
                pFont->m_nYMin = getInt(pProps[n].Value);
            else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "XMax" ) ) )
                pFont->m_nXMax = getInt(pProps[n].Value);
            else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "YMax" ) ) )
                pFont->m_nYMax = getInt(pProps[n].Value);
            else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "VerticalSubstitutes" ) ) )
                pFont->m_bHaveVerticalSubstitutedGlyphs = getBool(pProps[n].Value);
            else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "EncodingVector" ) ) )
            {
                Sequence< NamedValue > aEncoding;
                pProps[n].Value >>= aEncoding;
                sal_Int32 nEnc = aEncoding.getLength();
                const NamedValue* pEnc = aEncoding.getConstArray();
                for( sal_Int32 m = 0; m < nEnc; m++ )
                {
                    sal_Unicode cCode = *pEnc[m].Name.getStr();
                    sal_Int32 nGlyph = getInt(pEnc[m].Value);
                    pFont->m_aEncodingVector[ cCode ] = nGlyph;
                }
            }
            else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "NonEncoded" ) ) )
            {
                Sequence< NamedValue > aEncoding;
                pProps[n].Value >>= aEncoding;
                sal_Int32 nEnc = aEncoding.getLength();
                const NamedValue* pEnc = aEncoding.getConstArray();
                for( sal_Int32 m = 0; m < nEnc; m++ )
                {
                    sal_Unicode cCode = *pEnc[m].Name.getStr();
                    OUString aGlyphName( getString(pEnc[m].Value) );
                    pFont->m_aNonEncoded[ cCode ] = OUStringToOString(aGlyphName,RTL_TEXTENCODING_ASCII_US);
                }
            }
            else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "CharacterMetrics" ) ) )
            {
                // fill pFont->m_pMetrics->m_aMetrics
                // expect triples of int: int -> CharacterMetric.{ width, height }
                Sequence< sal_Int32 > aSeq;
                pProps[n].Value >>= aSeq;
                sal_Int32 nInts = aSeq.getLength();
                const sal_Int32* pInts = aSeq.getConstArray();
                for( sal_Int32 m = 0; m < nInts; m+=3 )
                {
                    pFont->m_pMetrics->m_aMetrics[ pInts[m] ].width = static_cast<short int>(pInts[m+1]);
                    pFont->m_pMetrics->m_aMetrics[ pInts[m] ].height = static_cast<short int>(pInts[m+2]);
                }
            }
            else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "XKernPairs" ) ) )
            {
                // fill pFont->m_pMetrics->m_aXKernPairs
                // expection name: <unicode1><unicode2> value: ((height << 16)| width)
                Sequence< NamedValue > aKern;
                pProps[n].Value >>= aKern;
                KernPair aPair;
                const NamedValue* pVals = aKern.getConstArray();
                int nPairs = aKern.getLength();
                for( int m = 0; m < nPairs; m++ )
                {
                    if( pVals[m].Name.getLength() == 2 )
                    {
                        aPair.first = pVals[m].Name.getStr()[0];
                        aPair.second = pVals[m].Name.getStr()[1];
                        sal_Int32 nKern = getInt( pVals[m].Value );
                        aPair.kern_x = static_cast<short int>(nKern & 0xffff);
                        aPair.kern_y = static_cast<short int>((sal_uInt32(nKern) >> 16) & 0xffff);
                        pFont->m_pMetrics->m_aXKernPairs.push_back( aPair );
                    }
                }
            }
        }
        // sanity check
        if( pFont->m_nPSName                        &&
            pFont->m_nFamilyName                    &&
            ! pFont->m_pMetrics->m_aMetrics.empty() )
        {
            m_aOverrideFonts.push_back( m_nNextFontID );
            m_aFonts[ m_nNextFontID++ ] = pFont;            
        }
        else
        {
            DBG_ASSERT( 0, "override font failed" );
            delete pFont;
        }
    }
    
    return true;
}

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