最近公司给商户做的App 允许App把卖出的商品信息通过打印机 打印标签

所以了解了一下iOS 和 打印机 之间的交互 (Ps:用的不是UIPrinter 那个扫面打印机 发送信息打印的那个框架)

主要功能 打印 .中文. 数字. 二维码

1.连接打印机

连接打印机可以通过 网线 USB 蓝牙 或者WiFi . 我们用App肯定是通过WiFi或者蓝牙连接

至于蓝牙怎么连接 可以 看看前面的文章,本文采用WiFi 链接.

App和打印机通过socket通信.使用的是 GCDAsyncSocket

做过socket通信的应该对这个库都贼鸡儿熟悉,  不多介绍.想了解的可以看看资料,用法也很简单Git地址

App通过socket向打印机发送指令控制打印机动作.  所谓指令就是标题ESC/POS 指令

ESC指令  我觉得这个PDF清晰一点 ESC指令2

开发之前先耐心看一下指令集,现在脑中有一个概念

通过端口和IP地址 连接打印机

    @IBAction func connectAction(_ sender: Any) {let queue = DispatchQueue.global()self.socket = GCDAsyncSocket.init(delegate: self, delegateQueue: queue)do {try self.socket.connect(toHost: address, onPort: 9100)} catch  {print("出毛病了")}}

可以实现GCDAsyncSocketDelegate 查看socket的连接状态

extension HomeViewController: GCDAsyncSocketDelegate {func socket(_ sock: GCDAsyncSocket, didConnectTo url: URL) {}func socket(_ sock: GCDAsyncSocket, didConnectToHost host: String, port: UInt16) {print("链接成功")}func socketDidDisconnect(_ sock: GCDAsyncSocket, withError err: Error?) {print("断开连接")}func socket(_ sock: GCDAsyncSocket, didWriteDataWithTag tag: Int) {print("写入成功")}func socket(_ sock: GCDAsyncSocket, shouldTimeoutReadWithTag tag: Int, elapsed: TimeInterval, bytesDone length: UInt) -> TimeInterval {return 10}}

2.POS简单指令

/*** 这些数据源自爱普生指令集,为POS机硬件默认*/
let ESC:UInt8 = 27;//换码
let FS:UInt8 = 28;//文本分隔符
let GS:UInt8 = 29;//组分隔符
let DLE:UInt8 = 16;//数据连接换码
let EOT:UInt8 = 4;//传输结束
let ENQ:UInt8 = 5;//询问字符
let SP:UInt8 = 32;//空格
let HT:UInt8 = 9;//横向列表
let LF:UInt8 = 10;//打印并换行(水平定位)
let CR:UInt8 = 13;//归位键
let FF:UInt8 = 12;//走纸控制(打印并回到标准模式(在页模式下) )
let CAN:UInt8 = 24;//作废(页模式下取消打印数据 )

swift方法

//MARK:-- ------------------------打印机初始化-----------------------------/*** 打印机初始化* @return*/func init_printer() -> [UInt8] {let foo : [UInt8] = [UInt8(ESC), 64]return foo}//MARK:-- ------------------------换行-----------------------------/*** 换行* @param* @return*/func nextLine(number:Int) -> [UInt8] {var foo:[UInt8] = [UInt8].init();for _ in 0..<number {foo.append(LF)}return foo}//MARK:-- ------------------------打印空格-------------------------/*** 打印空格* @param* @return*/func blankWithCount(number:Int) -> [UInt8] {var foo:[UInt8] = [UInt8].init();for _ in 0..<number {foo.append(SP)}return foo}//MARK:-- ------------------------下划线-----------------------------/*** 绘制下划线(1点宽)* @return*/func underlineWithOneDotWidthOn() -> [UInt8] {var foo:[UInt8] = [UInt8].init();foo.append(ESC);foo.append(45)foo.append(1)return foo}
 /*** 绘制下划线(2点宽)* @return*/func underlineWithTwoDotWidthOn() -> [UInt8] {var foo:[UInt8] = [UInt8].init();foo.append(ESC);foo.append(45)foo.append(2)return foo}/*** 取消绘制下划线* @return*/func underlineOff() -> [UInt8] {var foo:[UInt8] = [UInt8].init();foo.append(ESC);foo.append(45)foo.append(0)return foo}//MARK:-- ------------------------加粗-----------------------------/*** 选择加粗模式* @return*/func boldOn() -> [UInt8] {var foo:[UInt8] = [UInt8].init();foo.append(ESC);foo.append(69)foo.append(0xF)return foo}/*** 取消加粗模式* @return*/func boldOff() -> [UInt8] {var foo:[UInt8] = [UInt8].init();foo.append(ESC);foo.append(69)foo.append(0)return foo}//MARK:-- ------------------------对齐-----------------------------/*** 左对齐* @return*/func alignLeft() -> [UInt8] {var foo:[UInt8] = [UInt8].init();foo.append(ESC);foo.append(97)foo.append(0)return foo}/*** 居中对齐* @return*/func alignCenter() -> [UInt8] {var foo:[UInt8] = [UInt8].init();foo.append(ESC);foo.append(97)foo.append(1)return foo}/*** 右对齐* @return*/func alignRight() -> [UInt8] {var foo:[UInt8] = [UInt8].init();foo.append(ESC);foo.append(97)foo.append(2)return foo}/*** 水平方向向右移动col列* @param col* @return*/func alignRight(col:UInt8) -> [UInt8] {var foo:[UInt8] = [UInt8].init();foo.append(ESC);foo.append(68)foo.append(col)foo.append(0)return foo}//------------------------字体变大-----------------------------/*** 字体变大为标准的n倍* @param num* @return*/func fontSize(font:Int8) -> [UInt8] {var realSize:UInt8 = 0switch font {case 1:realSize = 0;breakcase 2:realSize = 17;breakcase 3:realSize = 34;breakcase 4:realSize = 51;breakcase 5:realSize = 68;breakcase 6:realSize = 85;breakcase 7:realSize = 102;breakcase 8:realSize = 119;breakdefault:break}var foo:[UInt8] = [UInt8].init();foo.append(GS);foo.append(33)foo.append(realSize)return foo}//------------------------字体变小-----------------------------/*** 字体取消倍宽倍高* @param num* @return*/
//    public static byte[] fontSizeSetSmall(int num)
//{
//    byte[] result = new byte[3];
//    result[0] = ESC;
//    result[1] = 33;
//
//    return result;
//    }//------------------------切纸-----------------------------/*** 进纸并全部切割* @return*/func feedPaperCutAll() -> [UInt8] {var foo:[UInt8] = [UInt8].init();foo.append(GS);foo.append(86)foo.append(65)foo.append(0)return foo}/*** 进纸并切割(左边留一点不切)* @return*/func feedPaperCutPartial() -> [UInt8] {var foo:[UInt8] = [UInt8].init();foo.append(GS);foo.append(86)foo.append(66)foo.append(0)return foo}func mergerPaper() -> [UInt8] {var foo:[UInt8] = [UInt8].init();foo.append(ESC)foo.append(109)return foo}

上面是封装的一些常用的简单的方法, 大家需求各有不同,可以根据指令集 自己封装

3.编码格式

发送打印数据的时候 要注意编码格式

比如打印收据好多都是中文的, 打印机支持的是GBK标准. 发送的数据流就要按照这个编码格式编码

    //MARK:-- 根据标签发送不同的指令func sendPrinterContent(content:String) {let gbkEncoding = CFStringConvertEncodingToNSStringEncoding(UInt32(CFStringEncodings.GB_18030_2000.rawValue))let data = content.data(using: String.Encoding(rawValue: gbkEncoding))print("\(content)")self.socket.write(data!, withTimeout: -1, tag: 1);}

4.打印二维码

后台给的数据打印完毕之后,会在后边把一些信息以二维码的形式展示出来.

Emmmmmm 这个难住了. 但是资料或者说别人做好的东西,我们总是能搜到的.站在别人的肩膀上总是好的

这里借鉴了一下 大佬的Git地址

抄了两个方法 惭愧惭愧

首先分析一下打印机 打印图形的命令

2.3 图形打印指令图形打印指令见 表 2.36~表 2.39。

表 2.36

图形垂直取模数据填充

指令名称

图形垂直取模数据填充

指令代码

ASCII :ESC * m Hl Hh [d]k

十进制 :27 42 m Hl Hh [d]k

十六进制 :1B 2A m Hl Hh [d]k

功能描述

打印纵向取模图像数据,参数意义如下:m 为点图格式:

m 模式 水平比例 垂直比例0 8点单密度 ×2 ×3
1 8点双密度 ×1 ×332 24点单密度 ×2 ×133 24点双密度 ×1 ×1

Hl、Hh 为水平方向点数(Hl+256×Hh)[d]k 为点图数据
k 用于指示点图数据字节数,不参加传输

参数范围

58mm 纸宽:
m = 0、1、32、33

1≤Hl + Hh×256≤384
0 ≤ d ≤ 255
k=Hl + Hh × 256(当m=0、1)
k=(Hl + Hh × 256) × 3(当m=32、33)

80mm 纸宽:
m = 0、1、32、33

1≤Hl + Hh×256≤576
0 ≤ d ≤ 255
k=Hl + Hh × 256(当m=0、1)
k=(Hl + Hh × 256) × 3(当m=32、33)

默认值

注意事项

[d]k 相应位为 1 则表示该点打印,相应位为 0,则表示该点不打印图像水平方向超出打印区域的部分将被忽略点图数据与打印效果的关系如下:

此指令只填充打印缓存,图像的打印要在接收到打印指令后才开始,图像打印完毕后打印缓存被清空
若需要打印的图像高度较大,可以先拆分为若干条高度为 8(m = 0、1)或 24(m = 32

 

33)点的图像分别打印填充图形数据后,可以继续填充其它信息,以使图形与其它信息一同被打印填充点图后,一般使用 ESC J(n = 24)指令进行打印,也可以使用 LF 指令进行打印,但是 LF 指令会引发进纸操作(按行间距进纸),使得多行图像间断不连续

使用示例

 

表 2.37

图片水平取模数据打印

指令名称

图片水平取模数据打印

指令代码

ASCII :GS v 0
十进制 :29 118 48 m xL xH yL yH [d]k

十六进制:1D 76 30 m xL xH yL yH [d]k

功能描述

打印横向取模图像数据,参数意义如下:m 为位图方式:

m 模式水平比例垂直比例0,48 正常 ×1 ×11,49 倍宽 ×2 ×12,50 倍高 ×1 ×23,51 倍宽倍高 ×2 ×2

xL、xH 为水平方向字节数(xL + xH × 256)yL、yH 为竖直方向点数(yL + yH × 256)[d]k 为点图数据
k 为点图数据字节数,k 用于示意,不用传输

参数范围

58mm 纸宽:
0 ≤ m ≤ 3;48 ≤ m ≤ 51
1 ≤ xL+xH×256 ≤ 48
0 ≤ yL ≤255,0 ≤ yH ≤255
0 ≤ d ≤ 255
k = (Hl + Hh×256)×(yL + yH×256)

80mm 纸宽:
0 ≤ m ≤ 3;48 ≤ m ≤ 51
1≤ xL + xH×256 ≤ 72
0 ≤ yL ≤ 255,0 ≤ yH ≤ 2550 ≤ d ≤ 255
k = (Hl + Hh×256)×(yL + yH×256)

默认值

注意事项

[d]k 相应位为 1 则表示该点打印,相应位为 0,则表示该点不打印若图像水平字节数超出打印区域,超出部分将被忽略此指令执行时按图像大小进纸,不受 ESC 2、ESC 3 的行间距设置影响此指令执行后,打印坐标复位到左边距位置处,图像内容被清空位图数据与打印效果的关系如下:

续上表

 
此指令带有打印功能,边传数据边打印,不需要再使用打印指令
   

上面的文档 是两种取模打印方式.  图形水平取模打印 和 图形垂直取模打印

主要要细细的看 打印命令 和 注意事项

打印命令就是我们怎么发送个打印机的数据格式.  一定要按照这个格式发送才可以

注意事项解释一下就是: 我们拿到要转换的图片, 去图片位图bitmap.  bitmap每一个点都要转换非黑即白,

打印机接受的数据只打印1 , 0不打印. 形成黑白的图片.

所以说转换的重点就是 1.拿到bitmap 2.转换成我们要打印的data

1.拿到bitmap

- (CGContextRef) newBitmapRGBA8ContextFromImage:(CGImageRef) image {CGContextRef context = NULL;CGColorSpaceRef colorSpace;uint32_t *bitmapData;size_t bitsPerPixel = 32;size_t bitsPerComponent = 8;size_t bytesPerPixel = bitsPerPixel / bitsPerComponent;size_t width = CGImageGetWidth(image);size_t height = CGImageGetHeight(image);size_t bytesPerRow = width * bytesPerPixel;size_t bufferLength = bytesPerRow * height;colorSpace = CGColorSpaceCreateDeviceRGB();if(!colorSpace) {NSLog(@"Error allocating color space RGB\n");return NULL;}// Allocate memory for image databitmapData = (uint32_t *)malloc(bufferLength);if(!bitmapData) {NSLog(@"Error allocating memory for bitmap\n");CGColorSpaceRelease(colorSpace);return NULL;}//Create bitmap contextcontext = CGBitmapContextCreate(bitmapData,width,height,bitsPerComponent,bytesPerRow,colorSpace,kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedLast);    // RGBAif(!context) {free(bitmapData);NSLog(@"Bitmap context not created");}CGColorSpaceRelease(colorSpace);return context;
}

2.转换成需要打印的data

-(NSData*) imageToThermalData:(UIImage *)image{CGImageRef imageRef = image.CGImage;// Create a bitmap context to draw the uiimage intoCGContextRef context = [self newBitmapRGBA8ContextFromImage:imageRef];if(!context) {return NULL;}size_t width = CGImageGetWidth(imageRef);size_t height = CGImageGetHeight(imageRef);CGRect rect = CGRectMake(0, 0, width, height);// Draw image into the context to get the raw image dataCGContextDrawImage(context, rect, imageRef);// Get a pointer to the datauint32_t *bitmapData = (uint32_t *)CGBitmapContextGetData(context);if(bitmapData) {uint8_t *m_imageData = (uint8_t *) malloc(width * height/8 + 8*height/8);memset(m_imageData, 0, width * height/8 + 8*height/8);int result_index = 0;for(int y = 0; (y + 24) < height; ) {m_imageData[result_index++] = 27;m_imageData[result_index++] = 51;m_imageData[result_index++] = 0;m_imageData[result_index++] = 27;m_imageData[result_index++] = 42;m_imageData[result_index++] = 33;m_imageData[result_index++] = width%256;m_imageData[result_index++] = width/256;for(int x = 0; x < width; x++) {int value = 0;for (int temp_y = 0 ; temp_y < 8; ++temp_y){uint8_t *rgbaPixel = (uint8_t *) &bitmapData[(y+temp_y) * width + x];uint32_t gray = 0.3 * rgbaPixel[RED] + 0.59 * rgbaPixel[GREEN] + 0.11 * rgbaPixel[BLUE];if (gray < 127){value += 1<<(7-temp_y)&255;}}m_imageData[result_index++] = value;value = 0;for (int temp_y = 8 ; temp_y < 16; ++temp_y){uint8_t *rgbaPixel = (uint8_t *) &bitmapData[(y+temp_y) * width + x];uint32_t gray = 0.3 * rgbaPixel[RED] + 0.59 * rgbaPixel[GREEN] + 0.11 * rgbaPixel[BLUE];if (gray < 127){value += 1<<(7-temp_y%8)&255;}}m_imageData[result_index++] = value;value = 0;for (int temp_y = 16 ; temp_y < 24; ++temp_y){uint8_t *rgbaPixel = (uint8_t *) &bitmapData[(y+temp_y) * width + x];uint32_t gray = 0.3 * rgbaPixel[RED] + 0.59 * rgbaPixel[GREEN] + 0.11 * rgbaPixel[BLUE];if (gray < 127){value += 1<<(7-temp_y%8)&255;}}m_imageData[result_index++] = value;}m_imageData[result_index++] = 13;m_imageData[result_index++] = 10;y += 24;}NSMutableData *data = [[NSMutableData alloc] initWithCapacity:0];[data appendBytes:m_imageData length:result_index];free(bitmapData);return data;} else {NSLog(@"Error getting bitmap pixel data\n");}CGContextRelease(context);return nil ;
}

这两个方法 用到了一个 小枚举

typedef enum {ALPHA = 0,BLUE = 1,GREEN = 2,RED = 3
} PIXELS;

上面生成的data 就可以直接调用打印方法

- (NSData *)getDataForPrint{UIImage *image = [UIImage_catagory imageOfQRFromURL:@"C0000001POS031502761784548" codeSize:300];image = [UIImage_catagory excludeFuzzyImageFromCIImage:image.CIImage size:300];NSData *data = [self imageToThermalData:image];[self.socket writeData:data withTimeout:-1 tag:1];return data;
}

iOS 连接打印机 ESC/POS 指令打印 打印图片二维码相关推荐

  1. TSC打印机打印条形码和二维码,JS实现方式

    自上一篇TSC打印条形码和二维码的文章发布以来,有不 少网友咨询相关技术, 今天全部整理一下,实现的源码和文件也全部公开. -- 2019.03.28  整理最新的js打印的实现方式 说明: 1.以下 ...

  2. electron打印条形码、二维码

    功能:在electron打印条形码或者二维码 在百度搜索找不到合适的博客与插件,就在github上找到一个插件 electron-pos-printer 可以一键打印图片.文本.二维码.条形码.表格 ...

  3. python——生成带logo的二维码图片并且保存、控制打印机打印图片二维码、整合打印(获取输入框的值)、打包成exe文件

    1.生成带logo的二维码图片并且保存 前提条件:在D盘里有logo.png的图片,生成的二维码图片在D盘里的111.png import qrcode from PIL import Image# ...

  4. 【干货#007】标签机打印小程序参数二维码的方法

    缘起 微信小程序参数二维码是针对小程序特定页面,设定相应参数,用户扫描后可以进入相应页面的方法,具有多种用途. 目前,有多种方式可以生成小程序二维码图片,然后直接打印出来.但在使用标签机打印二维码时, ...

  5. vue批量生成二维码,打印生成的二维码,并批量下载生成的二维码,qrcode

    通过使用 qrcode 生成二维码, 使用 jszip 打包批量二维码文件, 使用 file-saver 下载打包好的zip文件, 使用 vue-print-nb 打印生成的二维码 生成二维码: 打印 ...

  6. 飞鹅小票打印机嵌入生成指定小程序页面二维码的解决方案 | 扫普通链接二维码打开小程序示例 | 生成正方形小程序码

    部分朋友不需要打印机的业务,则 忽略有关打印机的部分 即可. 其他有关 微信小程序配置的介绍是通用的!通用的! 生成正方形小程序码,请看 标题一. 扫普通链接生成的二维码打开小程序,请看 标题二. 目 ...

  7. IOS抖音短视频APP开发关于扫描二维码,并根据文本生成二维码

    IOS抖音短视频APP开发关于扫描二维码,(根据光线强弱显示隐藏闪光灯)并根据文本生成二维码. WeakSelf; //IOS抖音短视频APP开发构建扫描样式视图 _scanView = [[WSLS ...

  8. iOS相册图片二维码识别

    前言:最近客户要求开发一个功能,类似微信长按图片识别图片中的二维码,一开始我使用了ZXingObjC,但是完成后被测试出有些二维码识别不了,所以只能另寻它法,之后更换为苹果系统自带的识别图片二维码的功 ...

  9. uni-app H5+ 连接蓝牙打印机打印文字及二维码

    基于Native.js 实现的连接蓝牙打印机 打印效果图 核心代码 测试代码 运行设备及环境 PS: PPS: Demo 打印效果图 核心代码 /*** @Description: 蓝牙打印类 基于h ...

最新文章

  1. java虚拟机资源根目录_Java路径问题最终解决方案—可定位所有资源的相对路径寻址 - java - CSDN技术......
  2. c语言scanf结果在printf前,C语言中的scanf与printf
  3. How to allow/block PING on Linux server – IPTables rules for icmp---reference
  4. u-boot,linux,文件系统移植笔记1
  5. HTML演练 0917 需求说明 我喜欢的影视剧
  6. Ubuntu9.04更新源
  7. TweenLite中文帮助手册
  8. layout_gravity和gravity的区别
  9. python具有可嵌入性_如何构建可嵌入Python
  10. 动态规划-最少硬币问题
  11. man时括号里的数字是啥意思
  12. ege管理系统_网上人才管理系统方案
  13. LaTex的图文安装--TexLife+SumtraPDF+Vscode
  14. 注册电气工程师考试考取事宜与考试大纲
  15. android 远程控制工具,安卓版远程协助软件
  16. Spring Data JPA/Hibernate 运行期动态模型、动态实体建表、动态字段查询的方式
  17. 给MacBook装win7遇到的坑
  18. 标题EMC干货之共模干扰 转载
  19. 抖音直播间一直没人为什么?提高抖音直播间人气:国仁楠哥
  20. mmap和mmap64

热门文章

  1. python:实现stone paper scissor石头、布、剪刀(附完整源码)
  2. 动态规划之背包问题---01背包---完全背包---多重背包
  3. 设计模式这扇窗之我见
  4. mac电脑如何恢复出厂设置?
  5. oracle 数据库密码 特殊字符,【案例】Oracle用户密码含特殊字符$ 登录数据库时异常案例...
  6. LiveHome桌面伴侣简介
  7. Javascript查找字符串中的某个值,截取其之前。和之后的值
  8. 路由器java灯一直闪_java – Vertx的路由器问题
  9. H3C交换机开启DHCP服务
  10. 初学爬虫-veer图片下载