📄 13. 使用打印机.txt
字号:
TC_RA_ABLE, TEXT ("TC_RA_ABLE Can do raster fonts:"),
TC_VA_ABLE, TEXT ("TC_VA_ABLE Can do vector fonts:")
} ;
static struct
{
int iIndex ;
TCHAR * szTitle ;
BITS (*pbits)[] ;
int iSize ;
}
bitinfo[] =
{
CURVECAPS, TEXT ("CURVCAPS (Curve Capabilities)"),
(BITS (*)[]) curves, sizeof (curves) / sizeof (curves[0]),
LINECAPS, TEXT ("LINECAPS (Line Capabilities)"),
(BITS (*)[]) lines, sizeof (lines) / sizeof (lines[0]),
POLYGONALCAPS, TEXT ("POLYGONALCAPS (Polygonal Capabilities)"),
(BITS (*)[]) poly, sizeof (poly) / sizeof (poly[0]),
TEXTCAPS, TEXT ("TEXTCAPS (Text Capabilities)"),
(BITS (*)[]) text, sizeof (text) / sizeof (text[0])
} ;
static TCHAR szBuffer[80] ;
BITS (*pbits)[] = bitinfo[iType].pbits ;
int i, iDevCaps = GetDeviceCaps (hdcInfo, bitinfo[iType].iIndex) ;
TextOut (hdc, cxChar, cyChar, bitinfo[iType].szTitle,
lstrlen (bitinfo[iType].szTitle)) ;
for (i = 0 ; i < bitinfo[iType].iSize ; i++)
extOut (hdc, cxChar, (i + 3) * cyChar, szBuffer,
wsprintf (szBuffer, TEXT ("%-55s %3s"), (*pbits)[i].szDesc,
iDevCaps & (*pbits)[i].iMask ? TEXT ("Yes") : TEXT ("No")));
}
DEVCAPS2.RC (摘录)
//Microsoft Developer Studio generated resource script.
#include "resource.h"
#include "afxres.h"
/////////////////////////////////////////////////////////////////////////////
// Menu
DEVCAPS2 MENU DISCARDABLE
BEGIN
POPUP "&Device"
BEGIN
MENUITEM "&Screen",IDM_SCREEN, CHECKED
END
POPUP "&Capabilities"
BEGIN
MENUITEM "&Basic Information",IDM_BASIC
MENUITEM "&Other Information",IDM_OTHER
MENUITEM "&Curve Capabilities",IDM_CURVE
MENUITEM "&Line Capabilities",IDM_LINE
MENUITEM "&Polygonal Capabilities",IDM_POLY
MENUITEM "&Text Capabilities",IDM_TEXT
END
END
RESOURCE.H (摘录)
// Microsoft Developer Studio generated include file.
// Used by DevCaps2.rc
#define IDM_SCREEN 40001
#define IDM_BASIC 40002
#define IDM_OTHER 40003
#define IDM_CURVE 40004
#define IDM_LINE 40005
#define IDM_POLY 40006
#define IDM_TEXT 40007
因为DEVCAPS2只取得打印机的信息内容,使用者仍然可以从DEVCAPS2的菜单中选择所需打印机。如果使用者想比较不同打印机的功能,可以先用打印机文件夹增加各种打印驱动程序。
PrinterProperties呼叫
DEVCAPS2的「Device」菜单中上还有一个称为「Properties」的选项。要使用这个选项,首先得从 Device菜单中选择一个打印机,然后再选择Properties,这时弹出一个对话框。对话框从何而来呢?它由打印机驱动程序呼叫,而且至少还让使用者选择纸的尺寸。大多数打印机驱动也可以让使用者在「直印(portrait)」或「横印(landscape)」模式中进行选择。在直印模式(一般为内定模式)下,纸的短边是顶部。在横印模式下,纸的长边是顶部。如果改变该模式,则所作的改变将在DEVCAPS2程序从GetDeviceCaps函数取得的信息中反应出来:水平尺寸和分辨率将与垂直尺寸和分辨率交换。彩色绘图机的「Properties」对话框内容十分广泛,它们要求使用者输入安装在绘图机上之画笔的颜色和使用之绘图纸(或透明胶片)的型号。
所有打印机驱动程序都包含一个称为ExtDeviceMode的输出函数,它呼叫对话框并储存使用者输入的信息。有些打印机驱动程序也将这些信息储存在系统登录的自己拥有的部分中,有些则不然。那些储存信息的打印机驱动程序在下次执行Windows时将存取该信息。
允许使用者选择打印机的Windows程序通常只呼叫PrintDlg(本章后面我会展示用法)。这个有用的函数在准备打印时负责和使用者之间所有的通讯工作,并负责处理使用者要求的所有改变。当使用者单击「Properties」按钮时,PrintDlg还会启动属性表格对话框。
程序还可以通过直接呼叫打印机驱动程序的ExtDeviceMode或ExtDeveModePropSheet函数,来显示打印机的属性对话框,然而,我不鼓励您这样做。像DEVCAPS2那样,透过呼叫PrinterProperties来启动对话框会好得多。
PrinterProperties要求打印机对象的句柄,您可以通过OpenPrinter函数来得到。当使用者取消属性表格对话框时,PrinterProperties传回,然后使用者通过呼叫ClosePrinter,释放打印机句柄。DEVCAPS2就是这样做到这一点的。
程序首先取得刚刚在Device菜单中选择的打印机名称,并将其存入一个名为szDevice的字符数组中。
GetMenuString ( hMenu, nCurrentDevice, szDevice,
sizeof (szDevice) / sizeof (TCHAR), MF_BYCOMMAND) ;
然后,使用OpenPrinter获得该设备的句柄。如果呼叫成功,那么程序接着呼叫PrinterProperties启动对话框,然后呼叫ClosePrinter释放设备句柄:
if (OpenPrinter (szDevice, &hPrint, NULL))
{
PrinterProperties (hwnd, hPrint) ;
ClosePrinter (hPrint) ;
}
检查BitBlt支持
您可以用GetDeviceCaps函数来取得页中可打印区的尺寸和分辨率(通常,该区域不会与整张纸的大小相同)。如果使用者想自己进行缩放操作,也可以获得相对的图素宽度和高度。
打印机能力的大多数信息是用于GDI而不是应用程序的。通常,在打印机不能做某件事时,GDI会仿真出那项功能。然而,这是应用程序应该事先检查的。
以RASTERCAPS(「位映像支持」)参数呼叫GetDeviceCaps,它传回的RC_BITBLT位包含了另一个重要的打印机特性,该位标示设备是否能进行位块传送。大多数点阵打印机、激光打印机和喷墨打印机都能进行位块传送,而大多数绘图机却不能。不能处理位块传送的设备不支持下列GDI函数:CreateCompatibleDC、CreateCompatibleBitmap、PatBlt、BitBlt、StretchBlt、GrayString、DrawIcon、SetPixel、GetPixel、FloodFill、ExtFloodFill、FillRgn、FrameRgn、InvertRgn、PaintRgn、FillRect、FrameRect和InvertRect。这是在视讯显示器上使用GDI函数与在打印机上使用它们的唯一重要区别。
最简单的打印程序
现在可以开始打印了,我们尽可能简单地开始。事实上,我们的第一个程序只是让打印机走纸而已。程序13-3的FORMFEED程序,展示了打印所需的最小需求。
程序13-3 FORMFEED
FORMFEED.C
/*-----------------------------------------------------------------------
FORMFEED.C -- Advances printer to next page
(c) Charles Petzold, 1998
------------------------------------------------------------------------*/
#include <windows.h>
HDC GetPrinterDC (void) ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpszCmdLine, int iCmdShow)
{
static DOCINFO di = { sizeof (DOCINFO), TEXT ("FormFeed") } ;
HDC hdcPrint = GetPrinterDC () ;
if (hdcPrint != NULL)
{
if (StartDoc (hdcPrint, &di) > 0)
if (StartPage (hdcPrint) > 0 && EndPage (hdcPrint) > 0)
EndDoc (hdcPrint) ;
DeleteDC (hdcPrint) ;
}
return 0 ;
}
这个程序也需要前面程序13-1中的GETPRNDC.C文件。
除了取得打印机设备内容(然后再删除它)外,程序只呼叫了我们在本章前面讨论过的四个打印函数。FORMFEED首先呼叫StartDoc开始一个新的文件,它测试从StartDoc传回的值,只有传回值是正数时,才继续下去:
if (StartDoc (hdcPrint, &di) > 0)
StartDoc的第二个参数是指向DOCINFO结构的指针。该结构在第一个字段包含了结构的大小,在第二个字段包含了字符串「FormFeed」。当文件正在被打印或者在等待打印时,这个字符串将出现在打印机任务队列中的「Document Name」列中。通常,该字符串包含进行打印的应用程序名称和被打印的文件名称。
如果StartDoc成功(由一个正的传回值表示),那么FORMFEED呼叫StartPage,紧接着立即呼叫EndPage。这一程序将打印机推进到新的一页,再次对传回值进行测试:
if (StartPage (hdcPrint) > 0 && EndPage (hdcPrint) > 0)
最后,如果不出错,文件就结束:
EndDoc (hdcPrint) ;
要注意的是,只有当没出错时,才呼叫EndDoc函数。如果其它打印函数中的某一个传回错误代码,那么GDI实际上已经中断了文件的打印。如果打印机目前未打印,这种错误代码通常会使打印机重新设定。测试打印函数的传回值是检测错误的最简单方法。如果您想向使用者报告错误,就必须呼叫GetLastError来确定错误。
如果您写过MS-DOS下的简单利用打印机走纸的程序,就应该知道,对于大多数打印机,ASCII码12启动走纸。为什么不简单地使用C的链接库函数open,然后用write输出ASCII码12呢?当然,您完全可以这么做,但是必须确定打印机连结的是串行端口还是并列埠。然后您还要确定另外的程序(例如,打印队列程序)是不是正在使用打印机。您并不希望在文件打印到一半时被别的程序把正在打印的那张纸送出打印机,对不对?最后,您还必须确定ASCII码12是不是所连结打印机的走纸字符,因为并非所有打印机的走纸字符都是12。事实上,在PostScript中的走纸命令便不是12,而是单字showpage。
简单地说,不要试图直接绕过Windows;而应该坚持在打印中使用Windows函数。
打印图形和文字
在一个Windows程序中,打印所需的额外负担通常比FORMFEED程序高得多,而且还要用GDI函数来实际打印一些东西。我们来写个打印一页文字和图形的程序,采用FORMFEED程序中的方法,并加入一些新的东西。该程序将有三个版本PRINT1、PRINT2和PRINT3。为避免程序代码重复,每个程序都用前面所示的GETPRNDC.C文件和PRINT.C文件中的函数,如程序13-4所示。
程序13-4 PRINT
PRINT.C
/*------------------------------------------------------------------------
PRINT.C -- Common routines for Print1, Print2, and Print3
--------------------------------------------------------------------------*/
#include <windows.h>
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
BOOL PrintMyPage (HWND) ;
extern HINSTANCE hInst ;
extern TCHAR szAppName[] ;
extern TCHAR szCaption[] ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
HWND hwnd ;
MSG msg ;
WNDCLASS wndclass ;
wndclass.style = CS_HREDRAW | CS_VREDRAW ;
wndclass.lpfnWndProc = WndProc ;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = hInstance ;
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
wndclass.lpszMenuName = NULL ;
wndclass.lpszClassName = szAppName ;
if (!RegisterClass (&wndclass))
{
MessageBox ( NULL, TEXT ("This program requires Windows NT!"),
szAppName, MB_ICONERROR) ;
return 0 ;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -