/******************************Module*Header*******************************\
* Module Name: pfeobj.cxx
*
* Non-inline methods for physical font entry objects.
*
* Created: 30-Oct-1990 09:32:48
* Author: Gilman Wong [gilmanw]
*
* Copyright (c) 1990-1995 Microsoft Corporation
\**************************************************************************/
// #pragma warning (disable: 4509)

#include "precomp.hxx"
#include "flhack.hxx"


#ifdef FE_SB
INT
_CRTAPI1 CompareRoutine(WCHAR *pwc1, WCHAR *pwc2)
{
    
    return(*pwc1-*pwc2);
    
}
#endif

/******************************Public*Routine******************************\
*
* ULONG cComputeGISET
*
* similar to cComputeGlyphSet in mapfile.c, computes the number of
* distinct glyph handles in a font and the number of runs, ie. number of
* contiguous ranges of glyph handles
*
*
* History:
*  03-Aug-1995 -by- Bodin Dresevic [BodinD]
* Wrote it.
\**************************************************************************/


ULONG cComputeGISET (
    USHORT * pgi,
    ULONG    cgi,
    GISET  * pgiset,
    ULONG    cGiRuns
    )
{
    ULONG    iRun, iFirst, iFirstNext;
    ULONG    cgiTotal = 0, cgiRun;

// now compute cRuns if pgiset == 0 and fill the glyphset if pgiset != 0

    for (iFirst = 0, iRun = 0; iFirst < cgi; iRun++, iFirst = iFirstNext)
    {
    // find iFirst corresponding to the next range.

        for (iFirstNext = iFirst + 1; iFirstNext < cgi; iFirstNext++)
        {
            if ((pgi[iFirstNext] - pgi[iFirstNext - 1]) > 1)
                break;
        }

    // note that this line here covers the case when there are repetitions
    // in the pgi array.

        cgiRun    = pgi[iFirstNext-1] - pgi[iFirst] + 1;
        cgiTotal += cgiRun;

        if (pgiset != NULL)
        {
            pgiset->agirun[iRun].giLow  = pgi[iFirst];
            pgiset->agirun[iRun].cgi = (USHORT) cgiRun;
        }
    }

// store results if need be

    if (pgiset != NULL)
    {
        ASSERTGDI(iRun == cGiRuns, "gdisrv! iRun != cRun\n");

        pgiset->cGiRuns  = cGiRuns;

    // init the sum before entering the loop

        pgiset->cgiTotal = cgiTotal;
    }

    return iRun;
}

/******************************Public*Routine******************************\
*
* bComputeGISET, similar to ComputeGlyphSet, only for gi's
*
* History:
*  03-Aug-1995 -by- Bodin Dresevic [BodinD]
* Wrote it.
\**************************************************************************/

#include "stdlib.h"


VOID vSortPlacebo
(
USHORT        *pwc,
ULONG          cChar
)
{
    ULONG i;

    for (i = 1; i < cChar; i++)
    {
    // upon every entry to this loop the array 0,1,..., (i-1) will be sorted

        INT j;
        WCHAR wcTmp = pwc[i];

        for (j = i - 1; (j >= 0) && (pwc[j] > wcTmp); j--)
        {
            pwc[j+1] = pwc[j];
        }
        pwc[j+1] = wcTmp;
    }
}




BOOL bComputeGISET(IFIMETRICS * pifi, FD_GLYPHSET *pfdg, GISET **ppgiset)
{
    BOOL bRet = TRUE;
    *ppgiset = NULL;
    GISET *pgiset;

    if (pfdg->flAccel & (GS_16BIT_HANDLES | GS_8BIT_HANDLES))
    {
    // first check if this is an accelerated case where handles are the same
    // as glyph indicies

        ULONG cig = 0;
        if (pifi->cjIfiExtra > offsetof(IFIEXTRA, cig))
        {
            cig = ((IFIEXTRA *)(pifi + 1))->cig;
        }

        if (cig)
        {
        // one run only from zero to (cig-1);

            if (pgiset = (GISET*)PALLOCMEM(offsetof(GISET,agirun) + 1 * sizeof(GIRUN),'slgG'))
            {
            // now fill in the array of runs

                pgiset->cgiTotal = cig;
                pgiset->cGiRuns = 1;
                pgiset->agirun[0].giLow = 0;
                pgiset->agirun[0].cgi = (USHORT)cig;

            // we are done now

                *ppgiset = pgiset;
            }
            else
            {
                bRet = FALSE;
            }
        }
        else
        {
        // one of the goofy fonts, we will do as before

            USHORT *pgi, *pgiBegin;

        // aloc tmp buffer to contain glyph handles of all glyphs in the font

            if (pgiBegin = (USHORT*)PALLOCMEM(pfdg->cGlyphsSupported * sizeof(USHORT),'slgG'))
            {
                pgi = pgiBegin;
                for (ULONG iRun = 0; iRun < pfdg->cRuns; iRun++)
                {
                    HGLYPH *phg, *phgEnd;
                    phg = pfdg->awcrun[iRun].phg;

                    if (phg) // non unicode handles
                    {
                        phgEnd = phg + pfdg->awcrun[iRun].cGlyphs;
                        for ( ; phg < phgEnd; pgi++, phg++)
                            *pgi = (USHORT)(*phg);
                    }
                    else // unicode handles
                    {
                        USHORT wcLo = pfdg->awcrun[iRun].wcLow;
                        USHORT wcHi = wcLo + pfdg->awcrun[iRun].cGlyphs - 1;
                        for ( ; wcLo <= wcHi; wcLo++, phg++)
                            *pgi = wcLo;
                    }
                }

            // now sort the array of glyph indicies. This array will be mostly
            // sorted so that our algorithm is efficient

#ifdef FE_SB
            qsort((void*)pgiBegin, pfdg->cGlyphsSupported, sizeof(WORD),
                  (int (__cdecl *)(const void *, const void *))CompareRoutine);
                
#else
            //qsort((void*)pgiBegin, sizeof(USHORT), sizeof(USHORT), NULL);
            vSortPlacebo(pgiBegin,pfdg->cGlyphsSupported);
#endif

            // once the array is sorted we can easily compute the number of giRuns

                ULONG cGiRun = cComputeGISET(pgiBegin, pfdg->cGlyphsSupported, NULL, 0);

                if (pgiset = (GISET*)PALLOCMEM(offsetof(GISET,agirun) + cGiRun * sizeof(GIRUN),'slgG'))
                {
                // now fill in the array of runs

                    cComputeGISET(pgiBegin, pfdg->cGlyphsSupported, pgiset, cGiRun);
                    *ppgiset = pgiset;
                }
                else
                {
                    bRet = FALSE;
                }

                VFREEMEM(pgiBegin);
            }
            else
            {
                bRet = FALSE;
            }
        }
    }
    return bRet;
}



//
// This is used to give ppfe->pkp something to point to if a driver
// error occurs.  That way, we won't waste time calling the driver
// again.
//

FD_KERNINGPAIR gkpNothing = { 0, 0, 0 };

static ULONG ulTimerPFE = 0;

/******************************Public*Routine******************************\
* PFEMEMOBJ::PFEMEMOBJ()                                                   *
*                                                                          *
* Constructor for physical font entry memory object.                       *
*                                                                          *
* History:                                                                 *
*  30-Oct-1990 -by- Gilman Wong [gilmanw]                                  *
* Wrote it.                                                                *
\**************************************************************************/

void PFEMEMOBJ::ctHelper()
{
    fs = 0;

    ppfe = (PFE *) HmgAlloc(
        offsetof(PFE,aiFamilyName) + gcfsCharSetTable * sizeof(BYTE),
        PFE_TYPE, HMGR_ALLOC_ALT_LOCK | HMGR_MAKE_PUBLIC);
}


/******************************Public*Routine******************************\
* PFEMEMOBJ::~PFEMEMOBJ()                                                  *
*                                                                          *
* Destructor for physical font entry memory object.                        *
*                                                                          *
* History:                                                                 *
*  30-Oct-1990 -by- Gilman Wong [gilmanw]                                  *
* Wrote it.                                                                *
\**************************************************************************/

void PFEMEMOBJ::dtHelper()
{
    if ((ppfe != PPFENULL) && !(fs & PFEMO_KEEPIT))
    {
        if (ppfe->pgiset)
            VFREEMEM(ppfe->pgiset); // fix this, do mem alloc in one attempt

        HmgFree((HOBJ) ppfe->hGet());
    }
}

/******************************Public*Routine******************************\
* VOID PFEOBJ::vDelete()                                                   *
*                                                                          *
* Destroy the PFE physical font entry object.                              *
*                                                                          *
* History:                                                                 *
*  30-Oct-1990 -by- Gilman Wong [gilmanw]                                  *
* Wrote it.                                                                *
\**************************************************************************/

VOID PFEOBJ::vDelete(PFECLEANUP *ppfec)
{

// Save driver allocated resources in PFECLEANUP so that we can later
// call the driver to free them.

    ppfec->pfdg  = ppfe->pfdg;
    ppfec->idfdg = ppfe->idfdg;

    ppfec->pifi  = ppfe->pifi;
    ppfec->idifi = ppfe->idifi;

    ppfec->pkp   = ppfe->pkp;
    ppfec->idkp  = ppfe->idkp;

    if (ppfe->pgiset)
    {
        VFREEMEM(ppfe->pgiset);
        ppfe->pgiset = NULL;
    }

// Free object memory and invalidate pointer.

    HmgFree((HOBJ) ppfe->hGet());
    ppfe = PPFENULL;
}

/******************************Public*Routine******************************\
* PFEOBJ::flFontType()
*
* Computes the flags defining the type of this font.  Allowed flags are
* identical to the flType flags returned in font enumeration.
*
* Return:
*   The flags.
*
* History:
*  04-Mar-1992 -by- Gilman Wong [gilmanw]
* Wrote it.
\**************************************************************************/

FLONG PFEOBJ::flFontType()
{
    FLONG flRet;
    IFIOBJ ifio(pifi());

// Compute the FontType flags, simulations are irrelevant

    flRet =
      ifio.bTrueType() ?
        TRUETYPE_FONTTYPE : (ifio.bBitmap() ? RASTER_FONTTYPE : 0);

// Add the device flag if this is also a device specific font.

    flRet |= (bDeviceFont()) ? DEVICE_FONTTYPE : 0;

    return (flRet);
}


/******************************Public*Routine******************************\
* PFEOBJ::efstyCompute()
*
* Compute the ENUMFONTSTYLE from the IFIMETRICS.
*
* Returns:
*   The ENUMFONTSTYLE of font.  Note that EFSTYLE_SKIP and EFSTYLE_OTHER are
*   not legal return values for this function.  These values are used only
*   to mark fonts for which another font already exists that fills our
*   category for a given enumeration of a family.
*
* History:
*  04-Aug-1992 -by- Gilman Wong [gilmanw]
* Wrote it.
\**************************************************************************/

ENUMFONTSTYLE PFEOBJ::efstyCompute()
{
    IFIOBJ ifio(pifi());

    switch (ifio.fsSelection() & (FM_SEL_ITALIC | FM_SEL_BOLD) )
    {
        case FM_SEL_ITALIC:
            return EFSTYLE_ITALIC;

        case FM_SEL_BOLD:
            return EFSTYLE_BOLD;

        case FM_SEL_ITALIC | FM_SEL_BOLD:
            return EFSTYLE_BOLDITALIC;

        default:
            return EFSTYLE_REGULAR;
    }
}


/******************************Public*Routine******************************\
* COUNT PFEOBJ::cKernPairs                                                 *
*                                                                          *
* Retrieve the pointer to the array of kerning pairs for this font face.   *
* The kerning pair array is loaded on demand, so it may or may not already *
* be cached in the PFE.                                                    *
*                                                                          *
* Returns:                                                                 *
*   Count of kerning pairs.                                                *
*                                                                          *
* History:                                                                 *
*  Mon 22-Mar-1993 21:31:15 -by- Charles Whitmer [chuckwh]                 *
* WARNING: Never access a pkp (pointer to a kerning pair) without an       *
* exception handler!  The kerning pairs could be living in a file across   *
* the net or even on removable media.  I've added the try-except here.     *
*                                                                          *
*  29-Oct-1992 -by- Gilman Wong [gilmanw]                                  *
* Wrote it.                                                                *
\**************************************************************************/

COUNT PFEOBJ::cKernPairs(FD_KERNINGPAIR **ppkp)
{
//
// If the pointer cached in the PFE isn't NULL, we already have the answer.
//
    if ( (*ppkp = ppfe->pkp) != (FD_KERNINGPAIR *) NULL )
        return ppfe->ckp;

//
// Create a PFFOBJ.  Needed to create driver user object as well as
// provide info needed to call driver function.
//
    PFFOBJ pffo(pPFF());
    ASSERTGDI(pffo.bValid(), "gdisrv!cKernPairsPFEOBJ(): invalid PPFF\n");

    PDEVOBJ pdo(pffo.hdev());

    if ( (ppfe->pkp = (FD_KERNINGPAIR *) (*PPFNDRV(pdo, QueryFontTree)) (
                            pffo.dhpdev(),
                            pffo.hff(),
                            ppfe->iFont,
                            QFT_KERNPAIRS,
                            &ppfe->idkp)) == (FD_KERNINGPAIR *) NULL )
    {
    //
    // Font has no kerning pairs and didn't even bother to send back
    // an empty list. By setting pointer to a zeroed FD_KERNINGPAIR and
    // setting count to zero, we will bail out early and avoid calling
    // the driver.
    //
        ppfe->pkp = &gkpNothing;
        ppfe->ckp = 0;

        return 0;
    }

// Find the end of the kerning pair array (indicated by a zeroed out
// FD_KERNINGPAIR structure).

    FD_KERNINGPAIR *pkpEnd = ppfe->pkp;

// Be careful, the table isn't guaranteed to stay around!

#if !defined(_ALPHA_)
    __try
#endif
    {
        while ((pkpEnd->wcFirst) || (pkpEnd->wcSecond) || (pkpEnd->fwdKern))
            pkpEnd += 1;
    }
#if !defined(_ALPHA_)
    __except (EXCEPTION_EXECUTE_HANDLER)
    {
        pkpEnd = ppfe->pkp = &gkpNothing;
    }
#endif

// Return the kerning pair pointer.

    *ppkp = ppfe->pkp;

//
// Return count (difference between the beginning and end pointers).
//
    return (ppfe->ckp = pkpEnd - ppfe->pkp);
}


/******************************Public*Routine******************************\
* bValidFont
*
* Last minute sanity checks to prevent a font that may crash the system
* from getting in.  We're primarily looking for things like potential
* divide-by-zero errors, etc.
*
* History:
*  30-Apr-1993 -by- Gilman Wong [gilmanw]
* Wrote it.
\**************************************************************************/

BOOL bValidFont(IFIMETRICS *pifi)
{
    BOOL bRet = TRUE;

// Em height is used to compute scaling factors.  Must not be zero or
// divide-by-zero may result.

    if (pifi->fwdUnitsPerEm == 0)
    {
        WARNING("bValidFont(): fwdUnitsPerEm is zero\n");
        bRet = FALSE;
    }

// Font height is used to compute scaling factors.  Must not be zero or
// divide-by-zero may result.

    if ((pifi->fwdWinAscender + pifi->fwdWinDescender) == 0)
    {
        WARNING("bValidFont(): font height is zero\n");
        bRet = FALSE;
    }

    return bRet;
}


/******************************Public*Routine******************************\
* BOOL PFEMEMOBJ::bInit
*
* This function copies data into the PFE from the supplied buffer.  The
* calling routine should use the PFEMEMOBJ to create a PFE large enough
*
* History:
*  14-Jan-1991 -by- Gilman Wong [gilmanw]
* Wrote it.
\**************************************************************************/

BOOL PFEMEMOBJ::bInit
(
    PPFF         pPFF,          // handle to root PFF
    ULONG        iFont,         // index of font
    FD_GLYPHSET *pfdg,          // ptr to wc-->hg map
    ULONG        idfdg,         // driver ID for wc-->hg map
    PIFIMETRICS  pifi,          // ptr to IFIMETRICS
    ULONG        idifi,         // driver ID for IFIMETRICS
    BOOL        bDeviceFont    // mark as device font
#ifdef FE_SB
    ,BOOL         bEUDC          // mark as EUDC font
#endif
)
{
// Check font's validity.  This is not a comprehensive check, but rather
// a last minute check for things that may make the engine crash.  Each
// font/device driver still needs to make an effort to weed out its own
// bad fonts.

    if (!bValidFont(pifi))
    {
        WARNING("PFEMEMOBJ::bInit(): rejecting REALLY bad font\n");
        return FALSE;
    }

// init non-table stuff

    ppfe->pPFF        = pPFF;
    ppfe->iFont       = iFont;
    ppfe->pfdg        = pfdg;
    ppfe->idfdg       = idfdg;
    ppfe->pifi        = pifi;
    ppfe->idifi       = idifi;
    ppfe->pkp         = (FD_KERNINGPAIR *) NULL;
    ppfe->idifi       = (ULONG) NULL;
    ppfe->ckp         = 0;
    ppfe->flPFE       = 0;
    ppfe->pid         = 0;


    if (bDeviceFont)
    {
        ppfe->flPFE |= PFE_DEVICEFONT;
    }
    else if(pPFF->ppfv && (pPFF->ppfv[0]->pwszPath == NULL))
    {
    // CAUTION: It is enough to check one font only to determine if remote

        ppfe->flPFE |= PFE_REMOTEFONT;
        ppfe->pid = W32GetCurrentPID();
    }

    IFIOBJ ifio(ppfe->pifi);

#ifdef FE_SB
    if( bEUDC )
    {
        ppfe->flPFE |= PFE_EUDC;
    }

// mark it as a SBCS system font if the facename is right

    if((!_wcsicmp(ifio.pwszFaceName(),L"SYSTEM") ||
        !_wcsicmp(ifio.pwszFaceName(),L"FIXEDSYS") ||
        !_wcsicmp(ifio.pwszFaceName(),L"TERMINAL")))
    {
        ppfe->flPFE |= PFE_SBCS_SYSTEM;
    }

// we need to know quickly if this is vertical or not

    ppfe->bVerticalFace = ( pwszFaceName()[0] == (WCHAR) '@' ) ? TRUE : FALSE;

// Initialize EUDC QUICKLOOKUP Table
//
// These field was used, if this font is loaded as FaceName/Default linked EUDC.
//

    ppfe->ql.puiBits = NULL;
    ppfe->ql.wcLow   = 1;
    ppfe->ql.wcHigh  = 0;

#endif

// Record and increment the time stamp.

    ppfe->ulTimeStamp = ulTimerPFE;
    InterlockedIncrement((LONG *) &ulTimerPFE);

// Precalculate stuff from the IFIMETRICS.


    ppfe->iOrientation = ifio.lfOrientation();

// Compute UFI stuff

    if( ifio.TypeOneID() )
    {
        ppfe->ufi.Index = ifio.TypeOneID();
        ppfe->ufi.CheckSum = TYPE1_FONT_TYPE;
    }
    else
    {
        ppfe->ufi.CheckSum = pPFF->ulCheckSum;
        ppfe->ufi.Index = iFont;
    }

// init the GISET

    if (!bComputeGISET(pifi, pfdg, &ppfe->pgiset))
        return FALSE;

// initialize cAlt for this family name, the number of entries in font sub
// table that point to this fam name.

    ppfe->cAlt = 0;

// only tt fonts with multiple charsets can be multiply enumerated
// as being both themselves and whatever font sub table claims they are

    if (ppfe->pifi->dpCharSets)
    {
        PFONTSUB pfs = gpfsTable;
        PFONTSUB pfsEnd = gpfsTable + gcfsTable;
        WCHAR    awchCapName[LF_FACESIZE];

    // Want case insensitive search, so capitalize the name.

        cCapString(awchCapName, ifio.pwszFamilyName() , LF_FACESIZE);

    // Scan through the font substitution table for the key string.

        PWCHAR pwcA;
        PWCHAR pwcB;

        for (; pfs < pfsEnd; pfs++)
        {
        // Do the following inline for speed:
        //
        //  if (!wcsncmpi(pwchFacename, pfs->fcsFace.awch, LF_FACESIZE))
        //      return (pfs->fcsAltFace.awch);

        // only those entries in the Font Substitution which have the form
        // face1,charset1=face2,charset2
        // where both charset1 and charset2 are valid charsets
        // count for enumeration purposes.

            if (!(pfs->fcsAltFace.fjFlags | pfs->fcsFace.fjFlags))
            {
                for (pwcA=awchCapName,pwcB=pfs->fcsAltFace.awch; *pwcA==*pwcB; pwcA++,pwcB++)
                {
                    if (*pwcA == 0)
                    {
                        ppfe->aiFamilyName[ppfe->cAlt++] = pfs-gpfsTable;
                    }
                }
            }
        }
    }

    return TRUE;
}


/******************************Public*Routine******************************\
* BOOL PFEOBJ::bEmbedded()
*
* Determine if this is a embedded font by checking the ulID field in PFFOBJ
*
* Returns:
*   TRUE if embedded font, FALSE otherwise.
*
* History:
*  12-Jun-1996 -by- Xudong Wu [TessieW]
* Wrote it.
\**************************************************************************/
BOOL PFEOBJ::bEmbedded()
{
    return  ( (pPFF()->ulID) ? TRUE : FALSE );
}


/******************************Public*Routine******************************\
* BOOL PFEOBJ::bPIDEmbedded()
*
* Determine whether ulID is PID or TID in PFFOBJ
*
* Returns:
*   TRUE if PID, FALSE otherwise.
*
* History:
*  12-Jun-1996 -by- Xudong Wu [TessieW]
* Wrote it.
\**************************************************************************/
BOOL PFEOBJ::bPIDEmbedded()
{
    return ( (pPFF()->ulID) && ((pPFF()->flEmbed) ? FALSE : TRUE));
}


/******************************Public*Routine******************************\
* ULONG PFEOBJ::ulEmbedID()
*
* Returns:
*   PID/TID in PFFOBJ
*
*
* History:
*  12-Jun-1996 -by- Xudong Wu [TessieW]
* Wrote it.
\**************************************************************************/
ULONG PFEOBJ::ulEmbedID()
{
     return ((ULONG)pPFF()->ulID);
}


/******************************Public*Routine******************************\
* BOOL PFEOBJ::bFilteredOut(EFFILTER_INFO *peffi)
*
* Determine if this PFE should be rejected from the enumeration.  Various
* filtering parameters are passed in via the EFFILTER_INFO structure.
*
* Returns:
*   TRUE if font should be rejected, FALSE otherwise.
*
* History:
*  07-Aug-1992 -by- Gilman Wong [gilmanw]
* Wrote it.
\**************************************************************************/

BOOL PFEOBJ::bFilteredOut(EFFILTER_INFO *peffi)
{
    IFIOBJ ifio(pifi());

// Always filter out "dead" (fonts waiting to be deleted) fonts and "ghost"
// fonts.

    if ( bDead() || ifio.bGhostFont() )
        return TRUE;

#ifdef FE_SB
// Always filter out fonts that have been loaded as EUDC fonts.

    if( bEUDC() )
        return TRUE;
#endif

// Raster font filtering.

    if (peffi->bRasterFilter && ifio.bBitmap())
        return TRUE;

// TrueType font filtering.  The flag is somewhat of a misnomer as it
// is intended to exclude TrueType, even though the flag is named
// bNonTrueTypeFilter.

    if (peffi->bNonTrueTypeFilter && ifio.bTrueType())
        return(TRUE);

// Non-TrueType font filtering.  The flag is somewhat of a misnomer as it
// is intended to exclude non-TrueType, even though the flag is named
// bTrueTypeFilter.

    if (peffi->bTrueTypeFilter && !ifio.bTrueType())
        return TRUE;

// Aspect ratio filtering.  If an engine bitmap font, we will filter out
// unsuitable resolutions.

    if ( peffi->bAspectFilter
         && (!bDeviceFont())
         && ifio.bBitmap()
         && ( (peffi->ptlDeviceAspect.x != ifio.pptlAspect()->x)
               || (peffi->ptlDeviceAspect.y != ifio.pptlAspect()->y) ) )
        return TRUE;

// GACF_TTIGNORERASTERDUPE compatibility flag filtering.
// If any raster fonts exist in the same list as a TrueType font, then
// they should be excluded.

    if ( peffi->bTrueTypeDupeFilter
         && peffi->cTrueType
         && ifio.bBitmap())
        return TRUE;

// Filter out embedded fonts.  These fonts are hidden from enumeration.

    if (bEmbedded())
        return TRUE;

// In the case of a Generic text driver we must filter out all engine fonts

    if( ( peffi->bEngineFilter ) && !bDeviceFont() )
    {
        return(TRUE);
    }

// if this is a remote font we don't want to enumerate it

    if( ppfe->flPFE & PFE_REMOTEFONT )
    {
        return(TRUE);
    }

// finally check out if the font should be eliminated from the
// enumeration because it does not contain the charset requested:

    if (peffi->lfCharSetFilter != DEFAULT_CHARSET)
    {
    // the specific charset has been requested, let us see if the font
    // in question supports it:

        BYTE jCharSet = jMapCharset((BYTE)peffi->lfCharSetFilter, pifi());

        if (jCharSet != (BYTE)peffi->lfCharSetFilter)
            return TRUE; // does not support it, filter this font out.
    }

// Passed all tests.

    return FALSE;
}


 #if DBG

/******************************Public*Routine******************************\
* VOID PFEOBJ::vDump ()
*
* Debugging code.
*
* History:
*  25-Feb-1991 -by- Gilman Wong [gilmanw]
* Wrote it.
\**************************************************************************/

VOID PFEOBJ::vPrint()
{
    IFIOBJ ifio(pifi());

    DbgPrint("\nContents of PFE, PPFE = 0x%lx\n", ppfeGet());
    DbgPrint("pPFF   = 0x%lx\n", ppfe->pPFF);
    DbgPrint("iFont  = 0x%lx\n", ppfe->iFont);

    DbgPrint("lfHeight          = 0x%x\n",  ifio.lfHeight());
    DbgPrint(
        "Family Name       = %ws\n",
        ifio.pwszFamilyName()
        );
    DbgPrint(
        "Face Name         = %ws\n",
        ifio.pwszFaceName()
        );
    DbgPrint(
        "Unique Name       = %s\n\n",
        ifio.pwszUniqueName()
        );
}


/******************************Public*Routine******************************\
* VOID PFEOBJ::vDumpIFI ()
*
* Debugging code.  Prints PFE header and IFI metrics.
*
\**************************************************************************/

VOID PFEOBJ::vPrintAll()
{
    DbgPrint("\nContents of PFE, PPFE = 0x%lx\n", ppfeGet());
    DbgPrint("pPFF   = 0x%lx\n", ppfe->pPFF);
    DbgPrint("iFont  = 0x%lx\n", ppfe->iFont);
    DbgPrint("IFI Metrics\n");
     vPrintIFIMETRICS(ppfe->pifi);
    DbgPrint("\n");
}
#endif

/******************************Public*Routine******************************\
* EFSMEMOBJ::EFSMEMOBJ(COUNT cefe)
*
* Constructor for font enumeration state (EFSTATE) memory object.
*
* History:
*  07-Aug-1992 -by- Gilman Wong [gilmanw]
* Wrote it.
\**************************************************************************/

EFSMEMOBJ::EFSMEMOBJ(COUNT cefe, ULONG iEnumType_)
{
    fs = 0;
    pefs = (PEFSTATE) HmgAlloc((SIZE_T) (offsetof(EFSTATE, aefe) + cefe * sizeof(EFENTRY)),
                               EFSTATE_TYPE,
                               HMGR_ALLOC_LOCK);

    if (pefs != PEFSTATENULL)
    {
        vInit(cefe, iEnumType_);
    }
}

/******************************Public*Routine******************************\
* EFSMEMOBJ::~EFSMEMOBJ()
*
* Destructor for font enumeration state (EFSTATE) memory object.
*
* History:
*  07-Aug-1992 -by- Gilman Wong [gilmanw]
* Wrote it.
\**************************************************************************/

EFSMEMOBJ::~EFSMEMOBJ()
{
// If object pointer not null, try to free the object's memory.

    if (pefs != PEFSTATENULL)
    {
        if (fs & EFSMO_KEEPIT)
        {
            DEC_EXCLUSIVE_REF_CNT(pefs);
        }
        else
        {
#if DBG
            if (pefs->cExclusiveLock != 1)
            {
               RIP("Not 1 EFSMEMOBJ\n");
            }
#endif

            HmgFree((HOBJ) pefs->hGet());
        }

        pefs = NULL;
    }
}

#define EFS_QUANTUM     16

/******************************Public*Routine******************************\
* BOOL EFSOBJ::bGrow
*
* Expand the EFENTRY table by the quantum amount.
*
* Returns:
*   TRUE if successful, FALSE if failed.
*
* History:
*  07-Aug-1992 -by- Gilman Wong [gilmanw]
* Wrote it.
\**************************************************************************/

BOOL EFSOBJ::bGrow (COUNT cefeMinIncrement)
{
    COUNT cefe;
    BOOL bRet = FALSE;

// Allocate a new EFSTATE bigger by the quantum amount.

    cefe = (COUNT) (pefs->pefeBufferEnd - pefs->aefe);

    if (cefeMinIncrement < EFS_QUANTUM)
        cefeMinIncrement = EFS_QUANTUM;
    cefe += cefeMinIncrement;

    EFSMEMOBJ efsmo(cefe, this->pefs->iEnumType);

// Validate new EFSTATE.

    if (efsmo.bValid())
    {
    // Copy the enumeration table.

        efsmo.vXerox(pefs);

    // Swap the EFSTATEs.

        if (HmgSwapHandleContents((HOBJ) hefs(),0,(HOBJ) efsmo.hefs(),0,EFSTATE_TYPE))
        {
        // swap pointers

            PEFSTATE pefsTmp = pefs;
            pefs = efsmo.pefs;
            efsmo.pefs = pefsTmp;               // destructor will delete old PFT
            bRet = TRUE;
        }
        else
            WARNING("gdisrv!bGrowEFSOBJ(): handle swap failed\n");
    }
    else
        WARNING("bGrowEFSOBJ failed alloc\n");

    return(bRet);
}

/******************************Public*Routine******************************\
* BOOL EFSOBJ::bAdd                                                        *
*                                                                          *
* Add a new EFENTRY to the table with the HPFE and ENUMFONTSTYLE.          *
*                                                                          *
* Returns:                                                                 *
*   FALSE if an error occurs, TRUE otherwise.                              *
*                                                                          *
* History:                                                                 *
*  07-Aug-1992 -by- Gilman Wong [gilmanw]                                  *
* Wrote it.                                                                *
\**************************************************************************/

BOOL EFSOBJ::bAdd(PFE *ppfe, ENUMFONTSTYLE efsty, FLONG fl, ULONG lfCharSetFilter)
{
// Check if the buffer needs to be expanded.

    COUNT cefeMinIncrement = 1; // will always enumerate at least one pfe

// if EnumFontFamilies is called, will enumerate the font under cAlt more names

    if (!(fl & FL_ENUMFAMILIESEX))
        cefeMinIncrement += ppfe->cAlt;

// if EnumFontFamiliesEx is called, and this font supports multiple charsets,
// this font will be enumerated no more than MAXCHARSETS times.

    if
    (
        (fl & FL_ENUMFAMILIESEX)             &&
        (lfCharSetFilter == DEFAULT_CHARSET) &&
        ppfe->pifi->dpCharSets
    )
    {
        cefeMinIncrement = MAXCHARSETS;
    }

    if ((pefs->pefeDataEnd + cefeMinIncrement) >= pefs->pefeBufferEnd)
    {
        if (!bGrow(cefeMinIncrement))
        {
        // Error code will be saved for us.

            WARNING("gdisrv!EFSOBJ__bAdd: cannot grow enumeration table\n");
            return FALSE;
        }
    }

// Add the new data and increment the data pointer.

    HPFE hpfe = (HPFE) ppfe->hGet();
    pefs->pefeDataEnd->hpfe  = hpfe;
    pefs->pefeDataEnd->efsty = efsty;
    pefs->pefeDataEnd->fjOverride = 0; // do not override

    if (fl & FL_ENUMFAMILIESEX)
        pefs->pefeDataEnd->fjOverride |= FJ_CHARSETOVERRIDE;

    pefs->pefeDataEnd->jCharSetOverride = (BYTE)lfCharSetFilter;
    pefs->pefeDataEnd       += 1;

// now check if called from EnumFonts or EnumFontFamilies so that the
// names from the
// [FontSubstitutes] section in the registry also need to be enumerated

    if (!(fl & FL_ENUMFAMILIESEX) && ppfe->cAlt) // alt names have to be enumerated too
    {
        for (ULONG i = 0; i < ppfe->cAlt; i++)
        {
        // the same hpfe, style etc. all the time, only lie about the name and charset

            pefs->pefeDataEnd->hpfe  = hpfe;
            pefs->pefeDataEnd->efsty = efsty;
            pefs->pefeDataEnd->fjOverride = (FJ_FAMILYOVERRIDE | FJ_CHARSETOVERRIDE);  // do override
            pefs->pefeDataEnd->iOverride = ppfe->aiFamilyName[i];
            pefs->pefeDataEnd->jCharSetOverride =
                gpfsTable[pefs->pefeDataEnd->iOverride].fcsFace.jCharSet;
            pefs->pefeDataEnd       += 1;
        }
    }

// now see if this is called from EnumFontFamiliesEx

    if ((fl & FL_ENUMFAMILIESEX) && (lfCharSetFilter == DEFAULT_CHARSET))
    {
    // The font needs to be enumerated once for every charset it supports

        if (ppfe->pifi->dpCharSets)
        {
            BYTE *ajCharSets = (BYTE*)ppfe->pifi + ppfe->pifi->dpCharSets;
            BYTE *ajCharSetsEnd = ajCharSets + MAXCHARSETS;

        // first fix up the one entry we just filled above

            (pefs->pefeDataEnd-1)->jCharSetOverride = ajCharSets[0];

        // this is from win95-J sources:

#define FEOEM_CHARSET 254

            for
            (
                BYTE *pjCharSets = ajCharSets + 1; // skip the first one, used already
                (*pjCharSets != DEFAULT_CHARSET) &&
                (*pjCharSets != OEM_CHARSET)     &&
                (*pjCharSets != FEOEM_CHARSET)   &&
                (pjCharSets < ajCharSetsEnd)     ;
                pjCharSets++
            )
            {
            // the same hpfe, style etc. all the time, only lie about the name and charset

                pefs->pefeDataEnd->hpfe  = hpfe;
                pefs->pefeDataEnd->efsty = efsty;
                pefs->pefeDataEnd->fjOverride = FJ_CHARSETOVERRIDE;
                pefs->pefeDataEnd->iOverride = 0;
                pefs->pefeDataEnd->jCharSetOverride = *pjCharSets;
                pefs->pefeDataEnd       += 1;
            }
        }
        else //  fix up the one entry we just filled above
        {
            (pefs->pefeDataEnd-1)->jCharSetOverride = ppfe->pifi->jWinCharSet;
        }
    }

// Success.

    return TRUE;
}



/******************************Public*Routine******************************\
* VOID EFSOBJ::vDelete ()
*
* Destroy the font enumeration state (EFSTATE) memory object.
*
* History:
*  07-Aug-1992 -by- Gilman Wong [gilmanw]
* Wrote it.
\**************************************************************************/

VOID EFSOBJ::vDeleteEFSOBJ()
{
    HmgFree((HOBJ) pefs->hGet());
    pefs = PEFSTATENULL;
}


/******************************Member*Function*****************************\
* VOID EFSMEMOBJ::vInit
*
* Initialize the EFSTATE object.
*
* History:
*  07-Aug-1992 -by- Gilman Wong [gilmanw]
* Wrote it.
\**************************************************************************/

VOID EFSMEMOBJ::vInit(COUNT cefe, ULONG iEnumType_)
{
// HPFE array empty, so initialize all pointer to the beginning of the array.

    pefs->pefeDataEnd  = pefs->aefe;
    pefs->pefeEnumNext = pefs->aefe;

// Except for this one.  Set this one to the end of the buffer.

    pefs->pefeBufferEnd = &pefs->aefe[cefe];

// Initialize the alternate name to NULL.

    pefs->pfcsOverride = NULL;

// init the enum type:

    pefs->iEnumType = iEnumType_;

// We don't need to bother with initializing the array.
}

/******************************Public*Routine******************************\
* VOID EFSMEMOBJ::vXerox(EFSTATE *pefeSrc)
*
* Copy the EFENTRYs from the source EFSTATE's table into this EFSTATE's table.
* The internal pointers will be updated to be consistent with the data.
*
* History:
*  07-Aug-1992 -by- Gilman Wong [gilmanw]
* Wrote it.
\**************************************************************************/

VOID EFSMEMOBJ::vXerox(EFSTATE *pefsSrc)
{
//
// Compute size of the table.
//
    COUNT cefe = pefsSrc->pefeDataEnd - pefsSrc->aefe;

    ASSERTGDI (
        cefe >= (COUNT) (pefs->pefeDataEnd - pefs->aefe),
        "gdisrv!vXeroxEFSMEMOBJ(): table to small\n"
        );

//
// Copy entries.
//
    RtlCopyMemory((PVOID) pefs->aefe, (PVOID) pefsSrc->aefe, (SIZE_T) cefe * sizeof(EFENTRY));

//
// Fixup the data pointer.
//
    pefs->pefeDataEnd = pefs->aefe + cefe;
}


/******************************Public*Routine******************************\
* bSetEFSTATEOwner
*
* Set the owner of the EFSTATE
*
* if the owner is set to OBJECTOWNER_NONE, this EFSTATE will not be useable
* until bSetEFSTATEOwner is called to explicitly give the lfnt to someone else.
*
* History:
*  07-Aug-1992 by Gilman Wong [gilmanw]
* Wrote it.
\**************************************************************************/

BOOL
bSetEFSTATEOwner(
    HEFS hefs,
    W32PID lPid)
{
    if (lPid == OBJECT_OWNER_CURRENT)
    {
        lPid = W32GetCurrentPID();
    }

    return HmgSetOwner((HOBJ) hefs, lPid, EFSTATE_TYPE);
}

BOOL
bGetNtoD(
    FD_XFORM*,
    EXTLOGFONTW*,
    IFIOBJ&,
    DCOBJ*,
    POINTL* const
    );

BOOL
bGetNtoD_Win31(
    FD_XFORM*,
    EXTLOGFONTW*,
    IFIOBJ&,
    DCOBJ*,
    FLONG,
    POINTL* const
    );


/******************************Public*Routine******************************\
* BOOL bSetFontXform
*
* Sets the FD_XFORM such that it can be used to realize the physical font
* with the dimensions specified in the wish list coordinates).  The
* World to Device xform (with translations removed) is also returned.
*
* Returns:
*   TRUE if successful, FALSE if an error occurs.
*
* History:
*  Tue 27-Oct-1992 23:18:39 by Kirk Olynyk [kirko]
* Moved it from PFEOBJ.CXX
*  19-Sep-1991 -by- Gilman Wong [gilmanw]
* Wrote it.
\**************************************************************************/

BOOL
PFEOBJ::bSetFontXform (
    XDCOBJ       &dco,               // realize for this device
    EXTLOGFONTW *pelfw,             // wish list (in logical coords)
    PFD_XFORM   pfd_xf,             // font transform
    FLONG       fl,
    FLONG       flSim,
    POINTL* const pptlSim,
    IFIOBJ&     ifio
    )
{
       BOOL bRet;

       EXFORMOBJ xo(dco, WORLD_TO_DEVICE); // synchronize the transformation

        if(dco.pdc->iGraphicsMode() == GM_COMPATIBLE)
        {
            bRet = bGetNtoD_Win31(
                    pfd_xf,
                    pelfw,
                    ifio,
                    (DCOBJ *)&dco,
                    fl,
                    pptlSim
                    );
        }
        else // GM_ADVANCED
        {
            bRet = bGetNtoD(
                    pfd_xf,
                    pelfw,
                    ifio,
                    (DCOBJ *)&dco,
                    pptlSim
                    );
        }

        if (!bRet)
        {
            WARNING(
                "gdisrv!bSetFontXformPFEOBJ(): failed to get Notional to World xform\n"
                );
            return FALSE;
        }

    //
    // The next line two lines of code flips the sign of the Notional y-coordinates
    // The effect is that the XFORMOBJ passed over the DDI makes the assumption that
    // Notional space is such that the y-coordinate increases towards the bottom.
    // This is opposite to the usual conventions of notional space and the font
    // driver writers must be made aware of this historical anomaly.
    //
        NEGATE_IEEE_FLOAT(pfd_xf->eYX);
        NEGATE_IEEE_FLOAT(pfd_xf->eYY);

    //
    // If the font can be scaled isotropicslly only then we make sure that we send
    // to the font driver isotropic transformations.
    //
    // If a device has set the TA_CR_90 bit, then it is possible
    // that we will send to the driver a transformation that is equivalent to an isotropic
    // transformation rotated by a multiple of 90 degress. This is the reason for the
    // second line of this transformation.
    //
        if (ifio.bIsotropicScalingOnly())
        {
            *(LONG*)&(pfd_xf->eXX) = *(LONG*)&(pfd_xf->eYY);
            *(LONG*)&(pfd_xf->eXY) = *(LONG*)&(pfd_xf->eYX);
            NEGATE_IEEE_FLOAT(pfd_xf->eXY);
        }

    return (TRUE);
}