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