/*++

Copyright (c) 1990  Microsoft Corporation

Module Name:

    misc.c

Abstract:

        This file implements the NT console server font routines.

Author:

    Therese Stowell (thereses) 22-Jan-1991

Revision History:

--*/

#include "precomp.h"
#pragma hdrstop

#ifdef DEBUG_PRINT
ULONG gDebugFlag = 0 ;
// ULONG gDebugFlag = _DBGOUTPUT | _DBGCHARS | _DBGFONTS | _DBGFONTS2 ;
#endif

PFONT_INFO FontInfo = NULL;
ULONG FontInfoLength;
ULONG NumberOfFonts;
BOOL gbEnumerateFaces = FALSE;


#define FE_ABANDONFONT 0
#define FE_SKIPFONT    1
#define FE_FONTOK      2

/*
 * Initial default fonts and face names
 */
PFACENODE gpFaceNames = NULL;

/*
 * TTPoints -- Initial font pixel heights for TT fonts
 */
SHORT TTPoints[] = {
    5, 6, 7, 8, 10, 12, 14, 16, 18, 20, 24, 28, 36, 72
};
#if defined(FE_SB)
/*
 * TTPointsDbcs -- Initial font pixel heights for TT fonts of DBCS.
 * So, This list except odd point size because font width is (SBCS:DBCS != 1:2).
 */
SHORT TTPointsDbcs[] = {
    6, 8, 10, 12, 14, 16, 18, 20, 24, 28, 36, 72
};
#endif


typedef struct _FONTENUMDATA {
    HDC hDC;
    BOOL bFindFaces;
    ULONG ulFE;
    PSHORT pTTPoints;
    UINT nTTPoints;
} FONTENUMDATA, *PFONTENUMDATA;


PFACENODE
AddFaceNode(PFACENODE *ppStart, LPTSTR ptsz) {
    PFACENODE pNew;
    PFACENODE *ppTmp;
    int cb;

    /*
     * Is it already here?
     */
    for (ppTmp = ppStart; *ppTmp; ppTmp = &((*ppTmp)->pNext)) {
        if (_tcscmp(((*ppTmp)->atch), ptsz) == 0) {
            // already there !
            return *ppTmp;
        }
    }

    cb = (_tcslen(ptsz) + 1) * sizeof(TCHAR);
    pNew = (PFACENODE)HeapAlloc(RtlProcessHeap(),0,sizeof(FACENODE) + cb);
    if (pNew == NULL) {
        return NULL;
    }

    pNew->pNext = NULL;
    pNew->dwFlag = 0;
    _tcscpy(pNew->atch, ptsz);
    *ppTmp = pNew;
    return pNew;
}


VOID
DestroyFaceNodes( VOID ) {
    PFACENODE pNext;
    PFACENODE pTmp;

    pTmp = gpFaceNames;
    while (pTmp != NULL) {
        pNext = pTmp->pNext;
        HeapFree(RtlProcessHeap(), 0, pTmp);
        pTmp = pNext;
    }
    gpFaceNames = NULL;
}


int
AddFont(
    ENUMLOGFONT *pelf,
    NEWTEXTMETRIC *pntm,
    int nFontType,
    HDC hDC,
    PFACENODE pFN
    )

/*++

    Add the font desribed by the LOGFONT structure to the font table if
    it's not already there.

--*/

{
    HFONT hFont;
    TEXTMETRIC tm;
    LONG nFont;
    COORD SizeToShow;
    COORD SizeActual;
    COORD SizeWant;
    BYTE tmFamily;
    SIZE Size;
    LPTSTR ptszFace = pelf->elfLogFont.lfFaceName;

    /* get font info */
    SizeWant.X = (SHORT)pelf->elfLogFont.lfWidth;
    SizeWant.Y = (SHORT)pelf->elfLogFont.lfHeight;

CreateBoldFont:
    pelf->elfLogFont.lfQuality = DEFAULT_QUALITY;
    hFont = CreateFontIndirect(&pelf->elfLogFont);
    if (!hFont) {
        DBGFONTS(("    REJECT  font (can't create)\n"));
        return FE_SKIPFONT;  // same font in other sizes may still be suitable
    }

    DBGFONTS2(("    hFont = %lx\n", hFont));

    //
    // BUGBUG
    // For reasons unbeknownst to me, removing this code causes GDI
    // to yack, claiming that the font is owned by another process.
    //

    SelectObject(hDC, hFont);
    GetTextMetrics(hDC, &tm);

    GetTextExtentPoint32(hDC, TEXT("0"), 1, &Size);
    SizeActual.X = (SHORT)Size.cx;
    SizeActual.Y = (SHORT)(tm.tmHeight + tm.tmExternalLeading);
    DBGFONTS2(("    actual size %d,%d\n", SizeActual.X, SizeActual.Y));
    tmFamily = tm.tmPitchAndFamily;
    if (TM_IS_TT_FONT(tmFamily) && (SizeWant.Y >= 0)) {
        SizeToShow = SizeWant;
        if (SizeWant.X == 0) {
            // Asking for zero width height gets a default aspect-ratio width.
            // It's better to show that width rather than 0.
            SizeToShow.X = SizeActual.X;
        }
    } else {
        SizeToShow = SizeActual;
    }

    //
    // The size shouldn't be zero. This is to help catch Windows Bug #332453.
    //
    ASSERT(SizeActual.X != 0 && SizeActual.Y != 0 && "If you hit this please e-mail jasonsch");

    DBGFONTS2(("    SizeToShow = (%d,%d), SizeActual = (%d,%d)\n",
            SizeToShow.X, SizeToShow.Y, SizeActual.X, SizeActual.Y));

    //
    // BUGBUG
    // There's a GDI bug - this assert fails occasionally.
    //ASSERT (tm.tmMaxCharWidth == pntm->tmMaxCharWidth);

    /*
     * NOW, determine whether this font entry has already been cached
     * LATER : it may be possible to do this before creating the font, if
     * we can trust the dimensions & other info from pntm.
     * Sort by size:
     *  1) By pixelheight (negative Y values)
     *  2) By height (as shown)
     *  3) By width (as shown)
     */
    for (nFont = 0; nFont < (LONG)NumberOfFonts; ++nFont) {
        COORD SizeShown;

        if (FontInfo[nFont].hFont == NULL) {
            DBGFONTS(("!   Font %x has a NULL hFont\n", nFont));
            continue;
        }

        if (FontInfo[nFont].SizeWant.X > 0) {
            SizeShown.X = FontInfo[nFont].SizeWant.X;
        } else {
            SizeShown.X = FontInfo[nFont].Size.X;
        }

        if (FontInfo[nFont].SizeWant.Y > 0) {
            // This is a font specified by cell height.
            SizeShown.Y = FontInfo[nFont].SizeWant.Y;
        } else {
            SizeShown.Y = FontInfo[nFont].Size.Y;
            if (FontInfo[nFont].SizeWant.Y < 0) {
                // This is a TT font specified by character height.
                if (SizeWant.Y < 0 && SizeWant.Y > FontInfo[nFont].SizeWant.Y) {
                    // Requested pixelheight is smaller than this one.
                    DBGFONTS(("INSERT %d pt at %x, before %d pt\n",
                            -SizeWant.Y, nFont, -FontInfo[nFont].SizeWant.Y));
                    break;
                }
            }
        }

        // DBGFONTS(("    SizeShown(%x) = (%d,%d)\n",nFont,SizeShown.X,SizeShown.Y));

        if (SIZE_EQUAL(SizeShown, SizeToShow) &&
                FontInfo[nFont].Family == tmFamily &&
                FontInfo[nFont].Weight == tm.tmWeight &&
                _tcscmp(FontInfo[nFont].FaceName, ptszFace) == 0) {
            /*
             * Already have this font
             */
            DBGFONTS2(("    Already have the font\n"));
            DeleteObject(hFont);
            return FE_FONTOK;
        }


        if ((SizeToShow.Y < SizeShown.Y) ||
                (SizeToShow.Y == SizeShown.Y && SizeToShow.X < SizeShown.X)) {
            /*
             * This new font is smaller than nFont
             */
            DBGFONTS(("INSERT at %x, SizeToShow = (%d,%d)\n", nFont,
                    SizeToShow.X,SizeToShow.Y));
            break;
        }
    }

    /*
     * If we have to grow our font table, do it
     */
    if (NumberOfFonts == FontInfoLength) {
        PFONT_INFO Temp;

        FontInfoLength += FONT_INCREMENT;
        Temp = (PFONT_INFO)HeapReAlloc(RtlProcessHeap(), 0, FontInfo,
                                       sizeof(FONT_INFO) * FontInfoLength);
        if (Temp == NULL) {
            FontInfoLength -= FONT_INCREMENT;
            return FE_ABANDONFONT;  // no point enumerating more - no memory!
        }
        FontInfo = Temp;
    }

    /*
     * The font we are adding should be inserted into the list,
     * if it is smaller than the last one.
     */
    if (nFont < (LONG)NumberOfFonts) {
        RtlMoveMemory(&FontInfo[nFont+1],
                      &FontInfo[nFont],
                      sizeof(FONT_INFO) * (NumberOfFonts - nFont));
    }

    /*
     * Store the font info
     */
    FontInfo[nFont].hFont = hFont;
    FontInfo[nFont].Family = tmFamily;
    FontInfo[nFont].Size = SizeActual;
    if (TM_IS_TT_FONT(tmFamily)) {
        FontInfo[nFont].SizeWant = SizeWant;
    } else {
        FontInfo[nFont].SizeWant.X = 0;
        FontInfo[nFont].SizeWant.Y = 0;
    }
    FontInfo[nFont].Weight = tm.tmWeight;
    FontInfo[nFont].FaceName = pFN->atch;
#if defined(FE_SB)
    FontInfo[nFont].tmCharSet = tm.tmCharSet;
#endif

    ++NumberOfFonts;

    /*
     * If this is a true type font, create a bold version too.
     */
    if (nFontType == TRUETYPE_FONTTYPE && !IS_BOLD(FontInfo[nFont].Weight)) {
          pelf->elfLogFont.lfWeight = FW_BOLD;
          goto CreateBoldFont;
    }

    return FE_FONTOK;  // and continue enumeration
}


VOID
InitializeFonts( VOID )
{
    EnumerateFonts(EF_DEFFACE);  // Just the Default font
}


VOID
DestroyFonts( VOID )
{
    ULONG FontIndex;

    if (FontInfo != NULL) {
        for (FontIndex = 0; FontIndex < NumberOfFonts; FontIndex++) {
            DeleteObject(FontInfo[FontIndex].hFont);
        }
        HeapFree(RtlProcessHeap(), 0, FontInfo);
        FontInfo = NULL;
        NumberOfFonts = 0;
    }

    DestroyFaceNodes();
}


/*
 * Returns bit combination
 *  FE_ABANDONFONT  - do not continue enumerating this font
 *  FE_SKIPFONT     - skip this font but keep enumerating
 *  FE_FONTOK       - font was created and added to cache or already there
 */
int
FontEnum(
    ENUMLOGFONT *pelf,
    NEWTEXTMETRIC *pntm,
    int nFontType,
    PFONTENUMDATA pfed
    )

/*++

    Is called exactly once by GDI for each font in the system.  This
    routine is used to store the FONT_INFO structure.

--*/

{
    UINT i;
    LPTSTR ptszFace = pelf->elfLogFont.lfFaceName;
    PFACENODE pFN;

    DBGFONTS(("  FontEnum \"%ls\" (%d,%d) weight 0x%lx(%d) %x -- %s\n",
            ptszFace,
            pelf->elfLogFont.lfWidth, pelf->elfLogFont.lfHeight,
            pelf->elfLogFont.lfWeight, pelf->elfLogFont.lfWeight,
            pelf->elfLogFont.lfCharSet,
            pfed->bFindFaces ? "Finding Faces" : "Creating Fonts"));

    //
    // reject variable width and italic fonts, also tt fonts with neg ac
    //


    if
    (
      !(pelf->elfLogFont.lfPitchAndFamily & FIXED_PITCH) ||
      (pelf->elfLogFont.lfItalic)                        ||
      !(pntm->ntmFlags & NTM_NONNEGATIVE_AC)
    )
    {
        if (! IsAvailableTTFont(ptszFace)) {
            DBGFONTS(("    REJECT  face (dbcs, variable pitch, italic, or neg a&c)\n"));
            return pfed->bFindFaces ? FE_SKIPFONT : FE_ABANDONFONT;
        }
    }

    /*
     * reject TT fonts for whoom family is not modern, that is do not use
     * FF_DONTCARE    // may be surprised unpleasantly
     * FF_DECORATIVE  // likely to be symbol fonts
     * FF_SCRIPT      // cursive, inappropriate for console
     * FF_SWISS OR FF_ROMAN // variable pitch
     */

    if ((nFontType == TRUETYPE_FONTTYPE) &&
            ((pelf->elfLogFont.lfPitchAndFamily & 0xf0) != FF_MODERN)) {
        DBGFONTS(("    REJECT  face (TT but not FF_MODERN)\n"));
        return pfed->bFindFaces ? FE_SKIPFONT : FE_ABANDONFONT;
    }

    /*
     * reject non-TT fonts that aren't OEM
     */
    if ((nFontType != TRUETYPE_FONTTYPE) &&
#if defined(FE_SB)
            (!gfFESystem || !IS_ANY_DBCS_CHARSET(pelf->elfLogFont.lfCharSet)) &&
#endif
            (pelf->elfLogFont.lfCharSet != OEM_CHARSET)) {
        DBGFONTS(("    REJECT  face (not TT nor OEM)\n"));
        return pfed->bFindFaces ? FE_SKIPFONT : FE_ABANDONFONT;
    }

    /*
     * reject non-TT fonts that are virtical font
     */
    if ((nFontType != TRUETYPE_FONTTYPE) &&
            (ptszFace[0] == TEXT('@'))) {
        DBGFONTS(("    REJECT  face (not TT and TATEGAKI)\n"));
        return pfed->bFindFaces ? FE_SKIPFONT : FE_ABANDONFONT;
    }

    /*
     * reject non-TT fonts that aren't Terminal
     */
    if (gfFESystem && (nFontType != TRUETYPE_FONTTYPE) &&
            (_tcscmp(ptszFace, TEXT("Terminal")) != 0)) {
        DBGFONTS(("    REJECT  face (not TT nor Terminal)\n"));
        return pfed->bFindFaces ? FE_SKIPFONT : FE_ABANDONFONT;
    }

    /*
     * reject Far East TT fonts that aren't Far East charset.
     */
    if (IsAvailableTTFont(ptszFace) &&
        !IS_ANY_DBCS_CHARSET(pelf->elfLogFont.lfCharSet) &&
        !IsAvailableTTFontCP(ptszFace,0)
       ) {
        DBGFONTS(("    REJECT  face (Far East TT and not Far East charset)\n"));
        return FE_SKIPFONT;    // should be enumerate next charset.
    }

    /*
     * Add or find the facename
     */
    pFN = AddFaceNode(&gpFaceNames, ptszFace);
    if (pFN == NULL) {
        return FE_ABANDONFONT;
    }

    if (pfed->bFindFaces) {
        DWORD dwFontType;
        if (nFontType == TRUETYPE_FONTTYPE) {
            DBGFONTS(("NEW TT FACE %ls\n", ptszFace));
            dwFontType = EF_TTFONT;
        } else if (nFontType == RASTER_FONTTYPE) {
            DBGFONTS(("NEW OEM FACE %ls\n",ptszFace));
            dwFontType = EF_OEMFONT;
        }
        pFN->dwFlag |= dwFontType | EF_NEW;
#if defined(FE_SB)
        if (IS_ANY_DBCS_CHARSET(pelf->elfLogFont.lfCharSet))
            pFN->dwFlag |= EF_DBCSFONT;
#endif
        return FE_SKIPFONT;
    }


    if (IS_BOLD(pelf->elfLogFont.lfWeight)) {
        DBGFONTS2(("    A bold font (weight %d)\n", pelf->elfLogFont.lfWeight));
        // return FE_SKIPFONT;
    }

    /*
     * Add the font to the table. If this is a true type font, add the
     * sizes from the array. Otherwise, just add the size we got.
     */
    if (nFontType & TRUETYPE_FONTTYPE) {
        for (i = 0; i < pfed->nTTPoints; i++) {
            pelf->elfLogFont.lfHeight = pfed->pTTPoints[i];
            pelf->elfLogFont.lfWidth  = 0;
            pelf->elfLogFont.lfWeight = 400;
            pfed->ulFE |= AddFont(pelf, pntm, nFontType, pfed->hDC, pFN);
            if (pfed->ulFE & FE_ABANDONFONT) {
                return FE_ABANDONFONT;
            }
        }
    } else {
            pfed->ulFE |= AddFont(pelf, pntm, nFontType, pfed->hDC, pFN);
            if (pfed->ulFE & FE_ABANDONFONT) {
                return FE_ABANDONFONT;
            }
    }

    return FE_FONTOK;  // and continue enumeration
}

BOOL
DoFontEnum(
    HDC hDC,
    LPTSTR ptszFace,
    PSHORT pTTPoints,
    UINT nTTPoints)
{
    BOOL bDeleteDC = FALSE;
    FONTENUMDATA fed;
    LOGFONT LogFont;

    DBGFONTS(("DoFontEnum \"%ls\"\n", ptszFace));
    if (hDC == NULL) {
        hDC = CreateDC(TEXT("DISPLAY"), NULL, NULL, NULL);
        bDeleteDC = TRUE;
    }

    fed.hDC = hDC;
    fed.bFindFaces = (ptszFace == NULL);
    fed.ulFE = 0;
    fed.pTTPoints = pTTPoints;
    fed.nTTPoints = nTTPoints;
    RtlZeroMemory(&LogFont, sizeof(LOGFONT));
    LogFont.lfCharSet = DEFAULT_CHARSET;
    if (ptszFace)
        _tcscpy(LogFont.lfFaceName, ptszFace);
    /*
     * EnumFontFamiliesEx function enumerates one font in every face in every character set. 
     */
    EnumFontFamiliesEx(hDC, &LogFont, (FONTENUMPROC)FontEnum, (LPARAM)&fed, 0);
    if (bDeleteDC) {
        DeleteDC(hDC);
    }
    return (fed.ulFE & FE_FONTOK) != 0;
}


VOID
RemoveFace(LPTSTR ptszFace)
{
    DWORD i;
    int nToRemove = 0;

    DBGFONTS(("RemoveFace %ls\n", ptszFace));
    //
    // Delete & Remove fonts with Face Name == ptszFace
    //
    for (i = 0; i < NumberOfFonts; i++) {
        if (_tcscmp(FontInfo[i].FaceName, ptszFace) == 0) {
            BOOL bDeleted = DeleteObject(FontInfo[i].hFont);
            DBGPRINT(("RemoveFace: hFont %lx was %sdeleted\n",
                    FontInfo[i].hFont, bDeleted ? "" : "NOT "));
            FontInfo[i].hFont = NULL;
            nToRemove++;
        } else if (nToRemove > 0) {
            /*
             * Shuffle from FontInfo[i] down nToRemove slots.
             */
            RtlMoveMemory(&FontInfo[i - nToRemove],
                    &FontInfo[i],
                    sizeof(FONT_INFO)*(NumberOfFonts - i));
            NumberOfFonts -= nToRemove;
            i -= nToRemove;
            nToRemove = 0;
        }
    }
    NumberOfFonts -= nToRemove;
}

TCHAR DefaultFaceName[LF_FACESIZE];
COORD DefaultFontSize;
BYTE  DefaultFontFamily;
ULONG DefaultFontIndex = 0;
ULONG CurrentFontIndex = 0;

NTSTATUS
EnumerateFonts(
    DWORD Flags)
{
    TEXTMETRIC tm;
    HDC hDC;
    PFACENODE pFN;
    ULONG ulOldEnumFilter;
    BOOL  bEnumOEMFace = TRUE;
    DWORD FontIndex;
    DWORD dwFontType = 0;

    DBGFONTS(("EnumerateFonts %lx\n", Flags));

    dwFontType = (EF_TTFONT|EF_OEMFONT|EF_DEFFACE) & Flags;

    if (FontInfo == NULL) {
        //
        // allocate memory for the font array
        //
        NumberOfFonts = 0;

        FontInfo = (PFONT_INFO)HeapAlloc(RtlProcessHeap(),0,sizeof(FONT_INFO) * INITIAL_FONTS);
        if (FontInfo == NULL)
            return STATUS_NO_MEMORY;
        FontInfoLength = INITIAL_FONTS;
    }

    hDC = CreateDC(TEXT("DISPLAY"), NULL, NULL, NULL);

    // Before enumeration, turn off font enumeration filters.
    ulOldEnumFilter = SetFontEnumeration(0);
    SetFontEnumeration(ulOldEnumFilter & ~FE_FILTER_TRUETYPE);

    if (Flags & EF_DEFFACE) {
        SelectObject(hDC, GetStockObject(OEM_FIXED_FONT));
        GetTextMetrics(hDC, &tm);
        GetTextFace(hDC, LF_FACESIZE, DefaultFaceName);

        DefaultFontSize.X = (SHORT)(tm.tmMaxCharWidth);
        DefaultFontSize.Y = (SHORT)(tm.tmHeight+tm.tmExternalLeading);
        DefaultFontFamily = tm.tmPitchAndFamily;
#if !defined(FE_SB)
        DBGFONTS(("Default (OEM) Font %ls (%d,%d)\n", DefaultFaceName,
                DefaultFontSize.X, DefaultFontSize.Y));
#else
        if (IS_ANY_DBCS_CHARSET(tm.tmCharSet))
            DefaultFontSize.X /= 2;
        DBGFONTS(("Default (OEM) Font %ls (%d,%d) CharSet 0x%02X\n", DefaultFaceName,
                DefaultFontSize.X, DefaultFontSize.Y,
                tm.tmCharSet));
#endif

        // Make sure we are going to enumerate the OEM face.
        pFN = AddFaceNode(&gpFaceNames, DefaultFaceName);
        if (pFN)
            pFN->dwFlag |= EF_DEFFACE | EF_OEMFONT;
    }

    if (gbEnumerateFaces) {
        /*
         * Set the EF_OLD bit and clear the EF_NEW bit
         * for all previously available faces
         */
        for (pFN = gpFaceNames; pFN; pFN = pFN->pNext) {
            pFN->dwFlag |= EF_OLD;
            pFN->dwFlag &= ~EF_NEW;
        }

        //
        // Use DoFontEnum to get the names of all the suitable Faces
        // All facenames found will be put in gpFaceNames with
        // the EF_NEW bit set.
        //
        DoFontEnum(hDC, NULL, TTPoints, 1);
        gbEnumerateFaces = FALSE;
    }

    // Use DoFontEnum to get all fonts from the system.  Our FontEnum
    // proc puts just the ones we want into an array
    //
    for (pFN = gpFaceNames; pFN; pFN = pFN->pNext) {
        DBGFONTS(("\"%ls\" is %s%s%s%s%s%s\n", pFN->atch,
            pFN->dwFlag & EF_NEW        ? "NEW "        : " ",
            pFN->dwFlag & EF_OLD        ? "OLD "        : " ",
            pFN->dwFlag & EF_ENUMERATED ? "ENUMERATED " : " ",
            pFN->dwFlag & EF_OEMFONT    ? "OEMFONT "    : " ",
            pFN->dwFlag & EF_TTFONT     ? "TTFONT "     : " ",
            pFN->dwFlag & EF_DEFFACE    ? "DEFFACE "    : " "));

        if ((pFN->dwFlag & (EF_OLD|EF_NEW)) == EF_OLD) {
            // The face is no longer available
            RemoveFace(pFN->atch);
            pFN->dwFlag &= ~EF_ENUMERATED;
            continue;
        }
        if ((pFN->dwFlag & dwFontType) == 0) {
            // not the kind of face we want
            continue;
        }
        if (pFN->dwFlag & EF_ENUMERATED) {
            // we already enumerated this face
            continue;
        }

        if (pFN->dwFlag & EF_TTFONT) {
#if defined(FE_SB)
            if (gfFESystem && !IsAvailableTTFontCP(pFN->atch,0))
                DoFontEnum(hDC, pFN->atch, TTPointsDbcs, NELEM(TTPointsDbcs));
            else
#endif
                DoFontEnum(hDC, pFN->atch, TTPoints, NELEM(TTPoints));
        } else {
            DoFontEnum(hDC, pFN->atch, NULL, 0);

            // If we find that the face just enumerated is the same as OEM,
            // reset flag so we don't try to enumerate it again.

            if (!_tcsncmp(pFN->atch, DefaultFaceName, LF_FACESIZE)) {
                bEnumOEMFace = FALSE;
            }
        }
        pFN->dwFlag |= EF_ENUMERATED;
    }


    // After enumerating fonts, restore the font enumeration filter.
    SetFontEnumeration(ulOldEnumFilter);

    DeleteDC(hDC);

#if defined(FE_SB)
    if (gfFESystem )
    {
        for (FontIndex = 0; FontIndex < NumberOfFonts; FontIndex++) {
            if (FontInfo[FontIndex].Size.X == DefaultFontSize.X &&
                FontInfo[FontIndex].Size.Y == DefaultFontSize.Y &&
                IS_ANY_DBCS_CHARSET(FontInfo[FontIndex].tmCharSet) &&
                FontInfo[FontIndex].Family == DefaultFontFamily) {
                break;
            }
        }
    }
    else
    {
#endif
    for (FontIndex = 0; FontIndex < NumberOfFonts; FontIndex++) {
        if (FontInfo[FontIndex].Size.X == DefaultFontSize.X &&
            FontInfo[FontIndex].Size.Y == DefaultFontSize.Y &&
            FontInfo[FontIndex].Family == DefaultFontFamily) {
            break;
        }
    }
#if defined(FE_SB)
    }
#endif
    ASSERT(FontIndex < NumberOfFonts);
    if (FontIndex < NumberOfFonts) {
        DefaultFontIndex = FontIndex;
    } else {
        DefaultFontIndex = 0;
    }
    DBGFONTS(("EnumerateFonts : DefaultFontIndex = %ld\n", DefaultFontIndex));

    return STATUS_SUCCESS;
}