/*++ Copyright (c) 1997 Microsoft Corporation Module Name: prntsave.c Abstract: Routines to print or save the incompatibility reports. Functions PrintReport and SaveReport are called when the user clicks Save As... or Print... buttons in the UI. We then present a common dialog box to the user and perform the operation. Author: Jim Schmidt (jimschm) 13-Mar-1997 Revision History: --*/ #include "pch.h" #include "uip.h" #include #include #define DBG_PRINTSAVE "Print/Save" GROWBUFFER g_PunctTable = GROWBUF_INIT; VOID BuildPunctTable ( VOID ) { INFSTRUCT is = INITINFSTRUCT_GROWBUFFER; WORD cp; TCHAR cpString[16]; PTSTR p; PTSTR q; MULTISZ_ENUM e; DWORD d; GetGlobalCodePage (&cp, NULL); wsprintf (cpString, TEXT("%u"), (UINT) cp); g_PunctTable.End = 0; if (InfFindFirstLine (g_Win95UpgInf, TEXT("Wrap Exceptions"), cpString, &is)) { p = InfGetMultiSzField (&is, 1); if (EnumFirstMultiSz (&e, p)) { do { p = (PTSTR) e.CurrentString; while (*p) { q = p; if (StringIPrefix (p, TEXT("0x"))) { d = _tcstoul (p + 2, &p, 16); } else { d = _tcstoul (p, &p, 10); } if (q == p) { break; } GrowBufAppendDword (&g_PunctTable, d); if (*p) { p = (PTSTR) SkipSpace (p); } if (*p == TEXT(',')) { p++; } } } while (EnumNextMultiSz (&e)); } } GrowBufAppendDword (&g_PunctTable, 0); InfCleanUpInfStruct (&is); } VOID FreePunctTable ( VOID ) { FreeGrowBuffer (&g_PunctTable); } BOOL IsPunct ( MBCHAR Char ) { PDWORD p; if (_ismbcpunct (Char)) { return TRUE; } p = (PDWORD) g_PunctTable.Buf; if (p) { while (*p) { if (*p == Char) { return TRUE; } p++; } } return FALSE; } BOOL pGetSaveAsName ( IN HWND ParentWnd, IN OUT PTSTR Buffer ) /*++ Routine Description: Calls common dialog box to obtain the file name in which to save the compatibility report text file. Arguments: ParentWnd - A handle to the parent of the save as dialog Buffer - A caller-supplied buffer. Supplies a file name to initialize the common dialog box with, and receives the file name the user chose to use. Return Value: TRUE if the user clicked OK, or FALSE if the user canceled or an error occurred. --*/ { TCHAR CwdSave[MAX_TCHAR_PATH]; OPENFILENAME ofn; BOOL SaveFlag; TCHAR Filter[512]; PCTSTR FilterResStr; PTSTR p; TCHAR desktopFolder[MAX_TCHAR_PATH]; LPITEMIDLIST pIDL; BOOL b = FALSE; #define MAX_EXT 3 PCTSTR ext[MAX_EXT]; INT i = 0; FilterResStr = GetStringResource (MSG_FILE_NAME_FILTER); if (FilterResStr) { StringCopy (Filter, FilterResStr); FreeStringResource (FilterResStr); } else { MYASSERT (FALSE); Filter[0] = 0; } for (p = Filter ; *p ; p = _tcsinc (p)) { if (*p == TEXT('|')) { *p = 0; } } for (p = Filter; *p; p = GetEndOfString (p) + 1) { if (i & 1) { // // skip the * in "*.ext" // if extension is "*.*" reduce that to an empty string (no extension) // MYASSERT (i / 2 < MAX_EXT); ext[i / 2] = _tcsinc (p); if (StringMatch (ext[i / 2], TEXT(".*"))) { ext[i / 2] = S_EMPTY; } } i++; } if (SHGetSpecialFolderLocation (ParentWnd, CSIDL_DESKTOP, &pIDL) == S_OK) { LPMALLOC pMalloc; b = SHGetPathFromIDList (pIDL, desktopFolder); if (SHGetMalloc (&pMalloc) == S_OK) { IMalloc_Free (pMalloc, pIDL); IMalloc_Release (pMalloc); } } if (!b) { desktopFolder[0] = 0; } // Initialize OPENFILENAME ZeroMemory (&ofn, sizeof(OPENFILENAME)); ofn.lStructSize = sizeof(OPENFILENAME); ofn.hwndOwner = ParentWnd; ofn.lpstrFile = Buffer; ofn.lpstrFilter = Filter; ofn.nMaxFile = MAX_TCHAR_PATH; // Force to begin at Desktop // ofn.lpstrInitialDir = TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}"); ofn.lpstrInitialDir = desktopFolder; ofn.Flags = OFN_NOCHANGEDIR | // leave the CWD unchanged OFN_EXPLORER | OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY; // Let user select disk or directory GetCurrentDirectory (sizeof (CwdSave), CwdSave); SaveFlag = GetSaveFileName (&ofn); SetCurrentDirectory (CwdSave); // // if no extension provided, append the default one // this was selected by the user and returned in ofn.nFilterIndex // #define DEFAULT_EXTENSION TEXT(".htm") p = (PTSTR)GetFileNameFromPath (Buffer); if (!p) { p = Buffer; } if (!_tcschr (p, TEXT('.'))) { if (SizeOfString (Buffer) + sizeof (DEFAULT_EXTENSION) <= MAX_TCHAR_PATH * sizeof (TCHAR)) { if (ofn.nFilterIndex >= MAX_EXT + 1) { ofn.nFilterIndex = 1; } StringCat (p, ext[ofn.nFilterIndex - 1]); } } return SaveFlag; } UINT pCreateWordWrappedString ( OUT PTSTR Buffer, IN PCTSTR Str, IN UINT FirstLineSize, IN UINT RestLineSize ) /*++ Routine Description: Converts a string to a series of lines, where no line is bigger than LineSize. If a buffer is not supplied, this function estimates the number of bytes needed. If the code page is a far-east code page, then lines are broken at any multi-byte character, as well as at the spaces. Arguments: Buffer - If non-NULL, supplies address of buffer big enough to hold enlarged, wrapped string. If NULL, parameter is ignored. Str - Supplies the string that needs to be wrapped. FirstLineSize - Specifies the max size the first line can be. RestLineSize - Specifies the max size the remaining lines can be. Return Value: The size of the string copied to Buffer including the terminating NULL, or the size of the needed buffer if Buffer is NULL. --*/ { PCTSTR p, Start; UINT Col; PCTSTR LastSpace; CHARTYPE c; UINT Size; BOOL PrevCharMb; UINT LineSize; LineSize = FirstLineSize; p = Str; if (!p) return 0; Size = SizeOfString(Str); if (Buffer) { *Buffer = 0; } while (*p) { // Beginning of line Col = 0; LastSpace = NULL; Start = p; PrevCharMb = FALSE; do { // Is this a hard-coded line break? c = _tcsnextc (p); if (c == TEXT('\r') || c == TEXT('\n')) { LastSpace = p; p = _tcsinc (p); if (c == TEXT('\r') && _tcsnextc (p) == TEXT('\n')) { p = _tcsinc (p); } else { Size += sizeof (TCHAR); } c = TEXT('\n'); break; } else if (_istspace (c)) { LastSpace = p; } else if (IsLeadByte (*p)) { // MB chars are usually two cols wide Col++; if (PrevCharMb) { // // If this char is not punctuation, then we can // break here // if (!IsPunct (c)) { LastSpace = p; } } PrevCharMb = TRUE; } else { if (PrevCharMb) { LastSpace = p; } PrevCharMb = FALSE; } // Continue until line gets too long Col++; p = _tcsinc (p); } while (*p && Col < LineSize); // If no more text, or line that has no space needs to be broken if (!(*p) || (c != TEXT('\n') && !LastSpace)) { LastSpace = p; } if (Buffer) { StringCopyAB (Buffer, Start, LastSpace); Buffer = GetEndOfString (Buffer); } if (*p && c != TEXT('\n')) { p = LastSpace; Size += sizeof (TCHAR) * 2; } // remove space at start of wrapped line while (_tcsnextc (p) == TEXT(' ')) { Size -= sizeof (TCHAR); p = _tcsinc (p); } if (Buffer && *p) { Buffer = _tcsappend (Buffer, TEXT("\r\n")); } LineSize = RestLineSize; } return Size; } PCTSTR CreateIndentedString ( IN PCTSTR UnwrappedStr, IN UINT Indent, IN INT HangingIndent, IN UINT LineLen ) /*++ Routine Description: Takes an unwrapped string, word-wraps it (via pCreateWordWrappedString), inserts spaces before each line, optionally skipping the first line. If the code page is a far-east code page, then lines are broken at any multi-byte character, as well as at the spaces. Arguments: UnwrappedStr - A pointer to the string that is to be word-wrapped and adjusted with space inserts. Indent - The number of spaces to insert before each line. HangingIndent - The adjustment made to the indent after the first line LineLen - The maximum line size. Spaces must always be smaller than LineLen (and should be considerably smaller). FirstLine - If TRUE, the first line is indented. If FALSE, the first line is skipped ("hanging indent"). Return Value: A pointer to the indented string, or NULL if MemAlloc failed. The caller must free the string with MemFree. --*/ { UINT Size; UINT Count; PCTSTR p, q; PTSTR d; PTSTR Dest; PTSTR Str; UINT FirstLineLen; UINT RestLineLen; UINT FirstLineIndent; UINT RestLineIndent; UINT RealIndent; if (!UnwrappedStr) { return NULL; } MYASSERT ((INT)Indent + HangingIndent >= 0); FirstLineIndent = Indent; RestLineIndent = Indent + HangingIndent; MYASSERT (LineLen > FirstLineIndent); MYASSERT (LineLen > RestLineIndent); FirstLineLen = LineLen - FirstLineIndent; RestLineLen = LineLen - RestLineIndent; // // Estimate line size, then do the wrapping // Str = (PTSTR) MemAlloc ( g_hHeap, 0, pCreateWordWrappedString ( NULL, UnwrappedStr, FirstLineLen, RestLineLen ) ); if (!Str) { return NULL; } pCreateWordWrappedString ( Str, UnwrappedStr, FirstLineLen, RestLineLen ); if (!FirstLineIndent && !RestLineIndent) { return Str; } // // Count number of lines // for (Count = 1, p = Str ; *p ; p = _tcsinc (p)) { if (*p == TEXT('\n')) { Count++; } } // // Allocate a new buffer that is big enough for all the indented text // Size = max (FirstLineIndent, RestLineIndent) * Count + SizeOfString (Str); Dest = MemAlloc (g_hHeap, 0, Size); if (Dest) { *Dest = 0; // // Indent each line // p = Str; d = Dest; RealIndent = FirstLineIndent; while (*p) { for (Count = 0 ; Count < RealIndent ; Count++) { *d++ = TEXT(' '); } q = _tcschr (p, TEXT('\n')); if (!q) { q = GetEndOfString (p); } else { q = _tcsinc (q); } StringCopyAB (d, p, q); d = GetEndOfString (d); p = q; RealIndent = RestLineIndent; } } MemFree (g_hHeap, 0, Str); return Dest; } BOOL pSaveReportToDisk ( IN HWND Parent, IN PCTSTR FileSpec, IN BOOL Html, IN DWORD MinLevel ) { HANDLE File; BOOL b; PCTSTR Msg; // // Create the report file // File = CreateFile ( FileSpec, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); if (File == INVALID_HANDLE_VALUE) { LOG ((LOG_ERROR, "Error while saving report to %s", FileSpec)); return FALSE; } // // Save the report text to disk // b = FALSE; Msg = CreateReportText (Html, 70, MinLevel, FALSE); if (Msg) { b = WriteFileString (File, Msg); } FreeReportText(); // // Close the file and alert the user to save errors! // CloseHandle (File); return b; } BOOL SaveReport ( IN HWND Parent, OPTIONAL IN PCTSTR Path OPTIONAL ) /*++ Routine Description: Obtains a file name from the user via common Save As dialog, creates the file and writes an incompatibility list to disk. Arguments: Parent - The parent of the common dialog box. Optional If this is NULL, no UI is displayed. Path - The path to save to. If this is NULL, parent must be specified. Return Value: TRUE if the report was saved, or FALSE if the user canceled the operation or the save failed. The user is alerted when the save fails. --*/ { TCHAR Buffer[MAX_TCHAR_PATH + 4]; PCTSTR Str; BOOL b; DWORD attributes; PTSTR p; BOOL saved = FALSE; BOOL bSaveBothFormats = FALSE; MYASSERT(Parent != NULL || Path != NULL); // // Obtain a path, or use caller-supplied path // if (Path) { attributes = GetFileAttributes(Path); } if (!Path || attributes != 0xFFFFFFFF && (attributes & FILE_ATTRIBUTE_DIRECTORY)) { if (Path) { StringCopy(Buffer,Path); AppendPathWack(Buffer); bSaveBothFormats = TRUE; } else { Buffer[0] = 0; } Str = GetStringResource (MSG_DEFAULT_REPORT_FILE); MYASSERT (Str); if (!Str) { return FALSE; } StringCat (Buffer, Str); FreeStringResource (Str); } else { StringCopy(Buffer,Path); } if (Parent) { if (!pGetSaveAsName (Parent, Buffer)) { return FALSE; } } p = _tcsrchr (Buffer, TEXT('.')); if (!p || _tcschr (p, TEXT('\\'))) { p = GetEndOfString (Buffer); } // // Save as text, if extension is .txt // if (StringIMatch (p, TEXT(".txt"))) { b = pSaveReportToDisk (Parent, Buffer, FALSE, REPORTLEVEL_VERBOSE); if (Parent) { saved = b; } } else { b = TRUE; } // // Save as HTML unless user chose to save as .txt // if (b && !saved) { if (!Parent && p) { StringCopy (p, TEXT(".htm")); } b = pSaveReportToDisk (Parent, Buffer, TRUE, REPORTLEVEL_VERBOSE); } if (bSaveBothFormats) { StringCopy (p, TEXT(".txt")); b = pSaveReportToDisk (Parent, Buffer, FALSE, REPORTLEVEL_VERBOSE); } if (!b) { if (Parent) { ResourceMessageBox (Parent, MSG_CANT_SAVE, MB_OK, NULL); } } return TRUE; } VOID pFreePrintMem ( IN OUT PRINTDLG *ppd ) /*++ Routine Description: Frees all memory associated with PRINTDLG structure. Arguments: ppd - Pointer to PRINTDLG structure. Return Value: none --*/ { if (ppd->hDevMode) { GlobalFree (ppd->hDevMode); ppd->hDevMode = NULL; } if(ppd->hDevNames) { GlobalFree (ppd->hDevNames); ppd->hDevNames = NULL; } } VOID pInitPrintDlgStruct ( OUT PRINTDLG *ppd, IN HWND Parent, IN DWORD Flags ) /*++ Routine Description: Initializes PRINTDLG structure, setting the owner window and print dialog flags. Arguments: ppd - Pointer to PRINTDLG structure to be initialized Parent - Handle to parent window for the dialog Flags - PrintDlg flags (PD_*) Return Value: none (structure is initialized) --*/ { ZeroMemory (ppd, sizeof (PRINTDLG)); ppd->lStructSize = sizeof (PRINTDLG); ppd->hwndOwner = Parent; ppd->Flags = Flags; ppd->hInstance = g_hInst; } HDC pGetPrintDC ( IN HWND Parent ) /*++ Routine Description: Displays common dialog box to the user, and if the user chooses a printer, returns a device context handle. Arguments: Parent - The parent of the common dialog to be displayed Return Value: A handle to a device context of the chosen printer, or NULL if the user canceled printing. --*/ { PRINTDLG pd; pInitPrintDlgStruct ( &pd, Parent, PD_ALLPAGES|PD_NOPAGENUMS|PD_NOSELECTION|PD_RETURNDC ); if (PrintDlg (&pd)) { pFreePrintMem (&pd); return pd.hDC; } return NULL; } typedef struct { HDC hdc; INT Page; INT Line; RECT HeaderRect; // in logical units RECT PrintableRect; // in logical units TEXTMETRIC tm; INT LineHeight; INT TotalLines; // printable height/line height INT TotalCols; // printable width/char width HFONT FontHandle; BOOL PageActive; } PRINT_POSITION, *PPRINT_POSITION; PCTSTR pDrawLineText ( IN OUT PPRINT_POSITION PrintPos, IN PCTSTR Text, IN DWORD Flags, IN BOOL Header ) /*++ Routine Description: Draws a single line of text on a printer device context and returns a pointer to the next line or nul terminator. Arguments: PrintPos - A pointer to the current PRINT_POSITION structure that gives page position settings. Text - A pointer to the text string containing the line. Flags - Additional DrawText flags (DT_LEFT, DT_CENTER, DT_RIGHT and/or DT_RTLREADING) Header - TRUE if text should be written to header, or FALSE if it should be written to the current line Return Value: A pointer to the next line within the string, a pointer to the nul terminator, or NULL if an error occurred. --*/ { RECT rect; PCTSTR p; CHARTYPE ch; if (Header) { CopyMemory (&rect, &PrintPos->HeaderRect, sizeof (RECT)); } else { CopyMemory (&rect, &PrintPos->PrintableRect, sizeof (RECT)); rect.top += PrintPos->LineHeight * PrintPos->Line; } Flags = Flags & (DT_CENTER|DT_LEFT|DT_RIGHT|DT_RTLREADING); Flags |= DT_TOP|DT_EDITCONTROL|DT_NOPREFIX|DT_EXTERNALLEADING; for (ch = 0, p = Text ; *p ; p = _tcsinc (p)) { ch = _tcsnextc (p); if (ch == TEXT('\n') || ch == TEXT('\r')) { break; } } if (p != Text) { if (!DrawText (PrintPos->hdc, Text, p - Text, &rect, Flags)) { LOG ((LOG_ERROR, "Failure while sending text to printer.")); return NULL; } } // Skip past line break if (ch == TEXT('\r')) { p = _tcsinc (p); ch = _tcsnextc (p); } if (ch == TEXT('\n')) { p = _tcsinc (p); } return p; } BOOL pPrintString ( IN OUT PPRINT_POSITION PrintPos, IN PCTSTR MultiLineString ) /*++ Routine Description: Dumps a multi-line string to the printer. If necessary, the string may be printed on a new page. This function tries to eliminate widows and orphans by printing the entire string on the same page if possible. Arguments: PrintPos - The current position information, describing the printer device context, page number, line number and metrics. MultiLineString - A pointer to the string to print. Return Value: TRUE if printing was successful, or FALSE if an error occurrred. --*/ { INT LineCount; PCTSTR p; PCTSTR Str; PCTSTR Args[1]; TCHAR Buffer[32]; CHARTYPE ch; HDC hdc; hdc = PrintPos->hdc; // // Count lines in MultiLineString // ch = TEXT('\n'); for (LineCount = 0, p = MultiLineString ; *p ; p = _tcsinc (p)) { ch = _tcsnextc (p); if (ch == TEXT('\n')) { LineCount++; } } if (ch != TEXT('\n')) { LineCount++; } // // Widow/orphan suppression: If all lines do not fit on // the page, and we are more than half way down the page, // roll to the next page. // if (PrintPos->Line + LineCount > PrintPos->TotalLines) { if (PrintPos->Line > PrintPos->TotalLines / 2) { // Move to next page EndPage (hdc); PrintPos->PageActive = FALSE; PrintPos->Page++; PrintPos->Line = 0; } } // // Send each line in MultiLineString // while (*MultiLineString) { // // Draw header if necessary // if (!PrintPos->Line) { StartPage (hdc); PrintPos->PageActive = TRUE; SetMapMode (hdc, MM_TWIPS); SelectObject (hdc, PrintPos->FontHandle); SetBkMode (hdc, TRANSPARENT); //Rectangle (hdc, PrintPos->HeaderRect.left, PrintPos->HeaderRect.top, PrintPos->HeaderRect.right, PrintPos->HeaderRect.bottom); //Rectangle (hdc, PrintPos->PrintableRect.left, PrintPos->PrintableRect.top, PrintPos->PrintableRect.right, PrintPos->PrintableRect.bottom); wsprintf (Buffer, TEXT("%u"), PrintPos->Page); Args[0] = Buffer; // Left side Str = ParseMessageID ( MSG_REPORT_HEADER_LEFT, Args ); if (Str && *Str) { p = pDrawLineText (PrintPos, Str, DT_LEFT, TRUE); if (!p) { return FALSE; } } // Center Str = ParseMessageID ( MSG_REPORT_HEADER_CENTER, Args ); if (Str && *Str) { p = pDrawLineText (PrintPos, Str, DT_CENTER, TRUE); if (!p) { return FALSE; } } // Right side Str = ParseMessageID ( MSG_REPORT_HEADER_RIGHT, Args ); if (Str && *Str) { p = pDrawLineText (PrintPos, Str, DT_RIGHT, TRUE); if (!p) { return FALSE; } } } // // Draw line // MultiLineString = pDrawLineText ( PrintPos, MultiLineString, DT_LEFT, FALSE ); if (!MultiLineString) { return FALSE; } PrintPos->Line++; if (PrintPos->Line >= PrintPos->TotalLines) { EndPage (hdc); PrintPos->PageActive = FALSE; PrintPos->Page++; PrintPos->Line = 0; } } return TRUE; } VOID pCalculatePageMetrics ( IN OUT PPRINT_POSITION PrintPos ) /*++ Routine Description: Calculates all the page metrics (margins, header position, line count, col count, etc). Positions are in TWIPS and counts are in characters or lines. Arguments: PrintPos - Pointer to PRINT_POSITION structure which gives the printer device context. Structure receives metrics. Return Value: none --*/ { INT WidthPixels, HeightPixels; INT DpiX, DpiY; INT UnprintableLeftPixels, UnprintableTopPixels; POINT TempPoint; HDC hdc; hdc = PrintPos->hdc; // // Make no assumptions about hdc // SetMapMode (hdc, MM_TWIPS); SelectObject (hdc, PrintPos->FontHandle); GetTextMetrics (hdc, &PrintPos->tm); // // Get device dimensions // DpiX = GetDeviceCaps (hdc, LOGPIXELSX); DpiY = GetDeviceCaps (hdc, LOGPIXELSY); UnprintableLeftPixels = GetDeviceCaps (hdc, PHYSICALOFFSETX); UnprintableTopPixels = GetDeviceCaps (hdc, PHYSICALOFFSETY); WidthPixels = GetDeviceCaps (hdc, PHYSICALWIDTH); HeightPixels = GetDeviceCaps (hdc, PHYSICALHEIGHT); // Calulate 3/4 inch left/right margins PrintPos->HeaderRect.left = (DpiX * 3 / 4) - UnprintableLeftPixels; PrintPos->HeaderRect.right = WidthPixels - (DpiX * 3 / 4) - UnprintableLeftPixels; // Calculate 1/2 inch top margin for header PrintPos->HeaderRect.top = (DpiY / 2) - UnprintableTopPixels; PrintPos->HeaderRect.bottom = DpiY - UnprintableTopPixels; // Convert pixels (device units) into logical units DPtoLP (hdc, (LPPOINT) (&PrintPos->HeaderRect), 2); // Copy header's left & right margins to printable rect // Copy header's bottom margin to printable rect's top margin PrintPos->PrintableRect.left = PrintPos->HeaderRect.left; PrintPos->PrintableRect.right = PrintPos->HeaderRect.right; PrintPos->PrintableRect.top = PrintPos->HeaderRect.bottom; // Calculate printable rect's bottom margin (3/4 inch) TempPoint.x = 0; TempPoint.y = HeightPixels - (DpiY * 3 / 4) - UnprintableTopPixels; DPtoLP (hdc, &TempPoint, 1); PrintPos->PrintableRect.bottom = TempPoint.y; PrintPos->LineHeight = -(PrintPos->tm.tmHeight + PrintPos->tm.tmInternalLeading + PrintPos->tm.tmExternalLeading); MYASSERT (PrintPos->LineHeight); PrintPos->TotalLines = (PrintPos->PrintableRect.bottom - PrintPos->PrintableRect.top) / PrintPos->LineHeight; PrintPos->TotalCols = (PrintPos->PrintableRect.right - PrintPos->PrintableRect.left) / PrintPos->tm.tmAveCharWidth; } BOOL PrintReport ( IN HWND Parent, IN DWORD Level ) /*++ Routine Description: Obtains a printer from the user via common Print dialog, starts the print job and sends an incompatibility list to one or more pages. Arguments: Parent - A handle to the parent of the print dialog Return Value: TRUE if printing completed, or FALSE if it was canceled or an error occurred. --*/ { HDC hdc; PRINT_POSITION pp; LOGFONT Font; BOOL b; DOCINFO di; INT JobId; PCTSTR Msg; HANDLE DefaultUiFont; hdc = pGetPrintDC (Parent); if (!hdc) { return FALSE; // user canceled print dialog } if (!BeginMessageProcessing()) { // unexpected out-of-memory DeleteDC (hdc); return FALSE; } // // Initialize PRINT_POSITION // b = TRUE; TurnOnWaitCursor(); ZeroMemory (&pp, sizeof (pp)); pp.hdc = hdc; pp.Page = 1; // // Start doc // ZeroMemory (&di, sizeof (di)); di.cbSize = sizeof (di); di.lpszDocName = GetStringResource (MSG_REPORT_DOC_NAME); MYASSERT (di.lpszDocName); if (di.lpszDocName) { JobId = StartDoc (hdc, &di); if (!JobId) { LOG ((LOG_ERROR, "Cannot start print job.")); ResourceMessageBox (Parent, MSG_CANT_PRINT, MB_OK, NULL); b = FALSE; } } else { // // not enough memory // JobId = 0; b = FALSE; } if (b) { // // Create font // ZeroMemory (&Font, sizeof (Font)); DefaultUiFont = (HFONT) GetStockObject (DEFAULT_GUI_FONT); if (DefaultUiFont) { GetObject (DefaultUiFont, sizeof (Font), &Font); Font.lfHeight = 12 * 20; // height in TWIPS (1/20 of a point) Font.lfWeight = FW_NORMAL; Font.lfOutPrecision = OUT_TT_PRECIS; Font.lfPitchAndFamily = FIXED_PITCH|FF_MODERN; pp.FontHandle = CreateFontIndirect (&Font); if (!pp.FontHandle) { LOG ((LOG_ERROR, "Cannot create font for print operation.")); // // deferred this call to the end // //ResourceMessageBox (Parent, MSG_CANT_PRINT, MB_OK, NULL); b = FALSE; } } else { b = FALSE; } } if (b) { // // Create page metrics // pCalculatePageMetrics (&pp); DEBUGMSG ((DBG_PRINTSAVE, "PrintReport: LineHeight=%i", pp.LineHeight)); DEBUGMSG ((DBG_PRINTSAVE, "PrintReport: TotalLines=%i", pp.TotalLines)); DEBUGMSG ((DBG_PRINTSAVE, "PrintReport: TotalCols=%i", pp.TotalCols)); DEBUGMSG ((DBG_PRINTSAVE, "PrintReport: Header rect: (%i, %i)-(%i, %i)", pp.HeaderRect.left, pp.HeaderRect.top, pp.HeaderRect.right, pp.HeaderRect.bottom)); DEBUGMSG ((DBG_PRINTSAVE, "PrintReport: Printable rect: (%i, %i)-(%i, %i)", pp.PrintableRect.left, pp.PrintableRect.top, pp.PrintableRect.right, pp.PrintableRect.bottom)); // // Print the report // Msg = CreateReportText (FALSE, pp.TotalCols, Level, FALSE); if (Msg) { b = pPrintString (&pp, Msg); } FreeReportText(); } if (JobId) { if (b) { if (pp.PageActive) { EndPage (hdc); } EndDoc (hdc); } else { AbortDoc (hdc); } } DeleteDC (hdc); if (pp.FontHandle) { DeleteObject (pp.FontHandle); } TurnOffWaitCursor(); if (!b) { ResourceMessageBox (Parent, MSG_CANT_PRINT, MB_OK, NULL); } EndMessageProcessing(); return b; }