在Delphi中实现打印预览

在很多应用程序中,都需要程序具有打印预览功能,以避免用户由于选择不当出现打印错误。在Delphi下一般的打印预览实现方式为通过创建一个Tpanel的派生类并公开它的canvas属性来实现,这种方式需要编程者小心的调整比例尺或视区范围,使用较为不方便,笔者通过实践实现了用图元文件(Metafile)来实现打印预览的方法,这种方式不需要调整比例尺和视区范围。
   基本原理和方法:
   图元文件也称作“绘图类型”的图形,是以图形对象(线、圆弧、多边形)方式而不是像素方式来存储图像。图元文件将图形定义为编码的线段和图形。Windows系统下有两种类型的图元文件:标准型(WMF文件格式)和增强型(EMF文件格式)。
   使用图元文件的好处在于它能够任意大小缩放并且仍能保持一定的逼真度,这是因为图元文件通常由一系列矢量图形的图元组成,图元是指线条、填充的区域以及轮廓字体等。扩大或缩小图像只是简单地缩放定义这些图元的所有坐标点,所以当调整图像大小时,图元文件对图像的保存比像素更精确,效果更好。
   Delphi以Tmetafile对象对Win32增强型图元文件进行了封装,通过设置Enhanced属性可以设定图元文件是以标准型或可增强型格式存储。
   在Delphi中使用图元文件的基本方法是:
   ●创建图元文件
   ●使用参照设备的打印机句柄创建图元文件画布对象(Tmetafilecanvas)
   ●如同在打印机画布上一样,在图元文件画布上进行描绘
   ●将图元文件在屏幕上显示出来
   实现步骤:
   首先建立一个窗体文件,在适当位置放上一个panel控件(panel1),一个checkbox控件(chedkbox1),并设定checkbox的caption为”打印预览”,在panel上放置一个image控件(image1),并设image1的align属性为alclient。
   在USES中添加打印程序单元(Printers),尽管Delphi在生成窗体时会自动在USES部分加入许多程序单元,但打印程序单元却不在此列,只能手工给它加上。
   关键代码说明:
   用GetDeviceCaps (Printer.Handle,LOGPIXELSX)获取当前打印机的水平分辨率。
   如果想让打印程序在任何打印机上都能正常地打印,你就必须改变你的度量单位。
   dpm:=trunc(dpi/25.4);

//算出每毫米的打印机点数。
   MyMetafile:= TMetafile.Create;
   //创建图元文件
   TMetafileCanvas.Create (MyMetafile,printer.Handle)
   //以打印机句柄创建图元文件画布
   textout(trunc(sx* dpm),trunc(sy*dpm)',我的打印');//进行打印输出
   image1.Canvas.StretchDraw (image1.canvas.cliprect,MyMetafile);
   //显示图元文件
   程序源代码:
   unit u_print;
   interface
   uses
   Windows,Messages,SysUtils,Classes,Graphics,Controls,Forms,Dialogs,,
   StdCtrls,Buttons,printers,ExtCtrls;
   type
   TF_print = class(TForm)
   ………
   private
   {
Private declarations }
   public
   {
Public declarations }
   end;
   var
   F_print:TF_print;
   implementation
   {$R*.DFM}

procedure TF_print.BitBtn1Click (Sender: TObject);

begin
   close;
   end;
   //定义report过程,使真实打印和打印预览共用此过程
   procedure TF_print.report (Acanvas:Tcanvas);

var
   sx,sy,sy0,sx0:real;//起始位置
   dpi,dpm:real; //每毫米的点数
   ARect: TRect;

begin
   dpi:=GetDeviceCaps (Printer.Handle,LogPixelsX);

dpm:=trunc(dpi/25.4); //每毫米的点数
   sx:=100;sx0:=10;sy0:=10;sy:=sy0;

with Acanvas do
   try
   font.name:='宋体';
   font.size:=18;

textout(trunc(60*dpm),trunc(sy*dpm),'我的打印预览');
   sy:=sy+10;
   textout(trunc(60*dpm),trunc(sy*dpm),'姓名');

sy:=sy+10;

pen.Width:=5;

MoveTo(trunc(sx0*dpm),trunc('sy*dpm))',

LineTo(trunc((sX0+200)*dpm),trunc(sy*dpm));

font.size:=12;

sy:=sy+5;

textout(trunc(sx0*dpm),trunc(sy*dpm),'内容');

sy:=sy+5;
   MoveTo(trunc(sx0*dpm),trunc(sy*dpm)),

LineTo(trunc((sX0+200)*dpm), trunc(sy*dpm));

finally
   Free;

end;
   end;
   procedure TF_print.BitBtn2Click(Sender:TObject);

var mymetafile:Tmetafile;
   ifprt:boolean;

begin
   ifprt:=not checkbox1.checked;
   //是false则打印预览,true则打印
   if ifprt then
   begin
   printer.BeginDoc;
   report(printer.canvas);
   //将打印机画布对象传给report过程
   end
   else
   begin
   MyMetafile=TMetafile.Create

//创建图元文件
   //以打印机句柄创建图元文件画布对象(Tmetafilecanvas),并调用report过程
   report (TMetafileCanvas.Create (MyMetafile,printer.Handle));
   end;
   //如果是打印则结束打印
   if ifprt then printer.enddoc;
   //如果是打印预览,则在image1上显示出来
   if not ifprt then image1.Canvas.StretchDraw (image1.canvas.cliprect,MyMetafile);
   end;
   end.
   以上代码在Windows 98、Delphi 5.0下调试通过。