7.1.1 设备环境类 设备环境类CDC提供了绘制和打印的全部函数。为了能让用户使用一些特殊的设备环境,CDC还派生了CPaintDC、CClientDC、CWindowDC和CMetaFileDC类。 (1)CPaintDC比较特殊,它的构造函数和析构函数都是针对OnPaint进行的,但用户一旦获得相关的CDC指针,就可以将它当成任何设备环境(包括屏幕、打印机)指针来使用。CPaintDC类的构造函数会自动调用BeginPaint,而它的析构函数则会自动调用EndPaint。 (2)CClientDC只能在窗口的客户区(不包括边框、标题栏、菜单栏以及状态栏)中进行绘图,点(0,0)通常指的是客户区的左上角。而CWindowDC允许在窗口的任意位置中进行绘图,点(0,0)指整个窗口的左上角。CWindowDC和CClientDC构造函数分别调用GetWindowDC和GetDC,但它们的析构函数都是调用ReleaseDC函数。 (3)CMetaFileDC封装了在一个Windows图元文件中绘图的方法。图元文件是一系列与设备无关的图片的集合,由于它对图象的保存比像素更精确,因而往往在要求较高的场合下使用。目前的Windows已使用增强格式(enhanced-format)的32位图元文件来进行操作。
7.1.2 坐标映射 为了能保证打印的结果不受设备的影响,Windows定义了一些映射模式,这些映射模式决定了设备坐标和逻辑坐标之间的关系,如表所示。 映 射 模 式 映射模式 含 义 MM_TEXT 每个逻辑单位等于一个设备像素,x向右为正,y向下为正 MM_HIENGLISH 每个逻辑单位为0.001英寸,x向右为正,y向上为正 MM_LOENGLISH 每个逻辑单位为0.01英寸,x向右为正,y向上为正 MM_HIMETRIC 每个逻辑单位为0.01毫米,x向右为正,y向上为正 MM_LOMETRIC 每个逻辑单位为0.1毫米,x向右为正,y向上为正 MM_TWIPS 每个逻辑单位为一个点的1/20(一个点是1/72 英寸),x向右为正,y向上为正 MM_ANISOTROPIC x,y可变比例 MM_ISOTROPIC x,y等比例
7.1.3. CPoint、CSize和CRect CPoint、CSize和CRect类的构造函数 CPoint( int initX, int initY ); CPoint( POINT initPt ); CSize类带参数的常用构造函数原型如下: CSize( int initCX, int initCY ); CSize( SIZE initSize ); CRect类带参数的常用构造函数原型如下: CRect( int l, int t, int r, int b ); CRect( const RECT& srcRect ); CRect( LPCRECT lpSrcRect ); CRect( POINT point, SIZE size ); CRect( POINT topLeft, POINT bottomRight );
7.1.3. CPoint、CSize和CRect 2. CRect类的常用操作 CRect 类 常 用 的 成 员 函 数 成员函数 功能说明 int Width( ) const; 返回矩形的宽度 int Height( ) const; 返回矩形的高度 CSize Size( ) const; 返回矩形的大小,CSize中的cx和cy成员分别表示矩形的宽度和高度 CPoint& TopLeft( ); 返回矩形左下角的点坐标 CPoint& BottomRight( ); 返回矩形右下角的点坐标 CPoint CenterPoint( ) const; 返回CRect的中点坐标 BOOL IsRectEmpty() const; 如果一个矩形的宽度或高度是0或负值,则称这个矩形为空,返回TRUE BOOL IsRectNull() const; 如果一个矩形的上、左、下和右边的值都等于0,则返回TRUE BOOL PtInRect( POINT point ) const; 如果点point位于矩形中(包括点在矩形的边上),则返回TRUE void SetRect( int x1, int y1, int x2, int y2 ); 将矩形的各边设为指定的值,左上角点为(x1, y1),右下角点为(x2, y2) void SetRectEmpty(); 将矩形的所有坐标设置为零 void NormalizeRect( ); 使矩形符合规范 void OffsetRect( int x, int y ); void OffsetRect( POINT point ); void OffsetRect( SIZE size ); 移动矩形,水平和垂直移动量分别由x、y或point、size的两个成员来指定
7.1.4. 颜色和颜色对话框 颜色对话框 一个彩色象素的显示需要颜色空间的支持,常用的颜色空间有RGB和YUV两种。RGB颜色空间选用红(R)、绿(G)、蓝(B)三种基色分量,通过对这三种基色不同比例的混合,可以得到不同的彩色效果。而YUV颜色空间是将一个彩色像素表示成一个亮度分量(Y)和两个色度分量(U、V)。
7.1.5. 图形设备接口 Windows为设备环境提供了各种各样的绘图工具,例如用于画线的“画笔”、填充区域的“画刷”以及用于绘制文本的“字体”。MFC封装了这些工具,并提供相应的类来作为应用程序的图形设备接口GDI,这些类有一个共同的抽象基类CGdiObject,具体如表所示。 MFC 的 GDI 类 类 名 说 明 CBitmap “位图”是一种位矩阵,每一个显示象素都对应于其中的一个或多个位。用户可以利用位图来表示图像,也可以利用它来创建画刷。 CBrush “画刷”定义了一种位图形式的象素,利用它可对区域内部填充颜色或样式。 CFont “字体”是一种具有某种风格和尺寸的所有字符的完整集合,它常常被当作资源存于磁盘中,其中有一些还依赖于某种设备。 CPalette “调色板”是一种颜色映射接口,它允许应用程序在不干扰其他应用程序的前提下,可以充分利用输出设备的颜色描绘能力。 CPen “画笔”是一种用来画线及绘制有形边框的工具,用户可以指定它的颜色及宽度,并且可以指定它画实线、点线或虚线等。 CRgn “区域”是由多边形、椭圆或二者组合形成的一种范围,可以利用它来进行填充、裁剪以及鼠标点中测试等。
7.2.1. 画 笔 画笔是Windows应用程序中用来绘制各种直线和曲线的一种图形工具,它可分为修饰画笔和几何画笔两种类型。在这两种类型中,几何画笔的定义最复杂,它不但有修饰画笔的属性,而且还跟画刷的样式、阴影线类型有关,通常用在对绘图有较高要求的场合。而修饰画笔只有简单的几种属性,通常用在简单的直线和曲线等场合。 一个修饰画笔通常具有宽度、风格和颜色三种属性。画笔的宽度用来确定所画的线条宽度,它是用设备单位表示的。默认的画笔宽度是一个像素单位。画笔的颜色确定了所画的线条颜色。画笔的风格确定了所绘图形的线型,它通常有实线、虚线、点线、点划线、双点划线、不可见线和内框线等七种风格。
7.2.2 画 刷 画刷用于指定填充的特性,许多窗口、控件以及其他区域都需要用画刷进行填充绘制,它比画笔的内容更加丰富。 7.2.2 画 刷 画刷用于指定填充的特性,许多窗口、控件以及其他区域都需要用画刷进行填充绘制,它比画笔的内容更加丰富。 画刷的属性通常包括填充色、填充图案和填充样式三种。画刷的填充色和画笔颜色一样,都是使用COLORREF颜色类型,画刷的填充图案通常是用户定义的8 x 8位图,而填充样式往往是CDC内部定义的一些特性,它们都是以HS_为前缀的标识,如图所示: HS_BDIAGONAL HS_CROSS HS_DIAGCROSS HS_FDIAGONAL HS_HORIZONTAL HS_VERTICAL 图7.3 画刷的填充样式
7.2.3. 绘 图 示 例 Visual C++的MFC为用户的图形绘制提供了许多函数,这其中包括画点、线、矩形、多边形、圆弧、椭圆、扇形以及Bézier曲线等。下面的代码是一个简单图形的绘制示例,用来表示一个班级某门课程的成绩分布,它是一个直方图,反映<60、60~69、70~79、80~89以及>90五个分数段的人数,它需要绘制五个矩形,相邻矩形的填充样式还要有所区别,并且还需要显示各分数段的人数。 Ex_Draw运行结果
7.3.1 字体和字体对话框 1. 字体的属性和创建 typedef struct tagLOGFONT { 7.3.1 字体和字体对话框 1. 字体的属性和创建 typedef struct tagLOGFONT { LONG lfHeight; // 字体的逻辑高度 LONG lfWidth; // 字符的平均逻辑宽度 LONG lfEscapement; // 倾角 LONG lfOrientation; // 书写方向 LONG lfWeight; // 字体的粗细程度 BYTE lfItalic; // 斜体标志 BYTE lfUnderline; // 下划线标志 BYTE lfStrikeOut; // 删除线标志 BYTE lfCharSet; // 字符集,汉字必须为GB2312_CHARSET TCHAR lfFaceName[LF_FACESIZE] // 字样名称 // … } LOGFONT;
CF_SCREENFONTS, CDC* pdcPrinter = NULL, CWnd* pParentWnd = NULL ); 7.3.1 字体和字体对话框 2. 使用字体对话框 CFontDialog类提供了字体及其文本颜色选择的通用对话框,如图所示。 它的构造函数如下: CFontDialog( LPLOGFONT lplfInitial = NULL, DWORD dwFlags = CF_EFFECTS | CF_SCREENFONTS, CDC* pdcPrinter = NULL, CWnd* pParentWnd = NULL ); 字体对话框
int DrawText( const CString& str, LPRECT lpRect, UINT nFormat ); 7.3.2. 常用文本输出函数 文本的最终输出不仅依赖于文本的字体,而且还跟文本的颜色、对齐方式等有很大关系。 TextOut函数是用当前字体在指定位置 (x,y) 处显示一个文本。 TabbedTextOut也是用当前字体在指定位置处显示一个文本,但它还根据指定的制表位(Tab)设置相应字符位置,函数成功时返回输出文本的大小。 virtual int DrawText( LPCTSTR lpszString, int nCount, LPRECT lpRect, UINT nFormat ); int DrawText( const CString& str, LPRECT lpRect, UINT nFormat ); DrawText函数是当前字体在指定矩形中对文本进行格式化绘制。
7.3.3. 文本格式化属性 文本的格式属性通常包括文本颜色、对齐方式、字符间隔以及文本调整等。在绘图设备环境中,默认的文本颜色是黑色,而文本背景色为白色, 在CDC类中,SetTextColor、SetBkColor和SetBkMode函数就是分别用来设置文本颜色、文本背景色和背景模式,而与之相对应的GetTextColor、GetBkcolor和GetBkMode函数则是分别获取这三项属性的。 它们的原型如下: virtual COLORREF SetTextColor( COLORREF crColor ); COLORREF GetTextColor( ) const; virtual COLORREF SetBkColor( COLORREF crColor ); COLORREF GetBkColor( ) const; int SetBkMode( int nBkMode ); int GetBkMode( ) const;
7.3.4. 计算字符的几何尺寸 在CDC类中,GetTextMetrics(LPTEXTMETRIC lpMetrics)是用来获得指定映射模式下相关设备环境的字符几何尺寸及其它属性的,其TEXTMETRIC结构描述如下 ︰ typedef struct tagTEXTMETRIC { int tmHeight; // 字符的高度 (ascent + descent) int tmAscent; // 高于基准线部分的值 int tmDescent; // 低于基准线部分的值 int tmInternalLeading; // 字符内标 int tmExternalLeading; // 字符外标高 int tmAveCharWidth; // 字体中字符平均宽度 int tmMaxCharWidth; // 字符的最大宽度 // … } TEXTMETRIC;
7.3.5. 文档内容显示及其字体改变 (1) 用MFC AppWizard(exe)创建一个单文档应用程序Ex_Text,在创建的第6步将视图的基类选择为CScrollView。 (2) 为CEx_TextDoc类添加CStringArray类型的成员变量m_strContents,用来将读取的文档内容保存。 (3) 在CEx_TextDoc::Serialize函数中添加读取文档内容的代码: void CEx_TextDoc::Serialize(CArchive& ar) { if (ar.IsStoring()) {…} else { CString str; m_strContents.RemoveAll(); while (ar.ReadString(str)) { m_strContents.Add(str); }
7.3.5. 文档内容显示及其字体改变 (6) 用MFC ClassWizard为CEx_TextView类添加WM_LBUTTONDBLCLK(双击鼠标)的消息映射函数,并增加下列代码: void CEx_TextView::OnLButtonDblClk(UINT nFlags, CPoint point) { CFontDialog dlg(&m_lfText); if (dlg.DoModal() == IDOK){ dlg.GetCurrentFont(&m_lfText); Invalidate(); } CScrollView::OnLButtonDblClk(nFlags, point);
7.3.5. 文档内容显示及其字体改变 (7) 在CEx_TextView::OnDraw中添加下列代码: 7.3.5. 文档内容显示及其字体改变 (7) 在CEx_TextView::OnDraw中添加下列代码: void CEx_TextView::OnDraw(CDC* pDC) { CEx_TextDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); // 创建字体 CFont cf; cf.CreateFontIndirect(&m_lfText); CFont* oldFont = pDC->SelectObject(&cf); // 计算每行高度 TEXTMETRIC tm; pDC->GetTextMetrics(&tm); int lineHeight = tm.tmHeight + tm.tmExternalLeading; int y = 0; int tab = tm.tmAveCharWidth * 4; // 为一个TAB设置4个字符 // 输出并计算行的最大长度 int lineMaxWidth = 0; CString str; CSize lineSize(0,0); for (int i=0; i<pDoc->m_strContents.GetSize(); i++) {
7.3.5. 文档内容显示及其字体改变 for (int i=0; i<pDoc->m_strContents.GetSize(); i++) { str = pDoc->m_strContents.GetAt(i); pDC->TabbedTextOut(0, y, str, 1, &tab, 0); str = str + "A"; // 多计算一个字符宽度 lineSize = pDC->GetTabbedTextExtent(str, 1, &tab); if ( lineMaxWidth < lineSize.cx ) lineMaxWidth = lineSize.cx; y += lineHeight; } pDC->SelectObject(oldFont); int nLines = pDoc->m_strContents.GetSize() + 1; // 多算一行,以滚动窗口能全部显示文档内容 CSize sizeTotal; sizeTotal.cx = lineMaxWidth; sizeTotal.cy = lineHeight * nLines; SetScrollSizes(MM_TEXT, sizeTotal); // 设置滚动逻辑窗口的大小
7.4.1. 使用图形编辑器 创建一个新的图标或光标
7.4.1. 使用图形编辑器 2. 选用和定制显示设备
7.4.1. 使用图形编辑器 3. 设置光标热点 Windows系统借助光标“热点”来确定光标实际的位置。在图形编辑器的控制条上或光标属性对话框中都可以看到当前的光标“热点”位置。 设置热点按钮
7.4.2. 位 图 CBitmap类 CBitmap类封装了Windows的GDI位图操作所需的大部分函数。其中,LoadBitmap是位图的初始化函数,其函数原型如下: BOOL LoadBitmap( LPCTSTR lpszResourceName ); BOOL LoadBitmap( UINT nIDResource ); 2. GDI位图的显示 调用CBitmap类的CreateBitmap、CreateCompatibleBitmap以及 CreateBitmapIndirect函数创建一个适当的位图对象。 (2) 调用CDC::CreateCompatibleDC函数创建一个内存设备环境,以便位图在内存中保存下来,并与指定设备(窗口设备)环境相兼容; (3)调用CDC::SelectObject函数将位图对象选入内存设备环境中; (4) 调用CDC::BitBlt或CDC::StretchBlt函数将位图复制到实际设备环境中。 (5) 使用之后,恢复原来的内存设备环境。
7.4.3 图 标 2.使用光标资源 3.更改程序中的光标 1. 使用系统光标 7.4.3 图 标 1. 使用系统光标 Windows预定义了一些经常使用的标准光标,这些光标均可以使用 数CWinApp:: LoadStandardCursor加载到程序中,其函数原型如下: HCURSOR LoadStandardCursor( LPCTSTR lpszCursorName ) const; 2.使用光标资源 用编辑器创建或从外部调入的光标资源,可通过函数CWinApp::LoadCursor进行加载,其原型如下: HCURSOR LoadCursor( LPCTSTR lpszResourceName ) const; HCURSOR LoadCursor( UINT nIDResource ) const; 3.更改程序中的光标 更改应用程序中的光标除了可以使用GetClassLong和SetClassLong函数外,最简单的方法是用MFC ClassWizard映射WM_SETCURSOR消息,该消息是当光标移动到一个窗口内并且还没有捕捉到鼠标时产生的。
7.4.4. 光 标 使用系统光标 lpszCursorName用来指定一个标准光标名,它可以是下列宏定义: 7.4.4. 光 标 使用系统光标 lpszCursorName用来指定一个标准光标名,它可以是下列宏定义: IDC_ARROW 标准箭头光标 IDC_IBEAM 标准文本输入光标 IDC_WAIT 漏斗型计时等待光标 IDC_CROSS 十字形光标 IDC_UPARROW 垂直箭头光标 IDC_SIZEALL 四向箭头光标 IDC_SIZENWSE 左上至右下的双向箭头光标 IDC_SIZENESW 左下至右上的双向箭头光标 IDC_SIZEWE 左右双向箭头光标 IDC_SIZENS 上下双向箭头光标
7.4.4. 光 标 2. 使用光标资源 用编辑器创建或从外部调入的光标资源,可通过函数CWinApp::LoadCursor进行加载,其原型如下: HCURSOR LoadCursor( LPCTSTR lpszResourceName ) const; HCURSOR LoadCursor( UINT nIDResource ) const; 3. 更改程序中的光标 更改应用程序中的光标除了可以使用GetClassLong和SetClassLong函数外,最简单的方法是用MFC ClassWizard映射WM_SETCURSOR消息,该消息是当光标移动到一个窗口内并且还没有捕捉到鼠标时产生的。CWnd为此消息的映射函数定义这样的原型: afx_msg BOOL OnSetCursor( CWnd* pWnd, UINT nHitTest, UINT message );
7.4.4. 光 标 4. 示例 根据当前鼠标所在的位置来确定单文档应用程序光标的类型,当处在标题栏时为一个动画光标,当处在客户区时为一个自定义光标。
7.4.4. 光 标 4. 示例 用来根据当前鼠标所在的位置来确定单文档应用程序光标的类型,当处在标题栏时为一个动画光标,当处在客户区时为一个自定义光标。
7.5.1. 打印与打印预览机制 MFC打印过程 2. 打印预览过程 7.5.1. 打印与打印预览机制 MFC打印过程 用MFC AppWizard创建的文档应用程序运行后,当选择“文件”菜单中的“打印”命令时,就会弹出如图的通用打印对话框。 2. 打印预览过程 当选择了MFC创建的应用程序的“文件”菜单中的“打印预览”命令后,应用程序就会创建一个CPreviewDC对象,每当应用程序执行一个设置打印机设备环境属性的操作,也会同样对显示设备环境进行一个类似的操作。
7.5.2. 打印与打印预览的设计 1. CPrintInfo类 7.5.2. 打印与打印预览的设计 完整的打印和打印预览设计工作包括控制页边距和行距、设计页眉页脚、控制打印字体、选择打印模式、多页打印以及预览功能实现等。 1. CPrintInfo类 成 员 说 明 GetMinPage / SetMinPage 获取或设置文档的第一页页码 GetMaxPage / SetMaxPage 获取或设置文档的最后一页页码 GetFromPage / GetToPage 获取要打印的第一页和最后一页页码 BOOL m_bDirect 若不需要出现打印对话框而直接打印,则将此值设为TRUE;否则为FALSE。正常情况下不要改变这个值,若非要这么做,最好在调用CView::DoPreparePrinting之前改变此值 BOOL m_bPreview 表示文档是否要预览 BOOL m_bContinuePrinting 表示是否继续打印循环。若我们需要打印中的实时分页,则在重载函数OnPrepareDC中将其设置为FALSE,这是由于当文档达到终点时,将调用OnPrepareDC函数。缺省时,当当前打印页码大于1,OnPrepareDC将此值设置为FALSE,这就意味着:若文档的长度未被指定,应用程序即认为文档为1页。所以,重载函数OnPrepareDC调用基类的OnPrepareDC后,此值可能不为TRUE。 UINT m_nCurPage 表示当前正在打印的页码 UINT m_nNumPreviewPages 表示在预览模式下显示的页数,它可以是1或2 CRect m_rectDraw 表示页的有效打印区域,以逻辑坐标表示
7.5.2. 打印与打印预览的设计 2. 页面设置 页边距 (2) 页眉页脚 7.5.2. 打印与打印预览的设计 2. 页面设置 在文档或图形的打印之前,通常需要对页面进行设置,就像Office一样。页面设置一般包括页边距、每页行数、行距、每行字符个数以及页脚页眉等内容。 页边距 页边距是指打印的文本或图形的区域与打印纸边界之间的距离,包括左、右、上和下边距。设置时可参考CPrintInfo的成员变量m_rectDraw的数值,但m_rectDraw的数值表示的是有效打印区域,它本身与打印纸边界有一定的边距,而且这些边距在不同大小的纸张中是不一样的,因此首先要获取这些数值。 (2) 页眉页脚 打印文档时,往往需要打印文档的标题及页码或其他内容的页眉和页脚。
7.5.2. 打印与打印预览的设计 3. 行的文本处理 4. 多页打印 7.5.2. 打印与打印预览的设计 3. 行的文本处理 在调用CDC类的文本输出函数TabbedTextOut或TextOut时,若输出的字符数大于每行的总字符数,则多余的字符被裁剪,这是不允许的。故需使用一些技巧将多余的字符放在下一行中输出。 4. 多页打印 多页打印能将一个整个文档全部打印,这在打印和打印预览中必须要做到的。 void CEx_PrintView::OnPrepareDC(CDC* pDC, CPrintInfo* pInfo) { CView::OnPrepareDC(pDC, pInfo); if (pInfo){ if (pInfo->m_nCurPage <= pInfo->GetToPage()) pInfo-> m_bContinuePrinting = TRUE; else pInfo-> m_bContinuePrinting = FALSE; }
7.5.3. 完整的示例 用MFC AppWizard创建一个默认的单文档应用程序Ex_Print。 7.5.3. 完整的示例 用MFC AppWizard创建一个默认的单文档应用程序Ex_Print。 (2) 在Ex_PrintDoc.h文件中,为CEx_PrintDoc类添加两个公有型成员变量:一个是BOOL型的m_bNewDocument,用来确定是否是新的文档,另一个是CStringArray类型的m_strContents,用来保存文档中的内容。 (3) 在CEx_PrintDoc::Serialize函数中添加下列代码: void CEx_PrintDoc::Serialize(CArchive& ar) { if (ar.IsStoring()) {…} else CString str; m_strContents.RemoveAll(); while (ar.ReadString(str)) m_strContents.Add(str); m_bNewDocument = TRUE; }
7.5.3. 完整的示例 (4) 打开Ex_PrintView.h文件,在class CEx_PrintView : public CView语句前添加PAGEINFO结构的定义,具体代码前面已列出。 (5) 为CEx_PrintView类添加下列成员变量: public: PAGEINFO m_pageInfo; CStringArray m_arrText; // 处理后的文档内容 CUIntArray m_nLineArr // 记录每页的开始行号 (6) 为CEx_PrintView类添加成员函数SetPageInfo、PrintHead、PrintFoot和AdjustAllLine,具体代码前面已列出。
7.5.3. 完整的示例 (7) 在CEx_PrintView类的构造函数处,添加初始化代码: 7.5.3. 完整的示例 (7) 在CEx_PrintView类的构造函数处,添加初始化代码: (8) 在CEx_PrintView::OnPreparePrinting中设置当文档内容为空时的最大打印页数: BOOL CEx_PrintView::OnPreparePrinting(CPrintInfo* pInfo) { CEx_PrintDoc* pDoc = GetDocument(); int nSize = pDoc->m_strContents.GetSize(); if (nSize<1) pInfo->SetMaxPage(1); return DoPreparePrinting(pInfo); }
7.5.3. 完整的示例 (9) 用MFC ClassWizard为CEx_PrintView类OnPrepareDC函数,并添加设置映射模式和多页打印的代码: void CEx_PrintView::OnPrepareDC(CDC* pDC, CPrintInfo* pInfo) { pDC->SetMapMode(MM_LOMETRIC); // 单位0.1mm CView::OnPrepareDC(pDC, pInfo); CEx_PrintDoc* pDoc = GetDocument(); int nSize = pDoc->m_strContents.GetSize(); if ((pInfo)&&(nSize)){ if (pInfo->m_nCurPage<= pInfo->GetToPage()) pInfo-> m_bContinuePrinting = TRUE; else pInfo-> m_bContinuePrinting = FALSE; }
7.5.3. 完整的示例 (10) 用MFC ClassWizard为CEx_PrintView类增加OnPrint函数,并添加代码 7.5.3. 完整的示例 (10) 用MFC ClassWizard为CEx_PrintView类增加OnPrint函数,并添加代码 (11) 编译运行并测试。打开Ex_PrintView.cpp文件,然后选择“文件”“打印预览”,结果如图所示。