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

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

转自:http://delphi.ktop.com.tw/board.php?cid=30&fid=100&tid=75503

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.78 cm ,象 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;
 
 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值