/****************************Module*Header******************************\ * Module Name: FONTINST.C * * Module Descripton: * Unidrv's built in font installer. Generously borrowed from Rasdd's * font installer code. * * Warnings: * * Issues: * * Created: 22 October 1997 * Author: Srinivasan Chandrasekar [srinivac] * * Copyright (c) 1996, 1997 Microsoft Corporation \***********************************************************************/ #include "precomp.h" // // Global constants // static const DWORD FontInstallerHelpIDs[]= { IDD_ADD, IDH_SOFT_FONT_ADD_BTN, IDD_DELFONT, IDH_SOFT_FONT_DELETE_BTN, IDD_FONTDIR, IDH_SOFT_FONT_DIRECTORY, IDD_NEWFONTS, IDH_SOFT_FONT_NEW_LIST, IDD_CURFONTS, IDH_SOFT_FONT_INSTALLED_LIST, IDD_OPEN, IDH_SOFT_FONT_OPEN_BTN, TID_FONTDIR, IDH_SOFT_FONT_DIRECTORY, TID_NEWFONTS, IDH_SOFT_FONT_NEW_LIST, TID_CURFONTS, IDH_SOFT_FONT_INSTALLED_LIST, 0, 0 }; // // External functions // BOOL bSFontToFIData(FI_DATA *, HANDLE, BYTE *, DWORD); // // Structure used to remember state // typedef struct tagSFINFO { HANDLE hModule; // Module handle of calling program HANDLE hPrinter; // Printer handle passed by caller HANDLE hHeap; // Handle to heap that we allocate memory from DWORD dwFlags; // Miscellaneous flags DWORD cMaxFontNum; // Maximum ID of of fonts already in the file DWORD cFonts; // Number of fonts added from font file DWORD cCartridgeFonts; // Number of cartridge fonts in file PFNTDAT pFNTDATHead; // Head of linked list of FNTDATs PFNTDAT pFNTDATTail; // The last of them } SFINFO, *PSFINFO; // // Internal functions // void vFontInit(HWND, PSFINFO); void vAddFont(HWND, PSFINFO); void vDelFont(HWND, PSFINFO); void vDelSel(HWND, int); void vFontClean(PSFINFO); BOOL bNewFontDir(HWND, PSFINFO); BOOL bIsFileFont(PSFINFO, FI_DATA *, PWSTR); BOOL bFontUpdate(HWND, PSFINFO); BOOL InMultiSzSet(PWSTR, PWSTR); /****************************************************************************** * * FontInstProc * * Function: * Entry point for font installer dialog code. * * Arguments: * hWnd - Handle to window * usMsg - Message code * wParam - wParam * lParam - lParam * * Returns: * TRUE on success, FALSE otherwise * ******************************************************************************/ INT_PTR CALLBACK FontInstProc( HWND hWnd, // The window of interest UINT usMsg, // Message code WPARAM wParam, // Depends on above, but message subcode LPARAM lParam // Miscellaneous usage ) { POEMFONTINSTPARAM pfip; PSFINFO pSFInfo; switch( usMsg ) { case WM_INITDIALOG: // // Get the passed in parameter and set SFINFO as the window data // pfip = (POEMFONTINSTPARAM)lParam; if (!(pSFInfo = HEAPALLOC(pfip->hHeap, sizeof(SFINFO)))) return FALSE; memset(pSFInfo, 0, sizeof(SFINFO)); pSFInfo->hModule = pfip->hModule; pSFInfo->hPrinter = pfip->hPrinter; pSFInfo->hHeap = pfip->hHeap; pSFInfo->dwFlags = pfip->dwFlags; SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)pSFInfo); // // Get list of installed fonts and show them // vFontInit(hWnd, pSFInfo); return TRUE; case WM_COMMAND: pSFInfo = (PSFINFO)GetWindowLongPtr(hWnd, GWLP_USERDATA); switch (LOWORD(wParam)) { case IDD_OPEN: // User selects Open button return bNewFontDir(hWnd, pSFInfo); case IDD_NEWFONTS: // New font list if( HIWORD( wParam ) != CBN_SELCHANGE ) return FALSE; break; case IDD_CURFONTS: // Existing font activity if (HIWORD (wParam) != CBN_SELCHANGE) return FALSE; break; case IDD_DELFONT: // Delete the selected fonts vDelFont(hWnd, pSFInfo); return TRUE; case IDD_ADD: // Add the selected fonts vAddFont(hWnd, pSFInfo); return TRUE; case IDOK: // // Save the updated information // if (pSFInfo->dwFlags & FG_CANCHANGE) bFontUpdate(hWnd, pSFInfo); // // Fall thru // case IDCANCEL: EndDialog(hWnd, LOWORD(wParam) == IDOK ? TRUE : FALSE); return TRUE; default: return FALSE; } break; case WM_HELP: case WM_CONTEXTMENU: { PDRIVER_INFO_3 pDriverInfo3; pSFInfo = (PSFINFO)GetWindowLongPtr(hWnd, GWLP_USERDATA); if (!pSFInfo || !(pDriverInfo3 = MyGetPrinterDriver(pSFInfo->hPrinter, NULL, 3))) { return FALSE; } if (usMsg == WM_HELP) { WinHelp(((LPHELPINFO) lParam)->hItemHandle, pDriverInfo3->pHelpFile, HELP_WM_HELP, (ULONG_PTR)FontInstallerHelpIDs); } else { WinHelp((HWND) wParam, pDriverInfo3->pHelpFile, HELP_CONTEXTMENU, (ULONG_PTR)FontInstallerHelpIDs); } } break; case WM_DESTROY: pSFInfo = (PSFINFO)GetWindowLongPtr(hWnd, GWLP_USERDATA); vFontClean(pSFInfo); // Free what we consumed // // Free the SFINFO structure // HeapFree(pSFInfo->hHeap, 0, pSFInfo); return TRUE; default: return FALSE; // didn't process the message } return FALSE; } /****************************************************************************** * * BInstallSoftFont * * Function: * This function installs a softfont for the given printer * * Arguments: * hPrinter - Handle of printer to install fonts for * hHeap - Handle of heap to use to allocate memory * pInBuf - Pointer to PCL data buffer * dwSize - Size of buffer * * Returns: * TRUE on success, FALSE otherwise * ******************************************************************************/ BOOL APIENTRY BInstallSoftFont( HANDLE hPrinter, HANDLE hHeap, PBYTE pInBuf, DWORD dwSize ) { FNTDAT FntDat; HANDLE hOldFile = NULL; HANDLE hFontFile = NULL; DWORD cFonts = 0, i; BOOL bRc = FALSE; // // Parse the given PCL font // if (!bSFontToFIData(&FntDat.fid, hHeap, pInBuf, dwSize)) return FALSE; FntDat.pVarData = pInBuf; FntDat.dwSize = dwSize; // // Open exisiting font file // if (hOldFile = FIOpenFontFile(hPrinter, hHeap)) { cFonts = FIGetNumFonts(hOldFile); } // // Create a new font file // hFontFile = FICreateFontFile(hPrinter, hHeap, cFonts+1); if (!hFontFile) { WARNING(("Error creating a new font file\n")); goto EndInstallSoftFont; } // // Seek past header and font directory in new file // FIAlignedSeek(hFontFile, sizeof(UFF_FILEHEADER) + (cFonts + 1) * sizeof(UFF_FONTDIRECTORY)); for (i=0; iAttributes & PRINTER_ATTRIBUTE_LOCAL)) { if (! _BPrepareToCopyCachedFile(hPrinter, &CachedFile, REGVAL_CARTRIDGEFILENAME) || ! _BCopyCachedFile(NULL, &CachedFile)) bRc = FALSE; else bRc = TRUE; _VDisposeCachedFileInfo(&CachedFile); goto EndGetFCF; } bRc = TRUE; EndGetFCF: if (ppi2) HeapFree(hHeap, 0, ppi2); return bRc; } /****************************************************************************** * Internal helper functions ******************************************************************************/ /****************************************************************************** * * vFontInit * * Function: * Called to initialise the dialog before it is displayed to the * user. Requires making decisions about buttons based on any * existing fonts. * * Arguments: * hWnd - Handle to window * pSFInfo - Pointer to structure that holds state information * * Returns: * Nothing * ******************************************************************************/ void vFontInit( HWND hWnd, PSFINFO pSFInfo ) { HANDLE hFontFile; // Handle to font file INT iNum = 0; // Number of entries INT i; // Loop parameter DWORD cFonts = 0; // Number of fonts // // If there is a font file associated with this printer, open it and // read the fonts // if (hFontFile = FIOpenFontFile(pSFInfo->hPrinter, pSFInfo->hHeap)) { iNum = FIGetNumFonts(hFontFile); } for (i=0; icCartridgeFonts++; continue; } pwstr = FIGetFontName(hFontFile, i); if (!pwstr) continue; // Should not happen! // // Add font name to list of installed fonts // iFont = SendDlgItemMessage(hWnd, IDD_CURFONTS, LB_ADDSTRING, 0, (LPARAM)pwstr); // // Set the font number // SendDlgItemMessage(hWnd, IDD_CURFONTS, LB_SETITEMDATA, iFont, (LPARAM)i); cFonts++; // Increment number of fonts } pSFInfo->cMaxFontNum = (DWORD)i; // For separating new/old if (cFonts > 0) { // // There are existing fonts, so we can enable the DELETE button // pSFInfo->cFonts = cFonts; // Number of fonts added EnableWindow(GetDlgItem(hWnd, IDD_DELFONT), TRUE); } if (hFontFile) { FICloseFontFile(hFontFile); } if (pSFInfo->dwFlags & FG_CANCHANGE) { // // User has access to change stuff, so place a default directory // SetDlgItemText(hWnd, IDD_FONTDIR, L"A:\\"); } else { // // No permission to change things, so disable most of the dialog // EnableWindow( GetDlgItem( hWnd, IDD_FONTDIR ), FALSE ); EnableWindow( GetDlgItem( hWnd, TID_FONTDIR ), FALSE ); EnableWindow( GetDlgItem( hWnd, IDD_OPEN ), FALSE ); EnableWindow( GetDlgItem( hWnd, IDD_ADD ), FALSE ); EnableWindow( GetDlgItem( hWnd, IDD_DELFONT ), FALSE ); EnableWindow( GetDlgItem( hWnd, IDD_NEWFONTS ), FALSE ); EnableWindow( GetDlgItem( hWnd, TID_NEWFONTS ), FALSE ); } return; } /****************************************************************************** * * bNewFontDir * * Function: * Processes a new font directory. This means opening the * directory and passing the file names to the screening function. * * Arguments: * hWnd - Handle to window * pSFInfo - Pointer to structure that holds state information * * Returns: * TRUE on success, FALSE otherwise * ******************************************************************************/ BOOL bNewFontDir( HWND hWnd, PSFINFO pSFInfo ) { WIN32_FIND_DATA ffd; // Data about the file we find UINT iErrMode; // For manipulating error msgs INT cOKFiles; // Count the number of font files found HANDLE hDir; // FindFirstFile ... scanning HCURSOR hCursor; // Switch to wait symbol while reading INT cDN; // Length of directory name // (count of chars, excluding terminating null char) WCHAR wchDirNm[MAX_PATH];// Font directory + file name // // GetDlgItemText's 4th parameter is the max number of characters, not bytes. // cDN = GetDlgItemTextW(hWnd, IDD_FONTDIR, wchDirNm, sizeof(wchDirNm) / sizeof(WCHAR)); // // Check to see if the name will be too long: the 5 below is the // number of additional characters to add to the directory name: // namely, L"\\*.*". // if (cDN >= (CCHOF(wchDirNm) - 5)) { IDisplayErrorMessageBox(hWnd, MB_OK | MB_ICONERROR, IDS_FONTINST_FONTINSTALLER, IDS_FONTINST_DIRECTORYTOOLONG); return FALSE; } if (cDN > 0) { if (wchDirNm[cDN - 1] != (WCHAR)'\\' ) { StringCchCatW(wchDirNm, CCHOF(wchDirNm), L"\\"); cDN++; // One more now! } StringCchCatW(wchDirNm, CCHOF(wchDirNm), L"*.*"); // // Save error mode, and enable file open error box. // iErrMode = SetErrorMode(0); SetErrorMode(iErrMode & ~SEM_NOOPENFILEERRORBOX); hDir = FindFirstFile(wchDirNm, &ffd); SetErrorMode(iErrMode); // Restore old mode cOKFiles = 0; if (hDir == INVALID_HANDLE_VALUE) { // // Put up a dialog box to tell the user "no such directory". // if (GetLastError() == ERROR_PATH_NOT_FOUND) { IDisplayErrorMessageBox(hWnd, MB_OK | MB_ICONERROR, IDS_FONTINST_FONTINSTALLER, IDS_FONTINST_INVALIDDIR); } return FALSE; } // // Switch to the hourglass cursor while reading, since the data // is probably coming from a SLOW floppy. Also stop redrawing, // since the list box looks ugly during this time. // hCursor = SetCursor(LoadCursor(NULL, IDC_WAIT)); SendMessage(hWnd, WM_SETREDRAW, FALSE, 0L); do { // // Generate a file name which is passed to a function to determine // whether this file is understood by us. This function returns // FALSE if it does not understand the file; otherwise it returns // TRUE, and also a string to display. We display the string, // and remember the file name for future use. // LONG_PTR iFont; // List Box index FI_DATA FD; // Filled in by bIsFileFont PFNTDAT pFNTDAT; // For remembering it all StringCchCopyW(&wchDirNm[cDN], CCHOF(wchDirNm) - cDN, ffd.cFileName); if (bIsFileFont(pSFInfo, &FD, wchDirNm)) { // // Part of the data returned is a descriptive string // for the font. We need to display this to the user. // We also allocate a structure we use to keep track of // all the data we have. This includes the file name // that we have! // pFNTDAT = (PFNTDAT)HEAPALLOC(pSFInfo->hHeap, sizeof(FNTDAT)); if (pFNTDAT == NULL) { break; } if (pSFInfo->pFNTDATHead == NULL) { // // Starting a chain, so remember the first. // AND also enable the Add button in the dialog, // now that we have something to add. // pSFInfo->pFNTDATHead = pFNTDAT; EnableWindow(GetDlgItem(hWnd, IDD_ADD), TRUE); } if (pSFInfo->pFNTDATTail) pSFInfo->pFNTDATTail->pNext = pFNTDAT; pSFInfo->pFNTDATTail = pFNTDAT; pFNTDAT->pNext = 0; pFNTDAT->pVarData = NULL; pFNTDAT->dwSize = 0; pFNTDAT->fid = FD; wcsncpy(pFNTDAT->wchFileName, wchDirNm, cDN); StringCchCatW(pFNTDAT->wchFileName, CCHOF(pFNTDAT->wchFileName), ffd.cFileName); // // Display this message, and tag it with the address // of the data area we just allocated. // iFont = SendDlgItemMessage(hWnd, IDD_NEWFONTS, LB_ADDSTRING, 0, (LPARAM)FD.dsIdentStr.pvData); SendDlgItemMessage(hWnd, IDD_NEWFONTS, LB_SETITEMDATA, iFont, (LPARAM)pFNTDAT); ++cOKFiles; // One more to the list } } while (FindNextFile(hDir, &ffd)); // // Now can redraw the box & return to the previous cursor // SendMessage(hWnd, WM_SETREDRAW, TRUE, 0L); InvalidateRect(hWnd, NULL, TRUE); SetCursor(hCursor); // // Finished with the directory now, so close it up // FindClose(hDir); if (cOKFiles == 0) { // // Didn't find any files, so tell the user // IDisplayErrorMessageBox(hWnd, MB_OK | MB_ICONERROR, IDS_FONTINST_FONTINSTALLER, IDS_FONTINST_NOFONTFOUND); } } else { // // Empty font directory name! // IDisplayErrorMessageBox(hWnd, MB_OK | MB_ICONERROR, IDS_FONTINST_FONTINSTALLER, IDS_FONTINST_NODIRNAME); } return TRUE; } /****************************************************************************** * * vAddFont * * Function: * Called to move the new selected fonts to the font list * * Arguments: * hWnd - Handle to window * pSFInfo - Pointer to structure that holds state information * * Returns: * Nothing * ******************************************************************************/ void vAddFont( HWND hWnd, PSFINFO pSFInfo ) { LONG_PTR cSel; // Number of entries selected LONG_PTR *piSel; // List of selected fonts INT iI; // Loop index // // Find the selected items in the new font box and move them to the // Installed box. Also set up the linked list of stuff to pass // to the common font installer code should the user decide to // update the list. // cSel = SendDlgItemMessage(hWnd, IDD_NEWFONTS, LB_GETSELCOUNT, 0, 0); piSel = (LONG_PTR *)HEAPALLOC(pSFInfo->hHeap, (DWORD)(cSel * sizeof(INT))); if (piSel == NULL ) { IDisplayErrorMessageBox(hWnd, MB_OK | MB_ICONERROR, IDS_FONTINST_FONTINSTALLER, IDS_FONTINST_OUTOFMEMORY); return; } // // Disable updates to reduce screen flicker // SendMessage(hWnd, WM_SETREDRAW, FALSE, 0L); SendDlgItemMessage(hWnd, IDD_NEWFONTS, LB_GETSELITEMS, cSel, (LPARAM)piSel); for (iI=0; iIfid.dsIdentStr.pvData); SendDlgItemMessage(hWnd, IDD_CURFONTS, LB_SETITEMDATA, iFont, (LPARAM)pFontData); } if (iI > 0) EnableWindow(GetDlgItem(hWnd, IDD_DELFONT), TRUE); // // Can now delete the selected items: we no longer need them // vDelSel(hWnd, IDD_NEWFONTS); // // Re enable updates // SendMessage(hWnd, WM_SETREDRAW, TRUE, 0L); InvalidateRect(hWnd, NULL, TRUE); HeapFree(pSFInfo->hHeap, 0, (LPSTR)piSel); return; } /****************************************************************************** * * vDelFont * * Function: * Called when the Delete button is clicked. We discover which * items in the Installed fonts list box are selected, and mark these * for deletion. We do NOT delete them, simply remove them from * display and mark for deletion later. * * Arguments: * hWnd - Handle to window * pSFInfo - Pointer to structure that holds state information * * Returns: * Nothing * ******************************************************************************/ void vDelFont( HWND hWnd, PSFINFO pSFInfo ) { INT iI; // Loop index LONG_PTR cSel; // Number of selected items LONG_PTR *piSel; // From heap, contains selected items list // // Obtain the list of selected items in the Installed list box. // Then place any existing fonts into the to delete list, and // move any new fonts back into the New fonts list. // cSel = SendDlgItemMessage(hWnd, IDD_CURFONTS, LB_GETSELCOUNT, 0, 0); piSel = (LONG_PTR *)HEAPALLOC(pSFInfo->hHeap, (DWORD)(cSel * sizeof(INT))); if (piSel == NULL) { IDisplayErrorMessageBox(hWnd, MB_OK | MB_ICONERROR, IDS_FONTINST_FONTINSTALLER, IDS_FONTINST_OUTOFMEMORY); return; } // // Disable updates to reduce screen flicker // SendMessage(hWnd, WM_SETREDRAW, FALSE, 0L); SendDlgItemMessage(hWnd, IDD_CURFONTS, LB_GETSELITEMS, cSel, (LPARAM)piSel); for (iI=0; iI= (LONG_PTR)pSFInfo->cMaxFontNum) { // // We are deleting a font that we just installed, so add it back // into the new fonts, so that it remains visible. // LONG_PTR iFont; // New list box index WCHAR awch[256]; // ??? if (SendDlgItemMessage(hWnd, IDD_CURFONTS, LB_GETTEXT, piSel[iI], (LPARAM)awch) != LB_ERR) { // // Have the text and value, so back into the new list // iFont = SendDlgItemMessage(hWnd, IDD_NEWFONTS, LB_ADDSTRING, 0, (LPARAM)awch); SendDlgItemMessage(hWnd, IDD_NEWFONTS, LB_SETITEMDATA, iFont, (LPARAM)iVal); } } } // // Now delete them from the list // vDelSel(hWnd, IDD_CURFONTS); // // Disable the delete button if there are no fonts. // if (SendDlgItemMessage( hWnd, IDD_CURFONTS, LB_GETCOUNT, 0, 0L) == 0) EnableWindow(GetDlgItem(hWnd, IDD_DELFONT), FALSE); // /// Re-enable updates // SendMessage(hWnd, WM_SETREDRAW, TRUE, 0L); InvalidateRect(hWnd, NULL, TRUE); HeapFree(pSFInfo->hHeap, 0, (LPSTR)piSel); return; } /****************************************************************************** * * vDelSel * * Function: * Delete all selected items in the specified list box. * * Arguments: * hWnd - Handle to window * iBox - Identifies the list box * * Returns: * Nothing * ******************************************************************************/ void vDelSel( HWND hWnd, INT iBox ) { INT iSel; // // Find how many items are selected, then retrieve their index // one at a time until they are all deleted. This is needed because // otherwise we delete the wrong ones! This is because the data is // presented to us as an array of indices, and these are wrong when // we start deleting them. // while (SendDlgItemMessage(hWnd, iBox, LB_GETSELITEMS, 1, (LPARAM)&iSel) > 0) SendDlgItemMessage(hWnd, iBox, LB_DELETESTRING, iSel, 0L); return; } /****************************************************************************** * * vFontClean * * Function: * Clean up all the dangling bits & pieces we have left around. * * Arguments: * pSFInfo - Pointer to structure that holds state information * * Returns: * Nothing * ******************************************************************************/ void vFontClean( PSFINFO pSFInfo ) { // // Look at the storage addresses we allocate. If non zero, // free them up and set to NULL to prevent a second freeing. // if (pSFInfo->pFNTDATHead) { // // The details of each new font we found. These form a linked // list, so we need to traverse the chain and free every entry. // FNTDAT *pFD0, *pFD1; for (pFD0 = pSFInfo->pFNTDATHead; pFD0; pFD0 = pFD1) { pFD1 = pFD0->pNext; // Next one, perhaps HeapFree(pSFInfo->hHeap, 0, (LPSTR)pFD0); } pSFInfo->pFNTDATHead = NULL; pSFInfo->pFNTDATTail = NULL; } return; } /****************************************************************************** * * bIsFileFont * * Function: * Called with a file name and returns TRUE if this file is a font * format we understand. Also returns a FONT_DATA structure. * * Arguments: * pSFInfo - Pointer to structure that holds state information * pFIDat - Information about the font to fill if successful * pwstr - Name of file to check * * Returns: * TRUE on success, FALSE otherwise * ******************************************************************************/ BOOL bIsFileFont( PSFINFO pSFInfo, FI_DATA *pFIDat, PWSTR pwstr ) { HANDLE hFile; BYTE *pbFile; DWORD dwSize; BOOL bRet; hFile = CreateFile(pwstr, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | SECURITY_SQOS_PRESENT | SECURITY_ANONYMOUS, NULL); if (hFile == INVALID_HANDLE_VALUE) { return FALSE; } if (!MapFileIntoMemory(pwstr, &pbFile, NULL)) { CloseHandle(hFile); return FALSE; } // // Want to find out how big the file is, so now seek to the // end, and see what address comes back! There appears to be // no other way to do this. // dwSize = SetFilePointer(hFile, 0L, NULL, FILE_END); bRet = bSFontToFIData(pFIDat, pSFInfo->hHeap, pbFile, dwSize); UnmapFileFromMemory((HFILEMAP)pbFile); CloseHandle(hFile); return bRet; } /****************************************************************************** * * bFontUpdate * * Function: * Update the font installer common file. Called when the user * has clicked on the OK button. * * Arguments: * hWnd - Handle to window * pSFInfo - Pointer to structure that holds state information * * Returns: * TRUE on success, FALSE otherwise * ******************************************************************************/ BOOL bFontUpdate( HWND hWnd, PSFINFO pSFInfo ) { HANDLE hOldFile = NULL; // Handle to old font file HANDLE hFontFile = NULL; // Handle to new font file DWORD cFonts; // Final number of fonts selected DWORD dwIndex; // Index into current font file LRESULT lrOldIndex; // Index into old font file DWORD i; // Loop index BOOL bRc = FALSE; // Return code // // Initialize some variables // hOldFile = hFontFile = NULL; // // Get number of fonts that have finally been added // cFonts = (DWORD)SendDlgItemMessage(hWnd, IDD_CURFONTS, LB_GETCOUNT, 0, 0); // // If no fonts have been added or deleted, we can skip doing anything. // Check for this case // if (cFonts == pSFInfo->cFonts) { BOOL bNoChange = TRUE; for (i=0; i= (LONG)(pSFInfo->cMaxFontNum)) { bNoChange = FALSE; break; } } if (bNoChange) { return TRUE; } } // // Open existing font file // hOldFile = FIOpenFontFile(pSFInfo->hPrinter, pSFInfo->hHeap); if (!hOldFile && pSFInfo->cMaxFontNum > 0) { WARNING(("Unable to open existing font file\n")); goto EndFontUpdate; } // // Create a new font file // hFontFile = FICreateFontFile(pSFInfo->hPrinter, pSFInfo->hHeap, cFonts+pSFInfo->cCartridgeFonts); if (!hFontFile) { WARNING(("Error creating new font file\n")); goto EndFontUpdate; } // // Seek past header and font directory in new file // FIAlignedSeek(hFontFile, sizeof(UFF_FILEHEADER) + (cFonts+pSFInfo->cCartridgeFonts) * sizeof(UFF_FONTDIRECTORY)); for (dwIndex=0; dwIndexcMaxFontNum)) { // // This is an old font from existing font file // if (!FICopyFontRecord(hFontFile, hOldFile, dwIndex, (DWORD)lrOldIndex)) { WARNING(("Error copying font record\n")); goto EndFontUpdate; } } else { // // This is a font being newly added // if (!FIAddFontRecord(hFontFile, dwIndex, (PFNTDAT)lrOldIndex)) { WARNING(("Error creating new font record\n")); goto EndFontUpdate; } } } // // NOTE: Do not change dwIndex - we continue to use it below // if (pSFInfo->cCartridgeFonts > 0) { // // Copy cartridge fonts to new font file // cFonts = FIGetNumFonts(hOldFile); for (i=0; i