前言
之前公司有個面向商戶的項目,需要連接商戶打印機(jī)打印小票的功能。于是對這方面進(jìn)行了學(xué)習(xí)研究,最后“順利”的完成了項目需求。這里主要是對項目中用到的知識點進(jìn)行一些總結(jié)。這篇文章主要包含的相關(guān)知識有:Socket、CoreBluetooth、網(wǎng)口小票打印機(jī)、藍(lán)牙小票打印機(jī)、ESC/POS打印命令集、圖片打印等。
概述
整個打印流程大致分可以為三個步驟,①鏈接打印機(jī);②編輯排版打印內(nèi)容;③發(fā)送數(shù)據(jù)給打印機(jī);
①和③根據(jù)不同的打印機(jī)類型,我們要采取不同的鏈接方式。網(wǎng)口打印機(jī)通過Socket進(jìn)行鏈接(需在同一局域網(wǎng)下),藍(lán)牙打印機(jī)自然是通過藍(lán)牙進(jìn)行鏈接。
②編輯排版打印內(nèi)容,需要通過ESC/POS打印命令集來做,以下會進(jìn)行相關(guān)的介紹。
其實,步驟②編輯排版打印內(nèi)容,放到后臺做是更加合理的,這樣Android和iOS兩端就避免了都要寫編輯排版的代碼,而且也能避免排版上的差異。我們公司也是這樣做的,所以步驟②就可以改為從后臺獲取要打印的數(shù)據(jù)。
ESC/POS打印命令集
簡介
WPSON StandardCode for Printer 是EPSON公司自己制定的針式打印機(jī)的標(biāo)準(zhǔn)化指令集,現(xiàn)在已成為針式打印機(jī)控制語言事實上的工業(yè)標(biāo)準(zhǔn)。ESC/POS打印命令集是ESC打印控制命令的簡化版本,現(xiàn)在大多數(shù)票據(jù)打印都采用ESC/POS指令集。其顯著特征是:其中很大一部分指令都是以ESC控制符開始的一串代碼。
打印機(jī)的型號種類有很多,不同的廠家也對其產(chǎn)品做了相應(yīng)的定制。但是,ESC/POS指令集基本都會支持。關(guān)于指令的詳細(xì)內(nèi)容,網(wǎng)上有很多文檔,另外每個品牌的官網(wǎng),也會有對應(yīng)的打印機(jī)指令文檔提供下載。我們可以下載下來研究。這里簡單介紹幾種常用的指令:
指令介紹
說明:一般打印機(jī)接受指令都支持三種格式:ASCII、十進(jìn)制、十六進(jìn)制。
1、初始化打印機(jī)
ASCII | 十進(jìn)制 | 十六進(jìn)制 |
---|---|---|
ESC @ | 27 64 | 1B 40 |
說明:清除打印緩沖區(qū),刪除用戶自定義字符,打印模式被設(shè)為上電時的默認(rèn)值模式。
代碼:
//重置打印機(jī)
- (void)resetPrinter {
Byte reset[] = {0x1B,0x40};
[self.printData appendBytes:reset length:1];
}
注意:經(jīng)筆者測試發(fā)現(xiàn),使用初始化命令,之后的一條命令可能會失效,目前未找到原因,可能是打印機(jī)問題。另外,由于此命令會清除緩沖區(qū),頻繁調(diào)用可能會導(dǎo)致數(shù)據(jù)丟失,因此盡量少用此命令。
2、打印并換行
ASCII | 十進(jìn)制 | 十六進(jìn)制 |
---|---|---|
LF | 10 | 0A |
說明:將打印緩沖區(qū)中的數(shù)據(jù)打印出來,并且按照當(dāng)前行間距,把打印紙向前推進(jìn)一行。
代碼:
//打印機(jī)并換行
- (void)printAndNewline {
Byte next[] = {0x0A};
[self.printData appendBytes:next length:1];
}
3、打印并走n點行紙
ASCII | 十進(jìn)制 | 十六進(jìn)制 |
---|---|---|
LESC J n | 27 74 n | 1B 4A n |
說明:打印緩沖區(qū)數(shù)據(jù)并走紙[ n × 縱向或橫向移動單位] 英寸。0 ≤n ≤ 255。最大走紙距離是956 mm(不同品牌打印機(jī)數(shù)值不同)。如果超出這個距離,取最大距離。
代碼:
//打印緩沖區(qū)數(shù)據(jù),并往前走紙n點行
- (void)printAndGoNPointLine:(int)n {
Byte line[] = {0x1B, 0x4A, n};
[self.printData appendBytes:line length:3];
}
注意:這里是走紙點行數(shù),要與字符行數(shù)區(qū)分
4、打印并走n行紙
ASCII | 十進(jìn)制 | 十六進(jìn)制 |
---|---|---|
ESC d n | 27 100 n | 1B 64 n |
說明:打印緩沖區(qū)里的數(shù)據(jù)并向前走紙n行(字符行)。0 ≤n ≤ 255。該命令不影響由ESC 2 或ESC 3設(shè)置的行間距。 最大走紙距離為1016 mm,當(dāng)所設(shè)的值大于1016 mm時,取最大值。
代碼:
//打印緩沖區(qū)數(shù)據(jù),并往前走紙n行
- (void)printAndGoNLine:(int)n {
Byte line[] = {0x1B, 0x64, n};
[self.printData appendBytes:line length:3];
}
注意:這里是走紙字符行數(shù),要與點行數(shù)區(qū)分。只有設(shè)置了行距后,此命令才有效。使用此命令前,要先使用換行指令,否則設(shè)置無效
5、設(shè)置默認(rèn)行距(1/6英寸)
ASCII | 十進(jìn)制 | 十六進(jìn)制 |
---|---|---|
ESC 2 | 27 50 | 1B 32 |
說明:選擇約3.75mm 行間距。約34個點。
代碼:
//設(shè)置默認(rèn)行間距
- (void)printDefaultLineSpace {
Byte defaultLineSpace[] = {0x1B,0x32};
[self.printData appendBytes:defaultLineSpace length:2];
}
6、設(shè)置行間距為n 點行
ASCII | 十進(jìn)制 | 十六進(jìn)制 |
---|---|---|
ESC 3 n | 27 51 n | 1B 33 n |
說明:設(shè)置行間距為[ n × 縱向或橫向移動單位] 英寸。
代碼:
//設(shè)置行間距為n個點
- (void)printLineSpace:(int)n {
Byte lineSpace[] = {0x1B,0x33,n};
[self.printData appendBytes:lineSpace length:3];
}
注意:使用此命令前,要先使用換行指令,否則設(shè)置無效
7、設(shè)置字符右間距
ASCII | 十進(jìn)制 | 十六進(jìn)制 |
---|---|---|
ESC SP n | 27 32 n | 1B 20 n |
說明:設(shè)置字符的右間距為[n×橫向移動單位或縱向移動單位]英寸。0 ≤ n ≤255。最大右間距是31.91毫米(255/203 英寸)。任何超過這個值的設(shè)置都自動轉(zhuǎn)換為最大右間距。
代碼:
//字符右間距
- (void)printCharRightSpace:(int)n {
Byte line[] = {0x1B, 0x20, n};
[self.printData appendBytes:line length:3];
}
注意:此命令對漢字無效
8、設(shè)置輸出對齊方式
ASCII | 十進(jìn)制 | 十六進(jìn)制 |
---|---|---|
ESC a n | 27 97 n | 1B 61 n |
說明:n = 0或48 為左對齊;n = 1或49為中間對齊;n = 2或50位右對齊。
代碼:
//設(shè)置對齊方式
- (void)setAlignment:(MNAlignmentType)alignmentType {
Byte align[] = {0x1B,0x61,alignmentType};
[self.printData appendBytes:align length:3];
}
9、設(shè)置字體大小
ASCII | 十進(jìn)制 | 十六進(jìn)制 |
---|---|---|
GS ! n | 29 33 n | 1D 21 n |
代碼:
//字符放大倍數(shù)
typedef enum: UInt8 {
MNPrintFont_1 = 0x00,
MNPrintFont_2 = 0x11,
MNPrintFont_3 = 0x22,
MNPrintFont_4 = 0x33,
MNPrintFont_5 = 0x44,
MNPrintFont_6 = 0x55,
MNPrintFont_7 = 0x66,
MNPrintFont_8 = 0x77,
} MNPrintFont;
//設(shè)置字體大小
-(void)printCharSize:(MNPrintFont)printFont {
Byte font[] = {0x1D,0x21,printFont};
[self.printData appendBytes:font length:3];
};
10、選擇切紙模式和切紙
ASCII | 十進(jìn)制 | 十六進(jìn)制 |
---|---|---|
GS V m | 29 86 m | 1D 56 m |
ASCII | 十進(jìn)制 | 十六進(jìn)制 |
---|---|---|
GS V m n | 29 86 m n | 1D 56 m n |
說明:
- m=0,1,49 ;0 表示全切, 1表示半切,當(dāng)打印機(jī)沒有半切功能時,全切;
- m=66, 0≤n≤255 ;當(dāng)m=66時, n表示走紙到(約18mm)+[n*0.125mm] 位置切紙
代碼:
//切紙模式
typedef enum :UInt8 {
MNCutPaperModelFull = 0x00,
MNCutPaperModelHalf = 0x01,
MNCutPaperModelFeedPaperHalf = 0x66
}MNCutPaperModel;
- (void)printCutPaper:(MNCutPaperModel)model Num:(int)n {
if (model == MNCutPaperModelFull) {
Byte cut[] = {0x1D, 0x56, model, n};
[self.printData appendBytes:cut length:4];
} else {
Byte cut[] = {0x1D, 0x56, model};
[self.printData appendBytes:cut length:3];
}
}
注意這條指令需要打印機(jī)支持切紙
10、產(chǎn)生錢箱脈沖(開錢箱)
ASCII | 十進(jìn)制 | 十六進(jìn)制 |
---|---|---|
ESC p m t1 t2 | 27 112 m t1 t2 | 1B 70 m t1 t2 |
說明:
- m = 0, 1, 48, 49 ; 0 ≤ t1 ≤ 255, 0 ≤ t2 ≤ 255 ;
- 輸出由t1和t2設(shè)定的錢箱開啟脈沖到由m指定的引腳:
M | 十進(jìn)制 |
---|---|
0, 48 | 錢箱插座的引腳 2 |
1, 49 | 錢箱插座的引腳 5 |
代碼:
//產(chǎn)生錢箱控制脈沖,一般一個打印機(jī)連接一個錢箱,這里默認(rèn)寫死了
-(void)printOpenCashDrawer {
Byte open[] = {0x1B, 0x70, 0x00, 0x80, 0xFF};
[self.printData appendBytes:open length:5];
}
注意這條指令需要打印機(jī)連接錢箱
打印內(nèi)容
說明:這里只要將打印內(nèi)容通過kCFStringEncodingGB_18030_2000
編碼,然后發(fā)送給打印機(jī)
代碼:
- (void)printWithContent:(NSString *)content {
NSStringEncoding enc = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000);
NSData *data = [content dataUsingEncoding:enc];
[self.printData appendData:data];
}
以上只是部分指令,可根據(jù)需求,參考指令集文檔再做相應(yīng)的添加。這里要提一下的是,小票打印多用于訂單詳情類信息,為了是排版更美觀,這里用的比較多的是制表符/t
來使每一列對齊,可以直接這樣使用[self.printManager printWithContent:@"\t"];
圖片打印
關(guān)于圖片打印,這里介紹兩種打印指令:
原理
因為小票打印機(jī)多為熱敏打印機(jī),或針式打印機(jī),且顏色只有黑白兩色。因此,要打印圖片,首先要獲取圖片的像素數(shù)據(jù),然后將圖片進(jìn)行黑白二值化處理,之后拼接打印數(shù)據(jù),黑色為打印的點,白色為不打印的點。如此逐行打印圖片數(shù)據(jù)。
調(diào)整分辨率
-(UIImage*)scaleImageWithImage:(UIImage*)image width:(NSInteger)width height:(NSInteger)height
{
CGSize size;
size.width = width;
size.height = height;
UIGraphicsBeginImageContext(size);
[image drawInRect:CGRectMake(0, 0, width, height)];
UIImage *scaledImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return scaledImage;
}
獲取像素數(shù)據(jù)
- 獲取圖像像素可以參考Getting the pixel data from a CGImage object;
-(CGContextRef)CreateARGBBitmapContextWithCGImageRef:(CGImageRef)inImage
{
CGContextRef context = NULL;
CGColorSpaceRef colorSpace;
void * bitmapData;
int bitmapByteCount;
int bitmapBytesPerRow;
// Get image width, height. We'll use the entire image.
size_t pixelsWide = CGImageGetWidth(inImage);
size_t pixelsHigh = CGImageGetHeight(inImage);
// Declare the number of bytes per row. Each pixel in the bitmap in this
// example is represented by 4 bytes; 8 bits each of red, green, blue, and
// alpha.
bitmapBytesPerRow = (int)(pixelsWide * 4);
bitmapByteCount = (int)(bitmapBytesPerRow * pixelsHigh);
// Use the generic RGB color space.
colorSpace =CGColorSpaceCreateDeviceRGB();
if (colorSpace == NULL)
{
return NULL;
}
// Allocate memory for image data. This is the destination in memory
// where any drawing to the bitmap context will be rendered.
bitmapData = malloc( bitmapByteCount );
if (bitmapData == NULL)
{
CGColorSpaceRelease( colorSpace );
return NULL;
}
// Create the bitmap context. We want pre-multiplied ARGB, 8-bits
// per component. Regardless of what the source image format is
// (CMYK, Grayscale, and so on) it will be converted over to the format
// specified here by CGBitmapContextCreate.
context = CGBitmapContextCreate (bitmapData,
pixelsWide,
pixelsHigh,
8, // bits per component
bitmapBytesPerRow,
colorSpace,
kCGImageAlphaPremultipliedFirst);
if (context == NULL)
{
free (bitmapData);
}
// Make sure and release colorspace before returning
CGColorSpaceRelease( colorSpace );
return context;
}
位圖模式指令
- 根據(jù)像素信息將圖片進(jìn)行黑白化處理,并逐行拼接打印信息
typedef enum {
ALPHA = 0,
BLUE = 1,
GREEN = 2,
RED = 3
} PIXELS;
- (NSData *) imageToThermalData:(UIImage*)image {
CGImageRef imageRef = image.CGImage;
// Create a bitmap context to draw the uiimage into
CGContextRef context = [self CreateARGBBitmapContextWithCGImageRef: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 data
CGContextDrawImage(context, rect, imageRef);
// Get a pointer to the data
uint32_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 ;
}
光柵位圖指令
#pragma mark ********************另一種打印圖片的方式****************************
typedef struct ARGBPixel {
u_int8_t red;
u_int8_t green;
u_int8_t blue;
u_int8_t alpha;
} ARGBPixel ;
#pragma mark 獲取打印圖片數(shù)據(jù)
-(NSData *)getDataForPrintWith:(UIImage *)image{
CGImageRef cgImage = [image CGImage];
size_t width = CGImageGetWidth(cgImage);
size_t height = CGImageGetHeight(cgImage);
NSData* bitmapData = [self getBitmapImageDataWith:cgImage];
const char * bytes = bitmapData.bytes;
NSMutableData * data = [[NSMutableData alloc] init];
//橫向點數(shù)計算需要除以8
NSInteger w8 = width / 8;
//如果有余數(shù),點數(shù)+1
NSInteger remain8 = width % 8;
if (remain8 > 0) {
w8 = w8 + 1;
}
/**
根據(jù)公式計算出 打印指令需要的參數(shù)
指令:十六進(jìn)制碼 1D 76 30 m xL xH yL yH d1...dk
m為模式,如果是58毫秒打印機(jī),m=1即可
xL 為寬度/256的余數(shù),由于橫向點數(shù)計算為像素數(shù)/8,因此需要 xL = width/(8*256)
xH 為寬度/256的整數(shù)
yL 為高度/256的余數(shù)
yH 為高度/256的整數(shù)
**/
NSInteger xL = w8 % 256;
NSInteger xH = width / (88 * 256);
NSInteger yL = height % 256;
NSInteger yH = height / 256;
Byte cmd[] = {0x1d,0x76,0x30,0,xL,xH,yL,yH};
[data appendBytes:cmd length:8];
for (int h = 0; h < height; h++) {
for (int w = 0; w < w8; w++) {
u_int8_t n = 0;
for (int i=0; i<8; i++) {
int x = i + w * 8;
u_int8_t ch;
if (x < width) {
int pindex = h * (int)width + x;
ch = bytes[pindex];
}
else{
ch = 0x00;
}
n = n << 1;
n = n | ch;
}
[data appendBytes:&n length:1];
}
}
return data;
}
#pragma mark 獲取圖片點陣圖數(shù)據(jù)
-(NSData *)getBitmapImageDataWith:(CGImageRef)cgImage{
size_t width = CGImageGetWidth(cgImage);
size_t height = CGImageGetHeight(cgImage);
NSInteger psize = sizeof(ARGBPixel);
ARGBPixel * pixels = malloc(width * height * psize);
NSMutableData* data = [[NSMutableData alloc] init];
[self ManipulateImagePixelDataWithCGImageRef:cgImage imageData:pixels];
for (int h = 0; h < height; h++) {
for (int w = 0; w < width; w++) {
int pIndex = (w + (h * (u_int32_t)width));
ARGBPixel pixel = pixels[pIndex];
if ((0.3*pixel.red + 0.59*pixel.green + 0.11*pixel.blue) <= 127) {
//打印黑
u_int8_t ch = 0x01;
[data appendBytes:&ch length:1];
}
else{
//打印白
u_int8_t ch = 0x00;
[data appendBytes:&ch length:1];
}
}
}
return data;
}
// 獲取像素信息
-(void)ManipulateImagePixelDataWithCGImageRef:(CGImageRef)inImage imageData:(void*)oimageData
{
// Create the bitmap context
CGContextRef cgctx = [self CreateARGBBitmapContextWithCGImageRef:inImage];
if (cgctx == NULL)
{
// error creating context
return;
}
// Get image width, height. We'll use the entire image.
size_t w = CGImageGetWidth(inImage);
size_t h = CGImageGetHeight(inImage);
CGRect rect = {{0,0},{w,h}};
// Draw the image to the bitmap context. Once we draw, the memory
// allocated for the context for rendering will then contain the
// raw image data in the specified color space.
CGContextDrawImage(cgctx, rect, inImage);
// Now we can get a pointer to the image data associated with the bitmap
// context.
void *data = CGBitmapContextGetData(cgctx);
if (data != NULL)
{
CGContextRelease(cgctx);
memcpy(oimageData, data, w * h * sizeof(u_int8_t) * 4);
free(data);
return;
}
// When finished, release the context
CGContextRelease(cgctx);
// Free image data memory for the context
if (data)
{
free(data);
}
return;
}
拼接圖片數(shù)據(jù),準(zhǔn)備發(fā)給打印機(jī)
//打印圖片
- (void)printWithImage:(UIImage *)image width:(float)width height:(float)height {
UIImage * printImage = [self scaleImageWithImage:image width:width height:height];
NSData *data = [self imageToThermalData:printImage];
[self.printData appendData:data];
}
提高圖片打印速度
由于打印圖片是根據(jù)像素點來逐行打印,因此數(shù)據(jù)量會遠(yuǎn)高于普通文字,這就造成了打印圖片的速度回比文字慢,尤其是藍(lán)牙打印機(jī)。解決方法可以從兩個方面入手,1、增加每次發(fā)送的數(shù)據(jù)量(主要針對藍(lán)牙打印機(jī));2、減少圖片的數(shù)據(jù)量。
增加每次發(fā)送的數(shù)據(jù)量(主要針對藍(lán)牙打印機(jī));
關(guān)于這一點,在下一篇講到藍(lán)牙時也會說到,由于藍(lán)牙硬件限制,每次給打印機(jī)發(fā)送的數(shù)據(jù)量是有限制的,因此要將打印數(shù)據(jù)拆分,循環(huán)發(fā)送,代碼如下:
- (void)printLongData:(NSData *)printContent{
NSUInteger cellMin;
NSUInteger cellLen;
//數(shù)據(jù)長度
NSUInteger strLength = [printContent length];
if (strLength < 1) {
return;
}
//MAX_CHARACTERISTIC_VALUE_SIZE = 120
NSUInteger cellCount = (strLength % MAX_CHARACTERISTIC_VALUE_SIZE) ? (strLength/MAX_CHARACTERISTIC_VALUE_SIZE + 1):(strLength/MAX_CHARACTERISTIC_VALUE_SIZE);
for (int i = 0; i < cellCount; i++) {
cellMin = i*MAX_CHARACTERISTIC_VALUE_SIZE;
if (cellMin + MAX_CHARACTERISTIC_VALUE_SIZE > strLength) {
cellLen = strLength-cellMin;
}
else {
cellLen = MAX_CHARACTERISTIC_VALUE_SIZE;
}
NSRange rang = NSMakeRange(cellMin, cellLen);
// 截取打印數(shù)據(jù)
NSData *subData = [printContent subdataWithRange:rang];
//循環(huán)寫入數(shù)據(jù)
[self.peripheral writeValue:subData forCharacteristic:self.characteristicInfo type:CBCharacteristicWriteWithResponse];
}
}
這里的MAX_CHARACTERISTIC_VALUE_SIZE
是個宏定義,表示每次發(fā)送的數(shù)據(jù)長度,經(jīng)筆者測試,當(dāng)MAX_CHARACTERISTIC_VALUE_SIZE = 20
時,打印文字是正常速度。但打印圖片的速度非常慢,應(yīng)該在硬件允許的范圍內(nèi),每次發(fā)盡量多的數(shù)據(jù)。不同品牌型號的打印機(jī),這個參數(shù)是不同的,筆者的藍(lán)牙打印機(jī)該值最多到140。超出后會出現(xiàn)無法打印問題。最后筆者將該值定為MAX_CHARACTERISTIC_VALUE_SIZE = 120
,測試了公司幾臺打印機(jī)都沒有問題。
另外iOS9以后增加了方法maximumWriteValueLengthForType:
可以獲取寫入特診的最大寫入數(shù)據(jù)量,但經(jīng)筆者測試,對于部分打印機(jī)(比如我們公司的)是不準(zhǔn)確的,因此,不要太依賴此方法,最好還是自己取一個合適的值。
減少圖片的數(shù)據(jù)量
要減少圖片的數(shù)據(jù)量,我們可以降低分辨率。通過研究指令集筆者發(fā)現(xiàn),光柵位圖的倍寬,橫向分辨率降低了一倍。倍高,縱向分辨率降低了一倍。因此,筆者嘗試選擇倍寬、倍高模式,即m=3;此時發(fā)現(xiàn)打印出的圖片尺寸比圖片要大一倍。這樣我們只要將圖片的寬、高分別除以2。
比如我們要打印寬、高為250的圖片。m = 3 時,打印命令改為:
Byte cmd[] = {0x1d,0x76,0x30,3,xL,xH,yL,yH};
調(diào)用時:
[self.printManager printWithImage:[UIImage imageNamed:@"1513654780"] width:250/2 height:250/2];
經(jīng)筆者測試,倍寬、倍高模式打印機(jī)圖片的速度,和打印文字速度相差無幾。但圖片的清晰度會有所下降。究竟使用哪種,可自行權(quán)衡。
使用舉例
示例代碼
這里只是簡單的講解舉例,代碼并沒有很好的封裝,我們可以根據(jù)自己的需求,封裝一個適合自己的模板類。
self.printManager.printData.length = 0;
// [self.printManager resetPrinter];
// [self.printManager printLineSpace:50];
[self.printManager printCharSize:MNPrintFont_2];
[self.printManager setAlignment:MNAlignmentTypeCenter];
[self.printManager printCharRightSpace:1];
[self.printManager printWithContent:@"這是標(biāo)題"];
[self.printManager printAndNewline];
// [self.printManager printAndGoNLine:1];
[self.printManager printAndGoNPointLine:60];
[self.printManager setAlignment:MNAlignmentTypeLeft];
[self.printManager printCharSize:MNPrintFont_1];
[self.printManager printWithContent:@"商品名稱"];
[self.printManager printWithContent:@"\t"];
[self.printManager printWithContent:@"\t"];
[self.printManager printWithContent:@"數(shù)量"];
[self.printManager printWithContent:@"\t"];
[self.printManager printWithContent:@"價格"];
[self.printManager printAndNewline];
// [self.printManager printAndGoNLine:1];
[self.printManager printAndGoNPointLine:34];
[self.printManager printWithContent:@"商品1"];
[self.printManager printWithContent:@"\t\t"];
[self.printManager printWithContent:@"2"];
[self.printManager printWithContent:@"\t"];
[self.printManager printWithContent:@"1999"];
[self.printManager printAndNewline];
// [self.printManager printAndGoNLine:1];
[self.printManager printAndGoNPointLine:25];
[self.printManager printWithContent:@"商品2"];
[self.printManager printWithContent:@"\t\t"];
[self.printManager printWithContent:@"200"];
[self.printManager printWithContent:@"\t"];
[self.printManager printWithContent:@"19"];
[self.printManager printAndNewline];
// [self.printManager printAndGoNLine:1];
[self.printManager printAndGoNPointLine:25];
[self.printManager printWithContent:@"商品3"];
[self.printManager printWithContent:@"\t\t"];
[self.printManager printWithContent:@"200"];
[self.printManager printWithContent:@"\t"];
[self.printManager printWithContent:@"19"];
[self.printManager printAndNewline];
// [self.printManager printAndGoNLine:2];
[self.printManager printAndGoNPointLine:25];
[self.printManager setAlignment:MNAlignmentTypeCenter];
[self.printManager printWithContent:@"-----------------------------"];
[self.printManager printAndNewline];
// [self.printManager printAndGoNLine:2];
[self.printManager printAndGoNPointLine:25];
[self.printManager setAlignment:MNAlignmentTypeRight];
[self.printManager printWithContent:@"總計:11598元"];
[self.printManager printAndNewline];
[self.printManager printAndGoNPointLine:100];
[self.printManager setAlignment:MNAlignmentTypeCenter];
[self.printManager printWithImage:[UIImage imageNamed:@"1513654780"] width:200 height:200];
[self.printManager printAndNewline];
[self.printManager printAndGoNPointLine:150];
效果圖
總結(jié)
篇幅所限,這一篇先介紹通過ESC/POS打印命令集,拼接打印指令,排版打印格式。接下來的文章會介紹如何通過藍(lán)牙或Socket將我們編輯的打印數(shù)據(jù)發(fā)送給打印機(jī)。