zoukankan      html  css  js  c++  java
  • [转]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;

  • 相关阅读:
    [thinkphp] 是如何输出一个页面的
    [thinkphp] 获取根目录绝对路径
    onethink 插件模板定位
    win7 安全模式开启声音
    百度贴吧楼层评论地址
    第一天问题
    [php] 解析JSON字符串
    NDK编译时两 .so之间调用问题
    CDN问题积累
    C++模板特化
  • 原文地址:https://www.cnblogs.com/moon25/p/1655542.html
Copyright © 2011-2022 走看看