佳博打印机demo
uGprinterNet类
基于delphi12
点击查看代码
unit uGprinterNet;
interface
uses
System.SysUtils, System.Classes, System.Types, System.DateUtils, IdTCPClient,
IdGlobal, Winapi.Windows, System.Math, IdException;
type
// 打印机状态枚举
TPrinterStatus = (psNormal, psPaperOut, psCoverOpen, psUnknownError);
type
// 二维码纠错等级枚举,对应手册n值
TQRErrorLevel = (qrL = 48, qrM = 49, qrQ = 50, qrH = 51);
TGpNetPrinter = class(TObject)
private
FTcp: TIdTCPClient;
FSendBuffer: TIdBytes;
FGBEnc: TEncoding; // 固定GB2312编码缓存
FStatusRetryInterval: Cardinal; // 状态轮询间隔(ms)
FStatusTimeout: Cardinal; // 状态检测总超时(ms)
procedure AppendByte(AByte: Byte);
procedure AppendBytes(const Data: TBytes);
procedure FlushSendBuffer;
function ReadPrinterStatus: TPrinterStatus; // 读取硬件状态
function WaitPrinterReady: Boolean; // 阻塞等待打印机就绪
function QueryPrinterInfo(QueryCode: Byte): string;
public
constructor Create;
destructor Destroy; override;
// 网络连接
procedure Connect(const AHost: string; APort: Word = 9100);
procedure Disconnect;
function IsConnected: Boolean;
function GetPrintBrand: string;
function GetPrintModel: string;
// 原有兼容接口
procedure SendCmd(const ACmd: string);
procedure SendBytes(const Data: TBytes);
// 打印样式扩展
procedure SetCharScale(W, H: Integer);
procedure SetBold(Enable: Boolean);
procedure SetDoubleWidth(Enable: Boolean);
procedure SetDoubleHeight(Enable: Boolean);
procedure SetQuadBigFont(Enable: Boolean);
procedure AlignCenter;
procedure AlignLeft;
procedure FeedLines(ALines: Integer);
procedure PrintLine;
procedure CutPaperFull;
procedure CutPaperHalf;
procedure Beep(const ANumber: Integer = 2; TimeCoeff: Byte = 2);
///步骤1:设置QR码单个模块点阵大小 <param name="DotSize">模块尺寸 1~9,默认3</param>
procedure SetQRModuleSize(DotSize: Byte = 3);
/// 步骤2:设置QR码纠错等级
procedure SetQRErrorLevel(Level: TQRErrorLevel = qrM);
/// 步骤3+4:写入内容并打印二维码(整合一键调用) <param name="QRText">二维码存储文本内容</param>
procedure PrintQRCode(const QRText: string);
// 状态配置
procedure SetStatusWaitParam(ARetryMs, ATimeoutMs: Cardinal);
//查询状态
function GetPrinterCurrentStatus: TPrinterStatus;
// 打印主入口:自动检测状态,异常阻塞等待恢复后再打印
function ExecutePrint: Boolean;
function ExecutePrintSimple: Boolean; //不检测状态直接打
end;
implementation
{ TGpNetPrinter }
constructor TGpNetPrinter.Create;
begin
inherited;
FTcp := TIdTCPClient.Create(nil);
SetLength(FSendBuffer, 0); //初始化空打印缓冲
FGBEnc := TEncoding.GetEncoding('GB2312');
// 默认等待参数:间隔300ms,总超时10秒
FStatusRetryInterval := 300; //默认轮询间隔
FStatusTimeout := 10000;
end;
destructor TGpNetPrinter.Destroy;
begin
Disconnect;
FTcp.Free;
FGBEnc.Free;
SetLength(FSendBuffer, 0);
inherited;
end;
procedure TGpNetPrinter.SetStatusWaitParam(ARetryMs, ATimeoutMs: Cardinal);
begin
FStatusRetryInterval := ARetryMs;
FStatusTimeout := ATimeoutMs;
end;
procedure TGpNetPrinter.Connect(const AHost: string; APort: Word = 9100);
begin
Disconnect;
FTcp.Host := AHost;
FTcp.Port := APort;
FTcp.Connect;
FTcp.IOHandler.ReadTimeout := 1000;
end;
procedure TGpNetPrinter.Disconnect;
begin
if FTcp.Connected then
FTcp.Disconnect;
end;
function TGpNetPrinter.IsConnected: Boolean;
begin
Result := FTcp.Connected;
end;
procedure TGpNetPrinter.PrintLine;
begin
SendCmd(#10 + '------------------------------------------------'); //#10换行
end;
procedure TGpNetPrinter.PrintQRCode(const QRText: string);
var
DataBuf: TBytes;
TotalLen, pL, pH: Word;
I: Integer;
begin
//转字节数组
DataBuf := TEncoding.UTF8.GetBytes(QRText);
// k = 总长度 -3;总长度 = 文本字节数 + 3
TotalLen := Length(DataBuf) + 3;
pL := TotalLen and $FF; // 低位
pH := (TotalLen shr 8) and $FF; // 高位
// ====== 步骤3:Function180 存储二维码数据 GS ( K pL pH 49 80 48 + 数据 ======
AppendByte($1D);
AppendByte($28);
AppendByte($6B);
AppendByte(Byte(pL));
AppendByte(Byte(pH));
AppendByte($31); // cn=49
AppendByte($50); // fn=80
AppendByte($30); // m=48
// 追加全部文本字节
for I := Low(DataBuf) to High(DataBuf) do
AppendByte(DataBuf[I]);
// ====== 步骤4:Function181 打印已存储的二维码 ======
AppendByte($1D);
AppendByte($28);
AppendByte($6B);
AppendByte($03);
AppendByte($00);
AppendByte($31);
AppendByte($51); // fn=81
AppendByte($30); // m=48
end;
function TGpNetPrinter.QueryPrinterInfo(QueryCode: Byte): string;
var
RecvBuf: TIdBytes;
begin
Result := '';
if not IsConnected then
exit ;
SetLength(FSendBuffer, 0);
// 下发 GS I n 1D 49 n
AppendByte($1D);
AppendByte($49);
AppendByte(QueryCode);
FTcp.IOHandler.Write(FSendBuffer);
//读取
FTCP.IOHandler.ReadBytes(RecvBuf, -1, False);
Result := Trim(TEncoding.ASCII.GetString(RecvBuf));
SetLength(FSendBuffer, 0);
end;
procedure TGpNetPrinter.AppendByte(AByte: Byte);
var
OldLen: Integer;
begin
OldLen := Length(FSendBuffer);
SetLength(FSendBuffer, OldLen + 1);
FSendBuffer[OldLen] := AByte;
end;
procedure TGpNetPrinter.AppendBytes(const Data: TBytes);
var
SrcIdBuf: TIdBytes;
OldLen: Integer;
begin
if Length(Data) = 0 then
Exit;
SrcIdBuf := TIdBytes(Data);
OldLen := Length(FSendBuffer);
SetLength(FSendBuffer, OldLen + Length(SrcIdBuf));
Move(SrcIdBuf[0], FSendBuffer[OldLen], Length(SrcIdBuf));
end;
procedure TGpNetPrinter.Beep(const ANumber: Integer = 2; TimeCoeff: Byte = 2);
var
bTimes, bCoeff: Byte;
begin
if (ANumber < 1) or (ANumber > 9) then
bTimes := 2
else
bTimes := Byte(ANumber);
// 限制时长系数 1~9
if (TimeCoeff < 1) or (TimeCoeff > 9) then
bCoeff := 2
else
bCoeff := TimeCoeff;
AppendByte($1B);
AppendByte($42);
AppendByte(bTimes);
AppendByte(bCoeff);
end;
procedure TGpNetPrinter.FlushSendBuffer;
begin
if not IsConnected then
Exit;
if Length(FSendBuffer) = 0 then
Exit;
FTcp.IOHandler.Write(FSendBuffer);
SetLength(FSendBuffer, 0);
end;
function TGpNetPrinter.GetPrintBrand: string;
begin
Result := QueryPrinterInfo(66);
end;
function TGpNetPrinter.GetPrinterCurrentStatus: TPrinterStatus;
begin
result := ReadPrinterStatus();
end;
function TGpNetPrinter.GetPrintModel: string;
begin
Result := QueryPrinterInfo(67);
end;
procedure TGpNetPrinter.SendCmd(const ACmd: string);
var
TempBuf: TBytes;
begin
if ACmd.IsEmpty then
Exit;
TempBuf := FGBEnc.GetBytes(ACmd);
AppendBytes(TempBuf);
end;
procedure TGpNetPrinter.SendBytes(const Data: TBytes);
begin
AppendBytes(Data);
end;
// 加粗 1B 45 n
procedure TGpNetPrinter.SetBold(Enable: Boolean);
begin
AppendByte($1B);
AppendByte($45);
AppendByte(Ord(Enable));
end;
procedure TGpNetPrinter.SetCharScale(W, H: Integer);
var
n: Byte;
begin
// 边界限制,防止超出手册1~8范围
if W < 1 then
W := 1;
if W > 8 then
W := 8;
if H < 1 then
H := 1;
if H > 8 then
H := 8;
// 计算掩码n
n := (W shl 4) or H;
// 发送GS ! 指令
AppendByte($1D);
AppendByte($21);
AppendByte(n);
end;
// 倍宽
procedure TGpNetPrinter.SetDoubleWidth(Enable: Boolean);
begin
AppendByte($1D);
AppendByte($21);
AppendByte(IfThen(Enable, $11, $00));
end;
procedure TGpNetPrinter.SetQRErrorLevel(Level: TQRErrorLevel);
var
n: Byte;
begin
n := Byte(Level);
AppendByte($1D);
AppendByte($28);
AppendByte($6B);
AppendByte($03);
AppendByte($00);
AppendByte($31);
AppendByte($45); // fn=69
AppendByte(n);
end;
procedure TGpNetPrinter.SetQRModuleSize(DotSize: Byte);
var
n: Byte;
begin
if DotSize < 1 then
n := 1
else if DotSize > 9 then
n := 9
else
n := DotSize;
AppendByte($1D);
AppendByte($28);
AppendByte($6B);
AppendByte($03);
AppendByte($00);
AppendByte($31);
AppendByte($43); //FN=67
AppendByte(n);
end;
procedure TGpNetPrinter.SetQuadBigFont(Enable: Boolean);
var
n: Byte;
begin
if Enable then
n := 1
else
n := 0;
AppendByte($1C);
AppendByte($57);
AppendByte(n);
end;
// 倍高
procedure TGpNetPrinter.SetDoubleHeight(Enable: Boolean);
begin
AppendByte($1D);
AppendByte($21);
AppendByte(IfThen(Enable, $10, $00));
end;
// 居中
procedure TGpNetPrinter.AlignCenter;
begin
AppendByte($1B);
AppendByte($61);
AppendByte(1);
end;
// 左对齐
procedure TGpNetPrinter.AlignLeft;
begin
AppendByte($1B);
AppendByte($61);
AppendByte(0);
end;
// 走纸N行
procedure TGpNetPrinter.FeedLines(ALines: Integer);
var
I: Integer;
begin
for I := 1 to ALines do
AppendByte($0A);
end;
// 全切
procedure TGpNetPrinter.CutPaperFull;
begin
AppendByte($1D);
AppendByte($56);
AppendByte($41);
AppendByte(0);
end;
// 半切
procedure TGpNetPrinter.CutPaperHalf;
begin
AppendByte($1D);
AppendByte($56);
AppendByte($42);
AppendByte(0);
end;
{ 核心:读取打印机硬件状态 GS r 0 / GS r 1 }
function TGpNetPrinter.ReadPrinterStatus: TPrinterStatus;
var
SendCmd: TIdBytes;
RecvBuf: TIdBytes; // 专门用来接收返回字节
RespByte: Byte;
begin
Result := psNormal; //超时放行打印。。
if not IsConnected then
Exit;
try
// 1. 清空旧打印残留缓冲(关键修复)
FTcp.IOHandler.InputBuffer.Clear;
// 发送读取打印机状态指令:GS r 0 ($1D $72 $00)
SetLength(SendCmd, 3);
SendCmd[0] := $10; // DLE
SendCmd[1] := $04; // EOT
SendCmd[2] := $02; // n=2 脱机/错误状态
FTcp.IOHandler.Write(SendCmd);
Sleep(50); // 微小延时,等待打印机回传字节
// 读取1字节状态返回
SetLength(RecvBuf, 0);
FTcp.IOHandler.ReadBytes(TIdBytes(RecvBuf), 1, False);
if Length(RecvBuf) >= 1 then
begin
RespByte := RecvBuf[0];
// Bit2=4:纸仓开盖 1=开盖
if (RespByte and 4) <> 0 then
begin
Result := psCoverOpen;
Exit;
end;
// Bit5=32:缺纸 1=缺纸
if (RespByte and 32) <> 0 then
begin
Result := psPaperOut;
Exit;
end;
// 无异常
Result := psNormal;
end;
except
// 捕获所有Indy网络超时/断连异常
on E: EIdException do
begin
Result := psUnknownError;
end;
end;
end;
{ 阻塞等待打印机就绪,超时返回False }
function TGpNetPrinter.WaitPrinterReady: Boolean;
var
StartTick: Cardinal;
CurStatus: TPrinterStatus;
begin
Result := False;
if not IsConnected then
Exit;
StartTick := GetTickCount;
while GetTickCount - StartTick < FStatusTimeout do
begin
CurStatus := ReadPrinterStatus;
case CurStatus of
psNormal:
begin
Result := True;
Break;
end;
psPaperOut, psCoverOpen:
begin
// 缺纸/开盖,等待指定间隔后重试
Sleep(FStatusRetryInterval);
end;
psUnknownError:
begin
Sleep(FStatusRetryInterval);
end;
end;
end;
end;
{ 打印主流程:先等设备就绪,再一次性发送缓冲 }
function TGpNetPrinter.ExecutePrint: Boolean;
begin
Result := False;
if not IsConnected then
Exit;
if Length(FSendBuffer) = 0 then
begin
Result := True;
Exit;
end;
// 阻塞等待打印机恢复正常(缺纸/开盖会循环等待,直到超时)
if not WaitPrinterReady then
Exit;
// 状态正常,执行发送
FlushSendBuffer;
Result := True;
end;
function TGpNetPrinter.ExecutePrintSimple: Boolean;
begin
Result := False;
if not IsConnected then
Exit;
if Length(FSendBuffer) = 0 then
begin
Result := True;
Exit;
end;
try
FlushSendBuffer;
Result := True;
except
Result := False;
end;
end;
end.
窗口设计:
点击查看代码
unit uJiaboDemo;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
Vcl.StdCtrls, uGprinterNet, IdBaseComponent, IdComponent, IdTCPConnection,
IdTCPClient, IdGlobal;
type
TForm2 = class(TForm)
Edit1: TEdit;
Button1: TButton;
Button2: TButton;
Button3: TButton;
Button4: TButton;
Memo1: TMemo;
Button5: TButton;
Label1: TLabel;
Label2: TLabel;
Edit2: TEdit;
procedure FormDestroy(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
procedure Button5Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form2: TForm2;
AGprinterNet: TGpNetPrinter;
implementation
{$R *.dfm}
procedure TForm2.Button1Click(Sender: TObject);
var
Sta: TPrinterStatus;
Tip: string;
begin
if not Assigned(AGprinterNet) then
begin
AGprinterNet := TGpNetPrinter.Create;
end;
AGprinterNet.Connect(Edit1.Text, 9100);
if not AGprinterNet.IsConnected then
begin
ShowMessage('未连接打印机');
Exit;
end;
Sta := AGprinterNet.GetPrinterCurrentStatus;
case Sta of
psNormal:
Tip := '打印机状态:正常,有纸、盖子闭合';
psPaperOut:
Tip := '打印机状态:缺纸,请加装打印纸';
psCoverOpen:
Tip := '打印机状态:纸仓盖子未关好';
psUnknownError:
Tip := '打印机无响应/网络断开';
end;
AGprinterNet.Beep;
AGprinterNet.ExecutePrint;
ShowMessage(Tip);
AGprinterNet.Disconnect;
end;
procedure TForm2.Button2Click(Sender: TObject);
begin
if not Assigned(AGprinterNet) then
begin
AGprinterNet := TGpNetPrinter.Create;
end;
// 3. 没连接才连接(不要每次打印都重连)
if not AGprinterNet.IsConnected then
begin
try
AGprinterNet.Connect(edit1.Text, 9100);
except
on E: Exception do
begin
ShowMessage('连接打印机失败:' + E.Message);
Exit;
end;
end;
end;
if Trim(Edit2.Text) = '' then
begin
ShowMessage('请输入打印内容');
Exit;
end;
if not AGprinterNet.IsConnected then
begin
ShowMessage('打印机连接失败,请检查IP与网线');
Exit;
end;
// ShowMessage('连接成功,正在打印...');
try
//1. 拼接打印内容(仅存入本地缓冲,不发包)
AGprinterNet.SetCharScale(4, 4); //倍宽
AGprinterNet.SendCmd(Edit2.Text + '四倍超大' + #10);
AGprinterNet.SetCharScale(3, 3); //倍宽
AGprinterNet.SendCmd(Edit2.Text + '3倍超大' + #10);
AGprinterNet.SetCharScale(2, 2); //倍宽
AGprinterNet.SendCmd(Edit2.Text + '二倍超大' + #10);
AGprinterNet.SetCharScale(1, 1);
AGprinterNet.PrintLine;
AGprinterNet.SetCharScale(1, 4); //倍宽
AGprinterNet.SendCmd(Edit2.Text + '四倍超大' + #10);
AGprinterNet.SetCharScale(1, 3); //倍宽
AGprinterNet.SendCmd(Edit2.Text + '3倍超大' + #10);
AGprinterNet.SetCharScale(1, 2); //倍宽
AGprinterNet.SendCmd(Edit2.Text + '二倍超大' + #10);
AGprinterNet.SetCharScale(1, 1);
AGprinterNet.PrintLine;
AGprinterNet.SetCharScale(4, 1); //倍宽
AGprinterNet.SendCmd(Edit2.Text + '四倍超大' + #10);
AGprinterNet.SetCharScale(3, 1); //倍宽
AGprinterNet.SendCmd(Edit2.Text + '3倍超大' + #10);
AGprinterNet.SetCharScale(2, 1); //倍宽
AGprinterNet.SendCmd(Edit2.Text + '二倍超大' + #10);
AGprinterNet.SetCharScale(1, 1);
AGprinterNet.PrintLine;
AGprinterNet.SetDoubleWidth(false); //倍宽
AGprinterNet.SetDoubleWidth(True); //倍宽
AGprinterNet.SendCmd(Edit2.Text + '倍宽' + #10);
AGprinterNet.SetDoubleWidth(false); //倍宽
AGprinterNet.SetDoubleHeight(True); //倍高
AGprinterNet.SendCmd(Edit2.Text + '倍高' + #10);
AGprinterNet.SetDoubleHeight(false); //倍高
AGprinterNet.SetBold(True); //加粗
AGprinterNet.SendCmd(Edit2.Text + '加粗' + #10);
AGprinterNet.SetBold(false); //加粗
AGprinterNet.SendCmd(Edit2.Text + '正常' + #10);
AGprinterNet.Beep;
// 2. 执行打印:内部自动检测打印机状态、发送全部数据
if AGprinterNet.ExecutePrint then
else
ShowMessage('打印机异常(缺纸/开盖/超时),打印失败');
// 关键:延迟断开,给TCP足够时间把数据发给打印机
Sleep(500);
except
on E: Exception do
begin
ShowMessage('打印出错:' + E.Message);
end;
end;
// 全部流程结束后再断开
AGprinterNet.Disconnect;
end;
procedure TForm2.Button3Click(Sender: TObject);
begin
if not Assigned(AGprinterNet) then
begin
AGprinterNet := TGpNetPrinter.Create;
end;
// 3. 没连接才连接(不要每次打印都重连)
if not AGprinterNet.IsConnected then
begin
try
AGprinterNet.Connect(Edit1.Text, 9100);
except
on E: Exception do
begin
ShowMessage('连接打印机失败:' + E.Message);
Exit;
end;
end;
end;
AGprinterNet.CutPaperFull; //只做本地拼接,不发送。
AGprinterNet.ExecutePrint; //必须加这行,本地拼接完成一并发送。
AGprinterNet.Disconnect;
end;
procedure TForm2.Button4Click(Sender: TObject);
begin
if not Assigned(AGprinterNet) then
begin
AGprinterNet := TGpNetPrinter.Create;
end;
// 3. 没连接才连接(不要每次打印都重连)
if not AGprinterNet.IsConnected then
begin
try
AGprinterNet.Connect('192.168.0.55', 9100);
except
on E: Exception do
begin
ShowMessage('连接打印机失败:' + E.Message);
Exit;
end;
end;
end;
AGprinterNet.SetCharScale(2, 2);
AGprinterNet.SendCmd('订单二维码');
AGprinterNet.SetCharScale(1, 1);
AGprinterNet.FeedLines(1);
// 2. 配置二维码参数(只需修改时调用,全局生效)
AGprinterNet.SetQRModuleSize(6); // 模块6点阵,中等大小
AGprinterNet.SetQRErrorLevel(qrM); // 纠错M级,平衡容量与容错
// 3. 写入并打印二维码内容
AGprinterNet.PrintQRCode(edit2.Text);
// 走纸+切纸
AGprinterNet.FeedLines(3);
AGprinterNet.CutPaperFull;
AGprinterNet.ExecutePrint;
end;
procedure TForm2.Button5Click(Sender: TObject);
var
Brand, Model: string;
begin
if not Assigned(AGprinterNet) then
begin
AGprinterNet := TGpNetPrinter.Create;
end;
// 3. 没连接才连接(不要每次打印都重连)
if not AGprinterNet.IsConnected then
begin
try
AGprinterNet.Connect(Edit1.Text, 9100);
except
on E: Exception do
begin
ShowMessage('连接打印机失败:' + E.Message);
Exit;
end;
end;
end;
Brand := AGprinterNet.GetPrintBrand;
Model := AGprinterNet.GetPrintModel;
ShowMessage('打印机品牌:'+Brand + #10+'型号: ' + Model);
end;
procedure TForm2.FormDestroy(Sender: TObject);
begin
AGprinterNet.Free;
end;
end.

浙公网安备 33010602011771号