[转]WindowsNT/200 环境下要自订纸张尺寸(自定义页面)

近日因项目需要接触了打印机,自定义纸张的大小问题困扰了我一个多星期,还以为XP系统无法自定义纸张,后来在网上搜索时无意中发现,打印机都有最小纸张的标准,如果小于或大于这个标准,就算自定义好了页面,都无法选择它(不会显示出来),如果用程序强制设置打印的话,它会按A4的标准走纸,比如我用的Epson LQ-300K+这款打印机,最小纸张是10CM*10.16CM,而我要的宽度正好小于10CM,后来我改成10CM后,就能正常打印了。使用下面第一段例子就可以搞定了!  

在 WindowsNT/200 環境下要自訂紙張尺寸所使用的方法與 Win9x 不同,

你必須先為目前的印表機定義一個自訂的 "Form"(呼叫 API: AddForm,

此 API 宣告於 WinSpool 單元中),然後把這個 Form 的名稱設定給

DEVMODES 結構中的 dmFormName 欄位。以下的函式可以直接拿來使用:

uses Windows, WinSpool, Printers;

(*------------------------------------------------------

Define a new Form (WinNT/2000 only).

If FormName already exists, do nothing and return.

If failed, an exception will be raised.

------------------------------------------------------*)

procedure PrnAddForm(const FormName: string; PaperWidth, PaperLength: integer);

var

PrintDevice, PrintDriver, PrintPort : array[0..255] of Char;

hDMode : THandle;

hPrinter: THandle;

FormInfo: TFormInfo1;

PaperSize: TSize;

PaperRect: TRect;

errcode: integer;

s: string;

begin

Printer.GetPrinter(PrintDevice, PrintDriver, PrintPort, hDMode);

OpenPrinter(PrintDevice, hPrinter, nil);

if hPrinter = 0 then

raise Exception.Create('Failed to open printer!');

FormInfo.Flags := FORM_USER;

FormInfo.pName := PChar(FormName);

PaperSize.cx := PaperWidth;

PaperSize.cy := PaperLength;

PaperRect.Left := 0;

PaperRect.Top := 0;

PaperRect.Right := PaperWidth;

PaperRect.Bottom := PaperLength;

FormInfo.Size := PaperSize;

FormInfo.ImageableArea := PaperRect;

if not AddForm(hPrinter, 1, @FormInfo) then

begin

errcode := GetLastError;

if errcode <> ERROR_FILE_EXISTS then // Form name exists?

begin

case errcode of

ERROR_ACCESS_DENIED: s := 'Access is denied';

ERROR_INVALID_HANDLE: s := 'The handle is invalid';

ERROR_NOT_READY: s := 'The device is not ready';

ERROR_CALL_NOT_IMPLEMENTED:

s := 'Function "AddForm" is not supported on this system';

else

s := 'Failed to add a Form (paper) name!';

end;

raise Exception.Create(s);

end;

end;

ClosePrinter(hPrinter);

end;

(*

Set custom paper size for WinNT/2000.

Make sure FormName is supported by current printer,

You can call PrnAddForm to define a new Form.

*)

procedure PrnSetPaperSizeNT(FormName: string; PaperWidth, PaperLength: integer);

var

Device, Driver, Port: array[0..80] of Char;

DevMode: THandle;

pDevmode: PDeviceMode;

begin

// Get printer device name etc.

Printer.GetPrinter(Device, Driver, Port, DevMode);

// force reload of DEVMODE

Printer.SetPrinter(Device, Driver, Port, 0) ;

// get DEVMODE handle

Printer.GetPrinter(Device, Driver, Port, DevMode);

if DevMode <> 0 then

begin

// lock it to get pointer to DEVMODE record

pDevMode := GlobalLock( DevMode );

if pDevmode <> nil then

try

with pDevmode^ do

begin

// modify form

StrLCopy( dmFormName, PChar(FormName), CCHFORMNAME-1 );

// tell printer driver that dmFormname field contains

// data it needs to inspect.

dmPaperWidth := PaperWidth;

dmPaperLength := PaperLength;

dmFields := dmFields or DM_FORMNAME or DM_PAPERWIDTH or DM_PAPERLENGTH;

end;

finally

GlobalUnlock( Devmode ); // unlock devmode handle.

end;

end; { If }

end;

procedure TForm1.Button1Click(Sender: TObject);

begin

PrnAddForm(

edFormName.Text,

StrToInt(edPaperWidth.Text),

StrToInt(edPaperLength.Text)

);

PrnSetPaperSizeNT(

edFormName.Text,

StrToInt(edPaperWidth.Text),

StrToInt(edPaperLength.Text)

);

Printer.BeginDoc;

Printer.Canvas.TextOut(10, 10, 'Printer test!');

Printer.EndDoc;

end;

在Delphi帮助中,AddForm定义如下:

BOOL AddForm(

HANDLE hPrinter, // handle to printer object

DWORD Level, // data-structure level

LPBYTE pForm // pointer to form info. data structure

);

下面是我在Delphi中定义的自定义函数AddPaper():

function AddPaper(PaperName: PChar;fPaperWidth,fPaperHeigth: Double): String;

var

PrintDevice, PrintDriver, PrintPort : array[0..255] of Char;

hDMode : THandle;

hPrinter: THandle;

FormInfo: TForminfo1;

PaperSize: TSize;

PaperRect: TRect;

PaperWidth,PaperHeigth: Integer;

function Zlxs(S: String;nWs: Integer): String; //整理小数位,并转化成厘米

begin

Try

Result:=FloatToStr(StrToFloat(S));

If pos('.',Result)>0 then

Result:=Copy(Result,1,pos('.',Result)+2);

Result:=FloatToStr(StrToFloat(Result)*10000);

Except

Result:='0';

end;

end;

begin

PaperWidth:=StrToInt(Zlxs(FloatToStr(fPaperWidth),3));

Paperheigth:=StrToInt(Zlxs(FloatToStr(fPaperheigth),3));

//判断是否安装打印机,并得到默认打印机的句柄

Printer.GetPrinter(PrintDevice, PrintDriver, PrintPort, hDMode);

OpenPrinter(PrintDevice, hPrinter, nil);

if hPrinter=0 then

begin

Result:='没有安装打印机!';

Exit;

end;

//定义结构

FormInfo.Flags:=FORM_USER;

FormInfo.pName:=PChar(PaperName);

PaperSize.cx:=PaperWidth;

PaperSize.cy:=PaperHeigth;

PaperRect.Left:=0;

PaperRect.Top:=0;

PaperRect.Right:=PaperSize.cx;

PaperRect.Bottom:=PaperSize.cy;

FormInfo.Size:=PaperSize;

FormInfo.ImageableArea:=PaperRect;

AddForm(hPrinter,1,@FormInfo); //添加纸张

ClosePrinter(hPrinter);

end;

3个参数:PaperName:你给纸张命的名(操作系统中叫描述格式),fPaperWidth:纸张宽度,fPaperHeigth:纸张高度。如果没有安装打印机,返回提示信息。如果已经有同样名称的纸张,函数不起作用,建议大家最好在名称中加入“_”,因为很少有这样命名的纸张,你的程序用你的专用纸张也不为过吧(谁叫Windows不提供,而我们偏偏又要用呢)?里面还有一个函数:Zlxs,这是用来整理小数的,经过试验,加入的纸张采用的单位是厘米时宽度用10000时只有1厘米,大家输入的往往是以厘米为单位的,且带小数,所以得用一个函数来将浮点数转换成整数。当然首先还得在uses段中加入Printers,winspool引用。以上代码在D5,D6+ Win 2000中运行通过。将这个函数加入管理系统中,在打印之前调用生成专用纸张,省时又省力。

这种方法应该是处理自定义纸张问题的正解,通用性强,也不会浪费打印机的链式(牵引)走纸功能。大家可以根据各自编程工具的方法进行定义,也可以做成.dll文件,这样不支持结构的编程工具,如VFP等也能使用了。

4、其他小技巧:

在使用OKI打印机时,我们有时会想把一行比较长的数据打在一张“US Std Fanfold”纸上,但“US Std Fanfold”宽度37.78cm,象OKI5330之类的打印机宽度有限,这么宽的纸放不下啊。我们可以采用自定义的方法实现,首先定义新格式,然后将“US Std Fanfold”的宽和高反过来,命个名:“US Std Fanfold(纵向)”,然后在报表设计中使用这张纸并采用横向打印就行了。

procedure UpdatePrint(Awidth,Aheight:integer);

const CustomFormName = 'ZJ Defined';

function Win95SetForm(PDevMode: PDeviceMode): Boolean;

begin

Printer.PrinterIndex := Printer.PrinterIndex;

PDevMode.dmFields := PDevMode.dmFields or DM_PAPERSIZE;

PDevMode.dmPaperSize := 256;

PDevMode.dmFields := PDevMode.dmFields or DM_PAPERWIDTH;

PDevMode.dmPaperWidth := AWidth;

PDevMode.dmFields := PDevMode.dmFields or DM_PAPERLENGTH;

PDevMode.dmPaperLength := AHeight;

Printer.PrinterIndex := Printer.PrinterIndex;

Result := True;

end;

function WinNTSetForm(PDevMode: PDeviceMode;

Device: PChar; Port: PChar): Boolean;

var

hPrinter: THandle;

pForm: Pointer;

cbNeeded: DWORD;

cReturned: DWORD;

FormInfo1: TFormInfo1;

begin

Result := False;

if OpenPrinter(Device, hPrinter, nil) then

begin

pForm := nil;

EnumForms(hPrinter, 1, pForm, 0, cbNeeded, cReturned);

GetMem(pForm, cbNeeded); //pForm的大小并分配内存

try

if EnumForms(hPrinter, 1, pForm, cbNeeded, cbNeeded, cReturned) then

begin

if DeleteForm(hPrinter, PChar(CustomFormName)) then

Dec(cReturned); //删除旧的Form

with FormInfo1 do

begin

Flags := 0;

pName := PChar(CustomFormName);

Size.cx := AWidth * 100;

Size.cy := AHeight * 100;

with ImageAbleArea do

begin

Left := 0;

Top := 0;

Right := Size.cx;

Bottom := Size.cy;

end;

end;

if AddForm(hPrinter, 1, @FormInfo1) then

begin

Printer.PrinterIndex := Printer.PrinterIndex;

PDevMode.dmFields := PDevMode.dmFields or DM_PAPERSIZE;

PDevMode.dmPaperSize := cReturned + 1;

Printer.PrinterIndex := Printer.PrinterIndex;

Result := True;

end;

end;

finally

FreeMem(pForm);

end;

end;

end;

var

Device, Driver, Port: array[0..127] of char;

hDevMode: THandle;

PDevMode: PDeviceMode;

begin

Printer.GetPrinter(Device, Driver, Port, hDevMode);

if hDevMode <> 0 then

begin

PDevMode := GlobalLock(hDevMode);

try

if (Win32Platform = VER_PLATFORM_WIN32s) or

(Win32Platform = VER_PLATFORM_WIN32_WINDOWS) then

Win95SetForm(PDevMode)

else if Win32Platform = VER_PLATFORM_WIN32_NT then

WinNTSetForm(PDevMode, Device, Port);

finally

GlobalUnlock(hDevMode);

end;

end

end;

将《Delphi中票据凭证的精确打印》一文中关于设置打印纸张长、宽的内容贴上来,供你参考

file://设置纸张高度-单位:mm

procedure SetPaperHeight(Value:integer);

var

Device : array[0..255] of char;

Driver : array[0..255] of char;

Port : array[0..255] of char;

hDMode : THandle;

PDMode : PDEVMODE;

begin

file://自定义纸张最小高度127mm

if Value < 127 then Value := 127;

file://自定义纸张最大高度432mm

if Value > 432 then Value := 432;

Printer.PrinterIndex := Printer.PrinterIndex;

Printer.GetPrinter(Device, Driver, Port, hDMode);

if hDMode <> 0 then

begin

pDMode := GlobalLock(hDMode);

if pDMode <> nil then

begin

pDMode^.dmFields := pDMode^.dmFields or DM_PAPERSIZE or

DM_PAPERLENGTH;

pDMode^.dmPaperSize := DMPAPER_USER;

pDMode^.dmPaperLength := Value * 10;

pDMode^.dmFields := pDMode^.dmFields or DMBIN_MANUAL;

pDMode^.dmDefaultSource := DMBIN_MANUAL;

GlobalUnlock(hDMode);

end;

end;

Printer.PrinterIndex := Printer.PrinterIndex;

end;

file://设置纸张宽度:单位--mm

Procedure SetPaperWidth(Value:integer);

var

Device : array[0..255] of char;

Driver : array[0..255] of char;

Port : array[0..255] of char;

hDMode : THandle;

PDMode : PDEVMODE;

begin

file://自定义纸张最小宽度76mm

if Value < 76 then Value := 76;

file://自定义纸张最大宽度216mm

if Value > 216 then Value := 216;

Printer.PrinterIndex := Printer.PrinterIndex;

Printer.GetPrinter(Device, Driver, Port, hDMode);

if hDMode <> 0 then

begin

pDMode := GlobalLock(hDMode);

if pDMode <> nil then

begin

pDMode^.dmFields := pDMode^.dmFields or DM_PAPERSIZE or

DM_PAPERWIDTH;

pDMode^.dmPaperSize := DMPAPER_USER;

file://将毫米单位转换为0.1mm单位

pDMode^.dmPaperWidth := Value * 10;

pDMode^.dmFields := pDMode^.dmFields or DMBIN_MANUAL;

pDMode^.dmDefaultSource := DMBIN_MANUAL;

GlobalUnlock(hDMode);

end;

end;

Printer.PrinterIndex := Printer.PrinterIndex;

end;

设定纸张大小

Procedure PrintPapersize(Width,Length:integer);

var

Device : array[0..cchDeviceName -1] of Char;

Driver : array[0..(MAX_PATH -1)] of Char;

Port : array[0..32]of Char;

hDMode : THandle;

pDMode : PDevMode;

begin

Printer.GetPrinter(Device,Driver,Port,hDMode);

if hDMode <> 0 then

begin

pDMode := GlobalLock(hDMode);

if pDMode <> nil then

begin

pDMode^.dmPaperSize := 256;

pDMode^.dmPaperLength :=Length ;

pDMode^.dmPaperWidth := Width;

pDMode^.dmFields :=pDMode^.dmFields or DM_PAPERSIZE;

pDMode^.dmFields :=pDMode^.dmFields or DM_PAPERLENGTH;

pDMode^.dmFields :=pDMode^.dmFields or DM_PAPERWIDTH;

ResetDC(Printer.Handle,pDMode^);

GlobalUnlock(hDMode);

end;

end;

end;

uses WinSpool, Printers, Windows;

function CustomAddForm (const Name:String; const Width, Height:Double; const PrinterName:String):Boolean;

var

FormInfo1: TFormInfo1;

pFormInfo: PFormInfo1;

hPrinter : THandle;

begin

Result := False;

if OpenPrinter(PChar(PrinterName),hPrinter,NIL) then

begin

with FormInfo1 do

begin

Flags := 0;

pName := PAnsiChar(Name);

Size.cx := Trunc(Width*1000);

Size.cy := Trunc(Height*1000);

ImageableArea.Left := 0;

ImageableArea.Top := 0;

ImageableArea.Bottom := Size.cy;

ImageableArea.Right := Size.cx;

end;

pFormInfo := @FormInfo1;

Result := AddForm(hPrinter,1,pFormInfo);

ClosePrinter(hPrinter);

end;

end;

有两个方法可以在win2000中设置自定义纸张:

1、手工添加

在“控制面板”、“打印机和传真”中选中一台打印机,在“文件”菜单的“服务器属性”中创建新格式即可。

2、程序动态修改

procedure SetPaperSize(X, Y: Integer);

// 单位是0.1mm

//改变devicemode结构

var

Device: array[0..255] of char;

Driver: array[0..255] of char;

Port: array[0..255] of char;

hDMode: THandle;

PDMode: PDEVMODE;

begin

Printer.PrinterIndex := Printer.PrinterIndex;

Printer.GetPrinter(Device, Driver, Port, hDMode);

if hDMode <> 0 then

begin

pDMode := GlobalLock(hDMode);

if pDMode <> nil then

begin

if (x = 0) or (y = 0) then

begin

{Set to legal}

pDMode^.dmFields := pDMode^.dmFields or dm_PaperSize;

{pDMode^.dmPaperSize := DMPAPER_LEGAL; changed by wulianmin}

pDMode^.dmPaperSize := DMPAPER_FANFOLD_US;

end

else

begin

{Set to custom size}

pDMode^.dmFields := pDMode^.dmFields or DM_PAPERSIZE or DM_PAPERWIDTH or DM_PAPERLENGTH;

pDMode^.dmPaperSize := DMPAPER_USER;

pDMode^.dmPaperWidth := x {SomeValueInTenthsOfAMillimeter};

pDMode^.dmPaperLength := y {SomeValueInTenthsOfAMillimeter};

end;

{Set the bin to use}

pDMode^.dmFields := pDMode^.dmFields or DMBIN_MANUAL;

pDMode^.dmDefaultSource := DMBIN_MANUAL;

GlobalUnlock(hDMode);

end;

end;

end;

我曾经用Delphi 5.0的Printer对象编写打印程序,在许多打印机上使用都没问题

(包括一些其他EPSON打印机),但是在EPSON 460上使用时不能打印,结果发现在这种

环境下,必须给Printer的Title属性赋值后,打印机才会真正去打印,具体方法如下:

Printer.BeginDoc;

Printer.Title := '在这里给打印文档起个名字';

{打印的内容};

Printer.EndDoc;

posted @ 2010-01-25 08:28  翼想天开的男孩  阅读(1067)  评论(0编辑  收藏  举报