/****************************************************************************\
*
*     PROGRAM: fontview.c
*
*     PURPOSE: Loads and displays fonts from the given filename
*
*     COMMENTS:
*
*     HISTORY:
*       02-Oct-1995 JonPa       Created It
*
\****************************************************************************/

#include <windows.h>                /* required for all Windows applications */
#include <commdlg.h>
#include <shellapi.h>
#include <shlwapi.h> 
#include <strsafe.h>
#include <wingdip.h>                /* prototype for GetFontRsourceInfo     */
#include <objbase.h>
#include "fontdefs.h"               /* specific to this program             */
#include "fvmsg.h"
#include "fvrc.h"
#include "ttdefs.h"



HANDLE hInst;                       /* current instance                     */
HWND ghwndView = NULL;
HWND ghwndFrame = NULL;
BOOL    gfPrint = FALSE;
TCHAR   gszFontPath[2 * MAX_PATH];
LPTSTR  gpszSampleText;
LPTSTR  gpszSampleAlph[3];
FFTYPE  gfftFontType;
LOGFONT glfFont;
DISPTEXT gdtDisplay;
HBRUSH  ghbr3DFace;
HBRUSH  ghbr3DShadow;


int gyScroll = 0;              // Vertical scroll offset in pels
int gcyLine = 0;

int gcxMinWinSize = CX_MIN_WINSIZE;
int gcyMinWinSize = CY_MIN_WINSIZE;

BOOL gbIsDBCS = FALSE;    // Indicates whether system default langID is DBCS
int  gNumOfFonts = 0;     // number of fonts in the file.
int  gIndexOfFonts = 0;   // current index of the fonts.
LPLOGFONT glpLogFonts;    // get global data by GetFontResourceInfo()

int apts[] = { 12, 18, 24, 36, 48, 60, 72 };
#define C_POINTS_LIST  (sizeof(apts) / sizeof(apts[0]))

#define CPTS_BTN_AREA   28
int gcyBtnArea = CPTS_BTN_AREA;
BTNREC gabtCmdBtns[] = {
    {   6,  6, 36, 16, IDB_DONE,      NULL, MSG_DONE,      NULL },
    {  -6,  6, 36, 16, IDB_PRINT,     NULL, MSG_PRINT,     NULL },
    {  68,  6, 20, 16, IDB_PREV_FONT, NULL, MSG_PREV_FONT, NULL }, // DBCS only.
    { -68,  6, 20, 16, IDB_NEXT_FONT, NULL, MSG_NEXT_FONT, NULL }  // DBCS only.
};

#define C_DBCSBUTTONS  2  // Prev & Next font are DBCS specific.
//
// This may be recalculated in WinMain to adjust for a DBCS locale.
//
int C_BUTTONS = (sizeof(gabtCmdBtns) / sizeof(gabtCmdBtns[0]));


#if DBG
void DDPrint( LPTSTR sz, DWORD dw ) {
    TCHAR szBuff[246];
    StringCchPrintf( szBuff, ARRAYSIZE(szBuff), sz, dw );

    OutputDebugString( szBuff );
}

#   define DDPRINT( s, d )  DDPrint( s, d )
#else
#   define DDPRINT( s, d )
#endif


#define IsZeroFSig( fs )  ( (fs)->fsUsb[0] == 0 && (fs)->fsUsb[1] == 0 && (fs)->fsUsb[2] == 0 && \
                                (fs)->fsUsb[3] == 0 && (fs)->fsCsb[0] == 0 && (fs)->fsCsb[1] == 0 )

BOOL NativeCodePageSupported(LPLOGFONT lplf) {
    BOOL fRet = FALSE;
    HDC hdc = CreateCompatibleDC(NULL);
    if (hdc)
    {
        HFONT hf, hfOld;
        FONTSIGNATURE fsig;
        CHARSETINFO  csi;

        DDPRINT( TEXT("System default code page: %d\n"), GetACP() );

        TranslateCharsetInfo( (LPDWORD)IntToPtr(GetACP()), &csi, TCI_SRCCODEPAGE );

        hf = CreateFontIndirect( lplf );

        hfOld = SelectObject( hdc, hf );

        GetTextCharsetInfo( hdc, &fsig, 0 );

        SelectObject( hdc, hfOld );

        DeleteObject(hf);

        if (IsZeroFSig( &fsig ) ) {
            // Font does not support GetTextCharsetInfo(), just go off of the lfCharSet value

            DDPRINT( TEXT("Font does not support GetTextCharsetInfo... \nTesting %d (font cs) against"), lplf->lfCharSet );
            DDPRINT( TEXT("%d (sys charset)\n"), csi.ciCharset );

            fRet = (lplf->lfCharSet == csi.ciCharset);

        } else {
            DDPRINT( TEXT("GTCI() worked...\nChecking font charset bits %08x"),  fsig.fsCsb[0] );
            DDPRINT( TEXT(" %08x against"),  fsig.fsCsb[1] );
            DDPRINT( TEXT(" system charset bits %08x "), csi.fs.fsCsb[0] );
            DDPRINT( TEXT("  %08x\n"), csi.fs.fsCsb[1] );

            fRet = ((csi.fs.fsCsb[0] &  fsig.fsCsb[0]) || (csi.fs.fsCsb[1] &  fsig.fsCsb[1]));
        }

        DeleteDC(hdc);
    }

    return fRet;
}

/****************************************************************************
*
*     FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int)
*
*     PURPOSE: calls initialization function, processes message loop
*
*
\****************************************************************************/
int APIENTRY WinMain(
    HINSTANCE hInstance,
    HINSTANCE hPrevInstance,
    LPSTR lpstrCmdLine,
    int nCmdShow
    )
{
    int i, iCpts;
    MSG msg;
    HACCEL  hAccel;
    HICON hIcon;
    USHORT wLanguageId;
    BOOL bCoInitialized = FALSE;

    //
    // Initialize the gbIsDBCS flag based on the current default language.
    //
    wLanguageId = LANGIDFROMLCID(GetThreadLocale());

    gbIsDBCS    = (LANG_JAPANESE == PRIMARYLANGID(wLanguageId)) ||
                  (LANG_KOREAN   == PRIMARYLANGID(wLanguageId)) ||
                  (LANG_CHINESE  == PRIMARYLANGID(wLanguageId));

    //
    // In a DBCS locale, exclude the Prev-Next font buttons.
    //
    if (!gbIsDBCS)
         C_BUTTONS -= C_DBCSBUTTONS;
    //
    // Need to initialize COM so that SHGetFileInfo will load the IExtractIcon handler
    // implemented in fontext.dll.
    //
    if (SUCCEEDED(CoInitialize(NULL)))
        bCoInitialized = TRUE;

    /*
     * Parse the Command Line
     *
     *  Use GetCommandLine() here (instead of lpstrCmdLine) so the
     *  command string will be in Unicode on NT
     */
    FillMemory( &gdtDisplay, sizeof(gdtDisplay), 0 );

    if (!ParseCommand( GetCommandLine(), gszFontPath, ARRAYSIZE(gszFontPath), &gfPrint ) ||
        (gfftFontType = LoadFontFile( gszFontPath, &gdtDisplay, &hIcon )) == FFT_BAD_FILE) {

        // Bad font file, inform user, and exit

        FmtMessageBox( NULL, MSG_APP_TITLE, NULL, MB_OK | MB_ICONSTOP,
                FALSE, MSG_BADFILENAME, gszFontPath );

        if (bCoInitialized)
            CoUninitialize();

        ExitProcess(1);
    }

    /*
     * Now finish initializing the display structure
     */
    gpszSampleAlph[0] = FmtSprintf(MSG_SAMPLEALPH_0);
    gpszSampleAlph[1] = FmtSprintf(MSG_SAMPLEALPH_1);
    gpszSampleAlph[2] = FmtSprintf(MSG_SAMPLEALPH_2);

    // find next line on display
    for( i = 0; i < CLINES_DISPLAY; i++ ) {
        if (gdtDisplay.atlDsp[i].dtyp == DTP_UNUSED)
            break;
    }

    // fill in sample alphabet
    gdtDisplay.atlDsp[i].pszText = gpszSampleAlph[0];
    gdtDisplay.atlDsp[i].cchText = lstrlen(gpszSampleAlph[0]);
    gdtDisplay.atlDsp[i].dtyp    = DTP_SHRINKTEXT;
    gdtDisplay.atlDsp[i].cptsSize = CPTS_SAMPLE_ALPHA;

    i++;
    gdtDisplay.atlDsp[i] = gdtDisplay.atlDsp[i-1];
    gdtDisplay.atlDsp[i].pszText = gpszSampleAlph[1];
    gdtDisplay.atlDsp[i].cchText = lstrlen(gpszSampleAlph[1]);

    i++;
    gdtDisplay.atlDsp[i] = gdtDisplay.atlDsp[i-1];
    gdtDisplay.atlDsp[i].pszText = gpszSampleAlph[2];
    gdtDisplay.atlDsp[i].cchText = lstrlen(gpszSampleAlph[2]);
    gdtDisplay.atlDsp[i].fLineUnder = TRUE;


    // now fill in sample Sentences
    iCpts = 0;

    if (gbIsDBCS)
    {
        //
        // Determine with string to use: the default or the language
        // specific.
        //
        switch (gdtDisplay.lfTestFont.lfCharSet) {
            case SYMBOL_CHARSET:
            case ANSI_CHARSET:
            case DEFAULT_CHARSET:
            case OEM_CHARSET:
                gpszSampleText = FmtSprintf(MSG_SAMPLETEXT);
                break;

            default:
                gpszSampleText = FmtSprintf(MSG_SAMPLETEXT_ALT);
                break;
        }
    }
    else
    {
        if(NativeCodePageSupported(&(gdtDisplay.lfTestFont))) {
            //
            // Native code page is supported, select that codepage
            // and print the localized string.
            //
            CHARSETINFO csi;

            TranslateCharsetInfo( (LPDWORD)IntToPtr(GetACP()), &csi, TCI_SRCCODEPAGE );

            gdtDisplay.lfTestFont.lfCharSet = (BYTE)csi.ciCharset;

            gpszSampleText =  FmtSprintf(MSG_SAMPLETEXT);

        } else {
            //
            // Font does not support the local code page.  Print
            // a random string up instead using the font's default charset.
            //
            gpszSampleText =  FmtSprintf(MSG_ALTSAMPLE);
        }
    }

    for( i += 1; i < CLINES_DISPLAY && iCpts < C_POINTS_LIST; i++ ) {
        if (gdtDisplay.atlDsp[i].dtyp == DTP_UNUSED) {
            gdtDisplay.atlDsp[i].pszText = gpszSampleText;
            gdtDisplay.atlDsp[i].cchText = lstrlen(gpszSampleText);
            gdtDisplay.atlDsp[i].dtyp    = DTP_TEXTOUT;
            gdtDisplay.atlDsp[i].cptsSize = apts[iCpts++];
        }
    }

    /*
     * Init the title font LOGFONT, and other variables
     */
    InitGlobals();

    if (!hPrevInstance) {
        if (!InitApplication(hInstance, hIcon)) {
            msg.wParam = FALSE;
            goto ExitProg;
        }
    }

    /* Perform initializations that apply to a specific instance */

    if (!InitInstance(hInstance, nCmdShow, gdtDisplay.atlDsp[0].pszText)) {
        msg.wParam = FALSE;
        goto ExitProg;
    }

    /* Acquire and dispatch messages until a WM_QUIT message is received. */
    hAccel = LoadAccelerators(hInstance, TEXT("fviewAccel"));

    while (GetMessage(&msg, NULL, 0L, 0L)) {
        if (!TranslateAccelerator(ghwndView, hAccel, &msg)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

ExitProg:
    for ( i = 0; i < C_BUTTONS; i++ )
        FmtFree( gabtCmdBtns[i].pszText );

    if (gbIsDBCS && glpLogFonts)
        FreeMem(glpLogFonts);

    RemoveFontResource( gszFontPath );

    if (bCoInitialized)
        CoUninitialize();

    return (int)(msg.wParam);
}


/****************************************************************************
*
*     FUNCTION: InitApplication(HANDLE)
*
*     PURPOSE: Initializes window data and registers window class
*
*     COMMENTS:
*
*         This function is called at initialization time only if no other
*         instances of the application are running.  This function performs
*         initialization tasks that can be done once for any number of running
*         instances.
*
*         In this case, we initialize a window class by filling out a data
*         structure of type WNDCLASS and calling the Windows RegisterClass()
*         function.  Since all instances of this application use the same window
*         class, we only need to do this when the first instance is initialized.
*
*
\****************************************************************************/

BOOL InitApplication(HANDLE hInstance, HICON hIcon)       /* current instance             */
{
    WNDCLASS  wc;
    BOOL fRet = FALSE;

    /* Fill in window class structure with parameters that describe the       */
    /* main window.                                                           */

    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = FrameWndProc;

    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;           /* Application that owns the class.   */
    wc.hIcon = hIcon ? hIcon : LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = ghbr3DFace;
    wc.lpszMenuName =  NULL;
    wc.lpszClassName = TEXT("FontViewWClass");

    /* Register the window class and return success/failure code. */

    if (RegisterClass(&wc)) {
        /* Fill in window class structure with parameters that describe the       */
        /* main window.                                                           */

        wc.style = CS_HREDRAW | CS_VREDRAW;
        wc.lpfnWndProc = ViewWndProc;

        wc.cbClsExtra = 0;
        wc.cbWndExtra = 0;
        wc.hInstance = hInstance;           /* Application that owns the class.   */
        wc.hIcon = NULL;
        wc.hCursor = LoadCursor(NULL, IDC_ARROW);
        wc.hbrBackground = GetStockObject(WHITE_BRUSH);
        wc.lpszMenuName =  NULL;
        wc.lpszClassName = TEXT("FontDisplayClass");

        fRet = RegisterClass(&wc);
    }

    return fRet;
}


/****************************************************************************
*
*     FUNCTION:  InitInstance(HANDLE, int)
*
*     PURPOSE:  Saves instance handle and creates main window
*
*     COMMENTS:
*
*         This function is called at initialization time for every instance of
*         this application.  This function performs initialization tasks that
*         cannot be shared by multiple instances.
*
*         In this case, we save the instance handle in a static variable and
*         create and display the main program window.
*
\****************************************************************************/

BOOL InitInstance( HANDLE  hInstance, int nCmdShow, LPTSTR  pszTitle)
{

    /* Save the instance handle in static variable, which will be used in  */
    /* many subsequence calls from this application to Windows.            */

    hInst = hInstance;

    /* Create a main window for this application instance.  */

    ghwndFrame = CreateWindow( TEXT("FontViewWClass"), pszTitle,
            WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN,
            CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL );

    /* If window could not be created, return "failure" */

    if (!ghwndFrame)
        return (FALSE);

    return (TRUE);               /* Returns the value from PostQuitMessage */

}

/****************************************************************************
*
*     FUNCTION: InitLogFont
*
\****************************************************************************/
void InitGlobals( void ) {
    TCHAR szMsShellDlg2[LF_FACESIZE];
    INT cyDPI,i, cxFiller, cxMaxTxt, cxTxt, cxMax;
    HDC hdc;
    HFONT hfOld;
    RECT rc;

    FillMemory( &glfFont, sizeof(glfFont), 0 );

    glfFont.lfCharSet         = DEFAULT_CHARSET;
    glfFont.lfOutPrecision    = OUT_DEFAULT_PRECIS;
    glfFont.lfClipPrecision   = CLIP_DEFAULT_PRECIS;
    glfFont.lfQuality         = DEFAULT_QUALITY;
    glfFont.lfPitchAndFamily  = DEFAULT_PITCH | FF_DONTCARE;

    if (LoadString(hInst, IDS_FONTFACE, szMsShellDlg2, ARRAYSIZE(szMsShellDlg2)))
        StringCchCopy(glfFont.lfFaceName, ARRAYSIZE(glfFont.lfFaceName), szMsShellDlg2);
    else
        StringCchCopy(glfFont.lfFaceName, ARRAYSIZE(glfFont.lfFaceName), TEXT("MS Shell Dlg2"));

    hdc = CreateCompatibleDC(NULL);
    cyDPI = GetDeviceCaps(hdc, LOGPIXELSY );

    hfOld = SelectObject( hdc, GetStockObject(DEFAULT_GUI_FONT));

    // Find out size of padding around text
    SetRect(&rc, 0, 0, 0, 0 );
    DrawText(hdc, TEXT("####"), -1, &rc, DT_CALCRECT | DT_CENTER);
    cxFiller = rc.right - rc.left;

    gcyBtnArea = MulDiv( gcyBtnArea, cyDPI, C_PTS_PER_INCH );
    cxMax = cxMaxTxt = 0;
    for( i = 0; i < C_BUTTONS; i++ ) {
        gabtCmdBtns[i].x  = MulDiv( gabtCmdBtns[i].x,  cyDPI, C_PTS_PER_INCH );
        gabtCmdBtns[i].y  = MulDiv( gabtCmdBtns[i].y,  cyDPI, C_PTS_PER_INCH );
        gabtCmdBtns[i].cx = MulDiv( gabtCmdBtns[i].cx, cyDPI, C_PTS_PER_INCH );
        gabtCmdBtns[i].cy = MulDiv( gabtCmdBtns[i].cy, cyDPI, C_PTS_PER_INCH );

        if (gabtCmdBtns[i].cx > cxMax)
            cxMax = gabtCmdBtns[i].cx;

        gabtCmdBtns[i].pszText = FmtSprintf( gabtCmdBtns[i].idText );
        SetRect(&rc, 0, 0, 0, 0 );
        DrawText(hdc, gabtCmdBtns[i].pszText, -1, &rc, DT_CALCRECT | DT_CENTER);

        cxTxt = rc.right - rc.left + cxFiller;

        if (cxMaxTxt < cxTxt) {
            cxMaxTxt = cxTxt;
        }
    }

    //
    // Make sure buttons are big enough for text! (So localizer's won't have
    // to change code.
    //
    if (cxMax < cxMaxTxt) {
        for( i = 0; i < C_BUTTONS; i++ ) {
            gabtCmdBtns[i].cx = gabtCmdBtns[i].cx * cxMaxTxt / cxMax;
        }
    }

    //
    // Make sure buttons don't overlap
    //
    i = C_BUTTONS - 1;
    cxMax = gabtCmdBtns[0].x + gabtCmdBtns[0].cx + gabtCmdBtns[0].x + gabtCmdBtns[i].cx + (-gabtCmdBtns[i].x) +
            (2 * GetSystemMetrics(SM_CXSIZEFRAME));

    if (cxMax > gcxMinWinSize)
        gcxMinWinSize = cxMax;

    SelectObject(hdc, hfOld);
    DeleteDC(hdc);

    gcyLine = MulDiv( CPTS_INFO_SIZE, cyDPI, C_PTS_PER_INCH );

    ghbr3DFace   = GetSysColorBrush(COLOR_3DFACE);
    ghbr3DShadow = GetSysColorBrush(COLOR_3DSHADOW);
}

/****************************************************************************
*
*     FUNCTION: SkipWhiteSpace
*
\****************************************************************************/
LPTSTR SkipWhiteSpace( LPTSTR psz ) {

    while( *psz == TEXT(' ') || *psz == TEXT('\t') || *psz == TEXT('\n') ) {
        psz = CharNext( psz );
    }

    return psz;
}


/****************************************************************************
*
*     FUNCTION: CloneString
*
\****************************************************************************/
LPTSTR CloneString(LPTSTR psz) {
    int cch;
    LPTSTR pszRet;
    cch = (lstrlen( psz ) + 1);

    pszRet = AllocMem(cch * sizeof(TCHAR));
    StringCchCopy( pszRet, cch, psz );
    return pszRet;
}


/****************************************************************************
*
*     FUNCTION: GetFileSizeFromName(pszFontPath)
*
\****************************************************************************/
DWORD GetFileSizeFromName( LPCTSTR pszPath ) {
    HANDLE hfile;
    DWORD cb = 0;

    hfile = CreateFile( pszPath, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL );
    if (hfile != INVALID_HANDLE_VALUE) {
        cb = GetFileSize( hfile, NULL );
        CloseHandle(hfile);
    }

    return cb;
}




HRESULT FindPfb (LPCTSTR pszPFM, LPTSTR pszPFB, size_t cchPFB);
HRESULT BuildType1FontSpec(LPCTSTR pszPFM, LPTSTR pszSpec, size_t cchSpec);


/****************************************************************************
*
*     FUNCTION: ParseCommand
*
\****************************************************************************/


BOOL ParseCommand( LPTSTR lpstrCmdLine, LPTSTR pszFontPath, size_t cchFontPath, BOOL *pfPrint ) {
    LPTSTR psz;
    BOOL fInQuote = FALSE;
    TCHAR szPfmPfb[(2 * MAX_PATH) + 1];  // +1 for possible '|' delimiter.

    //
    // Skip program name
    //
    for( psz = SkipWhiteSpace(lpstrCmdLine);
            *psz != TEXT('\0') && (fInQuote || *psz != TEXT(' ')); psz = CharNext(psz) ) {

        if (*psz == TEXT('\"')) {
            fInQuote = !fInQuote;
        }
    }

    if (*psz == TEXT('\0')) {
        *pszFontPath = TEXT('\0');
        return FALSE;
    }

    psz = SkipWhiteSpace(psz);

    //
    // Check for "/p"
    //
    if (psz[0] == TEXT('/') && (psz[1] == TEXT('p') || psz[1] == TEXT('P'))) {
        *pfPrint = TRUE;
        psz += 2;           // DBCS OK since we already verified that the
                            // chars were '/' and 'p', they can't be lead bytes
    } else
        *pfPrint = FALSE;

    psz = SkipWhiteSpace(psz);

    //
    // If the string ends in ".PFM"...
    //
    if (0 == lstrcmpi(PathFindExtension(psz), TEXT(".PFM")))
    {
        if (SUCCEEDED(BuildType1FontSpec(psz, szPfmPfb, ARRAYSIZE(szPfmPfb))))
        {
            psz = szPfmPfb;
        }
    }
    StringCchCopy( pszFontPath, cchFontPath, psz );
    return *psz != TEXT('\0');
}


/****************************************************************************
*
*     FUNCTION: GetGDILangID
*
*   REVIEW!  I believe this is how GDI determines the LangID, verify on
*   international builds.
*
\****************************************************************************/
WORD   GetGDILangID() {
    return (WORD)GetSystemDefaultLangID();
}



void ConvertTTStrToWinZStr( LPWSTR pwsz, LPVOID pvTTS, int cbMW ) {
    int i, cch;
    LPMWORD lpmw = pvTTS;

    cch = cbMW / sizeof(MWORD);

    for( i = 0; i < cch; i++ ) {
        *pwsz++ = MWORD2INT(*lpmw);
        lpmw++;
    }

    *pwsz = L'\0';
}


VOID ConvertDBCSTTStrToWinZStr( LPTSTR pwsz, LPCSTR pvTTS, ULONG cbMW ) {
    BYTE Name[256];
    WORD wordChar;
    BYTE *ansiName = Name;
    WORD *srcString = (WORD *)pvTTS;
    int length = 0;
    int cb = cbMW;

    for(;cb;cb-=2) {
        wordChar = *srcString;
        if(wordChar & 0x00FF) {
            *ansiName++ = (CHAR)((wordChar & 0x00FF));
            *ansiName++ = (CHAR)((wordChar & 0xFF00) >> 8);
            length += 2;
        } else {
            *ansiName++ = (CHAR)((wordChar & 0xFF00) >> 8);
            length++;
        }
        srcString++;
    }

    ansiName[length] = '\0';

    MultiByteToWideChar(CP_ACP,0,Name,length,pwsz,cbMW);
}

/****************************************************************************
*
*     FUNCTION: FindNameString
*
*   helper function for GetAlignedTTName
*
\****************************************************************************/
LPTSTR FindNameString(PBYTE pbTTData, int cNameRec, int idName, WORD wLangID)
{
    PTTNAMETBL ptnt;
    PTTNAMEREC ptnr;
    LPTSTR     psz;
    int        i;

    ptnt = (PTTNAMETBL)pbTTData;

    for( i = 0; i < cNameRec; i++ ) {
        LPVOID pvTTStr;

        ptnr = &(ptnt->anrNames[i]);
        if (MWORD2INT(ptnr->mwidPlatform) != TTID_PLATFORM_MS ||
            MWORD2INT(ptnr->mwidName) != idName               ||
            MWORD2INT(ptnr->mwidLang) != wLangID) {
            continue;
        }

        pvTTStr = (LPVOID)(pbTTData + MWORD2INT(ptnt->mwoffStrings)
                                    + MWORD2INT(ptnr->mwoffString));

        psz = AllocMem((MWORD2INT(ptnr->mwcbString) + sizeof(TEXT('\0'))) * 2);

        if ((MWORD2INT(ptnr->mwidEncoding) == TTID_MS_GB) ||
            (MWORD2INT(ptnr->mwidEncoding) == TTID_MS_WANSUNG) ||
            (MWORD2INT(ptnr->mwidEncoding) == TTID_MS_BIG5)) {
            ConvertDBCSTTStrToWinZStr( psz, pvTTStr, MWORD2INT(ptnr->mwcbString) );
        } else {
            ConvertTTStrToWinZStr( psz, pvTTStr, MWORD2INT(ptnr->mwcbString) );
        }

        return psz;
    }

    return NULL;
}



/****************************************************************************
*
*     FUNCTION: GetAlignedTTName
*
*   NOTE: This function returns an allocated string that must be freed
*   after use.
*
*   This function allocs a buffer to recopy the string into incase we are
*   running on a RISC machine with NT.  Since the string will be UNICODE
*   (ie. each char is a WORD), those strings must be aligned on WORD
*   boundaries.  Unfortunatly, TrueType files do not neccesarily align
*   the embedded unicode strings.  Furthur more, on NT we can not simply
*   return a pointer to the data stored in the input buffer, since the
*   'Unicode' strings stored in the TTF file are stored in Motorola (big
*   endian) format, and we need the unicode chars in Intel (little endian)
*   format. Last but not least, we need the returned string to be null terminated
*   so we need to either alloc the buffer for that case anyway.
*
\****************************************************************************/
LPTSTR GetAlignedTTName( PBYTE pbTTData, int idName ) {
    PTTNAMEREC ptnr;
    PTTNAMETBL ptnt;
    int cNameRec,i;
    LPTSTR psz;
    BOOL bFirstRetry;
    WORD wLangID = GetGDILangID();
    LCID lcid = GetThreadLocale();

    ptnt = (PTTNAMETBL)pbTTData;
    cNameRec = MWORD2INT(ptnt->mwcNameRec);

    //
    // Look For Microsoft Platform ID's
    //
    if (gbIsDBCS)
    {
        if ((psz = FindNameString(pbTTData, cNameRec, idName, wLangID)) != NULL) {
            return psz;
        }
        //
        // If we didn't find it, try English if we haven't already.
        //
        if ( wLangID != 0x0409 ) {
            if ((psz = FindNameString(pbTTData, cNameRec, idName, 0x0409)) != NULL) {
                return psz;
            }
        }
    }
    else
    {
        bFirstRetry = TRUE;

retry_lang:

        for( i = 0; i < cNameRec; i++ ) {
            LPVOID pvTTStr;
            ptnr = &(ptnt->anrNames[i]);
            if (MWORD2INT(ptnr->mwidPlatform) != TTID_PLATFORM_MS ||
                MWORD2INT(ptnr->mwidName) != idName               ||
                MWORD2INT(ptnr->mwidLang) != wLangID) {
                continue;
            }

            pvTTStr = (LPVOID)(pbTTData + MWORD2INT(ptnt->mwoffStrings) + MWORD2INT(ptnr->mwoffString));
            psz = AllocMem(MWORD2INT(ptnr->mwcbString) + sizeof(TEXT('\0')));

            ConvertTTStrToWinZStr( psz, pvTTStr, MWORD2INT(ptnr->mwcbString) );
            return psz;
        }

        //
        // Give 0x409 a try if there is no specified MAC language.
        //
        if (bFirstRetry && wLangID != 0x0409) {
            bFirstRetry = FALSE;
            wLangID     = 0x0409;
            goto retry_lang;
        }
    }

    //
    // Didn't find MS Platform, try Macintosh
    //
    for( i = 0; i < cNameRec; i++ ) {
        int cch;
        LPSTR pszMacStr;

        ptnr = &(ptnt->anrNames[i]);
        if (MWORD2INT(ptnr->mwidPlatform) != TTID_PLATFORM_MAC ||
            MWORD2INT(ptnr->mwidName) != idName                ||
            MWORD2INT(ptnr->mwidLang) != wLangID) {
            continue;
        }

        pszMacStr = (LPVOID)(pbTTData + MWORD2INT(ptnt->mwoffStrings) + MWORD2INT(ptnr->mwoffString));

        cch = MultiByteToWideChar(CP_MACCP, 0, pszMacStr, MWORD2INT(ptnr->mwcbString), NULL, 0);
        if (cch == 0)
            continue;

        cch += 1; // for null
        psz = AllocMem(cch * sizeof(TCHAR));
        if (psz == NULL)
            continue;

        cch = MultiByteToWideChar(CP_MACCP, 0, pszMacStr, MWORD2INT(ptnr->mwcbString), psz, cch);
        if (cch == 0) {
            FreeMem(psz);
            continue;
        }

        return psz;
    }

    //
    // Didn't find MS Platform nor Macintosh
    // 1. Try change Thread Locale to data Locale
    // 2. MultiByteToWideChar with Thread code page CP_THREAD_ACP
    //
    for( i = 0; i < cNameRec; i++ ) {
        int cch;
        LPSTR pszStr;

        ptnr = &(ptnt->anrNames[i]);
        if (MWORD2INT(ptnr->mwidName) != idName ||
            MWORD2INT(ptnr->mwidLang) == 0) {
            continue;
        }

        if (LANGIDFROMLCID(lcid) != MWORD2INT(ptnr->mwidLang)) {
            lcid = MAKELCID(MWORD2INT(ptnr->mwidLang), SORT_DEFAULT);
            if (!SetThreadLocale(lcid)) {
                break;
            }
        }

        pszStr = (LPVOID)(pbTTData + MWORD2INT(ptnt->mwoffStrings) + MWORD2INT(ptnr->mwoffString));

        cch = MultiByteToWideChar(CP_THREAD_ACP, 0, pszStr, MWORD2INT(ptnr->mwcbString), NULL, 0);
        if (cch == 0)
            continue;

        cch += 1; // for null
        psz = AllocMem(cch * sizeof(TCHAR));
        if (psz == NULL)
            continue;

        cch = MultiByteToWideChar(CP_THREAD_ACP, 0, pszStr, MWORD2INT(ptnr->mwcbString), psz, cch);
        if (cch == 0) {
            FreeMem(psz);
            continue;
        }

        return psz;
    }

    return NULL;
}


/****************************************************************************
*
*     FUNCTION: LoadFontFile
*
\****************************************************************************/
FFTYPE LoadFontFile( LPTSTR pszFontPath, PDISPTEXT pdtSmpl, HICON *phIcon ) {
    int cFonts;
    FFTYPE fft = FFT_BAD_FILE;
    SHFILEINFO sfi;
    LPTSTR pszAdobe;
    TCHAR szFPBuf[MAX_PATH];

    cFonts = AddFontResource( pszFontPath );

    if (gbIsDBCS)
    {
        //
        // save cFonts value to global variable.
        //
        gNumOfFonts = cFonts;
    }

    if (cFonts != 0) {
        LPLOGFONT lplf;
        DWORD cb;
        DWORD cbCFF = 0, cbMMSD = 0, cbDSIG = 0; // for OpenType
        BYTE *pbDSIG = NULL; // for OpenType
        BOOL  fIsTT;

        cb = sizeof(LOGFONT) * cFonts;

        if (gbIsDBCS)
        {
            //
            // save lplf to global variable.
            //
            glpLogFonts = lplf = AllocMem(cb);
        }
        else
        {
            lplf = AllocMem(cb);
        }

        // ?? Should this be GetFontResourceInfo (doesn't matter; but why force W)
        if (GetFontResourceInfoW( (LPTSTR)pszFontPath, &cb, lplf, GFRI_LOGFONTS )) {
            HDC hdc;
            HFONT hf, hfOld;
            LOGFONT lf;
            int nIndex;
            int cLoopReps = 1;

            BOOL fIsTrueTypeFont;
            DWORD dwSize = sizeof(BOOL);

            if(GetFontResourceInfoW((LPTSTR) pszFontPath, &dwSize, &fIsTrueTypeFont, GFRI_ISTRUETYPE)) {
                // If there is a raster & true type font on the system at the same time, 
                // and the height/width requested is supported by both fonts, the 
                // the font methods (which take the LOGFONT struct, *lplf) will select
                // the raster font (by design).  THis causes a problem when the user wants
                // to view the true type font; so, an extra check needs to be done to see if
                // the font requested is a true type, and if so then specify in the LOGFONT
                // struct to only show the true type font
                if(fIsTrueTypeFont) {
                    lplf->lfOutPrecision = OUT_TT_ONLY_PRECIS;
                }
            }

            //
            // This DBCS-aware code was originally placed within #ifdef DBCS
            // preprocessor statements.  For single-binary, these had to be
            // replaced with runtime checks.  The original code did some funky
            // things to execute a loop in DBCS builds but only a single iteration
            // in non-DBCS builds.  To do this, the "for" statement and it's
            // closing brace were placed in #ifdef DBCS like this:
            //
            // #ifdef DBCS
            //     for (nIndex = 0; nIndex < cFonts; nIndex++)
            //     {
            //          //
            //          // Other DBCS-specific code.
            //          //
            // #endif
            //          //
            //          // Code for both DBCS and non-DBCS systems
            //          // executes only once.
            //          //
            // #ifdef DBCS
            //     }
            // #endif
            //
            // While effective in a multi-binary configuration, this doesn't
            // translate well to a single-binary build.
            // To preserve the original logic without having to do major
            // reconstruction, I've replaced the loop sentinel variable with
            // "cLoopReps".  In non-DBCS locales, it is set to 1.  In DBCS
            // locales, it is assigned the value in "cFonts".
            //
            // [BrianAu 5/4/97]
            //

          if (gbIsDBCS)
              cLoopReps = cFonts;

          for (nIndex = 0; nIndex < cLoopReps; nIndex++) {
            if (gbIsDBCS)
            {
                lf = *(lplf + nIndex);

                //
                // Skip vertical font
                //
                if (lf.lfFaceName[0] == TEXT('@')) {
                    gNumOfFonts = (cFonts == 2) ? gNumOfFonts-1 : gNumOfFonts;
                    continue;
                }

                hf = CreateFontIndirect(&lf);
            }
            else
            {
                hf = CreateFontIndirect(lplf);
            }

            hdc = CreateCompatibleDC(NULL);

            if (hdc)
            {
                hfOld = SelectObject(hdc, hf);

                // Only otf fonts will have CFF table, tag is ' FFC'.

                cbCFF = GetFontData(hdc,' FFC', 0, NULL, 0);
                cbDSIG = GetFontData(hdc,'GISD', 0, NULL, 0);

                if (cbDSIG != GDI_ERROR)
                {
                    if ((pbDSIG = AllocMem(cbDSIG)) == NULL)
                    {
                        // Can't determine what's in the DSIG table.
                        // Continue as though the DSIG table does not exist.
                        cbDSIG = 0;
                    }
                    else
                    {
                        if (GetFontData (hdc, 'GISD', 0, pbDSIG, cbDSIG) == GDI_ERROR)
                        {
                            // Continue as though the DSIG table does not exist
                            cbDSIG = 0;
                        }
                        FreeMem(pbDSIG);
                    }
                }


                if (cbCFF == GDI_ERROR)
                    cbCFF = 0;

                if (cbDSIG == GDI_ERROR)
                    cbDSIG = 0;

                if (cbCFF || cbDSIG)
                {
                    fft = FFT_OTF;
                    if (cbCFF)
                    {
                        cbMMSD = GetFontData(hdc,'DSMM', 0, NULL, 0);
                        if (cbMMSD == GDI_ERROR)
                            cbMMSD = 0;
                    }
                }

                cb = GetFontData(hdc, TT_TBL_NAME, 0, NULL, 0);

                if (fft != FFT_OTF)
                {
                    fIsTT = (cb != 0 && cb != GDI_ERROR);
                    fft = fIsTT ? FFT_TRUETYPE : FFT_BITMAP;
                }

                if ((fft == FFT_TRUETYPE) || (fft == FFT_OTF)) {
                    int i;
                    LPBYTE lpTTData;
                    LPTSTR pszTmp;

                    lpTTData = AllocMem(cb);
                    GetFontData(hdc, TT_TBL_NAME, 0, lpTTData, cb);

                    i = 0;

                    //
                    // Title String
                    //
                    pdtSmpl->atlDsp[i].dtyp = DTP_SHRINKDRAW;
                    pdtSmpl->atlDsp[i].cptsSize = CPTS_TITLE_SIZE;
                    pdtSmpl->atlDsp[i].fLineUnder = TRUE;

                    pszTmp = GetAlignedTTName( lpTTData, TTID_NAME_FULLFONTNM );
                    if (pszTmp != NULL) {
                        if (gbIsDBCS)
                        {
                            //
                            // TTC Support.
                            //
                            if (nIndex == 0) {
                                pdtSmpl->atlDsp[i].pszText = CloneString(pszTmp);
                            } else {
                                pdtSmpl->atlDsp[i].pszText = FmtSprintf(MSG_TTC_CONCAT,
                                                                        pdtSmpl->atlDsp[i].pszText,
                                                                        pszTmp);
                            }

                            if (nIndex + 1 == cFonts) {
                                //
                                // If last this is last font, append "(True Type)"
                                //
                            pdtSmpl->atlDsp[i].pszText = FmtSprintf((fft == FFT_TRUETYPE) ? MSG_PTRUETYPEP : MSG_POPENTYPEP,
                                                                    pdtSmpl->atlDsp[i].pszText);
                            }
                            pdtSmpl->atlDsp[i].cchText = lstrlen(pdtSmpl->atlDsp[i].pszText);
                            FreeMem(pszTmp);
                        }
                        else
                        {
                            pdtSmpl->atlDsp[i].pszText = FmtSprintf((fft == FFT_TRUETYPE) ? MSG_PTRUETYPEP : MSG_POPENTYPEP, pszTmp);
                            pdtSmpl->atlDsp[i].cchText = lstrlen(pdtSmpl->atlDsp[i].pszText);
                            FreeMem(pszTmp);
                        }
                    } else {
                        if (gbIsDBCS)
                        {
                            //
                            // TTC support
                            //
                            if (nIndex == 0) {
                                pdtSmpl->atlDsp[i].pszText = CloneString(lf.lfFaceName);
                            } else {
                                pdtSmpl->atlDsp[i].pszText = FmtSprintf(MSG_TTC_CONCAT,
                                                                        pdtSmpl->atlDsp[i].pszText,
                                                                        lf.lfFaceName);
                            }

                            if (nIndex + 1 == cFonts) {
                                //
                                // If last this is last font, append "(True Type)"
                                //
                                pdtSmpl->atlDsp[i].pszText = FmtSprintf((fft == FFT_TRUETYPE) ? MSG_PTRUETYPEP : MSG_POPENTYPEP,
                                                                        pdtSmpl->atlDsp[i].pszText);
                            }
                            pdtSmpl->atlDsp[i].cchText = lstrlen(pdtSmpl->atlDsp[i].pszText);
                        }
                        else
                        {
                            pdtSmpl->atlDsp[i].pszText = CloneString(lplf->lfFaceName);
                            pdtSmpl->atlDsp[i].cchText = lstrlen(pdtSmpl->atlDsp[0].pszText);
                        }
                    }
                    i++;
                    pdtSmpl->atlDsp[i] = pdtSmpl->atlDsp[i-1];

                    //// insert an extra line to provide better description of the font

                    if (fft == FFT_OTF)
                    {
                        LPTSTR pszTemp = NULL;
                        WCHAR awcTmp[256];
                        awcTmp[0] = 0; // zero init

                        pdtSmpl->atlDsp[i].dtyp = DTP_NORMALDRAW;
                        pdtSmpl->atlDsp[i].cptsSize = CPTS_INFO_SIZE;
                        pdtSmpl->atlDsp[i].fLineUnder = FALSE;

                        pdtSmpl->atlDsp[i].pszText = FmtSprintf(
                                          MSG_POTF,
                                          awcTmp);

                        if (cbDSIG)
                        {
                            pszTemp = pdtSmpl->atlDsp[i].pszText;

                            pdtSmpl->atlDsp[i].pszText = FmtSprintf(
                                          MSG_PDSIG,
                                          pdtSmpl->atlDsp[i].pszText);

                            FmtFree(pszTemp);
                        }

                        pszTemp = pdtSmpl->atlDsp[i].pszText;
                        pdtSmpl->atlDsp[i].pszText = FmtSprintf(
                                      cbCFF ? MSG_PPSGLYPHS : MSG_PTTGLYPHS,
                                      pdtSmpl->atlDsp[i].pszText);
                        FmtFree(pszTemp);

                        pszTemp = pdtSmpl->atlDsp[i].pszText;
                        pdtSmpl->atlDsp[i].pszText = FmtSprintf(
                                      MSG_PINSTRUCTIONS,
                                      pdtSmpl->atlDsp[i].pszText);
                        FmtFree(pszTemp);

                        if (cbCFF)
                        {
                            pszTemp = pdtSmpl->atlDsp[i].pszText;
                            pdtSmpl->atlDsp[i].pszText = FmtSprintf(
                                          cbMMSD ? MSG_PMULTIPLEMASTER : MSG_PSINGLEMASTER,
                                          pdtSmpl->atlDsp[i].pszText);
                            FmtFree(pszTemp);
                        }

                        pdtSmpl->atlDsp[i].cchText = lstrlen(pdtSmpl->atlDsp[i].pszText);

                        i++;
                        pdtSmpl->atlDsp[i] = pdtSmpl->atlDsp[i-1];
                    }

                    //
                    // Typeface Name:
                    //
                    pdtSmpl->atlDsp[i].cptsSize = CPTS_INFO_SIZE;
                    pdtSmpl->atlDsp[i].dtyp = DTP_NORMALDRAW;
                    pdtSmpl->atlDsp[i].fLineUnder = FALSE;
                    pszTmp = GetAlignedTTName( lpTTData, TTID_NAME_FONTFAMILY );
                    if (pszTmp != NULL) {
                        pdtSmpl->atlDsp[i].pszText = FmtSprintf(MSG_TYPEFACENAME, pszTmp);
                        pdtSmpl->atlDsp[i].cchText = lstrlen(pdtSmpl->atlDsp[i].pszText);
                        FreeMem(pszTmp);
                        i++;
                        pdtSmpl->atlDsp[i] = pdtSmpl->atlDsp[i-1];
                    }

                    //
                    // File size:
                    //
                    pdtSmpl->atlDsp[i].pszText = FmtSprintf(MSG_FILESIZE,
                            ROUND_UP_DIV(GetFileSizeFromName(pszFontPath), CB_ONE_K));
                    pdtSmpl->atlDsp[i].cchText = lstrlen(pdtSmpl->atlDsp[i].pszText);

                    //
                    // Version:
                    //
                    pszTmp = GetAlignedTTName( lpTTData, TTID_NAME_VERSIONSTR );
                    if (pszTmp != NULL) {
                        i++;
                        pdtSmpl->atlDsp[i] = pdtSmpl->atlDsp[i-1];
                        pdtSmpl->atlDsp[i].pszText = FmtSprintf(MSG_VERSION, pszTmp);
                        pdtSmpl->atlDsp[i].cchText = lstrlen(pdtSmpl->atlDsp[i].pszText);
                        FreeMem( pszTmp );
                    }

                    //
                    // Copyright string
                    //
                    pszTmp = GetAlignedTTName( lpTTData, TTID_NAME_COPYRIGHT );
                    if (pszTmp != NULL) {
                        i++;
                        pdtSmpl->atlDsp[i] = pdtSmpl->atlDsp[i-1];
                        pdtSmpl->atlDsp[i].cptsSize = CPTS_COPYRIGHT_SIZE;
                        pdtSmpl->atlDsp[i].dtyp = DTP_WRAPDRAW;
                        pdtSmpl->atlDsp[i].pszText = FmtSprintf(MSG_COPYRIGHT, pszTmp);
                        pdtSmpl->atlDsp[i].cchText = lstrlen(pdtSmpl->atlDsp[i].pszText);
                        FreeMem( pszTmp );
                    }

                    pdtSmpl->atlDsp[i].fLineUnder = TRUE;

                    if (gbIsDBCS)
                    {
                        //
                        // TTC Support.
                        //
                        FreeMem(lpTTData);
                    }
                } else {

                    // Title String (Non TrueType case)

                    pdtSmpl->atlDsp[0].dtyp = DTP_SHRINKDRAW;
                    pdtSmpl->atlDsp[0].cptsSize = CPTS_TITLE_SIZE;
                    pdtSmpl->atlDsp[0].fLineUnder = TRUE;
                    pdtSmpl->atlDsp[0].pszText = CloneString(lplf->lfFaceName);
                    pdtSmpl->atlDsp[0].cchText = lstrlen(pdtSmpl->atlDsp[0].pszText);

                    // Use Default quality, so we can see GDI scaling of Bitmap Fonts
                    lplf->lfQuality = DEFAULT_QUALITY;
                    lplf->lfWidth = 0;
                }

                // If LPK is loaded then GetFontResourceInfo(GFRI_LOGFONTS) may return ANSI_CHARSET for some DBCS fonts.
                // Get the native char set.
                if (gbIsDBCS & NativeCodePageSupported(lplf)) {
                        //
                        // Native code page is supported, set that codepage
                        //
                        CHARSETINFO csi;
        
                        TranslateCharsetInfo( (LPDWORD)IntToPtr(GetACP()), &csi, TCI_SRCCODEPAGE );
        
                        lplf->lfCharSet = (BYTE)csi.ciCharset;
                }

                SelectObject(hdc, hfOld);
                DeleteDC(hdc);
            } // if (hdc)
            
            if (hf)
            {
                DeleteObject(hf);
            }

          } // for
            pdtSmpl->lfTestFont = *lplf;
        }

        if (!gbIsDBCS)
        {
            FreeMem(lplf);
        }
    }


    //
    // MAJOR HACK!
    //
    // Since ATM-Type1 fonts are split between two files, (*.PFM and *.PFB) we have done a hack
    // earlier in the code to find the missing filename and concatinate them together in
    // the form "FOO.PFM|FOO.PFB", so we can then call AddFontResource() with only one string.
    //
    // Since SHGetFileInfo does not understand this hacked filename format, we must split ATM-Type1
    // names appart here and then reconcat them after we call the shell api.
    //
    pszAdobe = pszFontPath;

    while( *pszAdobe && *pszAdobe != TEXT('|') )
        pszAdobe = CharNext(pszAdobe);

    if ( *pszAdobe ) {

        *pszAdobe = TEXT('\0');

        pdtSmpl->atlDsp[0].pszText = FmtSprintf(MSG_PTYPE1, pdtSmpl->atlDsp[0].pszText);
        pdtSmpl->atlDsp[0].cchText = lstrlen(pdtSmpl->atlDsp[0].pszText);

    } else {
        pszAdobe = NULL;
    }
    // end of HACK


    //
    // Get the associated icon for this font file type
    //
    if ( fft != FFT_BAD_FILE && SHGetFileInfo( pszFontPath, 0, &sfi, sizeof(sfi), SHGFI_ICON )) {
        *phIcon = sfi.hIcon;
    } else
        *phIcon = NULL;

    //
    // HACK - restore the '|' we nuked above
    //
    if ( pszAdobe != NULL ) {
        *pszAdobe = TEXT('|');
    }
    // end of HACK

    return fft;
}


/****************************************************************************
*
*     FUNCTION: DrawFontSample
*
* Parameters:
*
*   lprcPage    Size of the page in pels.  A page is either a printed
*               sheet (on a printer) or the Window.
*
*   cyOffset    Offset into the virtual sample text.  Used to "scroll" the
*               window up and down.  Positive number means start further
*               down in the virtual sample text as the top line in the
*               lprcPage.
*
*   lprcPaint   Rectangle to draw.  It is in the same coord space as
*               lprcPage.  Used to optimize window repaints, and to
*               support banding to printers.
*
*
\****************************************************************************/
int DrawFontSample( HDC hdc, LPRECT lprcPage, int cyOffset, LPRECT lprcPaint, BOOL fReallyDraw ) {
    int cyDPI;
    HFONT hfOld, hfText, hfDesk;
    LOGFONT lfTmp;
    int yBaseline = -cyOffset;
    int taOld,i;
    TCHAR szNumber[10];
    int cyShkTxt = -1, cptsShkTxt = -1;
    SIZE sz;
    int cxPage;

    DPRINT((DBTX("PAINTING")));

    cyDPI = GetDeviceCaps(hdc, LOGPIXELSY );
    taOld = SetTextAlign(hdc, TA_BASELINE);

    glfFont.lfHeight = MulDiv( -CPTS_COPYRIGHT_SIZE, cyDPI, C_PTS_PER_INCH );
    hfDesk = CreateFontIndirect(&glfFont);

    // Get hfOld for later
    hfOld = SelectObject(hdc, hfDesk);


    if (gbIsDBCS)
    {
        //
        // if two or more fonts exist, set correct typeface name
        //
        if (gNumOfFonts > 1 && gfftFontType == FFT_TRUETYPE) {
            gdtDisplay.atlDsp[INDEX_TYPEFACENAME].pszText =
                                FmtSprintf(MSG_TYPEFACENAME, gdtDisplay.lfTestFont.lfFaceName);
            gdtDisplay.atlDsp[INDEX_TYPEFACENAME].cchText =
                                lstrlen(gdtDisplay.atlDsp[INDEX_TYPEFACENAME].pszText);
        }
    }

    //
    // Find the longest shrinktext line so we can make sure they will fit
    // on the screen
    //
    cxPage = lprcPage->right - lprcPage->left;
    for( i = 0; i < CLINES_DISPLAY && gdtDisplay.atlDsp[i].dtyp != DTP_UNUSED; i++ ) {
        PTXTLN ptlCurrent = &(gdtDisplay.atlDsp[i]);

        if (ptlCurrent->dtyp == DTP_SHRINKTEXT) {
            lfTmp = gdtDisplay.lfTestFont;

            if (cptsShkTxt == -1)
                cptsShkTxt = ptlCurrent->cptsSize;

            cyShkTxt = MulDiv( -cptsShkTxt, cyDPI, C_PTS_PER_INCH );

            lfTmp.lfHeight = cyShkTxt;

            hfText = CreateFontIndirect( &lfTmp );
            SelectObject(hdc, hfText);

            GetTextExtentPoint32(hdc, ptlCurrent->pszText, ptlCurrent->cchText, &sz );

            SelectObject(hdc, hfOld);
            DeleteObject(hfText);

            // Make sure shrink lines are not too long
            if (sz.cx > cxPage) {

                DPRINT((DBTX(">>>Old lfH:%d sz.cx:%d cxPage:%d"), lfTmp.lfHeight, sz.cx, cxPage));

                cptsShkTxt = cptsShkTxt * cxPage / sz.cx;
                cyShkTxt = MulDiv( -cptsShkTxt, cyDPI, C_PTS_PER_INCH );

                DPRINT((DBTX(">>>New lfH:%d"),lfTmp.lfHeight));
            }
        }
    }


    //
    // Paint the screen/page
    //
    for( i = 0; i < CLINES_DISPLAY && gdtDisplay.atlDsp[i].dtyp != DTP_UNUSED; i++ ) {
        TEXTMETRIC tm;
        PTXTLN ptlCurrent = &(gdtDisplay.atlDsp[i]);

        // Create and select the font for this line

        if (ptlCurrent->dtyp == DTP_TEXTOUT || ptlCurrent->dtyp == DTP_SHRINKTEXT )
            lfTmp = gdtDisplay.lfTestFont;
        else
            lfTmp = glfFont;

        if (ptlCurrent->dtyp == DTP_SHRINKTEXT) {
            DPRINT((DBTX("PAINT:Creating ShrinkText Font:%s height:%d"), lfTmp.lfFaceName, lfTmp.lfHeight ));
            lfTmp.lfHeight = cyShkTxt;
        }
        else
            lfTmp.lfHeight = MulDiv( -ptlCurrent->cptsSize, cyDPI, C_PTS_PER_INCH );

        hfText = CreateFontIndirect( &lfTmp );
        SelectObject(hdc, hfText);


        // Get size characteristics for this line in the selected font
        if (ptlCurrent->dtyp == DTP_SHRINKDRAW) {

            GetTextExtentPoint32(hdc, ptlCurrent->pszText, ptlCurrent->cchText, &sz );

            // Make sure shrink lines are not too long
            if (sz.cx > cxPage) {

                SelectObject(hdc, hfOld);
                DeleteObject(hfText);

                DPRINT((DBTX("===Old lfH:%d sz.cx:%d cxPage:%d"), lfTmp.lfHeight, sz.cx, cxPage));

                lfTmp.lfHeight = MulDiv( -ptlCurrent->cptsSize * cxPage / sz.cx, cyDPI, C_PTS_PER_INCH );

                DPRINT((DBTX("===New lfH:%d"),lfTmp.lfHeight));

                hfText = CreateFontIndirect( &lfTmp );
                SelectObject(hdc, hfText);
            }
        }



        GetTextMetrics(hdc, &tm);

        yBaseline += (tm.tmAscent + tm.tmExternalLeading);
        DPRINT((DBTX("tmH:%d tmA:%d tmD:%d tmIL:%d tmEL:%d"), tm.tmHeight, tm.tmAscent, tm.tmDescent, tm.tmInternalLeading, tm.tmExternalLeading));

        // Draw the text
        switch(ptlCurrent->dtyp) {
            case DTP_NORMALDRAW:
            case DTP_SHRINKDRAW:
            case DTP_SHRINKTEXT:
                if (fReallyDraw) {
                    ExtTextOut(hdc, lprcPage->left, yBaseline, ETO_CLIPPED, lprcPaint,
                            ptlCurrent->pszText, ptlCurrent->cchText, NULL);
                }

                //
                // Bob says "This looks nice!" (Adding a little extra white space before the underline)
                //
                if (ptlCurrent->fLineUnder)
                    yBaseline += tm.tmDescent;

                break;

            case DTP_WRAPDRAW: {
                RECT rc;
                int cy;

                yBaseline += tm.tmDescent;
                SetRect(&rc, lprcPage->left, yBaseline - tm.tmHeight, lprcPage->right, yBaseline );

                DPRINT((DBTX("**** Org RC:(%d, %d, %d, %d)  tmH:%d"), rc.left, rc.top, rc.right, rc.bottom, tm.tmHeight));
                cy = DrawText(hdc, ptlCurrent->pszText, ptlCurrent->cchText, &rc,
                        DT_NOPREFIX | DT_WORDBREAK | DT_CALCRECT);


                DPRINT((DBTX("**** Cmp RC:(%d, %d, %d, %d)  cy:%d"), rc.left, rc.top, rc.right, rc.bottom, cy));
                if( cy > tm.tmHeight )
                    yBaseline = rc.bottom = rc.top + cy;

                if (fReallyDraw) {
                    SetTextAlign(hdc, taOld);
                    DrawText(hdc, ptlCurrent->pszText, ptlCurrent->cchText, &rc, DT_NOPREFIX | DT_WORDBREAK);
                    SetTextAlign(hdc, TA_BASELINE);
                }
                break;
            }

            case DTP_TEXTOUT:
                if (fReallyDraw) {
                    SIZE szNum;
                    int cchNum;
                    SelectObject(hdc, hfDesk );
                    StringCchPrintf( szNumber, ARRAYSIZE(szNumber), TEXT("%d"), ptlCurrent->cptsSize );
                    cchNum = lstrlen(szNumber);
                    ExtTextOut(hdc, lprcPage->left, yBaseline, ETO_CLIPPED, lprcPaint, szNumber, cchNum, NULL);


                    GetTextExtentPoint32(hdc, szNumber, cchNum, &szNum);

                    SelectObject(hdc, hfText);
                    ExtTextOut(hdc, lprcPage->left + szNum.cx * 2, yBaseline, ETO_CLIPPED, lprcPaint,
                            ptlCurrent->pszText, ptlCurrent->cchText, NULL);
                }
                break;
        }

        yBaseline += tm.tmDescent;

        if (fReallyDraw && ptlCurrent->fLineUnder) {
            MoveToEx( hdc, lprcPage->left, yBaseline, NULL);
            LineTo( hdc, lprcPage->right, yBaseline );

            // Leave space for the line we just drew
            yBaseline += 1;
        }

        SelectObject( hdc, hfOld );
        DeleteObject( hfText );
    }

    SelectObject(hdc, hfOld);
    SetTextAlign(hdc, taOld);
    DeleteObject(hfDesk);

    return yBaseline;
}

/****************************************************************************
*
*     FUNCTION: PaintSampleWindow
*
\****************************************************************************/
void PaintSampleWindow( HWND hwnd, HDC hdc, PAINTSTRUCT *pps ) {
    RECT rcClient;

    GetClientRect(hwnd, &rcClient);

    DrawFontSample( hdc, &rcClient, gyScroll, &(pps->rcPaint), TRUE );

}


/****************************************************************************
*
*     FUNCTION: FrameWndProc(HWND, unsigned, WORD, LONG)
*
*     PURPOSE:  Processes messages
*
*     MESSAGES:
*
*         WM_COMMAND    - application menu (About dialog box)
*         WM_DESTROY    - destroy window
*
*     COMMENTS:
*
*         To process the IDM_ABOUT message, call MakeProcInstance() to get the
*         current instance address of the About() function.  Then call Dialog
*         box which will create the box according to the information in your
*         fontview.rc file and turn control over to the About() function.  When
*         it returns, free the intance address.
*
\****************************************************************************/

LRESULT APIENTRY FrameWndProc(
        HWND hwnd,                /* window handle                   */
        UINT message,             /* type of message                 */
        WPARAM wParam,            /* additional information          */
        LPARAM lParam)            /* additional information          */
{
    static SIZE szWindow = {0, 0};

    switch (message) {

        case WM_PAINT: {
            HDC hdc;
            RECT rc;
            PAINTSTRUCT ps;
            int x;

            hdc = BeginPaint(hwnd, &ps);

            // get the window rect
            GetClientRect(hwnd, &rc);

            // extend only down by gcyBtnArea
            rc.bottom = rc.top + gcyBtnArea;

            // Fill rect with button face color (handled by class background brush)
            // FillRect(hdc, &rc, ghbr3DFace);

            // Fill small rect at bottom with edge color
            rc.top = rc.bottom - 2;
            FillRect(hdc, &rc, ghbr3DShadow);

            ReleaseDC(hwnd, hdc);

            EndPaint(hwnd, &ps);
            break;
        }

        case WM_CREATE: {
            HDC hdc;
            RECT rc;
            int i;

            GetClientRect(hwnd, &rc);
            szWindow.cx = rc.right - rc.left;
            szWindow.cy = rc.bottom - rc.top;

            for( i = 0; i < C_BUTTONS; i++ ) {
                int x = gabtCmdBtns[i].x;
                HWND hwndBtn;

                if (gbIsDBCS)
                {
                    DWORD dwStyle = 0;

                    //
                    // If font is not TrueType font or not TTC font,
                    // AND button id is previous/next,
                    // then just continue.
                    //
                    if ((gfftFontType != FFT_TRUETYPE ||
                         gNumOfFonts <= 1) &&
                        (gabtCmdBtns[i].id == IDB_PREV_FONT ||
                         gabtCmdBtns[i].id == IDB_NEXT_FONT)) {
                            continue;
                    }
                    //
                    // Set x potision for each button.
                    //
                    switch (gabtCmdBtns[i].id) {
                        case IDB_PREV_FONT:
                            x = szWindow.cx / 2 - gabtCmdBtns[i].cx - 5;
                            dwStyle = WS_DISABLED;  // initially disabled.
                            break;
                        case IDB_NEXT_FONT:
                            x = szWindow.cx / 2 + 5;
                            break;
                        default:
                            if (x < 0)
                                x = szWindow.cx + x - gabtCmdBtns[i].cx;
                    }
                    gabtCmdBtns[i].hwnd = hwndBtn = CreateWindow( TEXT("button"),
                            gabtCmdBtns[i].pszText,
                            BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | dwStyle,
                            x, gabtCmdBtns[i].y,
                            gabtCmdBtns[i].cx, gabtCmdBtns[i].cy,
                            hwnd, (HMENU)IntToPtr(gabtCmdBtns[i].id),
                            hInst, NULL);
                }
                else
                {
                    if (x < 0)
                        x = szWindow.cx + x - gabtCmdBtns[i].cx;

                    gabtCmdBtns[i].hwnd = hwndBtn = CreateWindow( TEXT("button"),
                            gabtCmdBtns[i].pszText, BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE,
                            x, gabtCmdBtns[i].y,
                            gabtCmdBtns[i].cx, gabtCmdBtns[i].cy,
                            hwnd, (HMENU)IntToPtr(gabtCmdBtns[i].id),
                            hInst, NULL);

                }
                if (hwndBtn != NULL) {
                    SendMessage(hwndBtn,
                                WM_SETFONT,
                                (WPARAM)GetStockObject(DEFAULT_GUI_FONT),
                                MAKELPARAM(TRUE, 0));
                }
            }

            ghwndView = CreateWindow( TEXT("FontDisplayClass"), NULL, WS_CHILD | WS_VSCROLL | WS_VISIBLE,
                    0, gcyBtnArea, szWindow.cx, szWindow.cy - gcyBtnArea, hwnd, 0, hInst, NULL );

            break;
        }

        case WM_GETMINMAXINFO: {
            LPMINMAXINFO lpmmi = (LPMINMAXINFO) lParam;

            lpmmi->ptMinTrackSize.x = gcxMinWinSize;
            lpmmi->ptMinTrackSize.y = gcyMinWinSize;

            break;
        }

        case WM_SIZE: {
            int cxNew, cyNew;
            HDC hdc;
            RECT rc;
            SCROLLINFO sci;

            cxNew = LOWORD(lParam);
            cyNew = HIWORD(lParam);

            if (cyNew != szWindow.cy || cxNew != szWindow.cx) {
                int i;

                if (gbIsDBCS)
                {
                    for( i = 0; i < C_BUTTONS; i++ ) {
                        int x = gabtCmdBtns[i].x;

                        //
                        // If font is not TrueType font or not TTC font,
                        // AND button id is previous/next,
                        // then just continue.
                        //
                        if ((gfftFontType != FFT_TRUETYPE ||
                             gNumOfFonts <= 1) &&
                            (gabtCmdBtns[i].id == IDB_PREV_FONT ||
                             gabtCmdBtns[i].id == IDB_NEXT_FONT)) {
                                continue;
                        }
                        //
                        // Set x potision for each button.
                        //
                        switch (gabtCmdBtns[i].id) {
                            case IDB_PREV_FONT:
                                SetWindowPos(gabtCmdBtns[i].hwnd,
                                             NULL,
                                             cxNew / 2 - gabtCmdBtns[i].cx - 5,
                                             gabtCmdBtns[i].y,
                                             0,
                                             0,
                                             SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE );
                                break;
                            case IDB_NEXT_FONT:
                                SetWindowPos(gabtCmdBtns[i].hwnd,
                                             NULL,
                                             cxNew /2 + 5,
                                             gabtCmdBtns[i].y,
                                             0,
                                             0,
                                             SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE );
                                break;
                            default:
                                if (x < 0) {
                                    SetWindowPos(gabtCmdBtns[i].hwnd,
                                                 NULL,
                                                 cxNew + x - gabtCmdBtns[i].cx,
                                                 gabtCmdBtns[i].y,
                                                 0,
                                                 0,
                                                 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE );
                                }
                        }
                    }
                }
                else // !DBCS
                {
                    for( i = 0; i < C_BUTTONS; i++ ) {
                        int x = gabtCmdBtns[i].x;

                        if (x < 0) {
                            SetWindowPos(gabtCmdBtns[i].hwnd, NULL, cxNew + x - gabtCmdBtns[i].cx, gabtCmdBtns[i].y, 0, 0,
                                    SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE );
                        }
                    }
                } // DBCS

                szWindow.cx = cxNew;
                szWindow.cy = cyNew;

                SetWindowPos(ghwndView, NULL, 0, gcyBtnArea, szWindow.cx, szWindow.cy - gcyBtnArea,
                        SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE );

            }
            break;
        }

        case WM_COMMAND:           /* message: command from application menu */
            if (LOWORD(wParam) != IDB_DONE)
                return SendMessage(ghwndView, message, wParam, lParam);

            PostMessage(ghwndFrame, WM_CLOSE, 0, 0);
            break;

        case WM_DESTROY: {
            int i;

            DestroyWindow(ghwndView);
            for( i = 0; i < C_BUTTONS; i++ ) {
                DestroyWindow(gabtCmdBtns[i].hwnd);
            }

            PostQuitMessage(0);
            break;
        }

        default:                          /* Passes it on if unproccessed    */
            return (DefWindowProc(hwnd, message, wParam, lParam));
    }
    return (0L);
}

/****************************************************************************
*
*     FUNCTION: ViewWndProc(HWND, unsigned, WORD, LONG)
*
*     PURPOSE:  Processes messages
*
*     MESSAGES:
*
*         WM_COMMAND    - application menu (About dialog box)
*         WM_DESTROY    - destroy window
*
*     COMMENTS:
*
*         To process the IDM_ABOUT message, call MakeProcInstance() to get the
*         current instance address of the About() function.  Then call Dialog
*         box which will create the box according to the information in your
*         fontview.rc file and turn control over to the About() function.  When
*         it returns, free the intance address.
*
\****************************************************************************/

LRESULT APIENTRY ViewWndProc(
        HWND hwnd,                /* window handle                   */
        UINT message,             /* type of message                 */
        WPARAM wParam,            /* additional information          */
        LPARAM lParam)            /* additional information          */
{
    static SIZE szWindow = {0, 0};
    static int  cyVirtPage = 0;

    switch (message) {

        case WM_CREATE: {
            HDC hdc;
            RECT rc;
            SCROLLINFO sci;
            int i;

            GetClientRect(hwnd, &rc);
            szWindow.cx = rc.right - rc.left;
            szWindow.cy = rc.bottom - rc.top;

            hdc = CreateCompatibleDC(NULL);
            cyVirtPage = DrawFontSample(hdc, &rc, 0, NULL, FALSE);
            DeleteDC(hdc);


            gyScroll = 0;

            sci.cbSize = sizeof(sci);
            sci.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
            sci.nMin = 0;
            sci.nMax = cyVirtPage;
            sci.nPage = szWindow.cy;
            sci.nPos = gyScroll;

            SetScrollInfo(hwnd, SB_VERT, &sci, TRUE );

            if (gfPrint)
                PostMessage(hwnd, WM_COMMAND, IDB_PRINT, 0);
            break;
        }

        case WM_SIZE: {
            int cxNew, cyNew;
            HDC hdc;
            RECT rc;
            SCROLLINFO sci;

            cxNew = LOWORD(lParam);
            cyNew = HIWORD(lParam);

            if (cyNew != szWindow.cy || cxNew != szWindow.cx) {
                int i;

                szWindow.cx = cxNew;
                szWindow.cy = cyNew;

                hdc = CreateCompatibleDC(NULL);
                SetRect(&rc, 0, 0, szWindow.cx, szWindow.cy);
                cyVirtPage = DrawFontSample(hdc, &rc, 0, NULL, FALSE);
                DeleteDC(hdc);

                if (cyVirtPage <= cyNew) {
                    // Disable the scrollbar
                    gyScroll = 0;
                }

                if (cyVirtPage > szWindow.cy && gyScroll > cyVirtPage - szWindow.cy)
                    gyScroll = cyVirtPage - szWindow.cy;

                sci.cbSize = sizeof(sci);
                sci.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
                sci.nMin = 0;
                sci.nMax = cyVirtPage;
                sci.nPage = cyNew;
                sci.nPos = gyScroll;

                SetScrollInfo(hwnd, SB_VERT, &sci, TRUE );
            }
            break;
        }

        case WM_VSCROLL: {
            int iCode = (int)LOWORD(wParam);
            int yPos = (int)HIWORD(wParam);
            int yNewScroll = gyScroll;

            switch( iCode ) {

            case SB_THUMBPOSITION:
            case SB_THUMBTRACK:
                if (yPos != yNewScroll)
                    yNewScroll = yPos;
                break;

            case SB_LINEUP:
                yNewScroll -= gcyLine;
                break;

            case SB_PAGEUP:
                yNewScroll -= szWindow.cy;
                break;

            case SB_LINEDOWN:
                yNewScroll += gcyLine;
                break;

            case SB_PAGEDOWN:
                yNewScroll += szWindow.cy;
                break;

            case SB_TOP:
                yNewScroll = 0;
                break;

            case SB_BOTTOM:
                yNewScroll = cyVirtPage;
                break;
            }

            if (yNewScroll < 0)
                yNewScroll = 0;

            if (yNewScroll > cyVirtPage - szWindow.cy)
                yNewScroll = cyVirtPage - szWindow.cy;

            if (yNewScroll < 0)
                yNewScroll = 0;

            if (gyScroll != yNewScroll) {
                SCROLLINFO sci;
                int dyScroll;

                dyScroll = gyScroll - yNewScroll;

                if (ABS(dyScroll) < szWindow.cy) {
                    ScrollWindowEx(hwnd, 0, dyScroll, NULL, NULL, NULL, NULL, SW_ERASE | SW_INVALIDATE);
                } else
                    InvalidateRect(hwnd, NULL, TRUE);

                gyScroll = yNewScroll;

                sci.cbSize = sizeof(sci);
                sci.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
                sci.nMin = 0;
                sci.nMax = cyVirtPage;
                sci.nPage = szWindow.cy;
                sci.nPos = gyScroll;

                SetScrollInfo(hwnd, SB_VERT, &sci, TRUE );
            }

            break;
        }


        case WM_COMMAND:           /* message: command from application menu */
            if( !DoCommand( hwnd, wParam, lParam ) )
                return (DefWindowProc(hwnd, message, wParam, lParam));
            break;

        case WM_PAINT: {
            HDC hdc;
            PAINTSTRUCT ps;

            hdc = BeginPaint( hwnd, &ps );
            PaintSampleWindow( hwnd, hdc, &ps );
            EndPaint( hwnd, &ps );
            break;
        }

        default:                          /* Passes it on if unproccessed    */
            return (DefWindowProc(hwnd, message, wParam, lParam));
    }
    return (0L);
}

/*********************************************\
*
* PRINT DLGS
*
*
\*********************************************/
HDC PromptForPrinter(HWND hwnd, HINSTANCE hInst, int *pcCopies ) {
    PRINTDLG pd;

    FillMemory(&pd, sizeof(pd), 0);

    pd.lStructSize = sizeof(pd);
    pd.hwndOwner = hwnd;
    pd.Flags = PD_RETURNDC | PD_NOSELECTION;
    pd.nCopies = 1;
    pd.hInstance = hInst;

    if (PrintDlg(&pd)) {
        *pcCopies = pd.nCopies;
        return pd.hDC;
    } else
        return NULL;
}

/****************************************************************************\
*
*     FUNCTION: PrintSampleWindow(hwnd)
*
*       Prompts for a printer and then draws the sample text to the printer
*
\****************************************************************************/
void PrintSampleWindow(HWND hwnd) {
    HDC hdc;
    DOCINFO di;
    int cxDPI, cyDPI, iPage, cCopies;
    RECT rcPage;
    HCURSOR hcur;

    hdc = PromptForPrinter(hwnd, hInst, &cCopies);
    if (hdc == NULL)
        return;

    hcur = SetCursor(LoadCursor(NULL, MAKEINTRESOURCE(IDC_WAIT)));

    cyDPI = GetDeviceCaps(hdc, LOGPIXELSY );
    cxDPI = GetDeviceCaps(hdc, LOGPIXELSX );

    /*
     * Set a one inch margine around the page
     */
    SetRect(&rcPage, 0, 0, GetDeviceCaps(hdc, HORZRES), GetDeviceCaps(hdc, VERTRES));

    rcPage.left    += cxDPI;
    rcPage.right   -= cxDPI;


    di.cbSize = sizeof(di);
    di.lpszDocName = gdtDisplay.atlDsp[0].pszText;
    di.lpszOutput = NULL;
    di.lpszDatatype = NULL;
    di.fwType = 0;

    StartDoc(hdc, &di);

    for( iPage = 0; iPage < cCopies; iPage++ ) {
        StartPage(hdc);

        DrawFontSample( hdc, &rcPage, -cyDPI, &rcPage, TRUE );

        EndPage(hdc);
    }

    EndDoc(hdc);

    DeleteDC(hdc);

    SetCursor(hcur);
}


/****************************************************************************\
*
*     FUNCTION: EnableCommandButtons(id, bEnable)
*
*       Enable/disable command button.
*
\****************************************************************************/
BOOL EnableCommandButton(int id, BOOL bEnable)
{
    int  i;
    HWND hwnd = NULL;

    for( i = 0; i < C_BUTTONS; i++ ) {
        if (gabtCmdBtns[i].id == id) {
            hwnd = gabtCmdBtns[i].hwnd;
            break;
        }
    }
    return (hwnd == NULL) ? FALSE: EnableWindow(hwnd, bEnable);
}


/****************************************************************************\
*
*     FUNCTION: ViewNextFont(iInc)
*
*       Show the previous/next font.
*
\****************************************************************************/
void ViewNextFont(int iInc)
{
    int index = gIndexOfFonts + iInc;

    while (1) {
        if ( index < 0 || index >= gNumOfFonts ) {
            //
            // if out of range, then return.
            //
            MessageBeep(MB_OK);
            return;
        }
        else if ((*(glpLogFonts + index)).lfFaceName[0] == TEXT('@')) {
            //
            // if the font is vertical font, skip this font and
            // try next/previous font.
            //
            index += iInc;
        }
        else {
            break;
        }
    }

    //
    // Enable/Disable Prev/Next buttons.
    //
    if (index == 0) {
        // first font
        EnableCommandButton(IDB_PREV_FONT, FALSE);
        EnableCommandButton(IDB_NEXT_FONT, TRUE);
    }
    else if (index == gNumOfFonts - 1) {
        // last font
        EnableCommandButton(IDB_PREV_FONT, TRUE);
        EnableCommandButton(IDB_NEXT_FONT, FALSE);
    }
    else {
        // other
        EnableCommandButton(IDB_PREV_FONT, TRUE);
        EnableCommandButton(IDB_NEXT_FONT, TRUE);
    }

    //
    // Show the new font.
    //
    gIndexOfFonts = index;
    gdtDisplay.lfTestFont = *(glpLogFonts + index);
    InvalidateRect(ghwndView, NULL, TRUE);
}


/****************************************************************************\
*
*     FUNCTION: DoCommand(HWND, unsigned, WORD, LONG)
*
*     PURPOSE:  Processes messages for "About" dialog box
*
*     MESSAGES:
*
*         WM_INITDIALOG - initialize dialog box
*         WM_COMMAND    - Input received
*
*     COMMENTS:
*
*         No initialization is needed for this particular dialog box, but TRUE
*         must be returned to Windows.
*
*         Wait for user to click on "Ok" button, then close the dialog box.
*
\****************************************************************************/
BOOL DoCommand( HWND hWnd, WPARAM wParam, LPARAM lParam )
{

    switch(LOWORD(wParam)){
        case IDB_PRINT: {
            PrintSampleWindow(hWnd);
            break;
        }

        case IDB_DONE: {
            PostMessage(ghwndFrame, WM_CLOSE, 0, 0);
            break;
        }

        case IDK_UP: {
            SendMessage(hWnd, WM_VSCROLL, SB_LINEUP, (LPARAM)NULL );
            break;
        }

        case IDK_DOWN: {
            SendMessage(hWnd, WM_VSCROLL, SB_LINEDOWN, (LPARAM)NULL );
            break;
        }

        case IDK_PGUP: {
            SendMessage(hWnd, WM_VSCROLL, SB_PAGEUP, (LPARAM)NULL );
            break;
        }

        case IDK_PGDWN: {
            SendMessage(hWnd, WM_VSCROLL, SB_PAGEDOWN, (LPARAM)NULL );
            break;
        }

        case IDB_PREV_FONT: {
            ViewNextFont(-1);
            break;
        }

        case IDB_NEXT_FONT: {
            ViewNextFont(1);
            break;
        }

        default: {
            return FALSE;
        }
    }

    return TRUE;
}



BOOL bFileExists(TCHAR*pszFile)
{
    HANDLE  hf;

    if ((hf = CreateFile(pszFile,
                         GENERIC_READ,
                         FILE_SHARE_READ,
                         NULL,
                         OPEN_EXISTING,
                         FILE_ATTRIBUTE_NORMAL,
                         NULL)) != INVALID_HANDLE_VALUE)
    {
        CloseHandle(hf);
        return TRUE;
    }

    return FALSE;
}


/******************************Public*Routine******************************\
*
* FindPfb, given pfm file, see if pfb file exists in the same dir or in the
* parent directory of the pfm file
*
* Given:  c:\foo\bar\font.pfm
* Check:  c:\foo\bar\font.pfb
*         c:\foo\font.pfb
*
* Given:  font.pfm
* Check:  font.pfb
*         ..\font.pfb
*
* History:
*  14-Jun-1994 -by- Bodin Dresevic [BodinD]
* Wrote it.
*
*  28-Feb-2002 -by- Brian Aust [BrianAu]
* Replaced all of Bodin's character manipulations with calls to 
* shlwapi path functions and strsafe helpers.
*
* Returns:
*     S_OK    - PFB file found.  Full path to PFB written to pszPFB buffer.
*     S_FALSE - PFB file not found.
*     Other   - Error HRESULT.
*
\**************************************************************************/

HRESULT FindPfb (LPCTSTR pszPFM, LPTSTR pszPFB, size_t cchPFB)
{
    TCHAR szPath[MAX_PATH];  // Working 'scratch' buffer.
    HRESULT hr;
    
    if (0 != lstrcmpi(PathFindExtension(pszPFM), TEXT(".PFM")))
    {
        //
        // Caller didn't provide a PFM file path.
        //
        return E_INVALIDARG;
    }

    //
    // Copy input path to our scratch buffer so we can modify it.
    //
    hr = StringCchCopy(szPath, ARRAYSIZE(szPath), pszPFM);
    if (SUCCEEDED(hr))
    {
        //
        // Does a PFB file exist in the same directory as the PFM file?
        //
        PathRenameExtension(szPath, TEXT(".PFB"));
        if (bFileExists(szPath))
        {
            hr = S_OK;  // Found a match!
        }
        else
        {
            LPCTSTR pszFileName = PathFindFileName(pszPFM);
            //
            // PFB doesn't exist in same directory.  
            // Try the parent directory.
            // Remove the file name so we have only a directory path.
            //
            if (!PathRemoveFileSpec(szPath))
            {
                //
                // This shouldn't happen.  We've already tested earlier
                // for content in the path string.  
                //
                hr = E_FAIL;
            }
            else
            {
                if (0 == szPath[0])
                {
                    //
                    // Removing the file spec left us with an empty string.
                    // That means a bare "font.pfm" name was passed in. 
                    // Build a relative path to the parent directory.
                    //
                    hr = StringCchPrintf(szPath, ARRAYSIZE(szPath), TEXT("..\\%s"), pszFileName);
                }
                else
                {
                    //
                    // Remove the containing directory so we have a path
                    // to the parent directory.
                    //
                    if (PathRemoveFileSpec(szPath))
                    {
                        //
                        // We're now at the parent directory.
                        // Build a full file path here.
                        //
                        if (PathAppend(szPath, pszFileName))
                        {
                            hr = S_OK;  // We have a path to test.
                        }
                        else
                        {
                            hr = E_FAIL;
                        }
                    }
                    else
                    {
                        //
                        // No parent directory exists in the path.  That 
                        // means, the PFM file is in the root of the path.
                        // We've already tested for a PFB in the same
                        // directory so our search is over.  No match.
                        //
                        hr = S_FALSE;
                    }
                }
            }
            if (S_OK == hr)
            {
                //
                // We have a valid path to search.  Replace the extension
                // with .PFB and see if the file exists.
                //
                PathRenameExtension(szPath, TEXT(".PFB"));
                if (!bFileExists(szPath))
                {
                    hr = S_FALSE;  // No match.
                }
            }
        }
        if (S_OK == hr)
        {
            //
            // Found matching PFB file.  Return the path to the caller.
            //
            hr = StringCchCopy(pszPFB, cchPFB, szPath);
        }
    }
    return hr;
}

//
// Given the path for a PFM file, try to locate a matching
// PFB file.  If one is found, the two paths are concatenated together
// and returned with a '|' character as a delimiter.  If a PFB file is 
// not found, the path to the PFM file is returned unaltered.
//
HRESULT BuildType1FontSpec(LPCTSTR pszPFM, LPTSTR pszSpec, size_t cchSpec)
{
    TCHAR szPFB[MAX_PATH];
    HRESULT hr = FindPfb(pszPFM, szPFB, ARRAYSIZE(szPFB));
    if (S_OK == hr)
    {
        //
        // PFB file found.  Build the concatenated PFM|PFB path string.
        //
        hr = StringCchPrintf(pszSpec, cchSpec, TEXT("%s|%s"), pszPFM, szPFB);
    }
    else if (S_FALSE == hr)
    {
        //
        // No PFB found.  Return the original PFM file path.
        //
        hr = StringCchCopy(pszSpec, cchSpec, pszPFM);
    }
    return hr;
}


