#include "precomp.h"


//
// FH.CPP
// Font Handling
//
// Copyright(c) Microsoft 1997-
//

#define MLZ_FILE_ZONE  ZONE_CORE



//
// FH_Init()
//
// This routine allocates a structure for the local font list, then fills
// it in.  It returns the number of local fonts in the list, or zero if
// something went wrong
//
UINT FH_Init(void)
{
    UINT    cFonts = 0;

    DebugEntry(FH_Init);

    //
    // Create the font array and the font index array
    //
    g_fhFonts = new FHLOCALFONTS;
    if (!g_fhFonts)
    {
        ERROR_OUT(("FH_Init: couldn't allocate g_fhFonts local list"));
        DC_QUIT;
    }

    ZeroMemory(g_fhFonts, sizeof(FHLOCALFONTS));
    SET_STAMP(g_fhFonts, FHLOCALFONTS);

    //
    // Now we consider the fonts individually, and store all acceptable
    // ones in the local font list.
    //
    FHConsiderAllLocalFonts();

    cFonts = g_fhFonts->fhNumFonts;
    if (cFonts)
    {
        FHSortAndIndexLocalFonts();
    }
    else
    {
        WARNING_OUT(( "No fonts found - this seems unlikely"));
    }

DC_EXIT_POINT:
    DebugExitDWORD(FH_Init, cFonts);
    return(cFonts);
}


//
// FH_Term()
//
void FH_Term(void)
{
    DebugEntry(FH_Term);

    if (g_fhFonts)
    {
        delete g_fhFonts;
        g_fhFonts = NULL;
    }

    DebugExitVOID(FH_Term);
}


//
// FH_ReceivedPacket - see fh.h
//
void  ASShare::FH_ReceivedPacket
(
    ASPerson *      pasPerson,
    PS20DATAPACKET  pPacket
)
{
    PFHPACKET       pFontsPacket;
    UINT            iLocal;
    UINT            iRemote;
    LPNETWORKFONT   pRemoteFont;
    POEREMOTEFONT   pLocalFont;
    UINT            cbSize;

    DebugEntry(ASShare::FH_ReceivedPacket);

    ValidatePerson(pasPerson);

    pFontsPacket = (PFHPACKET)pPacket;

    //
    // If the number we received isn't the same as before, we need to
    // possibly free the previous font block, and then allocate a new one.
    //
    // Once we're in a share with this person, every new joiner will cause
    // existing members to resend their local fonts, usually the same size.
    // So we can optimize and not realloc in that case.
    //
    if (pFontsPacket->cFonts != pasPerson->oecFonts)
    {
        if (pasPerson->poeFontInfo)
        {
            delete[] pasPerson->poeFontInfo;
            pasPerson->poeFontInfo = NULL;
            pasPerson->oecFonts = 0;
        }
        else
        {
            ASSERT(!pasPerson->oecFonts);
        }

        //
        // Allocate a new block
        //
        pasPerson->poeFontInfo = new OEREMOTEFONT[pFontsPacket->cFonts];
        if (!pasPerson->poeFontInfo)
        {
            ERROR_OUT(("Couldn't allocate %d fonts for FH packet from [%d]",
                pasPerson->mcsID));
            DC_QUIT;
        }

        ZeroMemory(pasPerson->poeFontInfo, pFontsPacket->cFonts * sizeof(OEREMOTEFONT));
        pasPerson->oecFonts = pFontsPacket->cFonts;
    }


    TRACE_OUT(("Received %d remote fonts in packet from person [%d]",
        pasPerson->oecFonts, pasPerson->mcsID));

    //
    // Consider each remote font. The multibyte fields of the NETWORKFONT
    // structure are flipped as they are read; otherwise we would have to
    // duplicate the logic about which fields are present in which version.
    //

    //
    // The size of each font is in the packet.
    //
    cbSize      = pFontsPacket->cbFontSize;
    pRemoteFont = pFontsPacket->aFonts;
    pLocalFont  = pasPerson->poeFontInfo;

    for (iRemote = 0; iRemote < pasPerson->oecFonts; iRemote++, pLocalFont++)
    {
        //
        // Copy the fields we store directly.
        //
        pLocalFont->rfFontFlags     = pRemoteFont->nfFontFlags;
        pLocalFont->rfAveWidth      = pRemoteFont->nfAveWidth;
        pLocalFont->rfAveHeight     = pRemoteFont->nfAveHeight;
        pLocalFont->rfAspectX       = pRemoteFont->nfAspectX;
        pLocalFont->rfAspectY       = pRemoteFont->nfAspectY;

        //
        // And the R2.0 field(s)...
        //
        if (m_oeCombinedOrderCaps.capsfFonts & CAPS_FONT_CODEPAGE)
        {
            pLocalFont->rfCodePage = pRemoteFont->nfCodePage;
        }
        //
        // And the other R2.0 field(s)...
        //
        if (m_oeCombinedOrderCaps.capsfFonts & CAPS_FONT_R20_SIGNATURE)
        {
            pLocalFont->rfSigFats    = pRemoteFont->nfSigFats;
            pLocalFont->rfSigThins   = pRemoteFont->nfSigThins;
            pLocalFont->rfSigSymbol  = pRemoteFont->nfSigSymbol;
        }
        if (m_oeCombinedOrderCaps.capsfFonts & CAPS_FONT_EM_HEIGHT)
        {
            pLocalFont->rfMaxAscent  = pRemoteFont->nfMaxAscent;
            TRACE_OUT(( "maxAscent %hd", pLocalFont->rfMaxAscent));
        }

        //
        // Set up an initial remote to local handle mapping, by scanning
        // for the first local font with the remote font's facename.
        //
        // We first set a default match value, in case we dont find a true
        // match - this value should never be referenced since we never get
        // sent fonts that we can't match (because we sent details of our
        // fonts to remote systems, and they should be using the same, or a
        // compatible, font matching algorithm.
        //
        // The mapping we obtain here is to the first local font that has
        // the remote font's facename, which is probably not the correct
        // font (ie there are probably multiple fonts with the same
        // facename).  This initial mapping will be updated when we do the
        // full matching for all remote fonts.  (See FHConsiderRemoteFonts
        // for details), but is sufficient, as all we will use it for until
        // then, is to obtain the facename.
        //
        // This approach means that we do not have to store the remote
        // facename, which is a useful saving on remote font details space.
        //
        // SFR5279: cannot default to zero because that means we give a
        // name to fonts that do not in fact match at all, causing us to
        // always waste effort in FHConsiderRemoteFonts and sometimes to
        // wrongly match two fonts that do not really match at all.
        //
        pLocalFont->rfLocalHandle= NO_FONT_MATCH;

        for (iLocal = 0; iLocal < g_fhFonts->fhNumFonts; iLocal++)
        {
            if (!lstrcmp(g_fhFonts->afhFonts[iLocal].Details.nfFaceName,
                          pRemoteFont->nfFaceName))
            {
                pLocalFont->rfLocalHandle = (TSHR_UINT16)iLocal;
                break;
            }
        }

        //
        // Advance to the next remote font.
        //
        pRemoteFont = (LPNETWORKFONT)((LPBYTE)pRemoteFont + cbSize);
    }

DC_EXIT_POINT:
    //
    // We have a new set of fonts, so determine the common list.
    //
    FH_DetermineFontSupport();

    DebugExitVOID(ASShare::FH_ReceivedPacket);
}

//
// FH_SendLocalFontInfo()
//
void ASShare::FH_SendLocalFontInfo(void)
{
    PFHPACKET       pFontPacket = NULL;
    LPBYTE          pNetworkFonts;
    UINT            pktSize;
    UINT            iFont;
    BOOL            fSendFont;
    UINT            cDummyFonts = 0;
#ifdef _DEBUG
    UINT            sentSize;
#endif // _DEBUG

    DebugEntry(ASShare::FH_SendLocalFontInfo);

    ASSERT(!m_fhLocalInfoSent);

    //
    //
    // Look at the combined capability flags to see whether the remote(s)
    // can cope with our preferred font structure (R20) or a slightly
    // older one (R11) or only the original flavor (pre R11).
    //
    //
    if (!(m_oeCombinedOrderCaps.capsfFonts & CAPS_FONT_R20_TEST_FLAGS))
    {
        WARNING_OUT(("Remotes in share don't support CAPS_FONT_R20"));
        m_fhLocalInfoSent = TRUE;
        DC_QUIT;
    }

    pktSize = sizeof(FHPACKET) + (g_fhFonts->fhNumFonts - 1) * sizeof(NETWORKFONT);
    pFontPacket = (PFHPACKET)SC_AllocPkt(PROT_STR_MISC, g_s20BroadcastID, pktSize);
    if (!pFontPacket)
    {
        WARNING_OUT(("Failed to alloc FH packet, size %u", pktSize));
        DC_QUIT;
    }

    //
    // Packet successfully allocated.  Fill in the data and send it.
    //
    pFontPacket->header.data.dataType = DT_FH;

    pFontPacket->cbFontSize = sizeof(NETWORKFONT);

    //
    // Copy the fonts we want to send into the network packet.
    //
    pNetworkFonts = (LPBYTE)pFontPacket->aFonts;
    cDummyFonts = 0;
    for (iFont = 0 ; iFont < g_fhFonts->fhNumFonts ; iFont++)
    {
        //
        // Assume we will send this font.
        //
        fSendFont = TRUE;

        //
        // Check whether font is ANSI charset or font CodePage capability
        // is supported.  If neither, skip on to next local font.
        //
        TRACE_OUT(( "TEST CP set OK: font[%u] CodePage[%hu]", iFont,
                                g_fhFonts->afhFonts[iFont].Details.nfCodePage));

        if ((g_fhFonts->afhFonts[iFont].Details.nfCodePage != ANSI_CHARSET) &&
            (!(m_oeCombinedOrderCaps.capsfFonts & CAPS_FONT_CODEPAGE))   )
        {
            TRACE_OUT(( "Dont send font[%u] CodePage[%hu]", iFont,
                                g_fhFonts->afhFonts[iFont].Details.nfCodePage));
            fSendFont = FALSE;
        }

        if (fSendFont)
        {
            //
            // We want to send this entry so copy across as much of the
            // stored details as the protocol level requires.
            // We then mask the flags and advance to the next location in
            // the packet.
            //
            memcpy(pNetworkFonts,
                      &g_fhFonts->afhFonts[iFont].Details,
                      sizeof(NETWORKFONT));

            ((LPNETWORKFONT)pNetworkFonts)->nfFontFlags &= ~NF_LOCAL;
        }
        else
        {
            //
            // If we determine that we do not want to send the current
            // font then we fill the corresponding entry in the network
            // packet with zeros. This ensures that an index into our
            // local font table is also an index into the network packet,
            // so no conversion is required. Setting the whole entry to
            // zero gives the font a NULL facename and zero size, which
            // will never match a real font.
            //
            ZeroMemory(pNetworkFonts, sizeof(NETWORKFONT));
            cDummyFonts++;
        }

        //
        // Move to the next entry in the font packet.
        //
        pNetworkFonts += sizeof(NETWORKFONT);
    }

    //
    // Note that at the end of this loop, we may not have sent any fonts,
    // eg where the remote system does not support the font CodePage
    // capability and we do not have any true ANSI fonts.  We send the
    // packet anyway, so the remote system sees that we have no fonts to
    // match.
    //

    //
    // Only now do we know the number of fonts we actually put in the
    // packet.
    //
    pFontPacket->cFonts = (TSHR_UINT16)g_fhFonts->fhNumFonts;

    //
    // Send the fonts packet on the MISC stream.  It has no dependency on
    // any updates and we want it to get across quickly.
    //
    if (m_scfViewSelf)
        FH_ReceivedPacket(m_pasLocal, &(pFontPacket->header));

#ifdef _DEBUG
    sentSize =
#endif // _DEBUG
    DCS_CompressAndSendPacket(PROT_STR_MISC, g_s20BroadcastID,
        &(pFontPacket->header), pktSize);

    TRACE_OUT(("FH packet size: %08d, sent %08d", pktSize, sentSize));
    TRACE_OUT(( "Sent font packet with %u fonts (inc %u dummies)",
                 g_fhFonts->fhNumFonts,
                 cDummyFonts));

    //
    // Set the flag that indicates that we have successfully sent the
    // font info.
    //
    m_fhLocalInfoSent = TRUE;

    //
    // The font info has been sent, so this may mean we can enable text
    // orders.
    //
    FHMaybeEnableText();

DC_EXIT_POINT:
    DebugExitVOID(ASShare::FH_SendLocalFontInfo);
}


//
// FUNCTION: FH_GetMaxHeightFromLocalHandle
//
// DESCRIPTION:
//
// Given an FH font handle (ie a handle originating from the locally
// supported font structure which was sent to the remote machine at the
// start of the call) this function returns the MaxBaseLineExt value stored
// with the LOCALFONT details
//
// PARAMETERS:
//
// fontHandle - font handle being queried.
//
// RETURNS: max font height
//
//
UINT  FH_GetMaxHeightFromLocalHandle(UINT  fontHandle)
{
    UINT rc;

    DebugEntry(FH_GetMaxHeightFromLocalHandle);

    //
    // First check that the font handle is valid.
    //
    if (fontHandle >= g_fhFonts->fhNumFonts)
    {
        ERROR_OUT(( "Invalid font handle %u", fontHandle));
        fontHandle = 0;
    }

    //
    // Return the max font height
    //
    rc = g_fhFonts->afhFonts[fontHandle].lMaxBaselineExt;

    DebugExitDWORD(FH_GetMaxHeightFromLocalHandle, rc);
    return(rc);
}


//
// FUNCTION: FH_GetFontFlagsFromLocalHandle
//
// DESCRIPTION:
//
// Given an FH font handle (ie a handle originating from the locally
// supported font structure which was sent to the remote machine at the
// start of the call) this function returns the FontFlags value stored with
// the LOCALFONT details
//
// PARAMETERS:
//
// fontHandle - font handle being queried.
//
// RETURNS: font flags
//
//
UINT  FH_GetFontFlagsFromLocalHandle(UINT  fontHandle)
{
    UINT rc;

    DebugEntry(FH_GetFontFlagsFromLocalHandle);

    //
    // First check that the font handle is valid.
    //
    if (fontHandle >= g_fhFonts->fhNumFonts)
    {
        ERROR_OUT(( "Invalid font handle %u", fontHandle));
        fontHandle = 0;
    }

    //
    // Return the font flags.
    //
    rc = g_fhFonts->afhFonts[fontHandle].Details.nfFontFlags;

    DebugExitDWORD(FH_GetFontFlagsFromLocalHandle, rc);
    return(rc);
}

//
// FUNCTION: FH_GetCodePageFromLocalHandle
//
// DESCRIPTION:
//
// Given an FH font handle (ie a handle originating from the locally
// supported font structure which was sent to the remote machine at the
// start of the call) this function returns the CodePage value stored with
// the LOCALFONT details
//
// PARAMETERS:
//
// fontHandle - font handle being queried.
//
// RETURNS: char set
//
//
UINT  FH_GetCodePageFromLocalHandle(UINT  fontHandle)
{
    UINT rc = 0;

    DebugEntry(FH_GetCodePageFromLocalHandle);

    //
    // First check that the font handle is valid.
    //
    if (fontHandle >= g_fhFonts->fhNumFonts)
    {
        ERROR_OUT(( "Invalid font handle %u", fontHandle));
        fontHandle = 0;
    }

    //
    // Return the char set.
    //
    rc = g_fhFonts->afhFonts[fontHandle].Details.nfCodePage;

    DebugExitDWORD(FH_GetCodePageFromLocalHandle, rc);
    return(rc);
}



//
// FH_ConvertAnyFontIDToLocal()
//
// DESCRIPTION:
// Converts any font name ID fields in the passed order from remote font
// face name IDs to local font facename IDs.
//
void  ASShare::FH_ConvertAnyFontIDToLocal
(
    LPCOM_ORDER pOrder,
    ASPerson *  pasPerson
)
{
    LPCOMMON_TEXTORDER   pCommon = NULL;

    DebugEntry(ASShare::FH_ConvertAnyFontIDToLocal);

    ValidatePerson(pasPerson);

    //
    // Get a pointer to the structure which is common to both TextOut and
    // ExtTextOut
    //
    if (TEXTFIELD(pOrder)->type == LOWORD(ORD_TEXTOUT))
    {
        pCommon = &TEXTFIELD(pOrder)->common;
    }
    else if (EXTTEXTFIELD(pOrder)->type == LOWORD(ORD_EXTTEXTOUT))
    {
        pCommon = &EXTTEXTFIELD(pOrder)->common;
    }
    else
    {
        ERROR_OUT(( "Order type not TextOut or ExtTextOut."));
        DC_QUIT;
    }

    TRACE_OUT(( "fonthandle IN %lu", pCommon->FontIndex));
    pCommon->FontIndex = FHGetLocalFontHandle(pCommon->FontIndex, pasPerson);
    TRACE_OUT(( "fonthandle OUT %lu", pCommon->FontIndex));

DC_EXIT_POINT:
    DebugExitVOID(ASShare::FH_ConvertAnyFontIDToLocal);
}

//
// FH_GetFaceNameFromLocalHandle - see fh.h
//
LPSTR  FH_GetFaceNameFromLocalHandle(UINT fontHandle, LPUINT pFaceNameLength)
{
    LPSTR pFontName = NULL;

    DebugEntry(FH_GetFaceNameFromLocalHandle);

    //
    // First check that the font handle is valid.
    //
    if (fontHandle >= g_fhFonts->fhNumFonts)
    {
        ERROR_OUT(( "Invalid font handle %u", fontHandle));
        fontHandle = 0;
    }

    //
    // Now get the facename
    //
    *pFaceNameLength = lstrlen(g_fhFonts->afhFonts[fontHandle].RealName);
    pFontName = g_fhFonts->afhFonts[fontHandle].RealName;

    DebugExitVOID(FH_GetFaceNameFromLocalHandle);
    return(pFontName);
}


//
// FH_DetermineFontSupport()
//
void  ASShare::FH_DetermineFontSupport(void)
{
    UINT            cCommonFonts;
    UINT            iLocal;
    ASPerson *      pasPerson;

    DebugEntry(ASShare::FH_DetermineFontSupport);

    //
    // First mark all local fonts as supported.
    //
    cCommonFonts = g_fhFonts->fhNumFonts;
    for (iLocal = 0; iLocal < g_fhFonts->fhNumFonts; iLocal++)
    {
        g_fhFonts->afhFonts[iLocal].SupportCode = FH_SC_EXACT_MATCH;
    }

    //
    // Work through all remote people (but not us)
    //
    ValidatePerson(m_pasLocal);

    for (pasPerson = m_pasLocal->pasNext;
        (cCommonFonts > 0) && (pasPerson != NULL);
        pasPerson = pasPerson->pasNext)
    {
        ValidatePerson(pasPerson);

        if (pasPerson->oecFonts)
        {
            cCommonFonts = FHConsiderRemoteFonts(cCommonFonts, pasPerson);
        }
        else
        {
            //
            // We do not have valid fonts for this person, so must not
            // send any text orders at all.
            //
            TRACE_OUT(( "Pending FONT INFO from person [%d]", pasPerson->mcsID));
            cCommonFonts = 0;
        }
    }

    //
    // We have determined the common supported fonts, and may be able to
    // enable text orders now.
    //
    FHMaybeEnableText();

    DebugExitVOID(ASShare::FH_DetermineFontSupport);
}



//
// FH_CreateAndSelectFont()
//
BOOL  FH_CreateAndSelectFont(HDC    surface,
                                                 HFONT*       pHNewFont,
                                                 HFONT*       pHOldFont,
                                                 LPSTR        fontName,
                                                 UINT         codepage,
                                                 UINT         fontMaxHeight,
                                                 UINT         fontHeight,
                                                 UINT         fontWidth,
                                                 UINT         fontWeight,
                                                 UINT         fontFlags)
{
    BOOL      rc;
    BYTE        italic;
    BYTE        underline;
    BYTE        strikeout;
    BYTE        pitch;
    BYTE        charset;
    BYTE        precis;

    DebugEntry(FH_CreateAndSelectFont);


    //
    // Set the return code to indicate failure (FALSE). We will change this
    // later if we successfully create the font.
    //
    rc = FALSE;

    //
    // Massage the data passed which describes the font into the correct
    // arrangement to pass on a create font call.  Then create a font.
    //

    //
    // If a facename passed is the null string then we are supposed to use
    // the system font.
    //
    if (fontName[0] == 0)
    {
        WARNING_OUT(( "Using system font"));
        *pHNewFont = GetStockFont(SYSTEM_FONT);
    }
    else
    {
        //
        // Determine the italic, underline, strikeout and pitch values from
        // the packed flags.
        //
        italic    = (BYTE)(fontFlags & NF_ITALIC);
        underline = (BYTE)(fontFlags & NF_UNDERLINE);
        strikeout = (BYTE)(fontFlags & NF_STRIKEOUT);

        if (fontFlags & NF_FIXED_PITCH)
        {
            pitch = FF_DONTCARE | FIXED_PITCH;
        }
        else
        {
            pitch = FF_DONTCARE | VARIABLE_PITCH;
        }

        //
        // Check whether this is a TrueType font.  This is important, as
        // the Windows Font mapper is biased towards non-TrueType, and it
        // is easy to do the subsequent decoding with a non-TrueType font.
        //
        // Note that the Windows headers do not define a name for the
        // required value (which is 0x04 in the manuals), so we use the
        // value used in the TextMetrics (which has the same value).
        //
        if (fontFlags & NF_TRUE_TYPE)
        {
            pitch |= TMPF_TRUETYPE;
            precis = OUT_TT_ONLY_PRECIS;
        }
        else
        {
            precis = OUT_RASTER_PRECIS;
        }

        //
        // The height we are passed is the character height, not the cell
        // height.  To indicate this to Windows we need to pass it in as a
        // negative value.
        //
        TRACE_OUT(( "CreateFont cx(%u) cy(%u) wt(%u) pitch(%u) name:%s",
                                                                 fontWidth,
                                                                 fontHeight,
                                                                 fontWeight,
                                                                 pitch,
                                                                 fontName ));

        //
        // Use the misleadingly named codepage value to calculate what
        // charset to ask Windows for.
        //
        if (codepage == NF_CP_WIN_ANSI)
        {
            charset = ANSI_CHARSET;
        }
        else if (codepage == NF_CP_WIN_OEM)
        {
            charset = OEM_CHARSET;
        }
        else if (codepage == NF_CP_WIN_SYMBOL)
        {
            charset = SYMBOL_CHARSET;
        }
        else
        {
            //
            // We have to trust our luck to Windows by specifying default
            // (meaning don't care).
            //
            charset = DEFAULT_CHARSET;
        }

        *pHNewFont = CreateFont(-(int)fontHeight,
                             fontWidth,
                             0,    // escapement
                             0,    // orientation
                             fontWeight,
                             italic,
                             underline,
                             strikeout,
                             charset,
                             precis,
                             CLIP_DEFAULT_PRECIS,
                             DEFAULT_QUALITY,
                             pitch,
                             fontName);
        if (*pHNewFont == NULL)
        {
            WARNING_OUT(( "Failed to create font %s", fontName));
            DC_QUIT;
        }
    }

    //
    // Now we have created the font we need to select it into the HDC
    // which was passed to us.
    //
    *pHOldFont = SelectFont(surface, *pHNewFont);
    if (*pHOldFont == NULL)
    {
        ERROR_OUT(( "Failed to select font %s", fontName));
        DeleteFont(*pHNewFont);
        *pHNewFont = NULL;
        DC_QUIT;
    }
    TRACE_OUT(( "Select new font: %p Old font: %", *pHNewFont,
                                               *pHOldFont));

    //
    // We have successfully created and selected the font.
    //
    rc = TRUE;

DC_EXIT_POINT:
    DebugExitDWORD(FH_CreateAndSelectFont, rc);
    return(rc);
}


//
// FHAddFontToLocalTable
//
// Adds the given font into the local font table, along with any renaming
// and approximate matches.
//
//
void  FHAddFontToLocalTable( LPSTR  faceName,
                                                 TSHR_UINT16 fontFlags,
                                                 TSHR_UINT16 codePage,
                                                 TSHR_UINT16 maxHeight,
                                                 TSHR_UINT16 aveHeight,
                                                 TSHR_UINT16 aveWidth,
                                                 TSHR_UINT16 aspectX,
                                                 TSHR_UINT16 aspectY,
                                                 TSHR_UINT16 maxAscent)
{
    TSHR_INT16       fatSig;
    TSHR_INT16       thinSig;
    TSHR_INT16       symbolSig;
    FHWIDTHTABLE  wTable;
    TSHR_UINT16      height;
    TSHR_UINT16      width;
    TSHR_UINT16      weight;
    LOCALFONT     thisFont;
    TSHR_UINT16      fIndex;

    //
    // SFRFONT: place marker.
    // Here would be the best place to adjust codepage; for example suppose
    // we find that CodePage 950 (Chinese) is so different on all platforms
    // that we just should not send text orders in this codepage, we can
    // set codePage=NF_CP_UNKNOWN and it will be discarded.
    //

    //
    // SFRFONT: no point hanging on to details of fonts with unknown
    // code pages; we cannot risk matching them.
    //
    if (codePage == NF_CP_UNKNOWN)
    {
        TRACE_OUT(( "unknown CP: discard"));
        DC_QUIT;
    }

    //
    // Check we still have room for more fonts.
    //
    if (g_fhFonts->fhNumFonts >= FH_MAX_FONTS)
    {
        //
        // We are already at our maximum number of fonts.
        //
        DC_QUIT;
    }

    //
    // Zero the fields where we store facenames to allow bytewise matches.
    //
    ZeroMemory(thisFont.Details.nfFaceName, FH_FACESIZE);
    ZeroMemory(thisFont.RealName, FH_FACESIZE);

    //
    // Store the easy bits!
    //
    thisFont.Details.nfFontFlags = fontFlags;
    thisFont.Details.nfAveWidth  = aveWidth;
    thisFont.Details.nfAveHeight = aveHeight;
    thisFont.Details.nfAspectX   = aspectX;
    thisFont.Details.nfAspectY   = aspectY;
    thisFont.Details.nfCodePage  = codePage;

    thisFont.lMaxBaselineExt     = maxHeight;

    //
    // Store the real name, for use when we want to create an instance of
    // this font.
    //
    lstrcpy (thisFont.RealName, faceName);

    //
    // Fill in the wire-format facename.
    //
    // NB - This has a machine-specific prefix, but for NT the prefix is an
    // empty string, so we can just use a strcpy without worrying about the
    // issues of adding a prefix.
    //
    lstrcpy (thisFont.Details.nfFaceName, faceName);

    //
    // Make sure the signatures are zero for now.
    //
    thisFont.Details.nfSigFats       = 0;
    thisFont.Details.nfSigThins      = 0;
    thisFont.Details.nfSigSymbol     = 0;

    //
    // Now calculate the signature and maxAscent for this font
    //
    weight = 0;                             // use default weight

    if ((fontFlags & NF_FIXED_SIZE) != 0)
    {
        //
        // Fixed size font: use actual font size for signatures/maxAscent
        //
        height = thisFont.lMaxBaselineExt;
        width  = thisFont.Details.nfAveWidth;

        thisFont.Details.nfMaxAscent = maxAscent;
    }
    else
    {
        //
        // Scalable font: use default height/width for signatures/maxAscent
        //
        height = NF_METRICS_HEIGHT;
        width  = NF_METRICS_WIDTH;

        thisFont.Details.nfMaxAscent = NF_METRICS_HEIGHT;
    }

    //
    // Initialise signature fields to zero (== NF_NO_SIGNATURE).  They will
    // be overwritten assuming we get a font width table OK.
    //
    fatSig    = 0;
    thinSig   = 0;
    symbolSig = 0;

    //
    // FHGenerateFontWidthTable also gives us a proper maxAscent value for
    // scalable fonts (i.e. based on its own rendition of the font)
    //
    if (FHGenerateFontWidthTable(&wTable,
                                 &thisFont,
                                  height,
                                  width,
                                  weight,
                                  thisFont.Details.nfFontFlags,
                                 &maxAscent))
    {
        //
        // If this is a scalable font, use the updated maxAscent value that
        // FHGenerateFontWidthTable has given us.
        //
        if (0 == (thisFont.Details.nfFontFlags & NF_FIXED_SIZE))
        {
            thisFont.Details.nfMaxAscent = maxAscent;
            TRACE_OUT(( "Set maxAscent = %d", thisFont.Details.nfMaxAscent));
        }

        //
        // We have all the raw data we need.  Calculate the signatures.
        //
        FHCalculateSignatures(&wTable, &fatSig, &thinSig, &symbolSig);
    }

    //
    // Store the signatures.  If the call to FHGenerateFontWidthTable
    // fails, the signatures are zero.
    //
    thisFont.Details.nfSigFats     =  (BYTE)fatSig;
    thisFont.Details.nfSigThins    =  (BYTE)thinSig;
    thisFont.Details.nfSigSymbol   = (TSHR_UINT16)symbolSig;

    TRACE_OUT(( "Font %hu signatures: (x%.4hx%.2hx%.2hx)",
             g_fhFonts->fhNumFonts,
             thisFont.Details.nfSigSymbol,
             (TSHR_UINT16)(thisFont.Details.nfSigThins),
             (TSHR_UINT16)(thisFont.Details.nfSigFats)));

    //
    // We can now copy the details to the end of the local table.
    //
    memcpy((void *)&g_fhFonts->afhFonts[g_fhFonts->fhNumFonts],
              (void *)&thisFont,
              sizeof(LOCALFONT));

    //
    // Count this font.
    //
    TRACE_OUT(( "Added record %s",
                                g_fhFonts->afhFonts[g_fhFonts->fhNumFonts].Details.nfFaceName));
    g_fhFonts->fhNumFonts++;

    TRACE_OUT(( "g_fhFonts->fhNumFonts now %u", g_fhFonts->fhNumFonts));

DC_EXIT_POINT:
    DebugExitVOID(FHAddFontToLocalTable);
}

//
// FHConsiderRemoteFonts
//
// Considers the remote fonts for a single remote person.
//
// Takes the existing number of supported fonts, and returns the number
// that are still common after considering this person.
//
UINT  ASShare::FHConsiderRemoteFonts
(
    UINT        cCanSend,
    ASPerson *  pasPerson
)
{
    UINT  iLocal;
    UINT  iRemote;
    UINT  cCanReceive=0;
    BOOL  fCanReceive, fOnlyAscii;
    UINT  sendSupportCode;
    UINT  bestSupportSoFar;

    DebugEntry(ASShare::FHConsiderRemoteFonts);

    ValidatePerson(pasPerson);
    //
    // Consider each of the still valid local fonts, and see if the remote
    // person also supports them.
    //

    //
    // SFR5396: LOOP ONE
    //
    // Look through all the LOCAL fonts, for ones where we find a match in
    // the remote font table.  These are fonts we can SEND, and for which
    // we must set g_fhFonts->afhFonts[].Supported.
    //
    // We also set the rfLocalHandle for remote fonts that we can receive
    // if we encounter them in this search.  We complete the search for
    // remote fonts that we can receive in LOOP TWO.
    //
    // Things we check in this loop: - we may already know there is no
    // match for this local name
    //      so drop out quickly.  - otherwise check through EVERY REMOTE
    // font looking for the
    //      best possible match.  (If we find an EXACT match, leave the
    //      inner loop early)
    //
    //
    for (iLocal=0;
         (cCanSend > 0) && (iLocal < g_fhFonts->fhNumFonts);
         iLocal++)
    {
        if (g_fhFonts->afhFonts[iLocal].SupportCode != FH_SC_NO_MATCH)
        {
            //
            //
            // This font is still valid so check it with all the remote
            // fonts for this person.
            //
            // Things we check in this loop:
            // -    do the face names match? if no - try next remote font.
            // -    the pitch: if one is FIXED pitch and one isn't try next
            // -    the codepages: are the local/remote the same?  This
            //          determines whether we send only ASCII chars.
            // -    scalability: possible combinations are:
            //       local fixed/remote scalable       (can send/not rcv)
            //       local scalable/remote scalable    (can send and rcv)
            //       local fixed/remote fixed, sizes match (send & rcv)
            //       local scalable/remote fixed      (cannot send/can rcv)
            //          for this last case, keep trying the remote fonts.
            //
            // In "back level" calls to Pre-R11 boxes we stop here but
            // force the matches to be approximate.  Otherwise check
            //
            // -    aspect ratios (if present): must match or try the
            //       next remote font.
            // -    signatures: these are used to finally decide whether
            //       the fonts are exact matches; good enough to treat as
            //       approximate matches or such poor matches that the
            //       font is not supported (cannot be sent).
            //
            //

//
// Handy SHORTHAND macroes.
//
#define REMOTEFONT pasPerson->poeFontInfo[iRemote]
#define LOCALFT  g_fhFonts->afhFonts[iLocal]
#define LOCALDETS  LOCALFT.Details

            //
            // Initially assume that the fonts do not match, but that
            // if they do they will match across the whole codepage
            // (not just the ascii set).
            //
            sendSupportCode  = FH_SC_NO_MATCH;
            bestSupportSoFar = FH_SC_NO_MATCH;
            fOnlyAscii       = FALSE;

            //
            //
            // Loop through all the remote fonts looking to see which, if
            // any, offers the best possible match.  Initially,
            // sendSupportCode is set to NO_MATCH; as we go through each
            // iteration we see if we can improve on the current setting
            // of sendSupportCode.  We leave the loop as soon as we find
            // an EXACT_MATCH ('cos we are not going to do any better than
            // that!) or when we run out of remote fonts.  The best match
            // found so far is kept in bestSupportSoFar.
            //
            //
            for (iRemote = 0;
                 (iRemote < pasPerson->oecFonts)
                                   && (sendSupportCode != FH_SC_EXACT_MATCH);
                 iRemote++)
            {
                //
                // If the remote font is already flagged as having no
                // possible match then skip out now. (We set this field
                // during the initial processing of the remote font).
                //
                if (REMOTEFONT.rfLocalHandle==NO_FONT_MATCH)
                {
                    continue;                                    // SFR5279
                }

                //
                // Check the face names...
                //
                if (lstrcmp(LOCALDETS.nfFaceName,
                    g_fhFonts->afhFonts[REMOTEFONT.rfLocalHandle].Details.nfFaceName))
                {
                    continue;
                }
                TRACE_OUT(( "Matched Remote Face Name %s",
                       g_fhFonts->afhFonts[REMOTEFONT.rfLocalHandle]
                                                .Details.nfFaceName));

                //
                // Check the pitch...
                //
                if( (LOCALDETS.nfFontFlags & NF_FIXED_PITCH)!=
                                   (REMOTEFONT.rfFontFlags & NF_FIXED_PITCH) )
                {
                    TRACE_OUT(( "Different Pitch %x %x",
                                LOCALDETS.nfFontFlags,
                                REMOTEFONT.rfFontFlags));
                    continue;
                }

                //
                //
                // If both systems support the font CodePage capability
                // (indicated by the remote capability flags - which are
                // the union of remote and local by now), check that the
                // CodePages and CodePage flags match, and if not,
                // restrict ourselves to sending the ASCII subset.
                //
                // If we support the font CodePage capability but  remote
                // system does not, then restrict ourselves to sending the
                // ASCII subset.
                //
                // If we do not support the font CodePage capability, then
                // we assume that the remote is only sending ANSI CodePage,
                // either because it doesn't know about the font CodePage
                // capability or because it can see that we don't support
                // it.  Therefore, we do not need to check the CodePage.
                // BUT: restrict ourselves to ASCII only.
                //
                //
                if (!(m_pasLocal->cpcCaps.orders.capsfFonts & CAPS_FONT_CODEPAGE))
                {
                    //
                    // We do not support codepage checking.
                    //
                    TRACE_OUT(( "not checking CP"));
                    fOnlyAscii = TRUE;
                }

                if ((m_oeCombinedOrderCaps.capsfFonts & CAPS_FONT_CODEPAGE)
                    && (LOCALDETS.nfCodePage != REMOTEFONT.rfCodePage)  )
                {
                    TRACE_OUT(( "Different CPs %hu %hu",
                                LOCALDETS.nfCodePage,
                                REMOTEFONT.rfCodePage));
                    //
                    //
                    // Assume that all codepages include ASCII.
                    //
                    //
                    fOnlyAscii = TRUE;
                }

                //
                //
                // If we support codepage, but the remote does not then
                // the remote will only be sending us ANSI chars. Make sure
                // that we send only ASCII subset.
                //
                //
                if ((m_pasLocal->cpcCaps.orders.capsfFonts & CAPS_FONT_CODEPAGE)  &&
                    !(m_oeCombinedOrderCaps.capsfFonts & CAPS_FONT_CODEPAGE))
                {
                    TRACE_OUT(( "Only ASCII"));
                    fOnlyAscii = TRUE;
                }

                //
                //
                // The face names and CodePages match and the fonts are of
                // the same type of pitch (ie both are fixed pitch or both
                // are variable pitch).
                //
                //
                if ((REMOTEFONT.rfFontFlags & NF_FIXED_SIZE) == 0)
                {
                    //
                    //
                    // The remote font is scalable, so we can send any font
                    // (with this facename) to the remote system, even if
                    // the local font is fixed sized. Set sendSupportCode
                    // to FH_SC_EXACT_MATCH now - we will change this to
                    // FH_SC_APPROX_MATCH later if other fields differ.
                    //
                    //
                    TRACE_OUT((
                  "Person [%d] Can SEND: remote SCALABLE %s (remote)%u to (local)%u",
                           pasPerson->mcsID,
                           LOCALDETS.nfFaceName,
                           iRemote, iLocal));
                    sendSupportCode = FH_SC_EXACT_MATCH;

                    //
                    //
                    // SFR5396: it is true that we can SEND this font
                    // because the remote version of the font is scalable.
                    // That does not mean that we can necessarily receive
                    // the font... unless ours is scalable too.
                    //
                    //
                    if ((LOCALDETS.nfFontFlags & NF_FIXED_SIZE)==0)
                    {
                        TRACE_OUT((
                               "Person [%d] Can RECEIVE remote font %u as local %u",
                               pasPerson->mcsID, iRemote, iLocal));
                        REMOTEFONT.rfLocalHandle = (TSHR_UINT16)iLocal;
                    }
                }
                else if (LOCALDETS.nfFontFlags & NF_FIXED_SIZE)
                {
                    //
                    //
                    // The remote font is fixed size and so is the local
                    // one, so check if the sizes match exactly.
                    //
                    //
                    if ((LOCALDETS.nfAveWidth == REMOTEFONT.rfAveWidth) &&
                        (LOCALDETS.nfAveHeight == REMOTEFONT.rfAveHeight))
                    {
                        //
                        //
                        // Our fixed size local font is the same as the
                        // fixed size font at the remote.  We set
                        // sendSupportCode to FH_SC_EXACT_MATCH now - we
                        // will change this to FH_SC_APPROX_MATCH later if
                        // other fields differ.
                        //
                        //
                        TRACE_OUT(("Person [%d] Matched remote fixed font %s %u to %u",
                               pasPerson->mcsID,
                               LOCALDETS.nfFaceName,
                               iRemote, iLocal));
                        sendSupportCode = FH_SC_EXACT_MATCH;
                        REMOTEFONT.rfLocalHandle = (TSHR_UINT16)iLocal;
                    }
                    else
                    {
                        TRACE_OUT(( "rejected %s ave width/heights "
                                      "local/remote width %d/%d height %d/%d",
                                   LOCALDETS.nfFaceName,
                                   LOCALDETS.nfAveWidth,
                                                  REMOTEFONT.rfAveWidth,
                                   LOCALDETS.nfAveHeight,
                                                  REMOTEFONT.rfAveHeight));
                    }
                }
                else
                {
                    TRACE_OUT((
                   "Can only RECEIVE %s %u Remote is fixed, but local %u not",
                             LOCALDETS.nfFaceName,
                             iRemote,
                             iLocal));
                    //
                    //
                    // SFR5396: while we cannot SEND this font because our
                    // local version is scalable, but the remote's is
                    // fixed - we can still receive the font in an order.
                    //
                    //
                    REMOTEFONT.rfLocalHandle = (TSHR_UINT16)iLocal;
                }

                //
                //
                // If we have have set the send support code to indicate
                // that we have matched we now consider any R1.1 info if it
                // is present.  As a result of this we may adjust the send
                // support code (from indicating an exact match) to
                // indicate either an approximate match or no match at all.
                //
                //
                if (!pasPerson->oecFonts)
                {
                    //
                    //
                    // The remote system did not send us any R11 font
                    // info. In this case we assume all font matches are
                    // approximate and restrict ourselves to the ascii
                    // subset.
                    //
                    //
                    if (sendSupportCode != FH_SC_NO_MATCH)
                    {
                        TRACE_OUT(( "No R11 so approx match only"));
                        sendSupportCode = FH_SC_APPROX_ASCII_MATCH;
                    }
                }
                else if (sendSupportCode != FH_SC_NO_MATCH)
                {
                    //
                    //
                    // The remote system did send us R11 font info and
                    // the font is flagged as matching.
                    //
                    //

                    if ((m_oeCombinedOrderCaps.capsfFonts
                                            & CAPS_FONT_R20_SIGNATURE)!=0)
                    {
                        //
                        //
                        // Check the signatures.
                        //
                        //
                        TRACE_OUT((
 "Person [%d] local %d (remote %d) signatures (x%.4hx%.2hx%.2hx v x%.4hx%.2hx%.2hx)",
                               pasPerson->mcsID,
                               iLocal,
                               iRemote,
                               LOCALDETS.nfSigSymbol,
                               (TSHR_UINT16)(LOCALDETS.nfSigThins),
                               (TSHR_UINT16)(LOCALDETS.nfSigFats),
                               REMOTEFONT.rfSigSymbol,
                               (TSHR_UINT16)(REMOTEFONT.rfSigThins),
                               (TSHR_UINT16)(REMOTEFONT.rfSigFats)));

                        if ((LOCALDETS.nfSigFats != REMOTEFONT.rfSigFats) ||
                            (LOCALDETS.nfSigThins != REMOTEFONT.rfSigThins) ||
                            (LOCALDETS.nfSigSymbol != REMOTEFONT.rfSigSymbol) ||
                            (REMOTEFONT.rfSigSymbol == NF_NO_SIGNATURE))
                        {
                            //
                            // Decide what to do from the signatures.
                            //
                            if (REMOTEFONT.rfSigSymbol == NF_NO_SIGNATURE)
                            {
                                TRACE_OUT(("NO match: remote no signature"));
                                sendSupportCode = FH_SC_APPROX_ASCII_MATCH;
                            }
                            else if ((LOCALDETS.nfSigFats == REMOTEFONT.rfSigFats)
                                  && (LOCALDETS.nfSigThins == REMOTEFONT.rfSigThins))
                            {
                                TRACE_OUT(( "our ASCII sigs match"));
                                sendSupportCode = FH_SC_EXACT_ASCII_MATCH;
                            }
                            else
                            {
                                //
                                // NOTE:
                                // We could use the "closeness" of the fat
                                // and thin signatures to help us decide
                                // whether to use approximate matching or
                                // not.  But currently we don't.
                                //
                                TRACE_OUT(( "Sig mismatch: APPROX_ASC"));
                                sendSupportCode = FH_SC_APPROX_ASCII_MATCH;
                            }
                        }
                        else
                        {
                            //
                            //
                            // All signatures match exactly.
                            // Leave SendSupportCode as FH_SC_EXACT_MATCH
                            //
                            //
                            TRACE_OUT(("EXACT MATCH: Signatures match exactly"));
                        }
                    }
                    else
                    {
                        //
                        // Not using signatures.  Do we care?
                        //
                        sendSupportCode = FH_SC_APPROX_MATCH;
                        TRACE_OUT(( "APPROX MATCH: no sigs"));
                    }

                    //
                    //
                    // Check the aspect ratio - but only if we do not
                    // already know that this font does not match.
                    //
                    //
                    if ( (sendSupportCode!=FH_SC_NO_MATCH) &&
                         ( (!(m_oeCombinedOrderCaps.capsfFonts
                                                          & CAPS_FONT_ASPECT))
                           || (LOCALDETS.nfAspectX != REMOTEFONT.rfAspectX)
                           || (LOCALDETS.nfAspectY != REMOTEFONT.rfAspectY) ))
                    {
                        //
                        //
                        // Either no aspect ratio was supplied or the
                        // aspect ratio differed.
                        //
                        //
                        if (sendSupportCode == FH_SC_EXACT_MATCH)
                        {
                            //
                            // Force delta-X text orders for mismatched
                            // aspect ratio.  Note we tested above to
                            // see whether supportCode == EXACT because if
                            // we have already "downgraded" support then
                            // we do not need to change it here
                            //
                            sendSupportCode = FH_SC_APPROX_MATCH;
                            TRACE_OUT(( "AR mismatch: APPROX_MATCH"));
                        }
                        else if (sendSupportCode == FH_SC_EXACT_ASCII_MATCH)
                        {
                            //
                            // Same again but for ASCII only.
                            //
                            sendSupportCode = FH_SC_APPROX_ASCII_MATCH;
                            TRACE_OUT(( "AR mismatch: APPROX_ASCII_MATCH"));
                        }
                    }
                }

                if (sendSupportCode != FH_SC_NO_MATCH)
                {
                    //
                    //
                    // Is this a better match than any we have seen
                    // before?
                    //
                    //
                    switch (sendSupportCode)
                    {
                        case FH_SC_EXACT_MATCH:
                        case FH_SC_APPROX_MATCH:
                            //
                            //
                            // Note that we do not have to worry about
                            // overwriting a bestSupportSoFar of EXACT
                            // with APPROX because we leave the loop when
                            // we get an exact match.
                            //
                            //
                            bestSupportSoFar = sendSupportCode;
                            break;

                        case FH_SC_EXACT_ASCII_MATCH:
                            //
                            //
                            // An approximate match over the whole 255
                            // code points is better than an exact one
                            // over just the ascii-s.  Debatable, but that
                            // is what I have decided.
                            //
                            //
                            if (bestSupportSoFar != FH_SC_APPROX_MATCH)
                            {
                                bestSupportSoFar = FH_SC_EXACT_ASCII_MATCH;
                            }
                            break;

                        case FH_SC_APPROX_ASCII_MATCH:
                            //
                            //
                            // An approximate match over just the ascii-s
                            // is better than nothing at all!
                            //
                            //
                            if (bestSupportSoFar == FH_SC_NO_MATCH)
                            {
                                bestSupportSoFar = FH_SC_APPROX_ASCII_MATCH;
                            }
                            break;

                        default:
                            ERROR_OUT(("invalid support code"));
                            break;

                    }
                }
            }

            sendSupportCode = bestSupportSoFar;

            //
            // If we matched the remote font, we have already updated
            // its local handle to
            // the matched local font.  While the local handle was already
            // set up, it was only set up to the first local font with the
            // same facename, rather than the correct font.
            //
            // If we did not match the remote font, mark it as not
            // supported, and decrement the common font count.
            //
            if (sendSupportCode != FH_SC_NO_MATCH)
            {
                TRACE_OUT(( "Local font %d/%s can be SENT (code=%u)",
                            iLocal,
                            LOCALDETS.nfFaceName,
                            sendSupportCode));
                if (fOnlyAscii)
                {
                    if (sendSupportCode == FH_SC_EXACT_MATCH)
                    {
                        sendSupportCode = FH_SC_EXACT_ASCII_MATCH;
                        TRACE_OUT(( "Adjust %d/%s to EXACT_ASC (code=%u)",
                                    iLocal,
                                    LOCALDETS.nfFaceName,
                                    sendSupportCode));
                    }
                    else
                    {
                        TRACE_OUT(( "Adjust %d/%s to APPROX_ASC (code=%u)",
                                    iLocal,
                                    LOCALDETS.nfFaceName,
                                    sendSupportCode));
                        sendSupportCode = FH_SC_APPROX_ASCII_MATCH;
                    }
                }
            }
            else
            {
                TRACE_OUT(( "Local font %d/%s cannot be SENT",
                            iLocal,LOCALDETS.nfFaceName));
                cCanSend--;
            }

            LOCALFT.SupportCode &= sendSupportCode;
        }
        else
        {
            TRACE_OUT(( "Cannot SEND %d/%s",iLocal,LOCALDETS.nfFaceName));
        }
    }

    //
    //
    // SFR5396: LOOP TWO
    //
    // Loop through all the remote fonts, looking for ones where we have
    // a locally matching font.  These are fonts that we can RECEIVE in
    // orders, and for which we need to map the remote font handle to the
    // local font handle.  This means setting REMOTEFONT.rfLocalHandle.
    //
    // By the time we reach here, REMOTEFONT.rfLocalHandle is already set
    // to:
    // -    NO_FONT_MATCH (in FH_ProcessRemoteFonts)
    // or   the index in the local table of a definite match found in LOOP1
    // or   the index of the first entry in the local table with the
    //      same face name as the remote font (set in FH_ProcessRemoteFonts)
    //
    // so - we can begin our search in the local table from
    // REMOTEFONT.rfLocalHandle.
    //
    //
    for (iRemote = 0;
         (iRemote < pasPerson->oecFonts);
         iRemote++)
    {
        iLocal = REMOTEFONT.rfLocalHandle;
        if (iLocal == NO_FONT_MATCH)
        {
            //
            // We have no fonts whatsoever that match this font name
            // Go round again... try the next REMOTE font.
            //
            continue;
        }

        TRACE_OUT(( "Can we receive %s?",
             g_fhFonts->afhFonts[REMOTEFONT.rfLocalHandle].Details.nfFaceName));
        for (fCanReceive = FALSE;
             (iLocal < g_fhFonts->fhNumFonts) && (!fCanReceive);
             iLocal++)
        {
            //
            // Check the face names...
            //
            if (lstrcmp(LOCALDETS.nfFaceName,
                g_fhFonts->afhFonts[REMOTEFONT.rfLocalHandle].Details.nfFaceName))
            {
                //
                // Try the next LOCAL font.
                //
                continue;
            }

            //
            // Check the pitch...
            //
            if((LOCALDETS.nfFontFlags & NF_FIXED_PITCH)!=
                                (REMOTEFONT.rfFontFlags & NF_FIXED_PITCH))
            {
                //
                // Different pitches, try the next local font.
                //
                TRACE_OUT(( "Pitch mismatch"));
                continue;
            }

            //
            //
            // The face names match and the fonts are of
            // the same type of pitch (ie both are fixed pitch or both
            // are variable pitch).
            //
            //
            if ((REMOTEFONT.rfFontFlags & NF_FIXED_SIZE) == 0)
            {
                if ((LOCALDETS.nfFontFlags & NF_FIXED_SIZE)==0)
                {
                    //
                    //
                    // The remote font is scalable.  Ours is also
                    // scalable then we can receive the font.
                    //
                    // We do not need to look at any more LOCAL fonts.
                    //
                    //
                    fCanReceive              = TRUE;
                }
            }
            else if (LOCALDETS.nfFontFlags & NF_FIXED_SIZE)
            {
                //
                //
                // The remote font is fixed size and so is the local
                // one, so check if the sizes match exactly.
                //
                //
                if ((LOCALDETS.nfAveWidth == REMOTEFONT.rfAveWidth) &&
                    (LOCALDETS.nfAveHeight == REMOTEFONT.rfAveHeight))
                {
                    //
                    //
                    // Our fixed size local font is the same as the
                    // fixed size font at the remote.
                    //
                    // We do not need to look at any more LOCAL fonts.
                    //
                    //
                    fCanReceive              = TRUE;
                }
                else
                {
                    TRACE_OUT(( "different fixed sizes"));
                }
            }
            else
            {
                //
                //
                // The remote is FIXED but the LOCAL is scalable.  We
                // can receive orders for text of this type (but not send)
                //
                // We do not need to look at any more LOCAL fonts.
                //
                //
                fCanReceive              = TRUE;
            }

            if (fCanReceive)
            {
               TRACE_OUT(("Person [%d] Can RECEIVE remote font %s %u as %u",
                      pasPerson->mcsID,
                      LOCALDETS.nfFaceName,
                      iRemote, iLocal));
                REMOTEFONT.rfLocalHandle = (TSHR_UINT16)iLocal;
                cCanReceive++;
            }
        }

    }

    TRACE_OUT(("Person [%d] Can SEND %d fonts", pasPerson->mcsID, cCanSend));
    TRACE_OUT(("Person [%d] Can RECEIVE %d fonts", pasPerson->mcsID, cCanReceive));

    DebugExitDWORD(ASShare::FHConsiderRemoteFonts, cCanSend);
    return(cCanSend);
}

//
// FHMaybeEnableText
//
// Enables or disables sending of text orders
//
void  ASShare::FHMaybeEnableText(void)
{
    BOOL            fEnableText = FALSE;
    ASPerson *      pasPerson;

    DebugEntry(ASShare::FHMaybeEnableText);

    //
    // To enable sending text orders we must have sent out our own packet
    // of fonts, and there must be no outstanding remote packets required.
    //
    if (m_fhLocalInfoSent)
    {
        //
        // Assume we can enable text orders.
        //
        fEnableText = TRUE;

        //
        // The local info was sent, so check remote dudes (not us)
        //
        ValidatePerson(m_pasLocal);
        for (pasPerson = m_pasLocal->pasNext; pasPerson != NULL; pasPerson = pasPerson->pasNext)
        {
            ValidatePerson(pasPerson);

            if (!pasPerson->oecFonts)
            {
                //
                // We have found a font packet that we have not yet
                // received, so must disable sending text, and can break
                // out of the search.
                //
                TRACE_OUT(( "No font packet yet from person [%d]", pasPerson->mcsID));
                fEnableText = FALSE;
                break;
            }
        }
    }
    else
    {
        TRACE_OUT(( "Local font info not yet sent"));
    }

    OE_EnableText(fEnableText);

    if (g_asCanHost)
    {
        //
        // Pass on new font data to the other tasks.
        //
        if (fEnableText)
        {
            OE_NEW_FONTS newFontData;

            //
            // Copy the data from the Share Core.
            //
            newFontData.fontCaps    = m_oeCombinedOrderCaps.capsfFonts;
            newFontData.countFonts  = (WORD)g_fhFonts->fhNumFonts;
            newFontData.fontData    = g_fhFonts->afhFonts;
            newFontData.fontIndex   = g_fhFonts->afhFontIndex;

            TRACE_OUT(( "Sending %d Fonts", g_fhFonts->fhNumFonts));

            //
            // Notify display driver of new fonts
            //
            OSI_FunctionRequest(OE_ESC_NEW_FONTS,
                                (LPOSI_ESCAPE_HEADER)&newFontData,
                                sizeof(newFontData));
        }
    }

    DebugExitVOID(ASShare::FHMaybeEnableText);
}

//
// FHGetLocalFontHandle
//
// Translate a remote font handle/local ID pair to a local font handle.
//
UINT  ASShare::FHGetLocalFontHandle
(
    UINT        remotefont,
    ASPerson *  pasPerson
)
{
    DebugEntry(ASShare::FHGetLocalFontHandle);

    ValidatePerson(pasPerson);

    if (!pasPerson->oecFonts)
    {
        WARNING_OUT(("Order packet from [%d] but no fonts", pasPerson->mcsID));
    }

    if (remotefont == DUMMY_FONT_ID)
    {
        //
        // The dummy font ID has been supplied for the remote font Id.
        // Substitute the first valid local font Id.
        //
        for (remotefont = 0;
             remotefont < pasPerson->oecFonts;
             remotefont++)
        {
            if (pasPerson->poeFontInfo[remotefont].rfLocalHandle !=
                                                                NO_FONT_MATCH)
            {
                break;
            }
        }
    }

    if (remotefont >= pasPerson->oecFonts)
    {
        //
        // The remote font is invalid.
        // There is no error value, we simply return the valid but
        // incorrect value 0.
        //
        TRACE_OUT(("Person [%d] Invalid font handle %u",
                 pasPerson->mcsID, remotefont));
        return(0);
    }

    DebugExitVOID(ASShare::FHGetLocalFontHandle);
    return(pasPerson->poeFontInfo[remotefont].rfLocalHandle);
}


//
//
// FUNCTION: FHCalculateSignatures
//
// DESCRIPTION:
//
// Given a width table, calculates the three font signatures that are
// included in the R2.0 NETWORKFONT structure.
//
// PARAMETERS:
//
//  pTable - pointer to width table
//  pSigFats, pSigThins, pSigSymbol - return the three signatures
//
// RETURNS:
//
//  None
//
//
void  FHCalculateSignatures(PFHWIDTHTABLE  pTable,
                                                LPTSHR_INT16       pSigFats,
                                                LPTSHR_INT16       pSigThins,
                                                LPTSHR_INT16       pSigSymbol)
{
    UINT    charI      = 0;
    UINT  fatSig     = 0;
    UINT  thinSig    = 0;
    UINT  symbolSig  = 0;

    DebugEntry(FHCalculateSignatures);

    ASSERT((pTable != NULL));
    ASSERT((pSigFats != NULL));
    ASSERT((pSigThins != NULL));
    ASSERT((pSigSymbol != NULL));

    //
    //  nfSigFats   the sum of the widths (in pels) of the chars
    //              0-9,@-Z,$,%,&. divided by two: the fat chars
    //  nfSigThins  the sum of the widths (in pels) of the chars
    //              0x20->0x7F EXCLUDING those summed in nfSigFats.
    //              Again - divided by two.  The thin chars.
    //  nfSigSymbol The sum of the widths (in pels) of the chars
    //              x80->xFF.
    //

    //
    // Loop for 0-9, some punctuation, A-Z. Then add $,% and &. i.e. mainly
    // fat characters.
    //
    for (charI= NF_ASCII_ZERO; charI<NF_ASCII_Z ; charI++ )
    {
        fatSig += pTable->charWidths[charI];
    }
    fatSig += pTable->charWidths[NF_ASCII_DOLLAR] +
        pTable->charWidths[NF_ASCII_PERCENT] +
        pTable->charWidths[NF_ASCII_AMPERSAND];

    //
    // thin sig covers the rest of the "ascii" characters (x20->7F) not
    // already included in fatSig.
    //
    for (charI= NF_ASCII_FIRST; charI<NF_ASCII_LAST ; charI++ )
    {
        thinSig += pTable->charWidths[charI];
    }
    thinSig -= fatSig;

    //
    // symbolSig covers the "non-ascii" characters (x0->1F, 80->FF)
    //
    for (charI= 0x00; charI<(NF_ASCII_FIRST-1) ; charI++ )
    {
        symbolSig += pTable->charWidths[charI];
    }
    for (charI= NF_ASCII_LAST+1; charI<0xFF ; charI++ )
    {
        symbolSig += pTable->charWidths[charI];
    }
    TRACE_OUT(( "Signatures: symbol %#lx thin %#lx fat %#lx",
             symbolSig, thinSig, fatSig));

    //
    // Halve the fat and thin sigs so that they fit into one byte each.
    //
    fatSig    /= 2;
    thinSig   /= 2;
    if ( (((TSHR_UINT16)symbolSig)==0)
         && (((BYTE)fatSig)==0) && (((BYTE)thinSig)==0))
    {
        //
        // Worry about the faint possibility that all three sums could add
        // up to a value of zero when truncated.
        //
        symbolSig=1;
    }

    //
    // Fill in return pointers.
    //
    *pSigFats   = (TSHR_INT16)fatSig;
    *pSigThins  = (TSHR_INT16)thinSig;
    *pSigSymbol = (TSHR_INT16)symbolSig;

    DebugExitVOID(FHCalculateSignatures);
}



//
// FHEachFontFamily
//
// This callback is called for each font family. We use it to build up a
// list of all the family names.
//
//
// Although wingdi.h defines the first two parameters for an ENUMFONTPROC
// as LOGFONT and TEXTMETRIC (thereby disagreeing with MSDN), tests show
// that the structures are actually as defined in MSDN (i.e.  we get valid
// information when accessing the extended fields)
//
int CALLBACK  FHEachFontFamily
(
    const ENUMLOGFONT   FAR * enumlogFont,
    const NEWTEXTMETRIC FAR * TextMetric,
    int                       FontType,
    LPARAM                    lParam
)
{
    LPFHFAMILIES                  lpFamilies = (LPFHFAMILIES)lParam;

    DebugEntry(FHEachFontFamily);

    ASSERT(!IsBadWritePtr(lpFamilies, sizeof(*lpFamilies)));

    if (lpFamilies->fhcFamilies == FH_MAX_FONTS)
    {
        //
        // We cannot support any more font families so stop enumerating.
        //
        WARNING_OUT(( "Can only handle %u families", FH_MAX_FONTS));
        return(FALSE); // Stop the enumeration
    }

    TRACE_OUT(("FHEachFontFamily:  %s", enumlogFont->elfLogFont.lfFaceName));

    ASSERT(lstrlen(enumlogFont->elfLogFont.lfFaceName) < FH_FACESIZE);
    lstrcpy(lpFamilies->afhFamilies[lpFamilies->fhcFamilies].szFontName,
              enumlogFont->elfLogFont.lfFaceName);

    lpFamilies->fhcFamilies++;

    DebugExitBOOL(FHEachFontFamily, TRUE);
    return(TRUE); // Continue enumerating
}

//
// FHEachFont
//
// This callback is called for each font.  It gathers and stores the font
// details.
//
//
//
// Although wingdi.h defines the first two parameters for an ENUMFONTPROC
// as LOGFONT and TEXTMETRIC (thereby disagreeing with MSDN), tests show
// that the structures are actually as defined in MSDN (i.e.  we get valid
// information when accessing the extended fields)
//
int CALLBACK  FHEachFont(const ENUMLOGFONT   FAR * enumlogFont,
                                      const NEWTEXTMETRIC FAR * TextMetric,
                                      int                       FontType,
                                      LPARAM                    lParam)
{
    HDC             hdc       = (HDC)lParam;
    TSHR_UINT16        fontflags = 0;
    TSHR_UINT16        CodePage  = 0;
    HFONT           hfont;
    HFONT           holdfont  = NULL;
    TEXTMETRIC      tm;
    BOOL            fAcceptFont;
    int             rc;

    DebugEntry(FHEachFont);

    TRACE_OUT(( "Family name: %s", enumlogFont->elfLogFont.lfFaceName));
    TRACE_OUT(( "Full name: %s", enumlogFont->elfFullName));

    if (g_fhFonts->fhNumFonts >= FH_MAX_FONTS)
    {
        //
        // We cannot support any more fonts so stop enumerating.
        //
        WARNING_OUT(( "Can only handle %u fonts", FH_MAX_FONTS));
        rc = 0;
        DC_QUIT; // Stop the enumeration
    }

    //
    // We want to continue...
    //
    rc = 1;

    //
    // Don't bother with this if it's a bold/italic variant.
    //
    // NOTE:
    // The elfFullName field is only valid for TrueType fonts on Win95.  For
    // non TrueType fonts, assume that the full name and face name are the
    // same.
    //
    if (!g_asWin95 || (FontType & TRUETYPE_FONTTYPE))
    {
        if (lstrcmp(enumlogFont->elfLogFont.lfFaceName, (LPCSTR)enumlogFont->elfFullName))
        {
            TRACE_OUT(( "Discarding variant: %s", enumlogFont->elfFullName));
            DC_QUIT;                   // Jump out, but don't stop enumerating!
        }
    }

    //
    // We now accumulate information on all local fonts in all CodePages.
    // This relies on the subsequent sending of local fonts and matching of
    // remote fonts taking into account the CodePage capabilities of the
    // systems.
    //

    //
    // On this pass we copy the details into our structure.
    //
    if (FontType & TRUETYPE_FONTTYPE)
    {
        //
        // This is a truetype font, which we simply accept without double
        // checking its metrics.  (Metric double checking to exclude
        // duplicates is of most relevance to fixed size fonts, which are
        // explicitly optimised for one screen size)
        //
        fAcceptFont = TRUE;

        //
        // Indicate TrueType (this will go in the NETWORKFONT structure
        // (i.e.  over the wire)
        //
        fontflags |= NF_TRUE_TYPE;

        //
        // Signal that we did not call CreateFont for this font.
        //
        hfont = NULL;
    }
    else
    {
        //
        // We create a font from the logical description, and select it so
        // that we can query its metrics.
        //
        // The point of this is that it allows us to identify fonts where
        // the logical font description is not a unique description of this
        // font, and hence if we cannot get to this font via a logical font
        // description, we cannot get to it at all.
        //
        // If we cannot get to it, then we cannot claim to support it.
        //
        // This selection operation is SLOW - of the order of a couple of
        // seconds in some extreme cases (for example where the font is
        // stored on a network drive, and pageing has to take place) and
        // when you can have hundreds of fonts this can add up to a
        // considerable time.
        //
        // Hence we only do the selection for non truetype fonts because
        // these are the fonts where it is easy to get multiple fonts of
        // the same logical description, though designed for different
        // display drivers.
        //

        //
        // Create a font from the logical font, so we can see what font
        // Windows actually choses.
        //
        hfont    = CreateFontIndirect(&enumlogFont->elfLogFont);
        holdfont = SelectFont(hdc, hfont);

        //
        // Find the metrics of the font that Windows has actually selected.
        //
        GetTextMetrics(hdc, &tm);

        //
        // Double check the aspect ratios - enumerate returns all fonts,
        // but it is possible to have fonts that are never matched by
        // Windows due to duplications.
        //
        fAcceptFont = ((tm.tmDigitizedAspectX == TextMetric->tmDigitizedAspectX)
                   &&  (tm.tmDigitizedAspectY == TextMetric->tmDigitizedAspectY));
    }

    //
    // Trace out the full text metrics for debugging.
    //

    if (fAcceptFont)
    {
        //
        // This font is accepted.
        //
        //
        // Determine the font flags settings.
        //
        if ((TextMetric->tmPitchAndFamily & TMPF_FIXED_PITCH) == 0)
        {
            //
            // Setting the TMPF_FIXED_PITCH bit in the text metrics is used
            // to indicate that the font is NOT fixed pitch.  What a
            // wonderfully named bit (see Microsoft CD for explanation).
            //
            fontflags |= NF_FIXED_PITCH;
        }

        if ((FontType & RASTER_FONTTYPE)         ||
            (FontType & TRUETYPE_FONTTYPE) == 0)
        {
            //
            // This is a raster font, but not a truetype font so it must be
            // of fixed size.
            //
            fontflags |= NF_FIXED_SIZE;
        }

        //
        // Get the font CodePage.  SFRFONT: must map from CharSet to
        // Codepage.  For now we only support ANSI and OEM charsets.  This
        // will need to change to support e.g BiDi/Arabic
        //
        CodePage = TextMetric->tmCharSet;
        if (CodePage == ANSI_CHARSET)
        {
            TRACE_OUT(( "ANSI codepage"));
            CodePage = NF_CP_WIN_ANSI;
        }
        else if (CodePage == OEM_CHARSET)
        {
            TRACE_OUT(( "OEM codepage"));
            CodePage = NF_CP_WIN_OEM;
        }
        else if (CodePage == SYMBOL_CHARSET)
        {
            TRACE_OUT(("Symbol codepage"));
            CodePage = NF_CP_WIN_SYMBOL;
        }
        else
        {
            TRACE_OUT(( "Charset %hu, unknown codepage", CodePage));
            CodePage = NF_CP_UNKNOWN;
        }


        //
        //
        // SFRFONT: We have replaced the "old" checksum which was based on
        // the actual bits making up the font to one based on the widths of
        // characters in the font.  The intention is that we use this to
        // ensure that the actual characters in the local font and in the
        // remote font which matches it are all the same width as each
        // other.
        //
        // We calculate this sum for all fonts (not just non-truetype as
        // before) because in cross platform calls with approximate font
        // matching it applies to fonts of all types.
        //
        //

        //
        //
        // There is considerable confusion caused by the terminology for
        // fonts characteristics.  The protocol uses two values MAXHEIGHT
        // and AVEHEIGHT.  In fact neither of these names is accurate
        // (MAXHEIGHT is not the maximum height of a char; and AVEHEIGHT is
        // not the average height of all chars).
        //
        // SFRFONT: we have added maxAscent to the protocol.  This is the
        // height of a capital letter (such as eM!) PLUS any internal
        // leading.  This value allows remote boxes to find the baseline -
        // the point at which the bottommost pel of a letter with no
        // descenders (e.g.  capital M) is to be drawn.  This is needed
        // because not all boxes in the call follow the windows convention
        // of specifying the start of text as being the top-left corner of
        // the first character cell.  maxAscent == tmAscent in the
        // TextMetric.
        //
        //
        FHAddFontToLocalTable((LPSTR)enumlogFont->elfLogFont.lfFaceName,
                              (TSHR_UINT16)fontflags,
                              (TSHR_UINT16)CodePage,
                              (TSHR_UINT16)TextMetric->tmHeight,
                              (TSHR_UINT16)(TextMetric->tmHeight -
                                           TextMetric->tmInternalLeading),
                              (TSHR_UINT16)TextMetric->tmAveCharWidth,
                              (TSHR_UINT16)TextMetric->tmDigitizedAspectX,
                              (TSHR_UINT16)TextMetric->tmDigitizedAspectY,
                              (TSHR_UINT16)TextMetric->tmAscent);
    }
    else
    {
        //
        // Windows returns a different font when we use this logical font
        // description - presumably because of duplicate fonts.  We
        // therfore must not claim to support this particular font.
        //
        TRACE_OUT(( "Discarding hidden font %s",
                 enumlogFont->elfLogFont.lfFaceName));
    }

    if (hfont)
    {
        //
        // We called CreateFont in processing this font, so now delete it
        // to clean up.
        //
        SelectFont(hdc, holdfont);

        //
        // We have finished with the font so delete it.
        //
        DeleteFont(hfont);
    }

DC_EXIT_POINT:
    DebugExitDWORD(FHEachFont, rc);
    return(rc);
}


//
// FHConsiderAllLocalFonts
//
// Considers the details of each of the fonts on the local system, and if
// acceptable adds them to the local font list.
//
//
void  FHConsiderAllLocalFonts(void)
{
    HDC             hdcDesktop;
    UINT            i;
    LPFONTNAME      newFontList;
    LPFHFAMILIES    lpFamilies = NULL;

    DebugEntry(FHConsiderAllLocalFonts);

    g_fhFonts->fhNumFonts       = 0;

    //
    // We can't enumerate all the fonts directly; we have to enumerate the
    // family names, then the fonts within each family.
    //
    // This alloc assumes the worst case memory-wise (i.e.  each
    // family contains a single font) and therefore we will usually
    // allocate more memory than we need.  We use LocalReAlloc later to fix
    // this.
    //
    lpFamilies = new FHFAMILIES;
    if (!lpFamilies)
    {
        ERROR_OUT(("Failed to alloc FHFAMILIES"));
        DC_QUIT;
    }

    SET_STAMP(lpFamilies, FHFAMILIES);

    hdcDesktop = GetWindowDC(HWND_DESKTOP);

    //
    // Find all the font family names.
    //
    lpFamilies->fhcFamilies = 0;
    EnumFontFamilies(hdcDesktop, NULL,(FONTENUMPROC)FHEachFontFamily,
                           (LPARAM)lpFamilies);

    TRACE_OUT(("Found %d font families ", lpFamilies->fhcFamilies));

    //
    // Now enumerate each font for each family
    //
    for (i = 0; i < lpFamilies->fhcFamilies; i++)
    {
        EnumFontFamilies(hdcDesktop, lpFamilies->afhFamilies[i].szFontName,
                               (FONTENUMPROC)FHEachFont,
                               (LPARAM)hdcDesktop);
    }

    ReleaseDC(HWND_DESKTOP, hdcDesktop);

DC_EXIT_POINT:
    //
    // Having considered all the fonts, we can now free the list of family
    // names.
    //
    if (lpFamilies)
    {
        delete lpFamilies;
    }

    DebugExitVOID(FHConsiderAllLocalFonts);
}

//
// FHGenerateFontWidthTable
//
BOOL  FHGenerateFontWidthTable(PFHWIDTHTABLE pTable,
                                                   LPLOCALFONT    pFontInfo,
                                                   UINT        fontHeight,
                                                   UINT        fontWidth,
                                                   UINT        fontWeight,
                                                   UINT        fontFlags,
                                                   LPTSHR_UINT16     pMaxAscent)

{
    HFONT     hNewFont;
    HFONT     hOldFont;
    BOOL        gdiRC;
    UINT        i;
    HDC         cachedDC;
    BOOL        localRC;
    BOOL        functionRC;
    TEXTMETRIC  textmetrics;
    int         width;
    UINT        aFontSizes[256];

    DebugEntry(FHGenerateFontWidthTable);

    //
    // Set the return value to FALSE (unsuccessful).  We will set it to
    // TRUE later if the function succeeds.
    //
    functionRC = FALSE;

    //
    // Set the old font handle to NULL.  If this is not NULL at the exit
    // point of this function then we will select it back into the cachedDC
    // device context.
    //
    hOldFont = NULL;

    //
    // Set the new font handle to NULL.  If this is not NULL at the exit
    // point of this function then the new font will be deleted.
    //
    hNewFont = NULL;

    //
    // Get a cached DC with which to do the query.
    //
    cachedDC = GetDC(HWND_DESKTOP);
    if (cachedDC == NULL)
    {
        WARNING_OUT(( "Failed to get DC"));
        DC_QUIT;
    }

    //
    // Get all the info we need from the local font table.
    //

    localRC = FH_CreateAndSelectFont(cachedDC,
                                    &hNewFont,
                                    &hOldFont,
                                    pFontInfo->RealName,
                                    pFontInfo->Details.nfCodePage,
                                    pFontInfo->lMaxBaselineExt,
                                    fontHeight,
                                    fontWidth,
                                    fontWeight,
                                    fontFlags);

    if (localRC == FALSE)
    {
        ERROR_OUT(( "Failed to create/select font %s, %u, %u",
                   pFontInfo->RealName,
                   fontHeight,
                   fontWidth));
        DC_QUIT;
    }

    //
    // Determine if the current font is a truetype font.
    //
    GetTextMetrics(cachedDC, &textmetrics);

    if (textmetrics.tmPitchAndFamily & TMPF_TRUETYPE)
    {
        //
        // Truetype fonts are ABC spaced.
        //
        ABC     abc[256];

        TRACE_OUT(("TrueType font %s, first char %d last char %d",
            pFontInfo->RealName, (UINT)(WORD)textmetrics.tmFirstChar,
            (UINT)(WORD)textmetrics.tmLastChar));

        //
        // Get all widths in one call - faster than getting them separately
        //
        GetCharABCWidths(cachedDC, 0, 255, abc);

        for (i = 0; i < 256; i++)
        {
            width = abc[i].abcA + abc[i].abcB + abc[i].abcC;

            if ((width < 0) || (width > 255))
            {
                //
                // Width is outside the range we can cope with, so quit.
                //
                TRACE_OUT(( "Width %d is outside range", width));
                DC_QUIT;
            }
            pTable->charWidths[i] = (BYTE)width;
        }

    }
    else
    {
        TRACE_OUT(( "Non-truetype font"));

        //
        // Check if the font is fixed or variable pitch - note that a clear
        // bit indicates FIXED, not the reverse which you might expect!
        //
        if ((textmetrics.tmPitchAndFamily & TMPF_FIXED_PITCH) == 0)
        {
            //
            // No need to call GetCharWidth for a fixed width font (and
            // more to the point it can return us bad values if we do)
            //
            for (i = 0; i < 256; i++)
            {
                aFontSizes[i] = textmetrics.tmAveCharWidth;
            }
        }
        else
        {
            //
            // Query the width of each character in the font.
            //
            ZeroMemory(aFontSizes, sizeof(aFontSizes));
            gdiRC = GetCharWidth(cachedDC,
                                 0,
                                 255,
                                 (LPINT)aFontSizes);
            if (gdiRC == FALSE)
            {
                ERROR_OUT(( "Failed to get char widths for %s, %u, %u",
                            pFontInfo->RealName,
                            fontHeight,
                            fontWidth));
                DC_QUIT;
            }
        }

        //
        // Now copy the widths into the width table.
        // We must adjust the widths to take account of any overhang
        // between characters.
        //
        for (i = 0; i < 256; i++)
        {
            width = aFontSizes[i] - textmetrics.tmOverhang;
            if ((width < 0) || (width > 255))
            {
                TRACE_OUT(( "Width %d is outside range", width));
                DC_QUIT;
            }
            pTable->charWidths[i] = (BYTE)width;
        }
    }

    //
    // The font table has been successfully generated.
    //
    functionRC = TRUE;

    TRACE_OUT(( "Generated font table for: %s", pFontInfo->RealName));

    //
    // Return the maxAscent value, as we have easy access to it here.  This
    // saves us having to create the font again later to find it.
    //
    TRACE_OUT(( "Updating maxAscent %hu -> %hu",
                 *pMaxAscent,
                 (TSHR_UINT16)textmetrics.tmAscent));
    *pMaxAscent = (TSHR_UINT16)textmetrics.tmAscent;

DC_EXIT_POINT:

    if (hOldFont != NULL)
    {
        SelectFont(cachedDC, hOldFont);
    }

    if (hNewFont != NULL)
    {
        DeleteFont(hNewFont);
    }

    if (cachedDC != NULL)
    {
        ReleaseDC(HWND_DESKTOP, cachedDC);
    }

    DebugExitDWORD(FHGenerateFontWidthTable, functionRC);
    return(functionRC);
}

//
// Define a macro to simplify the following code.  This returns the first
// character in the name of the font at position i in the local table.
//

//
// nfFaceName is an array of CHARs, which are SIGNED.  We need to treat them
// as UNSIGNED values, they are indeces from 0 to 255 into the font hash
// table.
//
#define LF_FIRSTCHAR(i)  (BYTE)g_fhFonts->afhFonts[i].Details.nfFaceName[0]

//
// Name:    FHSortAndIndexLocalFonts
//
// Purpose: Sorts local font table by font name and generates an index for
//          quicker searching in the display driver.
//
// Returns: None.
//
// Params:  None.
//
//
void FHSortAndIndexLocalFonts(void)
{
    TSHR_UINT16    thisIndexEntry;
    TSHR_UINT16    fontTablePos;

    DebugEntry(FHSortAndIndexLocalFonts);

    //
    // Check there are actually some fonts to sort/index
    //
    if (0 == g_fhFonts->fhNumFonts)
    {
        WARNING_OUT(( "No fonts to sort/index"));
        DC_QUIT;
    }

    //
    // Use qsort to do the sort.  We sort on the font name, ascending.
    // Therefore we must use STRCMP and not lstrcmp.  The latter sorts
    // by 'word' method, where upper case sorts before lower case.  But
    // our NT driver has no access to a similar routine.  And this code +
    // driver code must be in ssync for the driver to successfully search
    // the sorted font table.
    //

    FH_qsort(g_fhFonts->afhFonts, g_fhFonts->fhNumFonts, sizeof(LOCALFONT));
    TRACE_OUT(( "Sorted local font list"));

    //
    // Now generate the index.  Each element i in the g_fhFonts->afhFontIndex
    // array must indicate the first entry in the local font table
    // beginning with character i.  If there are no fonts beginning with
    // character i, then the element is set to USHRT_MAX (i.e.  a large
    // value).
    //

    //
    // First clear the index table to unused entries.
    //
    for (thisIndexEntry = 0;
         thisIndexEntry < FH_LOCAL_INDEX_SIZE;
         thisIndexEntry++)
    {
        g_fhFonts->afhFontIndex[thisIndexEntry] = USHRT_MAX;
    }

    //
    // Now fill in the useful information.
    //
    // This for loop steps through the index array, using the first
    // character of the first font in the local table as its start point.
    // Since the font table is alphabetically sorted, this will correspond
    // to the first index entry that needs filling in.
    //
    // The terminating condition for this loop may seem a little odd, but
    // works because fontTablePos will always reach a value of g_fhFonts->fhNumFonts
    // before thisIndexEntry gets to the last index element.
    //
    fontTablePos = 0;

    for (thisIndexEntry = LF_FIRSTCHAR(0);
         fontTablePos < g_fhFonts->fhNumFonts;
         thisIndexEntry++)
    {
        //
        // Don't do anything until we get to the index element
        // corresponding to the first character in the font pointed to by
        // fontTablePos.  (We'll be there straight away on the first pass)
        //
        if (thisIndexEntry == LF_FIRSTCHAR(fontTablePos))
        {
            //
            // We've found the first font table entry starting with
            // character thisIndexEntry, so enter it in the index.
            //
            g_fhFonts->afhFontIndex[thisIndexEntry] = fontTablePos;

            //
            // Now zip past the rest of the local font table entries that
            // start with this character, also checking that we haven't got
            // to the end of the font table.
            //
            // If the latter happens, it means we've finished and the check
            // in the for statement will ensure that we exit the loop.
            //
            while ((LF_FIRSTCHAR(fontTablePos) == thisIndexEntry) &&
                   (fontTablePos < g_fhFonts->fhNumFonts))
            {
                fontTablePos++;
            }
        }
    }

    TRACE_OUT(( "Built local font table index"));

DC_EXIT_POINT:
    DebugExitVOID(FHSortAndIndexLocalFonts);
}




//
// FHComp()
// This is a wrapper around strcmp(), which becomes an inline function in
// retail.  It also handles the casting of the LPVOIDs.
//
//
// Compare item 1, item 2
//
int FHComp
(
    LPVOID lpFont1,
    LPVOID lpFont2
)
{
    return(strcmp(((LPLOCALFONT)lpFont1)->Details.nfFaceName,
                   ((LPLOCALFONT)lpFont2)->Details.nfFaceName));
}


//
// FH_qsort(base, num, wid) - quicksort function for sorting arrays
//
// Purpose:
//       quicksort the array of elements
//       side effects:  sorts in place
//
// Entry:
//      char *base = pointer to base of array
//      unsigned num  = number of elements in the array
//      unsigned width = width in bytes of each array element
//
// Exit:
//       returns void
//
// Exceptions:
//




// sort the array between lo and hi (inclusive)

void FH_qsort
(
    LPVOID      base,
    UINT        num,
    UINT        width
)
{
    LPSTR       lo;
    LPSTR       hi;
    LPSTR       mid;
    LPSTR       loguy;
    LPSTR       higuy;
    UINT        size;
    char *lostk[30], *histk[30];
    int stkptr;                 // stack for saving sub-array to be processed

    // Note: the number of stack entries required is no more than
    // 1 + log2(size), so 30 is sufficient for any array

    ASSERT(width);
    if (num < 2)
        return;                 // nothing to do

    stkptr = 0;                 // initialize stack

    lo = (LPSTR)base;
    hi = (LPSTR)base + width * (num-1);        // initialize limits

    // this entry point is for pseudo-recursion calling: setting
    // lo and hi and jumping to here is like recursion, but stkptr is
    // prserved, locals aren't, so we preserve stuff on the stack
recurse:

    size = (UINT)(hi - lo) / width + 1;        // number of el's to sort

    // below a certain size, it is faster to use a O(n^2) sorting method
    if (size <= CUTOFF)
    {
         shortsort(lo, hi, width);
    }
    else
    {
        // First we pick a partititioning element.  The efficiency of the
        // algorithm demands that we find one that is approximately the
        // median of the values, but also that we select one fast.  Using
        // the first one produces bad performace if the array is already
        // sorted, so we use the middle one, which would require a very
        // weirdly arranged array for worst case performance.  Testing shows
        // that a median-of-three algorithm does not, in general, increase
        // performance.

        mid = lo + (size / 2) * width;      // find middle element
        swap(mid, lo, width);               // swap it to beginning of array

        // We now wish to partition the array into three pieces, one
        // consisiting of elements <= partition element, one of elements
        // equal to the parition element, and one of element >= to it.  This
        // is done below; comments indicate conditions established at every
        // step.

        loguy = lo;
        higuy = hi + width;

        // Note that higuy decreases and loguy increases on every iteration,
        // so loop must terminate.
        for (;;) {
            // lo <= loguy < hi, lo < higuy <= hi + 1,
            // A[i] <= A[lo] for lo <= i <= loguy,
            // A[i] >= A[lo] for higuy <= i <= hi

            do
            {
                loguy += width;
            }
            while ((loguy <= hi) && (FHComp(loguy, lo) <= 0));

            // lo < loguy <= hi+1, A[i] <= A[lo] for lo <= i < loguy,
            // either loguy > hi or A[loguy] > A[lo]

            do
            {
                higuy -= width;
            }
            while ((higuy > lo) && (FHComp(higuy, lo) >= 0));

            // lo-1 <= higuy <= hi, A[i] >= A[lo] for higuy < i <= hi,
            // either higuy <= lo or A[higuy] < A[lo]

            if (higuy < loguy)
                break;

            // if loguy > hi or higuy <= lo, then we would have exited, so
            // A[loguy] > A[lo], A[higuy] < A[lo],
            // loguy < hi, highy > lo

            swap(loguy, higuy, width);

            // A[loguy] < A[lo], A[higuy] > A[lo]; so condition at top
            // of loop is re-established
        }

        //     A[i] >= A[lo] for higuy < i <= hi,
        //     A[i] <= A[lo] for lo <= i < loguy,
        //     higuy < loguy, lo <= higuy <= hi
        // implying:
        //     A[i] >= A[lo] for loguy <= i <= hi,
        //     A[i] <= A[lo] for lo <= i <= higuy,
        //     A[i] = A[lo] for higuy < i < loguy

        swap(lo, higuy, width);     // put partition element in place

        // OK, now we have the following:
        //    A[i] >= A[higuy] for loguy <= i <= hi,
        //    A[i] <= A[higuy] for lo <= i < higuy
        //    A[i] = A[lo] for higuy <= i < loguy

        // We've finished the partition, now we want to sort the subarrays
        // [lo, higuy-1] and [loguy, hi].
        // We do the smaller one first to minimize stack usage.
        // We only sort arrays of length 2 or more.

        if ( higuy - 1 - lo >= hi - loguy ) {
            if (lo + width < higuy) {
                lostk[stkptr] = lo;
                histk[stkptr] = higuy - width;
                ++stkptr;
            }                           // save big recursion for later

            if (loguy < hi) {
                lo = loguy;
                goto recurse;           // do small recursion
            }
        }
        else {
            if (loguy < hi) {
                lostk[stkptr] = loguy;
                histk[stkptr] = hi;
                ++stkptr;               // save big recursion for later
            }

            if (lo + width < higuy) {
                hi = higuy - width;
                goto recurse;           // do small recursion
            }
        }
    }

    // We have sorted the array, except for any pending sorts on the stack.
    // Check if there are any, and do them.

    --stkptr;
    if (stkptr >= 0) {
        lo = lostk[stkptr];
        hi = histk[stkptr];
        goto recurse;           // pop subarray from stack
    }
    else
        return;                 // all subarrays done
}


//
// shortsort(hi, lo, width) - insertion sort for sorting short arrays
//
// Purpose:
//       sorts the sub-array of elements between lo and hi (inclusive)
//       side effects:  sorts in place
//       assumes that lo < hi
//
// Entry:
//      char *lo = pointer to low element to sort
//      char *hi = pointer to high element to sort
//      unsigned width = width in bytes of each array element
//
// Exit:
//       returns void
//
// Exceptions:
//

void shortsort
(
    char *lo,
    char *hi,
    unsigned int width
)
{
    char *p, *max;

    // Note: in assertions below, i and j are alway inside original bound of
    // array to sort.

    while (hi > lo) {
        // A[i] <= A[j] for i <= j, j > hi
        max = lo;
        for (p = lo+width; p <= hi; p += width) {
            // A[i] <= A[max] for lo <= i < p
            if (FHComp(p, max) > 0)
            {
                max = p;
            }
            // A[i] <= A[max] for lo <= i <= p
        }

        // A[i] <= A[max] for lo <= i <= hi

        swap(max, hi, width);

        // A[i] <= A[hi] for i <= hi, so A[i] <= A[j] for i <= j, j >= hi

        hi -= width;

        // A[i] <= A[j] for i <= j, j > hi, loop top condition established
    }
    // A[i] <= A[j] for i <= j, j > lo, which implies A[i] <= A[j] for i < j,
    // so array is sorted
}


//
// swap(a, b, width) - swap two elements
//
// Purpose:
//     swaps the two array elements of size width
//
// Entry:
//       char *a, *b = pointer to two elements to swap
//       unsigned width = width in bytes of each array element
//
// Exit:
//       returns void
//
// Exceptions:
//

 void swap (
    char *a,
    char *b,
    unsigned int width
    )
{
    char tmp;

    if ( a != b )
        // Do the swap one character at a time to avoid potential alignment
        // problems.
        while ( width-- ) {
            tmp = *a;
            *a++ = *b;
            *b++ = tmp;
        }
}