最近公司需要用热敏打印机POS88V实现打印小票。本文采用Kotlin 语言来实现
ESC @
[名称] 初始化打印机
[格式] ASCII ESC @
Hex 1B 40
Decimal 27 64
[描述] 清除打印缓冲区数据,打印模式被设为上电时的默认值模式。
[注释] · DIP开关的设置不进行再次检测。
· 接收缓冲区内容保留。
· 宏定义保留。
· flash位图数据不擦除。
· flash用户数据不擦除。
· 维护计数器值不擦除。
· 由GS ( E 指定的设置值不擦除。
它支持3指令格式,分别是ASCII ,Hex ,Decimal 。 我喜欢用hex ,所以建个HexCommands 类维护各种指令
/**
* 采用16进制定义指令
*/
enum class HexCommands(vararg parameters: Int) {
/**
* 初始化打印机
*/
INIT_PRINTER(0x1B, 0x40),
/**
* 选择标准模式
*/
STANDARD_MODE(0x1B, 0x53),
/**
* 进纸并且半切纸
*/
FEED_AND_CUT(0x1D, 0x56, 65),
/**
* 设置绝对打印位置
*/
PRINT_ABSOLUTE_LOCATION(0x1B, 0x24),
/**
* 选择/取消加粗模式
*/
TEXT_BOLD(0x1B, 0x45),
/**
* 选择字符对齐模式
*/
TEXT_ALIGNMENT(0x1B, 0x61);
val hexValues = parameters
}
如何建立连接呢?我这里采用网络方式,用socket 很方便实现
class PosPrinter {
var encoding: String = "GBK"
private lateinit var client: Socket
private lateinit var writer: PrintWriter
@Throws(IOException::class, UnsupportedEncodingException::class)
fun connect(ip: String, port: Int = 9100, timeout: Long = 1, timeUnit: TimeUnit = TimeUnit.SECONDS) {
client = Socket()
client.connect(InetSocketAddress(ip, port), TimeUnit.MILLISECONDS.convert(timeout, timeUnit).toInt())
writer = PrintWriter(BufferedWriter(OutputStreamWriter(client.getOutputStream(), encoding)))
}
@Throws(IOException::class)
fun disconnect() {
writer.close()
client.close()
}
private fun write(hexCommands: HexCommands, vararg moreHexValues: Int) {
hexCommands.hexValues.forEach {
writer.write(it)
}
moreHexValues.forEach {
writer.write(it)
}
writer.flush()
}
fun initPosPrinter(): PosPrinter {
write(HexCommands.INIT_PRINTER)
return this
}
fun selectStandardMode(): PosPrinter {
write(HexCommands.STANDARD_MODE)
return this
}
fun printText(text: String): PosPrinter {
writer.write(text)
writer.flush()
return this
}
fun printLine(line: Int = 1): PosPrinter {
for (i in 0 until line) {
writer.write("\n")
writer.flush()
}
return this
}
fun printTextLine(text: String, line: Int = 1): PosPrinter {
printLine(line)
printText(text)
return this
}
fun innerPrint(function: PosPrinter.() -> PosPrinter): PosPrinter {
function(this)
return this
}
/**
* 打印空白(size个汉字的位置)
*/
fun printWordSpace(size: Int): PosPrinter {
for (i in 0 until size) {
writer.write(" ")
}
writer.flush()
return this
}
/**
* 选择字符对齐模式
*/
fun setTextBold(isBold: Boolean = false): PosPrinter {
write(HexCommands.TEXT_BOLD, if (isBold) 1 else 0)
return this
}
/**
* 选择字符对齐模式
*/
fun setTextAlignment(@TextAlignment textAlignment: Long): PosPrinter {
write(HexCommands.TEXT_ALIGNMENT, textAlignment.toInt())
return this
}
/**
* 设置绝对打印位置
*/
fun setTextAbsoluteLocation(@IntRange(from = 0, to = 255) offsetX: Int,
@IntRange(from = 0, to = 255) offsetY: Int = 1): PosPrinter {
write(HexCommands.PRINT_ABSOLUTE_LOCATION, offsetX, offsetY)
return this
}
fun feedAndCut(length: Int = 100): PosPrinter {
write(HexCommands.FEED_AND_CUT, length)
return this
}
companion object {
/**
* Align to the start of the paragraph, e.g. ALIGN_LEFT.
*
* Use with [.setTextAlignment]
*/
const val TEXT_ALIGNMENT_TEXT_START = 0L
/**
* Center the paragraph, e.g. ALIGN_CENTER.
*
* Use with [.setTextAlignment]
*/
const val TEXT_ALIGNMENT_CENTER = 1L
/**
* Align to the end of the paragraph, e.g. ALIGN_RIGHT.
*
* Use with [.setTextAlignment]
*/
const val TEXT_ALIGNMENT_TEXT_END = 2L
@IntDef(TEXT_ALIGNMENT_TEXT_START, TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_TEXT_END)
@Retention(AnnotationRetention.SOURCE)
annotation class TextAlignment
}
}
//订单菜品集合
private var goodsBean: MutableList? = null
private var posPrinter: PosPrinter? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
doAsync {
//初始化订单数据
initData()
posPrinter = PosPrinter()
posPrinter!!.connect("192.168.2.150")
posPrinter!!.initPosPrinter()
.selectStandardMode()
.setTextBold(true)
.setTextAlignment(PosPrinter.TEXT_ALIGNMENT_CENTER)
.printText("*** 天龙店铺 ***")
.printLine()
.setTextBold(false)
.setTextAlignment(PosPrinter.TEXT_ALIGNMENT_TEXT_START)
.printTextLine("订单编号:1005199")
.printTextLine("交易机台:test")
.printTextLine("交易时间:2016/2/19 12:34:53")
.printTextLine("支付方式:微信支付")
.printLine(2)
.printText("商品")
.setTextAbsoluteLocation(20)
.printText("单价")
.printWordSpace(3)
.printText("数量")
.printWordSpace(3)
.printText("小计")
.printTextLine("----------------------------------------------")
.innerPrint({
for (foods in goodsBean!!) {
printTextLine(foods.name)
setTextAbsoluteLocation(20)
printText(foods.price)
printWordSpace(3)
printText(foods.number)
printWordSpace(3)
printText(foods.sum)
}
this
})
.printTextLine("----------------------------------------------")
.printLine()
.printText("总计(人民币):")
.printText("80.00")
.printLine()
.feedAndCut(50)
}
}
private fun initData() {
goodsBean = ArrayList()
(0..1).map { GoodsBean("测试商品" + it, "10.00", "2", "20.00") }
.forEach { goodsBean!!.add(it) }
}
data class GoodsBean(val name: String, val price: String, val number: String, val sum: String)