|
|
/****************************************************************************\
* * 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; }
|