/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



#define INCL_GRE_STRINGS
#define INCL_GPI
#define INCL_DOS

#include <string.h>
//#include <stdlib.h>
//#include <math.h>
#include <svpm.h>

#include <vcl/sysdata.hxx>
#include "tools/svwin.h"

#include "os2/saldata.hxx"
#include "os2/salgdi.h"

#include "vcl/svapp.hxx"
#include "outfont.hxx"
#include "vcl/font.hxx"
#include "fontsubset.hxx"
#include "sallayout.hxx"

#include "rtl/logfile.hxx"
#include "rtl/tencinfo.h"
#include "rtl/textcvt.h"
#include "rtl/bootstrap.hxx"


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

#include "tools/poly.hxx"
#include "tools/debug.hxx"
#include "tools/stream.hxx"

#include "basegfx/polygon/b2dpolygon.hxx"
#include "basegfx/polygon/b2dpolypolygon.hxx"
#include "basegfx/matrix/b2dhommatrix.hxx"

#ifndef __H_FT2LIB
#include <os2/wingdi.h>
#include <ft2lib.h>
#endif

#include "sft.hxx"

#ifdef GCP_KERN_HACK
#include <algorithm>
#endif

using namespace vcl;

// -----------
// - Inlines -
// -----------


inline W32FIXED FixedFromDouble( double d )
{
    const long l = (long) ( d * 65536. );
    return *(W32FIXED*) &l;
}

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

inline int IntTimes256FromFixed(W32FIXED f)
{
    int nFixedTimes256 = (f.value << 8) + ((f.fract+0x80) >> 8);
    return nFixedTimes256;
}

// -----------
// - Defines -
// -----------

// this is a special codepage code, used to identify OS/2 symbol font.
#define SYMBOL_CHARSET					65400

// =======================================================================

UniString ImplSalGetUniString( const sal_Char* pStr, xub_StrLen nLen = STRING_LEN)
{
	return UniString( pStr, nLen, gsl_getSystemTextEncoding(),
					  RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_DEFAULT |
					  RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_DEFAULT |
					  RTL_TEXTTOUNICODE_FLAGS_INVALID_DEFAULT );
}

// =======================================================================

static USHORT ImplSalToCharSet( CharSet eCharSet )
{
	// !!! Fuer DBCS-Systeme muss dieser Code auskommentiert werden und 0
	// !!! zurueckgegeben werden, solange die DBCS-Charsets nicht
	// !!! durchgereicht werden

	switch ( eCharSet )
	{
		case RTL_TEXTENCODING_IBM_437:
			return 437;

		case RTL_TEXTENCODING_IBM_850:
			return 850;

		case RTL_TEXTENCODING_IBM_860:
			return 860;

		case RTL_TEXTENCODING_IBM_861:
			return 861;

		case RTL_TEXTENCODING_IBM_863:
			return 863;

		case RTL_TEXTENCODING_IBM_865:
			return 865;
		case RTL_TEXTENCODING_MS_1252:
			return 1004;
		case RTL_TEXTENCODING_SYMBOL:
			return 65400;
	}

	return 0;
}

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

static CharSet ImplCharSetToSal( USHORT usCodePage )
{
	switch ( usCodePage )
	{
		case 437:
			return RTL_TEXTENCODING_IBM_437;

		case 850:
			return RTL_TEXTENCODING_IBM_850;

		case 860:
			return RTL_TEXTENCODING_IBM_860;

		case 861:
			return RTL_TEXTENCODING_IBM_861;

		case 863:
			return RTL_TEXTENCODING_IBM_863;

		case 865:
			return RTL_TEXTENCODING_IBM_865;
		case 1004:
			return RTL_TEXTENCODING_MS_1252;
		case 65400:
			return RTL_TEXTENCODING_SYMBOL;
	}

	return RTL_TEXTENCODING_DONTKNOW;
}

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

static FontFamily ImplFamilyToSal( PM_BYTE bFamilyType )
{
    switch ( bFamilyType )
    {
        case 4:
            return FAMILY_DECORATIVE;
        case 3:
            return FAMILY_SCRIPT;
    }

    return FAMILY_DONTKNOW;
}

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

static FontWeight ImplWeightToSal( USHORT nWeight )
{
	// Falls sich jemand an die alte Doku gehalten hat
	if ( nWeight > 999 )
		nWeight /= 1000;

	switch ( nWeight )
	{
		case 1:
			return WEIGHT_THIN;

		case 2:
			return WEIGHT_ULTRALIGHT;

		case 3:
			return WEIGHT_LIGHT;

		case 4:
			return WEIGHT_SEMILIGHT;

		case 5:
			return WEIGHT_NORMAL;

		case 6:
			return WEIGHT_SEMIBOLD;

		case 7:
			return WEIGHT_BOLD;

		case 8:
			return WEIGHT_ULTRABOLD;

		case 9:
			return WEIGHT_BLACK;
	}

	return WEIGHT_DONTKNOW;
}

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

static UniString ImpStyleNameToSal( const char* pFamilyName,
								   const char* pFaceName,
								   USHORT nLen )
{
	if ( !nLen )
		nLen = strlen(pFamilyName);

	// strip FamilyName from FaceName
	if ( strncmp( pFamilyName, pFaceName, nLen ) == 0 )
	{
		USHORT nFaceLen = (USHORT)strlen( pFaceName+nLen );
		// Ist Facename laenger, schneiden wir den FamilyName ab
		if ( nFaceLen > 1 )
			return UniString( pFaceName+(nLen+1), gsl_getSystemTextEncoding());
		else
			return UniString();
	}
	else
		return UniString( pFaceName, gsl_getSystemTextEncoding());
}

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

inline FontPitch ImplLogPitchToSal( PM_BYTE fsType )
{
    if ( fsType & FM_TYPE_FIXED )
        return PITCH_FIXED;
    else
        return PITCH_VARIABLE;
}

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

inline PM_BYTE ImplPitchToWin( FontPitch ePitch )
{
    if ( ePitch == PITCH_FIXED )
        return FM_TYPE_FIXED;
    //else if ( ePitch == PITCH_VARIABLE )

	return 0;
}

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

static ImplDevFontAttributes Os2Font2DevFontAttributes( const PFONTMETRICS pFontMetric)
{
    ImplDevFontAttributes aDFA;

    // get font face attributes
    aDFA.meFamily       = ImplFamilyToSal( pFontMetric->panose.bFamilyType);
    aDFA.meWidthType    = WIDTH_DONTKNOW;
    aDFA.meWeight       = ImplWeightToSal( pFontMetric->usWeightClass);
    aDFA.meItalic       = (pFontMetric->fsSelection & FM_SEL_ITALIC) ? ITALIC_NORMAL : ITALIC_NONE;
    aDFA.mePitch        = ImplLogPitchToSal( pFontMetric->fsType );
    aDFA.mbSymbolFlag   = (pFontMetric->usCodePage == SYMBOL_CHARSET);

	// get the font face name
	// the maName field stores the font name without the style, so under OS/2
	// we must use the family name
	aDFA.maName = UniString( pFontMetric->szFamilyname, gsl_getSystemTextEncoding());

	aDFA.maStyleName = ImpStyleNameToSal( pFontMetric->szFamilyname,
 										  pFontMetric->szFacename,
 										  strlen( pFontMetric->szFamilyname) );

    // get device specific font attributes
    aDFA.mbOrientation  = (pFontMetric->fsDefn & FM_DEFN_OUTLINE) != 0;
    aDFA.mbDevice       = (pFontMetric->fsDefn & FM_DEFN_GENERIC) ? FALSE : TRUE;

    aDFA.mbEmbeddable   = false;
    aDFA.mbSubsettable  = false;
    DWORD fontType = Ft2QueryFontType( 0, pFontMetric->szFamilyname);
    if( fontType == FT2_FONTTYPE_TRUETYPE && !aDFA.mbDevice)
		aDFA.mbSubsettable = true;
    // for now we can only embed Type1 fonts
    if( fontType == FT2_FONTTYPE_TYPE1 )
        aDFA.mbEmbeddable = true;

    // heuristics for font quality
    // -   standard-type1 > opentypeTT > truetype > non-standard-type1 > raster
    // -   subsetting > embedding > none
    aDFA.mnQuality = 0;
    if( fontType == FT2_FONTTYPE_TRUETYPE )
        aDFA.mnQuality += 50;
    if( aDFA.mbSubsettable )
        aDFA.mnQuality += 200;
    else if( aDFA.mbEmbeddable )
        aDFA.mnQuality += 100;

    // #i38665# prefer Type1 versions of the standard postscript fonts
    if( aDFA.mbEmbeddable )
    {
        if( aDFA.maName.EqualsAscii( "AvantGarde" )
        ||  aDFA.maName.EqualsAscii( "Bookman" )
        ||  aDFA.maName.EqualsAscii( "Courier" )
        ||  aDFA.maName.EqualsAscii( "Helvetica" )
        ||  aDFA.maName.EqualsAscii( "NewCenturySchlbk" )
        ||  aDFA.maName.EqualsAscii( "Palatino" )
        ||  aDFA.maName.EqualsAscii( "Symbol" )
        ||  aDFA.maName.EqualsAscii( "Times" )
        ||  aDFA.maName.EqualsAscii( "ZapfChancery" )
        ||  aDFA.maName.EqualsAscii( "ZapfDingbats" ) )
            aDFA.mnQuality += 500;
    }    

    // TODO: add alias names

    return aDFA;
}

// =======================================================================

// raw font data with a scoped lifetime
class RawFontData
{
public:
    explicit	RawFontData( HDC, DWORD nTableTag=0 );
    			~RawFontData() { delete[] mpRawBytes; }
    const unsigned char*	get() const { return mpRawBytes; }
    const unsigned char*	steal() { unsigned char* p = mpRawBytes; mpRawBytes = NULL; return p; }
    const int				size() const { return mnByteCount; }

private:
    unsigned char*	mpRawBytes;
    int				mnByteCount;
};

RawFontData::RawFontData( HPS hPS, DWORD nTableTag )
:	mpRawBytes( NULL )
,	mnByteCount( 0 )
{
	// get required size in bytes
    mnByteCount = ::Ft2GetFontData( hPS, nTableTag, 0, NULL, 0 );
    if( mnByteCount == FT2_ERROR )
        return;
    else if( !mnByteCount )
        return;

	// allocate the array
    mpRawBytes = new unsigned char[ mnByteCount ];

	// get raw data in chunks small enough for GetFontData()
	int nRawDataOfs = 0;
	DWORD nMaxChunkSize = 0x100000;
	for(;;)
	{
		// calculate remaining raw data to get
		DWORD nFDGet = mnByteCount - nRawDataOfs;
		if( nFDGet <= 0 )
			break;
		// #i56745# limit GetFontData requests
		if( nFDGet > nMaxChunkSize )
			nFDGet = nMaxChunkSize;
		const DWORD nFDGot = ::Ft2GetFontData( hPS, nTableTag, nRawDataOfs,
			(void*)(mpRawBytes + nRawDataOfs), nFDGet );
		if( !nFDGot )
			break;
		else if( nFDGot != FT2_ERROR )
			nRawDataOfs += nFDGot;
		else
		{
			// was the chunk too big? reduce it
			nMaxChunkSize /= 2;
			if( nMaxChunkSize < 0x10000 )
				break;
		}
	}

	// cleanup if the raw data is incomplete
	if( nRawDataOfs != mnByteCount )
	{
		delete[] mpRawBytes;
		mpRawBytes = NULL;
	}
}

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

// =======================================================================

ImplOs2FontData::ImplOs2FontData( PFONTMETRICS _pFontMetric,
    int nHeight, PM_BYTE nPitchAndFamily )
:	ImplFontData( Os2Font2DevFontAttributes(_pFontMetric), 0 ),
    pFontMetric( _pFontMetric ),
	meOs2CharSet( _pFontMetric->usCodePage),
    mnPitchAndFamily( nPitchAndFamily ),
    mpFontCharSets( NULL ),
    mpUnicodeMap( NULL ),
    mbDisableGlyphApi( false ),
    mbHasKoreanRange( false ),
    mbHasCJKSupport( false ),
    mbAliasSymbolsLow( false ),
    mbAliasSymbolsHigh( false ),
    mnId( 0 )
{
    SetBitmapSize( 0, nHeight );
}

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

ImplOs2FontData::~ImplOs2FontData()
{
    delete[] mpFontCharSets;

    if( mpUnicodeMap )
        mpUnicodeMap->DeReference();
}

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

sal_IntPtr ImplOs2FontData::GetFontId() const
{
    return mnId;
}

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

void ImplOs2FontData::UpdateFromHPS( HPS hPS ) const
{
    // short circuit if already initialized
    if( mpUnicodeMap != NULL )
        return;

    ReadCmapTable( hPS );
    ReadOs2Table( hPS );

    // even if the font works some fonts have problems with the glyph API
    // => the heuristic below tries to figure out which fonts have the problem
	DWORD	fontType = Ft2QueryFontType( 0, pFontMetric->szFacename);
    if( fontType != FT2_FONTTYPE_TRUETYPE 
		&& (pFontMetric->fsDefn & FM_DEFN_GENERIC) == 0)
		mbDisableGlyphApi = true;
}

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

bool ImplOs2FontData::HasGSUBstitutions( HPS hPS ) const
{
    if( !mbGsubRead )
        ReadGsubTable( hPS );
    return !maGsubTable.empty();
}

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

bool ImplOs2FontData::IsGSUBstituted( sal_Ucs cChar ) const
{
    return( maGsubTable.find( cChar ) != maGsubTable.end() );
}

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

const ImplFontCharMap* ImplOs2FontData::GetImplFontCharMap() const
{
    mpUnicodeMap->AddReference();
    return mpUnicodeMap;
}

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

static unsigned GetUInt( const unsigned char* p ) { return((p[0]<<24)+(p[1]<<16)+(p[2]<<8)+p[3]);}
static unsigned GetUShort( const unsigned char* p ){ return((p[0]<<8)+p[1]);}
static signed GetSShort( const unsigned char* p ){ return((short)((p[0]<<8)+p[1]));}
static inline DWORD CalcTag( const char p[4]) { return (p[0]+(p[1]<<8)+(p[2]<<16)+(p[3]<<24)); }

void ImplOs2FontData::ReadOs2Table( HPS hPS ) const
{
    const DWORD Os2Tag = CalcTag( "OS/2" );
    DWORD nLength = Ft2GetFontData( hPS, Os2Tag, 0, NULL, 0 );
    if( (nLength == FT2_ERROR) || !nLength )
        return;
    std::vector<unsigned char> aOS2map( nLength );
    unsigned char* pOS2map = &aOS2map[0];
    DWORD nRC = Ft2GetFontData( hPS, Os2Tag, 0, pOS2map, nLength );
    sal_uInt32 nVersion = GetUShort( pOS2map );
    if ( nVersion >= 0x0001 && nLength >= 58 )
    {
        // We need at least version 0x0001 (TrueType rev 1.66)
        // to have access to the needed struct members.
        sal_uInt32 ulUnicodeRange1 = GetUInt( pOS2map + 42 );
        sal_uInt32 ulUnicodeRange2 = GetUInt( pOS2map + 46 );
#if 0
        sal_uInt32 ulUnicodeRange3 = GetUInt( pOS2map + 50 );
        sal_uInt32 ulUnicodeRange4 = GetUInt( pOS2map + 54 );
#endif

        // Check for CJK capabilities of the current font
        mbHasCJKSupport = (ulUnicodeRange2 & 0x2DF00000);
        mbHasKoreanRange= (ulUnicodeRange1 & 0x10000000)
                        | (ulUnicodeRange2 & 0x01100000);
        mbHasArabicSupport = (ulUnicodeRange1 & 0x00002000);
    }
}


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

void ImplOs2FontData::ReadGsubTable( HPS hPS ) const
{
    mbGsubRead = true;

    // check the existence of a GSUB table
    const DWORD GsubTag = CalcTag( "GSUB" );
    DWORD nRC = Ft2GetFontData( hPS, GsubTag, 0, NULL, 0 );
    if( (nRC == FT2_ERROR) || !nRC )
        return;

    // parse the GSUB table through sft
    // TODO: parse it directly

    // sft needs the full font file data => get it
    const RawFontData aRawFontData( hPS );
    if( !aRawFontData.get() )
    	return;

    // open font file
    sal_uInt32 nFaceNum = 0;
    if( !*aRawFontData.get() )  // TTC candidate
        nFaceNum = ~0U;  // indicate "TTC font extracts only"

    TrueTypeFont* pTTFont = NULL;
    ::OpenTTFontBuffer( (void*)aRawFontData.get(), aRawFontData.size(), nFaceNum, &pTTFont );
    if( !pTTFont )
        return;

    // add vertically substituted characters to list
    static const sal_Unicode aGSUBCandidates[] = {
        0x0020, 0x0080, // ASCII
        0x2000, 0x2600, // misc
        0x3000, 0x3100, // CJK punctutation
        0x3300, 0x3400, // squared words
        0xFF00, 0xFFF0, // halfwidth|fullwidth forms
    0 };

    for( const sal_Unicode* pPair = aGSUBCandidates; *pPair; pPair += 2 )
        for( sal_Unicode cChar = pPair[0]; cChar < pPair[1]; ++cChar )
            if( ::MapChar( pTTFont, cChar, 0 ) != ::MapChar( pTTFont, cChar, 1 ) )
                maGsubTable.insert( cChar ); // insert GSUBbed unicodes

    CloseTTFont( pTTFont );
}

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

void ImplOs2FontData::ReadCmapTable( HPS hPS ) const
{
    if( mpUnicodeMap != NULL )
        return;

    bool bIsSymbolFont = (meOs2CharSet == SYMBOL_CHARSET);
    // get the CMAP table from the font which is selected into the DC
    const DWORD nCmapTag = CalcTag( "cmap" );
    const RawFontData aRawFontData( hPS, nCmapTag );
    // parse the CMAP table if available
    if( aRawFontData.get() ) {
        CmapResult aResult;
        ParseCMAP( aRawFontData.get(), aRawFontData.size(), aResult );
        mbDisableGlyphApi |= aResult.mbRecoded;
        aResult.mbSymbolic = bIsSymbolFont;
        if( aResult.mnRangeCount > 0 )
            mpUnicodeMap = new ImplFontCharMap( aResult );
    }

    if( !mpUnicodeMap )
        mpUnicodeMap = ImplFontCharMap::GetDefaultMap( bIsSymbolFont );
    mpUnicodeMap->AddReference();
}

// =======================================================================

void Os2SalGraphics::SetTextColor( SalColor nSalColor )
{
	CHARBUNDLE cb;

	cb.lColor = MAKE_SALCOLOR( SALCOLOR_RED( nSalColor ),
						  SALCOLOR_GREEN( nSalColor ),
						  SALCOLOR_BLUE( nSalColor ) );

	// set default color attributes
	Ft2SetAttrs( mhPS,
				 PRIM_CHAR,
				 CBB_COLOR,
				 0,
				 &cb );
}

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

USHORT Os2SalGraphics::ImplDoSetFont( ImplFontSelectData* i_pFont, float& o_rFontScale, int nFallbackLevel)
{

#if OSL_DEBUG_LEVEL>10
    debug_printf( "Os2SalGraphics::ImplDoSetFont mbPrinter=%d\n", mbPrinter);
#endif

	ImplOs2FontData* pFontData = (ImplOs2FontData*)i_pFont->mpFontData;
	PFONTMETRICS 	pFontMetric = NULL;
	FATTRS		  	aFAttrs;
	PM_BOOL		  	bOutline = FALSE;
	APIRET			rc;			

	memset( &aFAttrs, 0, sizeof( FATTRS ) );
	aFAttrs.usRecordLength = sizeof( FATTRS );

	aFAttrs.lMaxBaselineExt = i_pFont->mnHeight;
	aFAttrs.lAveCharWidth	= i_pFont->mnWidth;

	// do we have a pointer to the FONTMETRICS of the selected font? -> use it!
	if ( pFontData )
	{
		pFontMetric = pFontData->GetFontMetrics();

		bOutline = (pFontMetric->fsDefn & FM_DEFN_OUTLINE) != 0;

		// use match&registry fields to get correct match
		aFAttrs.lMatch	     	= pFontMetric->lMatch;
		aFAttrs.idRegistry 		= pFontMetric->idRegistry;			
		aFAttrs.usCodePage 		= pFontMetric->usCodePage;

		if ( bOutline )
		{
			aFAttrs.fsFontUse |= FATTR_FONTUSE_OUTLINE;
			if ( i_pFont->mnOrientation )
				aFAttrs.fsFontUse |= FATTR_FONTUSE_TRANSFORMABLE;
		}
		else
		{
			aFAttrs.lMaxBaselineExt = pFontMetric->lMaxBaselineExt;
			aFAttrs.lAveCharWidth	= pFontMetric->lAveCharWidth;
		}

	}
#if OSL_DEBUG_LEVEL>1
	if (pFontMetric->szFacename[0] == 'D') {
		rc = 0; // debugger breakpoint
	}
#endif

	// use family name for outline fonts
    if ( mbPrinter ) {
		// use font face name for printers because otherwise ft2lib will fail 
		// to select the correct font for GPI (ticket#117)
		strncpy( (char*)(aFAttrs.szFacename), pFontMetric->szFacename, sizeof( aFAttrs.szFacename ) );
	} else if ( !pFontMetric) {
		// use OOo name if fontmetrics not available!
		ByteString aName( i_pFont->maName.GetToken( 0 ), gsl_getSystemTextEncoding());
		strncpy( (char*)(aFAttrs.szFacename), aName.GetBuffer(), sizeof( aFAttrs.szFacename ) );
	} else if ( bOutline) {
		// use fontmetric family name for outline fonts
		strncpy( (char*)(aFAttrs.szFacename), pFontMetric->szFamilyname, sizeof( aFAttrs.szFacename ) );
	} else {
		// use real font face name for bitmaps (WarpSans only)
		strncpy( (char*)(aFAttrs.szFacename), pFontMetric->szFacename, sizeof( aFAttrs.szFacename ) );
	}

	if ( i_pFont->meItalic != ITALIC_NONE )
		aFAttrs.fsSelection |= FATTR_SEL_ITALIC;
	if ( i_pFont->meWeight > WEIGHT_MEDIUM )
		aFAttrs.fsSelection |= FATTR_SEL_BOLD;

#if OSL_DEBUG_LEVEL>1
	if (pFontMetric->szFacename[0] == 'A') {
		debug_printf( "Os2SalGraphics::SetFont hps %x lMatch '%d'\n", mhPS, pFontMetric->lMatch);
		debug_printf( "Os2SalGraphics::SetFont hps %x fontmetrics facename '%s'\n", mhPS, pFontMetric->szFacename);
		debug_printf( "Os2SalGraphics::SetFont hps %x fattrs facename '%s'\n", mhPS, aFAttrs.szFacename);
		debug_printf( "Os2SalGraphics::SetFont hps %x fattrs height '%d'\n", mhPS, aFAttrs.lMaxBaselineExt);
		debug_printf( "Os2SalGraphics::SetFont hps %x fattrs width '%d'\n", mhPS, aFAttrs.lAveCharWidth);
	}
#endif

	// set default font
	rc = Ft2SetCharSet( mhPS, LCID_DEFAULT);
	// delete selected font
	rc = Ft2DeleteSetId( mhPS, nFallbackLevel + LCID_BASE);

	// create new logical font
	if ( (rc=Ft2CreateLogFont( mhPS, NULL, nFallbackLevel + LCID_BASE, &aFAttrs)) == GPI_ERROR ) {
#if OSL_DEBUG_LEVEL>1
		ERRORID nLastError = WinGetLastError( GetSalData()->mhAB );
    	debug_printf( "Os2SalGraphics::SetFont hps %x Ft2CreateLogFont failed err %x\n", mhPS, nLastError );
#endif
		return SAL_SETFONT_REMOVEANDMATCHNEW;
	}

#if OSL_DEBUG_LEVEL>1
	// select new font
	rc = Ft2SetCharSet( mhPS, nFallbackLevel + LCID_BASE);

	// query fontmetric of new font
	FONTMETRICS aOS2Metric = {0};
	rc = Ft2QueryFontMetrics( mhPS, sizeof( aOS2Metric ), &aOS2Metric );

	if (pFontMetric->szFacename[0] == 'D') {
		debug_printf( "Os2SalGraphics::SetFont Ft2QueryFontMetrics fattrs facename '%s'\n", aOS2Metric.szFacename);
		debug_printf( "Os2SalGraphics::SetFont Ft2QueryFontMetrics fattrs height '%d'\n", aOS2Metric.lMaxBaselineExt);
		debug_printf( "Os2SalGraphics::SetFont Ft2QueryFontMetrics fattrs width '%d'\n", aOS2Metric.lAveCharWidth);
	}
#endif

	// apply font sizing, rotation
	CHARBUNDLE aBundle;

	ULONG nAttrsDefault = 0;
	ULONG nAttrs = CBB_SET;
	aBundle.usSet = nFallbackLevel + LCID_BASE;

	if ( bOutline )
	{
		nAttrs |= CBB_BOX;
		aBundle.sizfxCell.cy = MAKEFIXED( i_pFont->mnHeight, 0 );

		if ( !i_pFont->mnWidth )
		{
			LONG nXFontRes;
			LONG nYFontRes;
			LONG nHeight;

			// Auf die Aufloesung achten, damit das Ergebnis auch auf
			// Drucken mit 180*360 DPI stimmt. Ausserdem muss gerundet
			// werden, da auf meinem OS2 beispielsweise als
			// Bildschirmaufloesung 3618*3622 PixelPerMeter zurueck-
			// gegeben wird
			GetResolution( nXFontRes, nYFontRes );
			nHeight = i_pFont->mnHeight;
			nHeight *= nXFontRes;
			nHeight += nYFontRes/2;
			nHeight /= nYFontRes;
			aBundle.sizfxCell.cx = MAKEFIXED( nHeight, 0 );
		}
		else
			aBundle.sizfxCell.cx = MAKEFIXED( i_pFont->mnWidth, 0 );
	}

	// set orientation for outlinefonts
	if ( i_pFont->mnOrientation )
	{
		if ( bOutline )
		{
			nAttrs |= CBB_ANGLE;
			double alpha = (double)(i_pFont->mnOrientation);
			alpha *= 0.0017453292;	 // *PI / 1800
			mnOrientationY = (long) (1000.0 * sin( alpha ));
			mnOrientationX = (long) (1000.0 * cos( alpha ));
			aBundle.ptlAngle.x = mnOrientationX;
			aBundle.ptlAngle.y = mnOrientationY;
		}
		else
		{
			mnOrientationX = 1;
			mnOrientationY = 0;
			nAttrs |= CBB_ANGLE;
			aBundle.ptlAngle.x = 1;
			aBundle.ptlAngle.y = 0;
		}
	}
	else
	{
		mnOrientationX = 1;
		mnOrientationY = 0;
		nAttrs |= CBB_ANGLE;
		aBundle.ptlAngle.x = 1;
		aBundle.ptlAngle.y = 0;
	}

	rc = Ft2SetAttrs( mhPS, PRIM_CHAR, nAttrs, nAttrsDefault, &aBundle );

#if OSL_DEBUG_LEVEL>1
	rc = Ft2QueryFontMetrics( mhPS, sizeof( aOS2Metric ), &aOS2Metric );
	if (pFontMetric->szFacename[0] == 'D') {
		debug_printf( "Os2SalGraphics::SetFont Ft2QueryFontMetrics fattrs facename '%s'\n", aOS2Metric.szFacename);
		debug_printf( "Os2SalGraphics::SetFont Ft2QueryFontMetrics fattrs height '%d'\n", aOS2Metric.lMaxBaselineExt);
		debug_printf( "Os2SalGraphics::SetFont Ft2QueryFontMetrics fattrs width '%d'\n", aOS2Metric.lAveCharWidth);
	}
#endif

	return 0;
}


USHORT Os2SalGraphics::SetFont( ImplFontSelectData* pFont, int nFallbackLevel )
{

    // return early if there is no new font
    if( !pFont )
    {
#if 0
        // deselect still active font
        if( mhDefFont )
            Ft2SetCharSet( mhPS, mhDefFont );
        // release no longer referenced font handles
        for( int i = nFallbackLevel; i < MAX_FALLBACK; ++i )
        {
            if( mhFonts[i] )
                Ft2DeleteSetId( mhPS, mhFonts[i] );
            mhFonts[ i ] = 0;
        }
#endif
        mhDefFont = 0;
        return 0;
    }

#if OSL_DEBUG_LEVEL>1
    debug_printf( "Os2SalGraphics::SetFont\n");
#endif

    DBG_ASSERT( pFont->mpFontData, "WinSalGraphics mpFontData==NULL");
    mpOs2FontEntry[ nFallbackLevel ] = reinterpret_cast<ImplOs2FontEntry*>( pFont->mpFontEntry );
    mpOs2FontData[ nFallbackLevel ] = static_cast<const ImplOs2FontData*>( pFont->mpFontData );

	ImplDoSetFont( pFont, mfFontScale, nFallbackLevel);

    if( !mhDefFont )
    {
        // keep default font
        mhDefFont = nFallbackLevel + LCID_BASE;
    }
    else
    {
        // release no longer referenced font handles
        for( int i = nFallbackLevel; i < MAX_FALLBACK; ++i )
        {
            if( mhFonts[i] )
            {
#if 0
                Ft2DeleteSetId( mhPS, mhFonts[i] );
#endif
                mhFonts[i] = 0;
            }
        }
    }

    // store new font in correct layer
    mhFonts[ nFallbackLevel ] = nFallbackLevel + LCID_BASE;

    // now the font is live => update font face
    if( mpOs2FontData[ nFallbackLevel ] )
        mpOs2FontData[ nFallbackLevel ]->UpdateFromHPS( mhPS );

    if( !nFallbackLevel )
    {
        mbFontKernInit = TRUE;
        if ( mpFontKernPairs )
        {
            delete[] mpFontKernPairs;
            mpFontKernPairs = NULL;
        }
        mnFontKernPairCount = 0;
    }

    // some printers have higher internal resolution, so their
    // text output would be different from what we calculated
    // => suggest DrawTextArray to workaround this problem
    if ( mbPrinter )
        return SAL_SETFONT_USEDRAWTEXTARRAY;
    else
        return 0;
}

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

void Os2SalGraphics::GetFontMetric( ImplFontMetricData* pMetric, int nFallbackLevel )
{
	FONTMETRICS aOS2Metric;
	Ft2QueryFontMetrics( mhPS, sizeof( aOS2Metric ), &aOS2Metric );

	// @TODO sync , int nFallbackLevel in 340

#if OSL_DEBUG_LEVEL>1
	debug_printf( "Os2SalGraphics::GetFontMetric hps %x\n", mhPS);
	if (aOS2Metric.szFacename[0] == 'A') {
		debug_printf( "Os2SalGraphics::GetFontMetric hps %x fontmetrics facename '%s'\n", mhPS, aOS2Metric.szFacename);
		debug_printf( "Os2SalGraphics::GetFontMetric hps %x fontmetrics lMatch '%d'\n", mhPS, aOS2Metric.lMatch);
	}
#endif

	pMetric->maName 			= UniString( aOS2Metric.szFamilyname, gsl_getSystemTextEncoding());
	pMetric->maStyleName		= ImpStyleNameToSal( aOS2Metric.szFamilyname,
													 aOS2Metric.szFacename,
													 strlen( aOS2Metric.szFamilyname ) );

    // device independent font attributes
    pMetric->meFamily       = ImplFamilyToSal( aOS2Metric.panose.bFamilyType);
    pMetric->mbSymbolFlag   = (aOS2Metric.usCodePage == SYMBOL_CHARSET);
    pMetric->meWeight       = ImplWeightToSal( aOS2Metric.usWeightClass );
    pMetric->mePitch        = ImplLogPitchToSal( aOS2Metric.fsType );
    pMetric->meItalic       = (aOS2Metric.fsSelection & FM_SEL_ITALIC) ? ITALIC_NORMAL : ITALIC_NONE;
    pMetric->mnSlant        = 0;

    // device dependend font attributes
    pMetric->mbDevice       = (aOS2Metric.fsDefn & FM_DEFN_GENERIC) ? FALSE : TRUE;
    pMetric->mbScalableFont = (aOS2Metric.fsDefn & FM_DEFN_OUTLINE) ? true : false;
    if( pMetric->mbScalableFont )
    {
        // check if there are kern pairs
        // TODO: does this work with GPOS kerning?
        pMetric->mbKernableFont = (aOS2Metric.sKerningPairs > 0);
    }
    else
    {
        // bitmap fonts cannot be rotated directly
        pMetric->mnOrientation  = 0;
        // bitmap fonts have no kerning
        pMetric->mbKernableFont = false;
    }

    // transformation dependend font metrics
	if ( aOS2Metric.fsDefn & FM_DEFN_OUTLINE )
	{
		pMetric->mnWidth	   = aOS2Metric.lEmInc;
	}
	else
	{
		pMetric->mnWidth	   = aOS2Metric.lAveCharWidth;
		pMetric->mnOrientation = 0;
	}
	pMetric->mnIntLeading		= aOS2Metric.lInternalLeading;
	pMetric->mnExtLeading		= aOS2Metric.lExternalLeading;
	pMetric->mnAscent			= aOS2Metric.lMaxAscender;
	pMetric->mnDescent			= aOS2Metric.lMaxDescender;

    // #107888# improved metric compatibility for Asian fonts...
    // TODO: assess workaround below for CWS >= extleading
    // TODO: evaluate use of aWinMetric.sTypo* members for CJK
    if( mpOs2FontData[0] && mpOs2FontData[0]->SupportsCJK() )
    {
        pMetric->mnIntLeading += pMetric->mnExtLeading;

        // #109280# The line height for Asian fonts is too small.
        // Therefore we add half of the external leading to the
        // ascent, the other half is added to the descent.
        const long nHalfTmpExtLeading = pMetric->mnExtLeading / 2;
        const long nOtherHalfTmpExtLeading = pMetric->mnExtLeading - nHalfTmpExtLeading;

        // #110641# external leading for Asian fonts.
        // The factor 0.3 has been confirmed with experiments.
        long nCJKExtLeading = static_cast<long>(0.30 * (pMetric->mnAscent + pMetric->mnDescent));
        nCJKExtLeading -= pMetric->mnExtLeading;
        pMetric->mnExtLeading = (nCJKExtLeading > 0) ? nCJKExtLeading : 0;

        pMetric->mnAscent   += nHalfTmpExtLeading;
        pMetric->mnDescent  += nOtherHalfTmpExtLeading;

        // #109280# HACK korean only: increase descent for wavelines and impr
		// YD win9x only
    }

}

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

ULONG Os2SalGraphics::GetKernPairs( ULONG nPairs, ImplKernPairData* pKernPairs )
{
	DBG_ASSERT( sizeof( KERNINGPAIRS ) == sizeof( ImplKernPairData ),
				"Os2SalGraphics::GetKernPairs(): KERNINGPAIRS != ImplKernPairData" );

    if ( mbFontKernInit )
    {
        if( mpFontKernPairs )
        {
            delete[] mpFontKernPairs;
            mpFontKernPairs = NULL;
        }
        mnFontKernPairCount = 0;

        {
            KERNINGPAIRS* pPairs = NULL;
			FONTMETRICS aOS2Metric;
			Ft2QueryFontMetrics( mhPS, sizeof( aOS2Metric ), &aOS2Metric );
            int nCount = aOS2Metric.sKerningPairs;
            if( nCount )
            {
#ifdef GCP_KERN_HACK
                pPairs = new KERNINGPAIRS[ nCount+1 ];
                mpFontKernPairs = pPairs;
                mnFontKernPairCount = nCount;
				Ft2QueryKerningPairs( mhPS, nCount, (KERNINGPAIRS*)pPairs );
#else // GCP_KERN_HACK
				pPairs = (KERNINGPAIRS*)pKernPairs;
				nCount = (nCount < nPairs) ? nCount : nPairs;
				Ft2QueryKerningPairs( mhPS, nCount, (KERNINGPAIRS*)pPairs );
                return nCount;
#endif // GCP_KERN_HACK
            }
        }

        mbFontKernInit = FALSE;

        std::sort( mpFontKernPairs, mpFontKernPairs + mnFontKernPairCount, ImplCmpKernData );
    }

    if( !pKernPairs )
        return mnFontKernPairCount;
    else if( mpFontKernPairs )
    {
        if ( nPairs < mnFontKernPairCount )
            nPairs = mnFontKernPairCount;
        memcpy( pKernPairs, mpFontKernPairs,
                nPairs*sizeof( ImplKernPairData ) );
        return nPairs;
    }

    return 0;
}


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

static const ImplFontCharMap* pOs2DefaultImplFontCharMap = NULL;
static const sal_uInt32 pOs2DefaultRangeCodes[] = {0x0020,0x00FF};

const ImplFontCharMap* Os2SalGraphics::GetImplFontCharMap() const
{
    if( !mpOs2FontData[0] )
        return ImplFontCharMap::GetDefaultMap();
    return mpOs2FontData[0]->GetImplFontCharMap();
}

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

bool Os2SalGraphics::AddTempDevFont( ImplDevFontList* pFontList,
    const String& rFontFileURL, const String& rFontName )
{
#if OSL_DEBUG_LEVEL>0
    debug_printf("Os2SalGraphics::AddTempDevFont\n");
#endif
    return false;
}

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

void Os2SalGraphics::GetDevFontList( ImplDevFontList* pList )
{
	PFONTMETRICS	pFontMetrics;
	ULONG			nFontMetricCount;
	SalData*		pSalData;

#if OSL_DEBUG_LEVEL>0
	debug_printf("Os2SalGraphics::GetDevFontList mbPrinter=%d\n", mbPrinter);
#endif

	// install OpenSymbol
	HMODULE hMod;
	ULONG	ObjNum, Offset, rc;
	CHAR	Buff[2*_MAX_PATH];
	char 	drive[_MAX_DRIVE], dir[_MAX_DIR];
	char 	fname[_MAX_FNAME], ext[_MAX_EXT];
	// get module handle (and name)
	rc = DosQueryModFromEIP( &hMod, &ObjNum, sizeof( Buff), Buff, 
							&Offset, (ULONG)ImplSalGetUniString);
	DosQueryModuleName(hMod, sizeof(Buff), Buff);
	// replace module path with font path
	char* slash = strrchr( Buff, '\\');
	*slash = '\0';
	slash = strrchr( Buff, '\\');
	*slash = '\0';
	strcat( Buff, "\\FONTS\\OPENS___.TTF");
	rc = GpiLoadPublicFonts( GetSalData()->mhAB, Buff);

	if ( !mbPrinter )
	{
		// Bei Bildschirm-Devices cachen wir die Liste global, da
		// dies im unabhaengigen Teil auch so gemacht wird und wir
		// ansonsten auf geloeschten Systemdaten arbeiten koennten
		pSalData = GetSalData();
		nFontMetricCount	= pSalData->mnFontMetricCount;
		pFontMetrics		= pSalData->mpFontMetrics;
		// Bei Bildschirm-Devices holen wir uns die Fontliste jedesmal neu
		if ( pFontMetrics )
		{
			delete pFontMetrics;
			pFontMetrics		= NULL;
			nFontMetricCount	= 0;
		}
	}
	else
	{
		nFontMetricCount	= mnFontMetricCount;
		pFontMetrics		= mpFontMetrics;
	}

	// do we have to create the cached font list first?
	if ( !pFontMetrics )
	{
		// query the number of fonts available
		LONG nTemp = 0;
		nFontMetricCount = Ft2QueryFonts( mhPS,
										  QF_PUBLIC | QF_PRIVATE | QF_NO_DEVICE,
										  NULL, &nTemp,
										  sizeof( FONTMETRICS ), NULL );

		// procede only if at least one is available!
		if ( nFontMetricCount )
		{
			// allocate memory for font list
			pFontMetrics = new FONTMETRICS[nFontMetricCount];

			// query font list
			Ft2QueryFonts( mhPS,
						   QF_PUBLIC | QF_PRIVATE | QF_NO_DEVICE,
						   NULL,
						   (PLONG)&nFontMetricCount,
						   (LONG) sizeof( FONTMETRICS ),
						   pFontMetrics );
		}

		if ( !mbPrinter )
		{
			pSalData->mnFontMetricCount 		= nFontMetricCount;
			pSalData->mpFontMetrics 			= pFontMetrics;
		}
		else
		{
			mnFontMetricCount	= nFontMetricCount;
			mpFontMetrics		= pFontMetrics;
		}
	}

	// copy data from the font list
	for( ULONG i = 0; i < nFontMetricCount; i++ )
	{
		PFONTMETRICS pFontMetric = &pFontMetrics[i];

#if OSL_DEBUG_LEVEL>2
		debug_printf("Os2SalGraphics::GetDevFontList #%d,'%s'\n", i, pFontMetric->szFacename);
#endif

		// skip font starting with '@', this is an alias internally
		// used by truetype engine.
		if (pFontMetric->szFacename[0] == '@')
			continue;

		// skip bitmap fonts (but keep WarpSans)
		if ( (pFontMetric->fsDefn & FM_DEFN_OUTLINE) == 0 
			&& strncmp( pFontMetric->szFacename, "WarpSans", 8) )
			// Font nicht aufnehmen
			continue;

		// replace '-' in facename with ' ' (for ft2lib)
		char* dash = pFontMetric->szFacename;
		while( (dash=strchr( dash, '-')))
			*dash++ = ' ';

		// create new font list element
		ImplOs2FontData* pData 		= new ImplOs2FontData( pFontMetric, 0, 0 );

		// ticket#80: font id field is used for pdf font cache code.
		pData->SetFontId( i);

		// add font list element to font list
		pList->Add( pData );

	}
}

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

void Os2SalGraphics::GetDevFontSubstList( OutputDevice* pOutDev )
{
}

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

sal_Bool Os2SalGraphics::GetGlyphBoundRect( long nIndex, Rectangle& rRect )
{
    // use unity matrix
    MAT2 aMat;
    aMat.eM11 = aMat.eM22 = FixedFromDouble( 1.0 );
    aMat.eM12 = aMat.eM21 = FixedFromDouble( 0.0 );

    UINT nGGOFlags = GGO_METRICS;
    if( !(nIndex & GF_ISCHAR) )
        nGGOFlags |= GGO_GLYPH_INDEX;
    nIndex &= GF_IDXMASK;

    GLYPHMETRICS aGM;
    DWORD nSize = FT2_ERROR;
    nSize = Ft2GetGlyphOutline( mhPS, nIndex, nGGOFlags, &aGM, 0, NULL, &aMat );
    if( nSize == FT2_ERROR )
        return false;

    rRect = Rectangle( Point( +aGM.gmptGlyphOrigin.x, -aGM.gmptGlyphOrigin.y ),
        Size( aGM.gmBlackBoxX, aGM.gmBlackBoxY ) );
    rRect.Left()    = static_cast<int>( mfFontScale * rRect.Left() );
    rRect.Right()   = static_cast<int>( mfFontScale * rRect.Right() );
    rRect.Top()     = static_cast<int>( mfFontScale * rRect.Top() );
    rRect.Bottom()  = static_cast<int>( mfFontScale * rRect.Bottom() );
    return true;
}

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

sal_Bool Os2SalGraphics::GetGlyphOutline( long nIndex, ::basegfx::B2DPolyPolygon& rB2DPolyPoly )
{
#if OSL_DEBUG_LEVEL>0
	debug_printf("Os2SalGraphics::GetGlyphOutline\n");
#endif
    rB2DPolyPoly.clear();

    PM_BOOL bRet = FALSE;

    // use unity matrix
    MAT2 aMat;
    aMat.eM11 = aMat.eM22 = FixedFromDouble( 1.0 );
    aMat.eM12 = aMat.eM21 = FixedFromDouble( 0.0 );

    UINT nGGOFlags = GGO_NATIVE;
    if( !(nIndex & GF_ISCHAR) )
        nGGOFlags |= GGO_GLYPH_INDEX;
    nIndex &= GF_IDXMASK;

    GLYPHMETRICS aGlyphMetrics;
    DWORD nSize1 = FT2_ERROR;
    nSize1 = Ft2GetGlyphOutline( mhPS, nIndex, nGGOFlags, &aGlyphMetrics, 0, NULL, &aMat );

    if( !nSize1 )       // blank glyphs are ok
        bRet = TRUE;
    else if( nSize1 != FT2_ERROR )
    {
        PM_BYTE*   pData = new PM_BYTE[ nSize1 ];
        ULONG   nTotalCount = 0;
        DWORD   nSize2;
        nSize2 = Ft2GetGlyphOutline( mhPS, nIndex, nGGOFlags,
                &aGlyphMetrics, nSize1, pData, &aMat );

        if( nSize1 == nSize2 )
        {
            bRet = TRUE;

            int     nPtSize = 512;
            Point*  pPoints = new Point[ nPtSize ];
            sal_uInt8*   pFlags = new sal_uInt8[ nPtSize ];

            TTPOLYGONHEADER* pHeader = (TTPOLYGONHEADER*)pData;
            while( (PM_BYTE*)pHeader < pData+nSize2 )
            {
                // only outline data is interesting
                if( pHeader->dwType != TT_POLYGON_TYPE )
                    break;

                // get start point; next start points are end points
                // of previous segment
                int nPnt = 0;

                long nX = IntTimes256FromFixed( pHeader->pfxStart.x );
                long nY = IntTimes256FromFixed( pHeader->pfxStart.y );
                pPoints[ nPnt ] = Point( nX, nY );
                pFlags[ nPnt++ ] = POLY_NORMAL;

                bool bHasOfflinePoints = false;
                TTPOLYCURVE* pCurve = (TTPOLYCURVE*)( pHeader + 1 );
                pHeader = (TTPOLYGONHEADER*)( (PM_BYTE*)pHeader + pHeader->cb );
                while( (PM_BYTE*)pCurve < (PM_BYTE*)pHeader )
                {
                    int nNeededSize = nPnt + 16 + 3 * pCurve->cpfx;
                    if( nPtSize < nNeededSize )
                    {
                        Point* pOldPoints = pPoints;
                        sal_uInt8* pOldFlags = pFlags;
                        nPtSize = 2 * nNeededSize;
                        pPoints = new Point[ nPtSize ];
                        pFlags = new sal_uInt8[ nPtSize ];
                        for( int i = 0; i < nPnt; ++i )
                        {
                            pPoints[ i ] = pOldPoints[ i ];
                            pFlags[ i ] = pOldFlags[ i ];
                        }
                        delete[] pOldPoints;
                        delete[] pOldFlags;
                    }

                    int i = 0;
                    if( TT_PRIM_LINE == pCurve->wType )
                    {
                        while( i < pCurve->cpfx )
                        {
                            nX = IntTimes256FromFixed( pCurve->apfx[ i ].x );
                            nY = IntTimes256FromFixed( pCurve->apfx[ i ].y );
                            ++i;
                            pPoints[ nPnt ] = Point( nX, nY );
                            pFlags[ nPnt ] = POLY_NORMAL;
                            ++nPnt;
                        }
                    }
                    else if( TT_PRIM_QSPLINE == pCurve->wType )
                    {
                        bHasOfflinePoints = true;
                        while( i < pCurve->cpfx )
                        {
                            // get control point of quadratic bezier spline
                            nX = IntTimes256FromFixed( pCurve->apfx[ i ].x );
                            nY = IntTimes256FromFixed( pCurve->apfx[ i ].y );
                            ++i;
                            Point aControlP( nX, nY );

                            // calculate first cubic control point
                            // P0 = 1/3 * (PBeg + 2 * PQControl)
                            nX = pPoints[ nPnt-1 ].X() + 2 * aControlP.X();
                            nY = pPoints[ nPnt-1 ].Y() + 2 * aControlP.Y();
                            pPoints[ nPnt+0 ] = Point( (2*nX+3)/6, (2*nY+3)/6 );
                            pFlags[ nPnt+0 ] = POLY_CONTROL;

                            // calculate endpoint of segment
                            nX = IntTimes256FromFixed( pCurve->apfx[ i ].x );
                            nY = IntTimes256FromFixed( pCurve->apfx[ i ].y );

                            if ( i+1 >= pCurve->cpfx )
                            {
                                // endpoint is either last point in segment => advance
                                ++i;
                            }
                            else
                            {
                                // or endpoint is the middle of two control points
                                nX += IntTimes256FromFixed( pCurve->apfx[ i-1 ].x );
                                nY += IntTimes256FromFixed( pCurve->apfx[ i-1 ].y );
                                nX = (nX + 1) / 2;
                                nY = (nY + 1) / 2;
                                // no need to advance, because the current point
                                // is the control point in next bezier spline
                            }

                            pPoints[ nPnt+2 ] = Point( nX, nY );
                            pFlags[ nPnt+2 ] = POLY_NORMAL;

                            // calculate second cubic control point
                            // P1 = 1/3 * (PEnd + 2 * PQControl)
                            nX = pPoints[ nPnt+2 ].X() + 2 * aControlP.X();
                            nY = pPoints[ nPnt+2 ].Y() + 2 * aControlP.Y();
                            pPoints[ nPnt+1 ] = Point( (2*nX+3)/6, (2*nY+3)/6 );
                            pFlags[ nPnt+1 ] = POLY_CONTROL;

                            nPnt += 3;
                        }
                    }

                    // next curve segment
                    pCurve = (TTPOLYCURVE*)&pCurve->apfx[ i ];
                }

                // end point is start point for closed contour
                // disabled, because Polygon class closes the contour itself
                // pPoints[nPnt++] = pPoints[0];
				// #i35928#
				// Added again, but add only when not yet closed
				if(pPoints[nPnt - 1] != pPoints[0])
				{
                    if( bHasOfflinePoints )
                        pFlags[nPnt] = pFlags[0];

					pPoints[nPnt++] = pPoints[0];
				}

                // convert y-coordinates W32 -> VCL
                for( int i = 0; i < nPnt; ++i )
                    pPoints[i].Y() = -pPoints[i].Y();

                // insert into polypolygon
                Polygon aPoly( nPnt, pPoints, (bHasOfflinePoints ? pFlags : NULL) );
                // convert to B2DPolyPolygon
                // TODO: get rid of the intermediate PolyPolygon
                rB2DPolyPoly.append( aPoly.getB2DPolygon() );
            }

            delete[] pPoints;
            delete[] pFlags;
        }

        delete[] pData;
    }

    // rescaling needed for the PolyPolygon conversion
    if( rB2DPolyPoly.count() )
    {
        ::basegfx::B2DHomMatrix aMatrix;
        aMatrix.scale( 1.0/256, 1.0/256 );
        aMatrix.scale( mfFontScale, mfFontScale );
        rB2DPolyPoly.transform( aMatrix );
    }
    
    return bRet;
}

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

// TODO:  Replace this class with boost::scoped_array
class ScopedCharArray
{
public:
	inline explicit ScopedCharArray(char * pArray): m_pArray(pArray) {}

	inline ~ScopedCharArray() { delete[] m_pArray; }

	inline char * get() const { return m_pArray; }

private:
	char * m_pArray;
};

class ScopedFont
{
public:
	explicit ScopedFont(Os2SalGraphics & rData);

	~ScopedFont();

private:
	Os2SalGraphics & m_rData;
	ULONG m_hOrigFont;
};

ScopedFont::ScopedFont(Os2SalGraphics & rData): m_rData(rData)
{
#if 0
	m_hOrigFont = m_rData.mhFonts[0];
	m_rData.mhFonts[0] = 0; // avoid deletion of current font
#endif
}

ScopedFont::~ScopedFont()
{
#if 0
	if( m_hOrigFont )
	{
		// restore original font, destroy temporary font
		HFONT hTempFont = m_rData.mhFonts[0];
		m_rData.mhFonts[0] = m_hOrigFont;
		SelectObject( m_rData.mhDC, m_hOrigFont );
		DeleteObject( hTempFont );
	}
#endif
}

class ScopedTrueTypeFont
{
public:
	inline ScopedTrueTypeFont(): m_pFont(0) {}

	~ScopedTrueTypeFont();

	int open(void * pBuffer, sal_uInt32 nLen, sal_uInt32 nFaceNum);

	inline TrueTypeFont * get() const { return m_pFont; }

private:
	TrueTypeFont * m_pFont;
};

ScopedTrueTypeFont::~ScopedTrueTypeFont()
{
	if (m_pFont != 0)
		CloseTTFont(m_pFont);
}

int ScopedTrueTypeFont::open(void * pBuffer, sal_uInt32 nLen,
							 sal_uInt32 nFaceNum)
{
	OSL_ENSURE(m_pFont == 0, "already open");
    return OpenTTFontBuffer(pBuffer, nLen, nFaceNum, &m_pFont);
}

sal_Bool Os2SalGraphics::CreateFontSubset( const rtl::OUString& rToFile,
	const ImplFontData* pFont, long* pGlyphIDs, sal_uInt8* pEncoding,
	sal_Int32* pGlyphWidths, int nGlyphCount, FontSubsetInfo& rInfo )
{
	// TODO: use more of the central font-subsetting code, move stuff there if needed

	// create matching ImplFontSelectData
	// we need just enough to get to the font file data
	// use height=1000 for easier debugging (to match psprint's font units)
	ImplFontSelectData aIFSD( *pFont, Size(0,1000), 1000.0, 0, false );

	// TODO: much better solution: move SetFont and restoration of old font to caller
	ScopedFont aOldFont(*this);
	SetFont( &aIFSD, 0 );

    ImplOs2FontData* pWinFontData = (ImplOs2FontData*)aIFSD.mpFontData;
    pWinFontData->UpdateFromHPS( mhPS );
    const ImplFontCharMap* pImplFontCharMap = pWinFontData->GetImplFontCharMap();

#if OSL_DEBUG_LEVEL > 100
	// get font metrics
	TEXTMETRICA aWinMetric;
	if( !::GetTextMetricsA( mhDC, &aWinMetric ) )
		return FALSE;

	DBG_ASSERT( !(aWinMetric.tmPitchAndFamily & TMPF_DEVICE), "cannot subset device font" );
	DBG_ASSERT( aWinMetric.tmPitchAndFamily & TMPF_TRUETYPE, "can only subset TT font" );
#endif

    rtl::OUString aSysPath;
    if( osl_File_E_None != osl_getSystemPathFromFileURL( rToFile.pData, &aSysPath.pData ) )
        return FALSE;
    const rtl_TextEncoding aThreadEncoding = osl_getThreadTextEncoding();
    const ByteString aToFile( aSysPath.getStr(), (xub_StrLen)aSysPath.getLength(), aThreadEncoding );

	// check if the font has a CFF-table
	const DWORD nCffTag = CalcTag( "CFF " );
	const RawFontData aRawCffData( mhPS, nCffTag );
	if( aRawCffData.get() )
	{
		long nRealGlyphIds[ 256 ];
		for( int i = 0; i < nGlyphCount; ++i )
		{
			// TODO: remap notdef glyph if needed
			// TODO: use GDI's GetGlyphIndices instead? Does it handle GSUB properly?
			sal_uInt32 nGlyphIdx = pGlyphIDs[i] & GF_IDXMASK;
			if( pGlyphIDs[i] & GF_ISCHAR ) // remaining pseudo-glyphs need to be translated
				nGlyphIdx = pImplFontCharMap->GetGlyphIndex( nGlyphIdx );
			if( (pGlyphIDs[i] & (GF_ROTMASK|GF_GSUB)) != 0)	// TODO: vertical substitution
				{/*####*/}

			nRealGlyphIds[i] = nGlyphIdx;
		}

		// provide a font subset from the CFF-table
		FILE* pOutFile = fopen( aToFile.GetBuffer(), "wb" );
		rInfo.LoadFont( FontSubsetInfo::CFF_FONT, aRawCffData.get(), aRawCffData.size() );
		bool bRC = rInfo.CreateFontSubset( FontSubsetInfo::TYPE1_PFB, pOutFile, NULL,
				nRealGlyphIds, pEncoding, nGlyphCount, pGlyphWidths );
		fclose( pOutFile );
		return bRC;
	}

    // get raw font file data
    const RawFontData xRawFontData( mhPS, NULL );
    if( !xRawFontData.get() )
		return FALSE;

	// open font file
	sal_uInt32 nFaceNum = 0;
	if( !*xRawFontData.get() )	// TTC candidate
		nFaceNum = ~0U;	 // indicate "TTC font extracts only"

	ScopedTrueTypeFont aSftTTF;
	int nRC = aSftTTF.open( (void*)xRawFontData.get(), xRawFontData.size(), nFaceNum );
	if( nRC != SF_OK )
		return FALSE;

	TTGlobalFontInfo aTTInfo;
	::GetTTGlobalFontInfo( aSftTTF.get(), &aTTInfo );
	rInfo.m_nFontType   = FontSubsetInfo::SFNT_TTF;
	rInfo.m_aPSName		= ImplSalGetUniString( aTTInfo.psname );
	rInfo.m_nAscent		= +aTTInfo.winAscent;
	rInfo.m_nDescent	= -aTTInfo.winDescent;
	rInfo.m_aFontBBox	= Rectangle( Point( aTTInfo.xMin, aTTInfo.yMin ),
									Point( aTTInfo.xMax, aTTInfo.yMax ) );
	rInfo.m_nCapHeight	= aTTInfo.yMax; // Well ...

	// subset TTF-glyphs and get their properties
	// take care that subset fonts require the NotDef glyph in pos 0
	int nOrigCount = nGlyphCount;
	USHORT	  aShortIDs[ 256 ];
	sal_uInt8 aTempEncs[ 256 ];

	int nNotDef=-1, i;
	for( i = 0; i < nGlyphCount; ++i )
	{
		aTempEncs[i] = pEncoding[i];
		sal_uInt32 nGlyphIdx = pGlyphIDs[i] & GF_IDXMASK;
		if( pGlyphIDs[i] & GF_ISCHAR )
		{
			sal_Unicode cChar = static_cast<sal_Unicode>(nGlyphIdx); // TODO: sal_UCS4
			const bool bVertical = ((pGlyphIDs[i] & (GF_ROTMASK|GF_GSUB)) != 0);
			nGlyphIdx = ::MapChar( aSftTTF.get(), cChar, bVertical );
			if( (nGlyphIdx == 0) && pFont->IsSymbolFont() )
			{
				// #i12824# emulate symbol aliasing U+FXXX <-> U+0XXX
				cChar = (cChar & 0xF000) ? (cChar & 0x00FF) : (cChar | 0xF000);
				nGlyphIdx = ::MapChar( aSftTTF.get(), cChar, bVertical );
			}
		}
		aShortIDs[i] = static_cast<USHORT>( nGlyphIdx );
		if( !nGlyphIdx )
			if( nNotDef < 0 )
				nNotDef = i; // first NotDef glyph found
	}

	if( nNotDef != 0 )
	{
		// add fake NotDef glyph if needed
		if( nNotDef < 0 )
			nNotDef = nGlyphCount++;

		// NotDef glyph must be in pos 0 => swap glyphids
		aShortIDs[ nNotDef ] = aShortIDs[0];
		aTempEncs[ nNotDef ] = aTempEncs[0];
		aShortIDs[0] = 0;
		aTempEncs[0] = 0;
	}
	DBG_ASSERT( nGlyphCount < 257, "too many glyphs for subsetting" );

	// fill pWidth array
	TTSimpleGlyphMetrics* pMetrics =
		::GetTTSimpleGlyphMetrics( aSftTTF.get(), aShortIDs, nGlyphCount, aIFSD.mbVertical );
	if( !pMetrics )
		return FALSE;
	sal_uInt16 nNotDefAdv	= pMetrics[0].adv;
	pMetrics[0].adv			= pMetrics[nNotDef].adv;
	pMetrics[nNotDef].adv	= nNotDefAdv;
	for( i = 0; i < nOrigCount; ++i )
		pGlyphWidths[i] = pMetrics[i].adv;
	free( pMetrics );

	// write subset into destination file
	nRC = ::CreateTTFromTTGlyphs( aSftTTF.get(), aToFile.GetBuffer(), aShortIDs,
			aTempEncs, nGlyphCount, 0, NULL, 0 );
	return (nRC == SF_OK);
}

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

const void* Os2SalGraphics::GetEmbedFontData( const ImplFontData* pFont,
	const sal_Ucs* pUnicodes, sal_Int32* pCharWidths,
	FontSubsetInfo& rInfo, long* pDataLen )
{
	// create matching ImplFontSelectData
	// we need just enough to get to the font file data
	ImplFontSelectData aIFSD( *pFont, Size(0,1000), 1000.0, 0, false );

	// TODO: much better solution: move SetFont and restoration of old font to caller
	ScopedFont aOldFont(*this);
	SetFont( &aIFSD, 0 );

	// get the raw font file data
	RawFontData aRawFontData( mhPS );
	*pDataLen = aRawFontData.size();
	if( !aRawFontData.get() )
		return NULL;

	// get important font properties
	FONTMETRICS aOS2Metric;
	if (Ft2QueryFontMetrics( mhPS, sizeof( aOS2Metric ), &aOS2Metric ) == GPI_ERROR)
			*pDataLen = 0;
	rInfo.m_nFontType	= FontSubsetInfo::ANY_TYPE1;
	rInfo.m_aPSName		= ImplSalGetUniString( aOS2Metric.szFacename );
	rInfo.m_nAscent		= +aOS2Metric.lMaxAscender;
	rInfo.m_nDescent	= -aOS2Metric.lMaxDescender;
	rInfo.m_aFontBBox	= Rectangle( Point( 0, -aOS2Metric.lMaxDescender ),
			  Point( aOS2Metric.lMaxCharInc, aOS2Metric.lMaxAscender+aOS2Metric.lExternalLeading ) );
	rInfo.m_nCapHeight	= aOS2Metric.lMaxAscender; // Well ...

	// get individual character widths
	for( int i = 0; i < 256; ++i )
	{
		LONG nCharWidth = 0;
		const sal_Ucs cChar = pUnicodes[i];
		if( !Ft2QueryStringWidthW( mhPS, (LPWSTR)&cChar, 1, &nCharWidth ) )
			*pDataLen = 0;
		pCharWidths[i] = nCharWidth;
	}

	if( !*pDataLen )
		return NULL;

	const unsigned char* pData = aRawFontData.steal();
	return (void*)pData;
}

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

void Os2SalGraphics::FreeEmbedFontData( const void* pData, long /*nLen*/ )
{
	delete[] reinterpret_cast<char*>(const_cast<void*>(pData));
}

const Ucs2SIntMap* Os2SalGraphics::GetFontEncodingVector( const ImplFontData* pFont, const Ucs2OStrMap** pNonEncoded )
{
    // TODO: even for builtin fonts we get here... why?
    if( !pFont->IsEmbeddable() )
        return NULL;

    // fill the encoding vector
    // currently no nonencoded vector
    if( pNonEncoded )
        *pNonEncoded = NULL;

    const ImplOs2FontData* pWinFontData = static_cast<const ImplOs2FontData*>(pFont);
    const Ucs2SIntMap* pEncoding = pWinFontData->GetEncodingVector();
    if( pEncoding == NULL )
    {
        Ucs2SIntMap* pNewEncoding = new Ucs2SIntMap;
        #if 0
        // TODO: get correct encoding vector
        GLYPHSET aGlyphSet;
        aGlyphSet.cbThis = sizeof(aGlyphSet);
        DWORD aW = ::GetFontUnicodeRanges( mhPS, &aGlyphSet);
        #else
        for( sal_Unicode i = 32; i < 256; ++i )
            (*pNewEncoding)[i] = i;
        #endif
        pWinFontData->SetEncodingVector( pNewEncoding );
	pEncoding = pNewEncoding;
    }

    return pEncoding;
}

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

void Os2SalGraphics::GetGlyphWidths( const ImplFontData* pFont,
                                     bool bVertical,
                                     Int32Vector& rWidths,
                                     Ucs2UIntMap& rUnicodeEnc )
{
    // create matching ImplFontSelectData
    // we need just enough to get to the font file data
    ImplFontSelectData aIFSD( *pFont, Size(0,1000), 1000.0, 0, false );

    // TODO: much better solution: move SetFont and restoration of old font to caller
    ScopedFont aOldFont(*this);
    
    float fScale = 0.0;
    ImplDoSetFont( &aIFSD, fScale, 0);
    
    if( pFont->IsSubsettable() )
    {
        // get raw font file data
	const RawFontData xRawFontData( mhPS );
	if( !xRawFontData.get() )
		return;

        // open font file
        sal_uInt32 nFaceNum = 0;
        if( !*xRawFontData.get() )  // TTC candidate
            nFaceNum = ~0U;  // indicate "TTC font extracts only"
    
        ScopedTrueTypeFont aSftTTF;
        int nRC = aSftTTF.open( (void*)xRawFontData.get(), xRawFontData.size(), nFaceNum );
        if( nRC != SF_OK )
            return;
    
        int nGlyphs = GetTTGlyphCount( aSftTTF.get() );
        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( aSftTTF.get(),
                                                                        &aGlyphIds[0],
                                                                        nGlyphs,
                                                                        bVertical ? 1 : 0 );
            if( pMetrics )
            {
                for( int i = 0; i< nGlyphs; i++ )
                    rWidths[i] = pMetrics[i].adv;
                free( pMetrics );
                rUnicodeEnc.clear();
            }
            const ImplOs2FontData* pWinFont = static_cast<const ImplOs2FontData*>(pFont);
            const ImplFontCharMap* pMap = pWinFont->GetImplFontCharMap();
			DBG_ASSERT( pMap && pMap->GetCharCount(), "no map" );

			int nCharCount = pMap->GetCharCount();
            sal_uInt32 nChar = pMap->GetFirstChar();
			for( int i = 0; i < nCharCount; i++ )
            {
                if( nChar < 0x00010000 )
                {
                    sal_uInt16 nGlyph = ::MapChar( aSftTTF.get(),
                                                   static_cast<sal_uInt16>(nChar),
                                                   bVertical ? 1 : 0 );
                    if( nGlyph )
                        rUnicodeEnc[ static_cast<sal_Unicode>(nChar) ] = nGlyph;
                }
				nChar = pMap->GetNextChar( nChar );
            }
        }
    }
    else if( pFont->IsEmbeddable() )
    {
        // get individual character widths
        rWidths.clear();
        rUnicodeEnc.clear();
        rWidths.reserve( 224 );
        for( sal_Unicode i = 32; i < 256; ++i )
        {
            int nCharWidth = 0;
            if( Ft2QueryStringWidthW( mhPS, (LPWSTR)&i, 1, (LONG*)&nCharWidth ) )
            {
                rUnicodeEnc[ i ] = rWidths.size();
                rWidths.push_back( nCharWidth );
            }
        }
    }    
}

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

void Os2SalGraphics::DrawServerFontLayout( const ServerFontLayout& )
{}

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

SystemFontData Os2SalGraphics::GetSysFontData( int nFallbacklevel ) const
{
    SystemFontData aSysFontData;

    if (nFallbacklevel >= MAX_FALLBACK) nFallbacklevel = MAX_FALLBACK - 1;
    if (nFallbacklevel < 0 ) nFallbacklevel = 0;
    
    aSysFontData.nSize = sizeof( SystemFontData );
    aSysFontData.hFont = mhFonts[nFallbacklevel]; 
    aSysFontData.bFakeBold = false;
    aSysFontData.bFakeItalic = false;
    aSysFontData.bAntialias = true;
    aSysFontData.bVerticalCharacterType = false;
        
    return aSysFontData;
}

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