/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: textsvcs.cpp,v 1.1.2.1 2004/07/09 01:50:49 hubbe Exp $
 * 
 * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved.
 * 
 * The contents of this file, and the files included with this file,
 * are subject to the current version of the RealNetworks Public
 * Source License (the "RPSL") available at
 * http://www.helixcommunity.org/content/rpsl unless you have licensed
 * the file under the current version of the RealNetworks Community
 * Source License (the "RCSL") available at
 * http://www.helixcommunity.org/content/rcsl, in which case the RCSL
 * will apply. You may also obtain the license terms directly from
 * RealNetworks.  You may not use this file except in compliance with
 * the RPSL or, if you have a valid RCSL with RealNetworks applicable
 * to this file, the RCSL.  Please see the applicable RPSL or RCSL for
 * the rights, obligations and limitations governing use of the
 * contents of the file.
 * 
 * Alternatively, the contents of this file may be used under the
 * terms of the GNU General Public License Version 2 or later (the
 * "GPL") in which case the provisions of the GPL are applicable
 * instead of those above. If you wish to allow use of your version of
 * this file only under the terms of the GPL, and not to allow others
 * to use your version of this file under the terms of either the RPSL
 * or RCSL, indicate your decision by deleting the provisions above
 * and replace them with the notice and other provisions required by
 * the GPL. If you do not delete the provisions above, a recipient may
 * use your version of this file under the terms of any one of the
 * RPSL, the RCSL or the GPL.
 * 
 * This file is part of the Helix DNA Technology. RealNetworks is the
 * developer of the Original Code and owns the copyrights in the
 * portions it created.
 * 
 * This file, and the files included with this file, is distributed
 * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS
 * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET
 * ENJOYMENT OR NON-INFRINGEMENT.
 * 
 * Technology Compatibility Kit Test Suite(s) Location:
 *    http://www.helixcommunity.org/content/tck
 * 
 * Contributor(s):
 * 
 * ***** END LICENSE BLOCK ***** */

 
 /****************************************************************************
 *
 *  Text Services for rendering text onto a bitmap
 *
 */

#include "hxtypes.h" /*Must be included before windows.h for VC6 compiler*/

#ifdef _WINDOWS
#include <windows.h>
#endif

#include "stdlib.h"

#include "hxassert.h"
#include "hxstring.h"

#include "textsvcs.h"

#define LOW_BIT_MONOCHROME  0
#define HIGH_BIT_MONOCHROME (LOW_BIT_MONOCHROME+7)

HX_RESULT
CTextServices::bltToBitmapFromBitmap(UCHAR* pOffscreenBitmap,
	LONG32 lOffscreenWidth, LONG32 lOffscreenHeight, LONG32 lOffscreenDepth,
	INT32 lPosX, INT32 lPosY, FT_Bitmap* pFTGlyphBitmap,
	UINT32 ulARGBforegroundColor)
{
    HX_RESULT hxrslt = HXR_OK;
    UINT8 uBitNumForMonochrome = HIGH_BIT_MONOCHROME;
    UCHAR* p8SrcBits = NULL;

    // /XXXEH- bRowsInverted is TRUE for Windows. FALSE for others???
#if defined(_WINDOWS)
    BOOL bRowsInverted = TRUE;
#else
    BOOL bRowsInverted = FALSE;
#endif
    
    LONG32 lGlyphHeight = (LONG32)pFTGlyphBitmap->rows;
    LONG32 lGlyphWidth = (LONG32)pFTGlyphBitmap->width;

    LONG32 lWBoundsChecked =
	    lGlyphWidth+lPosX < lOffscreenWidth? lGlyphWidth+lPosX : lOffscreenWidth-1;

    INT32 lDestRow = lPosY;
    INT32 lSrcRow = 0;

    INT32 lNumPixelsPer32Bits = 4;

    // /XXXEH- need to handle non-32-bit destination-bitmap depths:
    if (32 != lOffscreenDepth)
    {
	HX_ASSERT(32 == lOffscreenDepth  &&  "need_to_handle_other_bit-depths");
	hxrslt = HXR_FAIL;
	goto cleanup;
    }

    if (ft_pixel_mode_grays == pFTGlyphBitmap->pixel_mode)
    {
	HX_ASSERT(256 == pFTGlyphBitmap->num_grays);
	lNumPixelsPer32Bits = 4;
    }
    else // /ft_pixel_mode_mono -- monochrome bitmap:
    {
	HX_ASSERT(ft_pixel_mode_mono == pFTGlyphBitmap->pixel_mode);
	lNumPixelsPer32Bits = 32;
    }

    if (4 != lNumPixelsPer32Bits  &&  32 != lNumPixelsPer32Bits)
    {
	// /We can't handle other than either 1 or 256 levels from the source
	// glyph bitmap.  FreeType 2 doesn't allow for anything else, anyway,
	// but just in case that changes in the future...
	HX_ASSERT(4 == lNumPixelsPer32Bits  ||  32 == lNumPixelsPer32Bits);
	hxrslt = HXR_FAIL;
	goto cleanup;
    }

    /* /XXXEH- the FT_Bitmap struct:
    typedef struct  FT_Bitmap_
    {
	int             rows;
	int             width;
	int             pitch;
	unsigned char*  buffer;
	short           num_grays;
	char            pixel_mode;
	char            palette_mode;
	void*           palette;

    } FT_Bitmap;
    */

    p8SrcBits = (UCHAR*)pFTGlyphBitmap->buffer;

    // /Go through and copy bits from the glyph bitmap to the offscreen one:
    for (uBitNumForMonochrome = HIGH_BIT_MONOCHROME, lDestRow = lPosY, lSrcRow = 0;
	    (lDestRow < lPosY + lGlyphHeight)  &&  (lDestRow < lOffscreenHeight)  &&
	    (lSrcRow < lGlyphHeight);
	    lDestRow++, lSrcRow++)
    {
        UINT32* p32DestBits = (UINT32*)pOffscreenBitmap +
		((bRowsInverted ? lOffscreenHeight - 1 - lDestRow : lDestRow) *
                lOffscreenWidth) + lPosX;
	if (32 == lNumPixelsPer32Bits  &&  lDestRow > lPosY)
	{
	    // /This finishes fixing CROSSPLAT-TEXT-BUG-0015:
	    // /Next byte starts the next row, so ignore pad bits:
	    p8SrcBits++;
	    uBitNumForMonochrome = HIGH_BIT_MONOCHROME;
	}
	else
	{
	    p8SrcBits = (UCHAR*)pFTGlyphBitmap->buffer +
		    ((lSrcRow/(lNumPixelsPer32Bits/4)) * lGlyphWidth);
	}

        UINT32 ulDestColNum = (UINT32)lWBoundsChecked;
	UINT32 ulSrcColNum = lGlyphWidth;
        while (ulDestColNum--  &&  ulSrcColNum--)
        {
            UINT8 u8GrayScale = (UINT8)(*p8SrcBits);
	    if (32 == lNumPixelsPer32Bits)
	    {
		// /Use the appropriate bit; this is 1 bit per pixel:
		u8GrayScale = (UINT8)(((*p8SrcBits)>>uBitNumForMonochrome) & 1);
	    }
	    
            if (0xFF <= u8GrayScale  ||
		    (32 == lNumPixelsPer32Bits  &&  u8GrayScale))
            {
		// /Fixes CROSSPLAT-TEXT-BUG-0003 by using foregroundColor
		// instead of grayscale:
                *p32DestBits = ulARGBforegroundColor;
            }
            else if (u8GrayScale)
            {
		// /Fixes CROSSPLAT-TEXT-BUG-0004:
		// /Translate from grayscale to text color: set to gray-
		// adjusted color and also set transparency level to allow
		// background image to blend in.  If background isn't 100%
		// transparent in the original value, then we need to blend
		// the original as well as the new values based on their
		// transparency values:
		if ((*p32DestBits & 0xFF000000) == 0xFF000000)
		{
		    // /100% transparent bkgrnd, so just set alpha:
		    *p32DestBits = ulARGBforegroundColor |
			    // /Set the alpha channel:
			    ((UINT32)(255-u8GrayScale) << 24);
		}
		else
		{
		    // /Break out foreground colors:
		    UINT16 usFGRed   = (ulARGBforegroundColor & 0xFF0000) >> 16;
		    UINT16 usFGGreen = (ulARGBforegroundColor & 0xFF00) >> 8;
		    UINT16 usFGBlue  =  ulARGBforegroundColor & 0xFF;
		    // /Break out background colors:
		    UINT16 usBGRed   = (*p32DestBits & 0xFF0000) >> 16;
		    UINT16 usBGGreen = (*p32DestBits & 0xFF00) >> 8;
		    UINT16 usBGBlue  =  *p32DestBits & 0xFF;

		    float fFGPercentAlpha = 1.0 - ((float)u8GrayScale/255.0);

		    // /If background is 100% opaque, leave alpha alone and 
		    // blend with existing (background) color:
		    if (!(*p32DestBits & 0xFF000000))
		    {
			UINT32 ulBlendedRed = (UINT32)(
				(fFGPercentAlpha * usBGRed) +
				((1.0-fFGPercentAlpha) * usFGRed) ) << 16;
			ulBlendedRed &= 0xFF0000;

			UINT32 ulBlendedGreen = (UINT32)(
				(fFGPercentAlpha * usBGGreen) +
				((1.0-fFGPercentAlpha) * usFGGreen) ) << 8;
			ulBlendedGreen &= 0xFF00;

			UINT32 ulBlendedBlue = (UINT32)(
				(fFGPercentAlpha * usBGBlue) +
				((1.0-fFGPercentAlpha) * usFGBlue) );
			ulBlendedBlue &= 0xFF;

			*p32DestBits = (ulBlendedRed | ulBlendedGreen | ulBlendedBlue);
		    }
		    else // /Blend each, and average their alpha:
		    {
			UINT32 ulBGAlpha = (*p32DestBits & 0xFF000000)>>24;
			float fBGPercentAlpha = (float)ulBGAlpha/255.0;

			float fProductFGAlphaBGAlpha =
				fFGPercentAlpha * fBGPercentAlpha;
			 // /Code above should assure this (see division, below):
			HX_ASSERT(fFGPercentAlpha < 1.0  &&  fProductFGAlphaBGAlpha < 1.0);
			// /This is the scale factor we need to apply so that
			// when the vid-surface blending happens, the values
			// end up where they were supposed to be:
			float fScaleFactorToCounterNextSiteAlphaBlend =
				1.0 / (1.0 - fProductFGAlphaBGAlpha);

			UINT32 ulBlendedRed = (UINT32)( ( ((1.0-fBGPercentAlpha) *
				fFGPercentAlpha * usBGRed) +
				((1.0-fFGPercentAlpha) * usFGRed) ) *
				fScaleFactorToCounterNextSiteAlphaBlend) << 16;
			if (ulBlendedRed > 0xFFFFFF)
			{
			    ulBlendedRed = 0xFF0000;
			}
			ulBlendedRed &= 0xFF0000;

			UINT32 ulBlendedGreen = (UINT32)( ( ((1.0-fBGPercentAlpha) *
				fFGPercentAlpha * usBGGreen) +
				((1.0-fFGPercentAlpha) * usFGGreen) ) *
				fScaleFactorToCounterNextSiteAlphaBlend) << 8;
			if (ulBlendedGreen > 0xFFFF)
			{
			    ulBlendedGreen = 0xFF00;
			}
			ulBlendedGreen &= 0xFF00;

			UINT32 ulBlendedBlue = (UINT32)( ( ((1.0-fBGPercentAlpha) *
				fFGPercentAlpha * usBGBlue) +
				((1.0-fFGPercentAlpha) * usFGBlue) ) *
				fScaleFactorToCounterNextSiteAlphaBlend);
			if (ulBlendedBlue > 0xFF)
			{
			    ulBlendedBlue = 0xFF;
			}
			ulBlendedBlue &= 0xFF;

			UINT32 ulNewAlpha = (UINT32)(255*fProductFGAlphaBGAlpha) << 24;
			*p32DestBits = (ulBlendedRed | ulBlendedGreen | ulBlendedBlue) |
				// /Set the alpha channel to the average of the
				// current bg alpha and the gray scale (right
				// shifting by 23 is the same as dividing by 2
				// and then << by 24):
				ulNewAlpha;
		    }
		}
            }
	    else // /0
	    {
		; // /XXXEH- finish this!  Fill in bgrect if font bgcolor set.
	    }
            p32DestBits++;

	    if (32 == lNumPixelsPer32Bits)
	    {
		if (LOW_BIT_MONOCHROME == uBitNumForMonochrome)
		{
		    uBitNumForMonochrome = HIGH_BIT_MONOCHROME;
		    // /Increment src ptr after every 8 bits:
		    p8SrcBits++;
		}
		else
		{
		    uBitNumForMonochrome--;
		}
	    }
	    else
	    {
		p8SrcBits++;
	    }
        }
    }

cleanup:
    return hxrslt;
}

HX_RESULT
CTextServices::renderTextSameFontFace(const char* pszString, UCHAR* pOffscreenBitmap,
			LONG32 lOffscreenWidth, LONG32 lOffscreenHeight,
			LONG32 lOffscreenDepth,
			INT32 lPosStartX, INT32 lPosStartY,
			BOOL bDoAntialias,
			FT_Encoding ftCharsetEncoding,
			UINT32 ulAvgCharWidthPixels /*0 to default*/,
			UINT32 ulCharHeightPixels,
			UINT32 ulARGBforegroundColor)
{
    return renderText(pszString, pOffscreenBitmap,
	    lOffscreenWidth, lOffscreenHeight, lOffscreenDepth,
	    lPosStartX, lPosStartY, bDoAntialias, ftCharsetEncoding,
	    USE_SAME_FAMILY_AS_PRIOR_CALL,
	    USE_SAME_BOLD_AS_PRIOR_CALL,
	    USE_SAME_ITALICS_AS_PRIOR_CALL,
	    ulAvgCharWidthPixels /*0 to default*/, ulCharHeightPixels,
	    ulARGBforegroundColor);
}

HX_RESULT
CTextServices::renderText(const char* pszString, UCHAR* pOffscreenBitmap,
			LONG32 lOffscreenWidth, LONG32 lOffscreenHeight,
			LONG32 lOffscreenDepth,
			INT32 lPosStartX, INT32 lPosStartY,
			BOOL bDoAntialias,
			FT_Encoding ftCharsetEncoding,
			const char* pszFontFamily, // /NULL means use prior face
			BOOL bIsBold, // /-ignored if pszFontFamily==NULL
			BOOL bIsItalicized, // /-ignored if pszFontFamily==NULL
			UINT32 ulAvgCharWidthPixels /*0 to default*/,
			UINT32 ulCharHeightPixels,
			UINT32 ulARGBforegroundColor)
{
    HX_RESULT hxrslt = HXR_OK;

    FT_ULong ulCharCode = 0;
    FT_UInt ulGlyphIndex = 0;
    FT_CharMap  ftFound = 0;
    FT_CharMap  ftCharmap = NULL;

    FT_Error ftError;

    INT32 lCurPenX = lPosStartX;
    INT32 lCurPenY = lPosStartY;

    INT32 lIndx = 0;

    FT_Render_Mode ftRenderMode = bDoAntialias ? ft_render_mode_normal
						: ft_render_mode_mono;

    // /XXXEH- use mbtrlen() if encoding is DBCS or use wcslen() for UNICODE:
    INT32 lStringlen = pszString? strlen(pszString):0;

    HX_ASSERT(NULL == USE_SAME_FAMILY_AS_PRIOR_CALL);
    // /If pszFontFamily is NULL, that means just re-use prior one which is
    // loaded in m_hFace.  If m_hFace is NULL, too, then there's a problem:
    if (!pszFontFamily  &&  !m_hFace)
    {
	HX_ASSERT(pszFontFamily  || m_hFace);
	hxrslt = HXR_INVALID_PARAMETER;
	goto cleanup;
    }

    if (!pszString  ||  !pOffscreenBitmap)
    {
	HX_ASSERT(pszString  &&  pOffscreenBitmap);
	hxrslt = HXR_INVALID_PARAMETER;
	goto cleanup;
    }

    if (!m_hLibrary)
    {
	ftError = FT_Init_FreeType(&m_hLibrary);
	if (ftError  ||  !m_hLibrary)
	{
	    hxrslt = HXR_FAIL;
	    goto cleanup;
	}
    }

    if (USE_SAME_FAMILY_AS_PRIOR_CALL != pszFontFamily)
    {
	if (m_hFace)
	{
	    FT_Done_Face(m_hFace);
	    m_hFace = NULL;
	}
    }

    if (!m_hFace)
    {
#if defined(XXXEH_USE_CACHED_FONT_FACE_OBJECTS)
	// /FT_New_Memory_Face() takes a pointer to the font file buffer and
	// its size in bytes instead of a file pathname. Other than that, it
	// has exactly the same semantics as FT_New_Face().
	// /NOTE: the FT_Open_Face function can be used to open a new font
	// face with a custom input stream as another option.
	ftError = FT_New_Memory_Face(m_hLibrary,
		buffer,    /* first byte in memory */
		size,      /* size in bytes        */
		0,         /* face_index           */
		&face );
#else

// /XXXEH- TODO: move this out into a function, and save the value returned
// after the first time it's called:
#ifdef _WINDOWS
	if (!m_pFontDirectory)
	{
	    // Try to read Font directory from Windows registry:
	    HKEY hKey(0);
	    if (::RegOpenKeyEx(HKEY_CURRENT_USER,
		    "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders",
		    0,
		    KEY_READ,
		    &hKey) == ERROR_SUCCESS)
	    {
		// Read the default value
		BYTE pData[MAX_PATH] = {0};
		DWORD cbData(MAX_PATH);
		if (ERROR_SUCCESS  == ::RegQueryValueEx(hKey, "Fonts",
			0, 0, pData, &cbData))
		{
		    const char* pTmpPtr = (const char*)pData;
		    UINT32 ulBackslashCount = 0;
		    while (*pTmpPtr)
		    {
			if ('\\' == *pTmpPtr)
			{
			    ulBackslashCount++;
			}
			pTmpPtr++;
		    }
		    // /Note: cbData includes extra for null-terminator, but
		    // we'll just put an extra "+1" on there just to be sure.
		    // Also, add terminating backslashes so filename can be
		    // appended on directly: 
		    m_pFontDirectory = new char[cbData + ulBackslashCount+2 +
			    TXTSVCS_MAX_FONT_NOPATH_FILENAME +1];
		    if (m_pFontDirectory)
		    {
			pTmpPtr = (const char*)pData;
			char* pTmpFntDir = m_pFontDirectory;
			while (*pTmpPtr)
			{
			    *pTmpFntDir = *pTmpPtr;
			    if ('\\' == *pTmpPtr)
			    {
				// /Double each backslash:
				*(++pTmpFntDir) = *pTmpPtr;
			    }
			    pTmpPtr++; pTmpFntDir++;
			}
			// /Now add final backslash(es):
			*pTmpFntDir = '\\';
			*(++pTmpFntDir) = '\\';
			*(++pTmpFntDir) = '\0'; // /Null-terminate it.
			m_ulStrLenFontDir = pTmpFntDir - m_pFontDirectory;
		    }
		    else
		    {
			hxrslt = HXR_OUTOFMEMORY;
			goto cleanup;
		    }
		}
		::RegCloseKey(hKey);
	    }
	}
#elif defined(_MACINTOSH)
#pragma message("####### Need to find where font files are stored by this OS")
#elif defined(_UNIX)
#pragma message("####### Need to find where font files are stored by this OS")
#else
#pragma message("####### Need to find where font files are stored by this OS")
#endif //if _WINDOWS / elif...//

	if (!m_pFontDirectory  ||  !m_ulStrLenFontDir)
	{
	    HX_ASSERT(m_pFontDirectory  &&  m_ulStrLenFontDir);
	    hxrslt = HXR_INVALID_PATH;
	    goto cleanup;
	}

	char* pszFontFilename = m_pFontDirectory;

	CHXString CHXStrFontFileName;
	hxrslt = getFontFileName(pszFontFamily, bIsBold, bIsItalicized,
		CHXStrFontFileName);
	if (HXR_OK != hxrslt)
	{
	    goto cleanup; // /Font is not installed.
	}

	// /This is an optimization that prevents us from having to allocate
	// new space for combining the path and the filename into one string.
	// The filename part is "removed" below by adding back in the old
	// NULL terminator:
	strcpy(&pszFontFilename[m_ulStrLenFontDir], /* Flawfinder: ignore */
		(const char*)CHXStrFontFileName);

	// /Load from file:
	ftError = FT_New_Face(m_hLibrary,
		pszFontFilename,
		0, // /This index tells which face to load from the font file.
		&m_hFace );

	pszFontFilename[m_ulStrLenFontDir] = '\0'; // /Restore to path only.
#endif /* END elif of ifdef(XXXEH_USE_CACHED_FONT_FACE_OBJECTS). */

	// /To get the # of faces a given font file contains, simply load its first
	// face (use face_index=0), then see the value of face->num_faces.  Each
	// face corresponds to something different, like "Arial Regular"
	// and "Arial Bold".
	/* FROM FreeType tutorial:
	The complete list of available fields in in the FT_FaceRec structure
	description. However, we'll describe here a few of them in more details:
	    - num_glyphs  Gives the number of glyphs available in the font face.
	    A glyph is simply a character image. It doesn't necessarily
	    correspond to a character code though.
	    - flags  A 32-bit integer containing bit flags used to describe some
	    face properties. For example, the flag FT_FACE_FLAG_SCALABLE is used
	    to indicate that the face's font format is scalable and that glyph
	    images can be rendered for all character pixel sizes. For more
	    information on face flags, please read the FreeType 2 API Reference.
	    - units_per_EM  This field is only valid for scalable formats (it is
	    set to 0 otherwise). It indicates the number of font units covered by
	    the EM.
	    - num_fixed_sizes  This field gives the number of embedded bitmap
	    strikes in the current face. A strike is simply a series of glyph
	    images for a given character pixel size. For example, a font face
	    could include strikes for pixel sizes 10, 12 and 14. Note that even
	    scalable font formats can have embedded bitmap strikes.
	    - fixed_sizes  this is a pointer to an array of FT_Bitmap_Size
	    elements. Each FT_Bitmap_Size indicates the horizontal and vertical
	    character pixel sizes for each of the strikes that are present in
	    the face.
	    Note that, generally speaking, these are not the cell size of the bitmap strikes
	 */

	if (FT_Err_Unknown_File_Format == ftError )
	{
	    hxrslt = HXR_INVALID_VERSION;
	    goto cleanup;
	}
	else if (ftError)
	{
	    // /The font file could not be opened or read, or it is broken:
	    hxrslt = HXR_RESOURCE_BADFILE;
	    goto cleanup;
	}
    } // /END creating m_hFace
    


    // /NOTE: FT_Set_Char_Size() should be used to use true point sizes, and
    // FT_Set_Pixel_Sizes() when physical size of each pixel doesn't matter:
    ftError = FT_Set_Pixel_Sizes(
	    m_hFace,
	    ulAvgCharWidthPixels,   // /(0 means same as height)
	    ulCharHeightPixels);

    if (ftError)
    {
	// /Error setting size usually means fixed-size font format (e.g.,
	// FNT, PCF) that doesn't handle the requested size:
	// /XXXEH- TODO: look at m_hFace->fixed_sizes array and find closest
	// size.
	HX_ASSERT(0  &&  "size_not_available_in_requested_font");
    }

    // /XXXEH- this does UNICODE charmap by default, then Latin-1 if no
    // UNICODE charmap, then US-ASCII if no Latin-1 charmap.  We need to
    // default to Latin-1:
    // /This returns 0 if glyph not found; by convention, this always
    // corresponds to a special glyph image called the "missing glyph",
    // commonly displayed as a box or a space:
#if defined(XXXEH_HANDLE_CHARSETS_IN_FREETYPE)
    //There are two ways to select a different charmap with FreeType 2.
    // The easiest is when the encoding you need already has a corresponding
    // enumeration defined in FT_FREETYPE_H, as ft_encoding_big5. In this
    // case, simply call FT_Select_CharMap as in:
    error = FT_Select_CharMap(m_hFace,                 /* target face object */
	    ft_encoding_none);   /* encoding.  Pre-defined values are:
	    ft_encoding_symbol, ft_encoding_unicode, ft_encoding_latin_2,
	    ft_encoding_sjis, ft_encoding_gb2312, ft_encoding_big5,
	    ft_encoding_wansung (appears to be Hangeul AKA KSC5601),
	    ft_encoding_johab, ft_encoding_apple_roman
	    */

    for (lIndx = 0; lIndx < face->num_charmaps; lIndx++ )
    {
	charmap = face->charmaps[lIndx];
	if ( charmap->platform_id == my_platform_id &&
		charmap->encoding_id == my_encoding_id )
	{
	    found = charmap;
	    break;
	}
    }

    if (!bFound)
    {
#error: XXXEH- decide what to do here.
    }

    /* now, select the charmap for the face object */
    error = FT_Set_CharMap(m_hFace, bFound);
    if (ftError)
    {
#error: XXXEH- decide what to do here.
    }
#endif /* XXXEH_HANDLE_CHARSETS_IN_FREETYPE. */

#if defined(XXXEH_DO_GLYPH_TRANSFORMS)
    error = FT_Set_Transform(m_hFace,
	    &pMatrix, // /Ppointer to 2x2 FT_Matrix (members: xx, xy, yx, yy)
	    &pDelta); // /Pointer to 2-D FT_Vector  (members: x, y)*/
#endif /* XXXEH_DO_GLYPH_TRANSFORMS */

    for (lIndx=0; lIndx < lStringlen; lIndx++ )
    {
#if 1
	ftError = FT_Load_Char(m_hFace, pszString[lIndx],
		// /FT_LOAD_RENDER indicates that the glyph image be
		// immediately converted to an anti-aliased bitmap, a shortcut
		// that avoids calling FT_Render_Glyph() explicitly:
		FT_LOAD_RENDER | (bDoAntialias? 0 : FT_LOAD_MONOCHROME));
#else /*XXXEH- remove this block as soon as the above is tested: */
	// /The following sequence of calls to FT_Load_Glyph()
	// FT_Get_Char_Index() and FT_Render_Glyph() can be consolidated into
	// one call to FT_Load_Char():
	// retrieve glyph index from character code
	FT_UInt glyph_index = FT_Get_Char_Index(m_hFace,
		// /XXXEH- need to handle DBCS & UNICODE indexing here:
		pszString[lIndx]);

	// load glyph image into the slot (erase previous one)
	ftError = FT_Load_Glyph(m_hFace, glyph_index, FT_LOAD_DEFAULT);
	if (ftError)
	{
	    continue;  // /XXXEH- handle errors or ignore?
	}

	// convert to an anti-aliased bitmap
	ftError = FT_Render_Glyph(m_hFace->glyph, ftRenderMode); // /ft_render_mode_normal);
#endif /* one call to FT_Load_Char() VS. calls to FT_Get_Char(), FT_Load_Glyph(), FT_Render_Glyph */
	if (ftError)
	{
	    HX_ASSERT(0); // /XXXEH- debug this.
	    continue; 
	}

#if defined(XXXEH_TESTING)
	// /This is the distance above the origin (baseline) for horizontal-
	// layout text.  (>>6 because it's in 64ths):
	LONG32 lBearingY = m_hFace->glyph->metrics.horiBearingY >> 6;
	HX_ASSERT(lBearingY == m_hFace->glyph->bitmap_top);
#endif /*END (_DEBUG)*/

	// now, draw to our overall offscreen area:
	bltToBitmapFromBitmap(pOffscreenBitmap,
		lOffscreenWidth, lOffscreenHeight, lOffscreenDepth,
		lCurPenX + m_hFace->glyph->bitmap_left, // /X: left to right
		lCurPenY +     // /Y: bottom to top
		// /Fixes CROSSPLAT-TEXT-BUG-0001:
		(ulCharHeightPixels-m_hFace->glyph->bitmap_top),
		&(m_hFace->glyph->bitmap),
		ulARGBforegroundColor);
     
	// increment pen position 
	lCurPenX += m_hFace->glyph->advance.x >> 6; // />>6 because it's in 64ths
	// Freetype demo's cryptic comment for this is: "unuseful for now..":
	lCurPenY += m_hFace->glyph->advance.y >> 6;
    }

cleanup:

    return hxrslt;
}


/////////////////////////////////////////////////////////////////////////////
HX_RESULT
CTextServices::getFontFileName(const char* pszFontFamily,
		BOOL bIsBold,
		BOOL bIsItalicized,
		CHXString& CHXStrFontFileName)
{
    HX_RESULT hxrslt = HXR_RESOURCE_NOT_FOUND;

    BOOL bUse_bd_insteadOf_b = FALSE;
    BOOL bFontHasBoldVer = TRUE;
    BOOL bFontHasItalicsVer = TRUE;

    CHXStrFontFileName = "times.ttf";
    
    if (!pszFontFamily)
    {
	HX_ASSERT(pszFontFamily);
	hxrslt = HXR_INVALID_PARAMETER;
	goto cleanup;
    }

#if defined(_MACINTOSH)  ||  defined(_UNIX)
#pragma message("####### XXXEH- need to TEST getFontFileName() on UNIX and MAC")
#endif    
    

    // /XXXEH- need to get font file names as mapped to font names in the
    // Windows registry (and in ?? on Mac and UNIX):
    // HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts
    // For now, just map based on initial installed font file names.  If they
    // get changed on a system, however, the following will then fail.
    
    // /Default to Times New Roman (but still return HXR_FAIL if requested font
    // is not found):
    if (!stricmp(pszFontFamily, "times new roman")  ||
	    !stricmp(pszFontFamily, "times"))
    {
	hxrslt = HXR_OK;
	CHXStrFontFileName = "times";
	bUse_bd_insteadOf_b = TRUE;
    }
    else if (!strnicmp(pszFontFamily, "arial", 5))
    {
	hxrslt = HXR_OK;
	CHXStrFontFileName = "arial";
	bUse_bd_insteadOf_b = TRUE;
	if (!strnicmp(pszFontFamily+5, " narrow", 5))
	{
	    CHXStrFontFileName += "N";
	    bUse_bd_insteadOf_b = FALSE;
	}
	// /XXXEH- should handle arial black & arial black italic
	// which are named "AriBlk.TTF" & "ARBLI___.TTF".
    }
    else if (!strnicmp(pszFontFamily, "courier", 7))
    {
	hxrslt = HXR_OK;
	CHXStrFontFileName = "COUR";
	if (!strnicmp(pszFontFamily+7, " new", 5))
	{
	    bUse_bd_insteadOf_b = TRUE;
	}
	else // /cour
	{
	    CHXStrFontFileName += "E";
	}
	// /XXXEH- should handle arial black & arial black italic
	// which are installed as "AriBlk.TTF" & "ARBLI___.TTF" on Windows.
    }
    else if (!strnicmp(pszFontFamily, "osaka", 7))
    {
	hxrslt = HXR_OK;
	CHXStrFontFileName = "arialuni.ttf"; // /XXXEH!
	// /XXXEH- there is no bold or italics version of the mincho font, so
	// ignore these (until we find a better system for dealing with this):
	bFontHasBoldVer = bFontHasItalicsVer = FALSE;
	goto cleanup;
    }
    // /XXXEH- TODO: add others here.

    if (bIsBold  &&  bFontHasBoldVer)
    {
	if (bIsItalicized  &&  bFontHasItalicsVer)
	{
	    CHXStrFontFileName += "bi";
	}
	else
	{
	    CHXStrFontFileName += "b";
	    if (bUse_bd_insteadOf_b)
	    {
		CHXStrFontFileName += "d";
	    }
	}
    }
    else if (bIsItalicized  &&  bFontHasItalicsVer)
    {
	CHXStrFontFileName += "i";
    }
    CHXStrFontFileName += ".ttf";

cleanup:
    return hxrslt;
}


