分享
 
 
 

Printing Tips and Tricks

王朝vc·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

Printing Tips and Tricks

Here are a few tips, tricks, and answers to frequently asked questions to help you write better printing code and resolve problems that aren't addressed in this chapter's sample programs.

Using the Print Dialog's Selection Button

The Print dialog that MFC displays before printing begins includes a Selection radio button that the user can click to print the current selection rather than the entire document or a range of pages. By default, the button is disabled. You can enable it by adding the following statement to OnPreparePrinting just before the call to DoPreparePrinting:

pInfo->m_pPD->m_pd.Flags &= ~PD_NOSELECTION;

To select the radio button after it's enabled, add this statement as well:

pInfo->m_pPD->m_pd.Flags ¦= PD_SELECTION;

The m_pPD data member of the CPrintInfo structure passed to OnPreparePrinting points to the CPrintDialog object that DoPreparePrinting uses to display the Print dialog box. CPrintDialog::m_pd holds a reference to the PRINTDLG structure the dialog is based on, and PRINTDLG's Flags field holds bit flags that define the dialog box's properties. Removing the PD_NOSELECTION flag added by CPrintInfo's constructor enables the Selection button, and adding a PD_SELECTION flag selects the button. If DoPreparePrinting returns a nonzero value, indicating that the dialog was dismissed with the OK button, you can find out whether the Selection button was selected by calling CPrintDialog::PrintSelection. A nonzero return value means the button was selected; 0 means it wasn't:

if (pInfo->m_pPD->PrintSelection ()) {

// Print the current selection.

}

You can call PrintSelection and other CPrintDialog functions that return information about settings entered in a Print or Print Setup dialog through the pInfo parameter passed to OnPreparePrinting after DoPreparePrinting returns. You can also call them through the pInfo parameter passed to OnBeginPrinting and other CView print overridables.

You can use CPrintInfo::m_pPD in other ways to modify the appearance and behavior of the Print dialog that DoPreparePrinting displays. Refer to the documentation that accompanies Visual C++ for more information about PRINTDLG and its data members.

Assume Nothing—And Test Thoroughly!

When you send output to the printed page, it's generally a mistake to assume anything about the printable area of the pages you'll be printing. Even if you know you're printing to, say, an 8½-by-11-inch page, the printable page area will differ for different printers. The printable page area can even differ for the same printer and the same paper size depending on which printer driver is being used, and the horizontal and vertical dimensions of the printable page area will be switched if the user opts to print in landscape rather than portrait mode. Rather than assume you have a given amount of space to work with, do as HexDump does and call GetDeviceCaps through the CDC pointer provided to CView print functions to determine the printable page area each time you print, or use CPrintInfo::m_rectDraw in your OnPrint function. This simple precaution will enable your printing code to work with any printer Windows can throw at it and will greatly reduce the number of problem reports you receive from users.

As you've already learned, calling GetDeviceCaps with HORZRES and VERTRES parameters returns the horizontal and vertical dimensions of the printable page area. You can pass the following values to GetDeviceCaps to get more information about a printer or other hardcopy device:

Value

Description

HORZRES

Returns the width of the printable page area in pixels.

VERTRES

Returns the height of the printable page area in pixels.

HORSIZE

Returns the width of the printable page area in millimeters.

VERTSIZE

Returns the height of the printable page area in millimeters.

LOGPIXELSX

Returns the number of pixels per inch in the horizontal direction (300 for a 300-dpi printer).

LOGPIXELSY

Returns the number of pixels per inch in the vertical direction (300 for a 300-dpi printer).

PHYSICALWIDTH

Returns the page width in pixels (2,550 for an 8½-by-11-inch page on a 300-dpi printer).

PHYSICALHEIGHT

Returns the page height in pixels (3,300 for an 8½-by-11-inch page on a 300-dpi printer).

PHYSICALOFFSETX

Returns the distance in pixels from the left side of the page to the beginning of the page's printable area.

PHYSICALOFFSETY

Returns the distance in pixels from the top of the page to the beginning of the page's printable area.

TECHNOLOGY

Returns a value that identifies the type of output device the DC pertains to. The most common return values are DT_RASDISPLAY for screens, DT_RASPRINTER for printers, and DT_PLOTTER for plotters.

RASTERCAPS

Returns a series of bit flags identifying the level of GDI support provided by the printer driver. For example, an RC_BITBLT flag indicates that the printer supports BitBlts, and RC_STRETCHBLT indicates that the printer supports StretchBlts.

NUMCOLORS

Returns the number of colors the printer supports. The return value is 2 for black-and-white printers.

You've already seen one use for the GetDeviceCaps NUMCOLORS parameter: to detect when a black-and-white printer is being used so that you draw print previews in shades of gray. The PHYSICALOFFSETX and PHYSICALOFFSETY parameters are useful for setting margin widths based on information the user enters in a Page Setup dialog. (MFC's CWinApp::OnFilePrintSetup function displays a Print Setup dialog instead of a Page Setup dialog, but you can display a Page Setup dialog yourself using MFC's CPageSetupDialog class.) If the user wants 1-inch margins on the left side of the page, for example, you can subtract the PHYSICALOFFSETX value returned by GetDeviceCaps from the number of pixels printed per inch (LOGPIXELSX) to compute the x offset from the left of the printable page area where printing should begin. If the printer driver returns accurate information, the resulting margin will fall within a few pixels of being exactly 1 inch. You can use the HORZRES, VERTRES, LOGPIXELSX, LOGPIXELSY, PHYSICALWIDTH, PHYSICALHEIGHT, PHYSICALOFFSETX, and PHYSICALOFFSETY values to characterize the printable area of a page and pinpoint exactly where on the page the printable area lies.

If you're concerned about the occasional hardcopy device that won't draw bitmaps, you can find out whether CDC::BitBlt and CDC::StretchBlt are supported by calling GetDeviceCaps with a RASTERCAPS parameter and checking the return flags. For the most part, only vector devices such as plotters don't support the GDI's Blt functions. If the driver for a raster device doesn't support blitting directly, the GDI will compensate by doing the blitting itself. You can determine outright whether printed output is destined for a plotter by calling GetDeviceCaps with a TECHNOLOGY parameter and checking to see if the return value equals DT_PLOTTER.

When you use a number of different printers to test an application that prints, you'll find that printer drivers are maddeningly inconsistent in the information they report and the output they produce. For example, some printer drivers return the same values for PHYSICALWIDTH and PHYSICALHEIGHT as they return for HORZRES and VERTRES. And sometimes an ordinary GDI function such as CDC::TextOut will work fine on hundreds of printers but will fail on one particular model because of a driver bug. Other times, a GDI function won't fail outright but will behave differently on different printers. I once ran across a printer driver that defaulted to the TRANSPARENT background mode even though other drivers for the same family of printers correctly set the device context's default background mode to OPAQUE. Printer drivers are notoriously flaky, so you need to anticipate problems and test as thoroughly as you can on as many printers as possible. The more ambitious your program's printing needs, the more likely that driver quirks will require you to write workarounds for problems that crop up only on certain printers.

Adding Default Pagination Support

HexDump calls CPrintInfo::SetMaxPage from OnBeginPrinting rather than from OnPreparePrinting because the pagination process relies on the printable page area and OnBeginPrinting is the first virtual CView function that's called with a pointer to a printer DC. Because the maximum page number isn't set until after OnPreparePrinting returns, the From box in the Print dialog is filled in (with a 1) but the To box isn't. Some users might think it incongruous that an application can correctly paginate a document for print preview but can't fill in the maximum page number in a dialog box. In addition to displaying the maximum page number correctly, many commercial applications display page breaks outside print preview and "Page mm of nn" strings in status bars. How do these applications know how the document will be paginated when they don't know what printer the document will be printed on or what the page orientation will be?

The answer is that they don't know for sure, so they make their best guess based on the properties of the default printer. The following code snippet initializes a CSize object with the pixel dimensions of the printable page area on the default printer or the last printer that the user selected in Print Setup. You can call it from OnPreparePrinting or elsewhere to compute a page count or to get the information you need to provide other forms of default pagination support:

CSize size;

CPrintInfo pi;

if (AfxGetApp ()->GetPrinterDeviceDefaults (&pi.m_pPD->m_pd)) {

HDC hDC = pi.m_pPD->m_pd.hDC;

if (hDC == NULL)

hDC = pi.m_pPD->CreatePrinterDC ();

if (hDC != NULL) {

CDC dc;

dc.Attach (hDC);

size.cx = dc.GetDeviceCaps (VERTRES);

size.cy = dc.GetDeviceCaps (HORZRES);

::DeleteDC (dc.Detach ());

}

}

CWinApp::GetPrinterDeviceDefaults initializes a PRINTDLG structure with values describing the default printing configuration. A 0 return means that the function failed, which usually indicates that no printers are installed or that a default printer hasn't been designated. CPrintInfo::CreatePrinterDC creates a device context handle from the information in the PRINTDLG structure encapsulated in a CPrintInfo object. With the device context in hand, it's a simple matter to wrap it in a CDC object and use CDC::GetDeviceCaps to measure the printable page area.

Enumerating Printers

Sometimes it's useful to be able to build a list of all the printers available so that the user can select a printer outside a Print or Print Setup dialog box. The following routine uses the Win32 ::EnumPrinters function to enumerate the printers currently installed and adds an entry for each to the combo box pointed to by pComboBox.

#include <winspool.h>

DWORD dwSize, dwPrinters;

::EnumPrinters (PRINTER_ENUM_LOCAL, NULL, 5,

NULL, 0, &dwSize, &dwPrinters);

BYTE* pBuffer = new BYTE[dwSize];

::EnumPrinters (PRINTER_ENUM_LOCAL, NULL, 5,

pBuffer, dwSize, &dwSize, &dwPrinters);

if (dwPrinters != 0) {

PRINTER_INFO_5* pPrnInfo = (PRINTER_INFO_5*) pBuffer;

for (UINT i=0; i<dwPrinters; i++) {

pComboBox->AddString (pPrnInfo->pPrinterName);

pPrnInfo++;

}

}

delete[] pBuffer;

The first call to ::EnumPrinters retrieves the amount of buffer space needed to hold an array of PRINTER_INFO_5 structures describing individual printers. The second call to ::EnumPrinters initializes the buffer pointed to by pBuffer with an array of PRINTER_INFO_5 structures. On return, dwPrinters holds a count of the printers enumerated (which equals the count of PRINTER_INFO_5 structures copied to the buffer), and each structure's pPrinterName field holds a pointer to a zero-delimited string containing the device name of the associated printer. Enumerating printers with PRINTER_INFO_5 structures is fast because no remote calls are required; all information needed to fill the buffer is obtained from the registry. For fast printer enumerations in Windows NT or Windows 2000, use PRINTER_INFO_4 structures instead.

If a printer is selected from the combo box and you want to create a device context for it, you can pass the device name copied from the PRINTER_INFO_5 structure to CDC::CreateDC as follows:

CString strPrinterName;

int nIndex = pComboBox->GetCurSel ();

pComboBox->GetLBText (nIndex, strPrinterName);

CDC dc;

dc.CreateDC (NULL, strPrinterName, NULL, NULL);

You can use the resulting CDC object just like the CDC objects whose addresses are passed to OnBeginPrinting and other CView print functions.

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有