Kotlin Multiplatform ESC/POS Thermal Printing Library
A single unified API for thermal printer discovery, connection, ESC/POS receipt building, and status monitoring β across Android, iOS, JVM/Desktop, and JS.
- β Multiplatform β Android, iOS, JVM/Desktop, JS
- β Multiple Transports β Bluetooth Classic, BLE, USB, Network TCP, Serial, Virtual
- β ESC/POS Receipt DSL β text styling, alignment, tables, dividers, images, barcodes, QR codes
- β Flow-based Discovery β reactive printer scanning via Kotlin coroutines
- β Status Monitoring β real-time paper out, cover open, error detection
- β Chunked Sending β prevents printer buffer overflow
- β Concurrency Protection β built-in mutex for safe multi-thread printing
- β Preview Rendering β render receipts virtually for UI preview
- β MIT Licensed β free for commercial and personal use
| Platform | Bluetooth Classic | BLE | USB | Network TCP | Status Query |
|---|---|---|---|---|---|
| Android | β | β | β | β | β |
| iOS | β | β | β | β | β |
| JVM/Desktop | β OS serial/queue | β BlueZ helper | β raw USB/serial | β | |
| Web (JS) | β Not supported | β (Chrome WebBLE) | β (WebUSB) | β Not supported |
Support depends on printer firmware, OS APIs, browser capabilities, and hardware transport. See Transport Support for details.
// settings.gradle.kts
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven {
url = uri("https://raw.githubusercontent.com/ringga-dev/kmp-printer/maven-repo")
}
}
}// build.gradle.kts
kotlin {
sourceSets {
commonMain.dependencies {
// Core Engine
implementation("io.github.ringga-dev:kmp_printer:2.3.6")
// Optional: Compose Multiplatform UI Components (Adaptive & Zero-Bloat)
implementation("io.github.ringga-dev:kmp_printer-ui:2.3.6")
}
}
}Maven users:
<!-- Core Engine --> <dependency> <groupId>io.github.ringga-dev</groupId> <artifactId>kmp_printer</artifactId> <version>2.3.6</version> </dependency> <!-- UI Components --> <dependency> <groupId>io.github.ringga-dev</groupId> <artifactId>kmp_printer-ui</artifactId> <version>2.3.6</version> </dependency>Available on Maven Central β no extra repository needed.
import ngga.ring.printer.KmpPrinter
import ngga.ring.printer.model.PrinterConfig
val printer = KmpPrinter()
val config = PrinterConfig(
name = "Receipt Printer",
connectionType = "NETWORK",
address = "192.168.1.50",
port = 9100,
characterPerLine = 32,
paperWidth = 58
)
printer.print(config) {
alignCenter()
bold(true)
line("β COFFEE SHOP")
bold(false)
divider()
tableRow(listOf("Americano", "2", "6.00"), listOf(2, 1, 1))
tableRow(listOf("Latte", "1", "4.50"), listOf(2, 1, 1))
divider()
line("Total: $10.50", align = "RIGHT")
feed(1)
qrCodeNative("https://example.com", center = true)
feed(3)
cut()
}.collect { status -> println(status) }printer.print(config) {
setCharset(PrinterCharset.USA_EUROPE)
alignCenter()
image(myBitmap, center = true)
feed(2)
bold(true)
fontSize(2)
line("HEADER")
fontSize(1)
bold(false)
divider('=')
table(listOf("Item", "Qty", "Price"), listOf(2, 1, 1))
tableRow(listOf("Product 1", "3", "9.99"), listOf(2, 1, 1))
barcode("123456789012", type = BarcodeType.EAN13)
feed(2)
cut()
}Mendukung model PrinterDevice yang strongly-typed per tipe koneksi (Bluetooth, Ble, Usb, Network, Serial, Virtual):
import ngga.ring.printer.model.PrinterConnection
import ngga.ring.printer.model.PrinterDevices
import ngga.ring.printer.model.PrinterProfile
// 1. Scan / Discovery Otomatis
printer.discoverDevices(PrinterConnection.BLUETOOTH).collect { devices ->
devices.forEach { device ->
println("Device: ${device.name} - ${device.address}")
}
}
// 2. Inisialisasi Manual via Factory
val cashierUsb = PrinterDevices.usb("Epson TM-T82", "USB_RAW:04B8:0202")
val kitchenNet = PrinterDevices.network("Kitchen", host = "192.168.1.200", port = 9100)
val portableBt = PrinterDevices.bluetooth("RPP02N", "00:11:22:33:44:55")
// 3. Cetak langsung dengan Device & Profile
printer.print(kitchenNet, profile = PrinterProfile.MM80) {
line("ORDER #102")
cut()
}Untuk aplikasi POS yang mencetak ke banyak printer secara bersamaan tanpa saling memutus koneksi:
val cashierSession = printer.openSession(PrinterDevices.usb("Kasir", "USB_RAW:04B8:0202"))
val kitchenSession = printer.openSession(PrinterDevices.network("Dapur", "192.168.1.200"))
// Cetak paralel ke kasir dan dapur
cashierSession.print { line("Struk Pembayaran") }
kitchenSession.print { line("Pesanan Dapur") }Dukungan pembuatan stiker label thermal (TSC/TSPL protocol):
printer.printLabel(cashierUsb, widthMm = 40.0, heightMm = 30.0) {
direction(0)
text(x = 10, y = 10, content = "PRODUK UTAMA", font = "3")
barcode(x = 10, y = 45, data = "89912345678", type = "128", height = 40)
qrcode(x = 200, y = 45, data = "https://example.com")
print(sets = 1, copies = 1)
}Penataan teks bahasa Arab dan RTL otomatis untuk printer thermal:
printer.print(portableBt) {
lineArabic("Ψ΄ΩΨ±Ψ§ ΩΨ²ΩΨ§Ψ±ΨͺΩΩ
- Ω
Ψ±ΨΨ¨Ψ§Ω Ψ¨ΩΩ
")
cut()
}printer.discovery("NETWORK") { log ->
println("Scanning: $log")
}.collect { devices ->
devices.forEach { device ->
println("Found: ${device.name} at ${device.address}:${device.port}")
}
}printer.monitorStatus(config, intervalMs = 2000).collect { status ->
when {
status.isPaperOut -> sendAlert("π’ Printer kehabisan kertas!")
status.isCoverOpen -> sendAlert("π Cover printer terbuka!")
!status.isOnline -> sendAlert("β οΈ Printer offline!")
}
}val networkConfig = PrinterConfig(
name = "Kitchen Printer",
connection = PrinterConnection.NETWORK,
profile = PrinterProfile.MM58,
address = "192.168.1.50"
)printer.print(config) {
printQuality(PrintQuality.Dark)
line("High density thermal output")
cut()
}PrinterConfig / PrinterProfile mendukung pengaturan margin, centering, dan
jarak antar baris:
val config = PrinterConfig(
name = "Receipt Printer",
connectionType = "NETWORK",
address = "192.168.1.50",
characterPerLine = 32,
paperWidth = 58,
paperWidthDots = 384, // direkomendasikan eksplisit β fallback heuristic bisa meleset
leftMargin = 20, // margin kiri dalam dots
rightMargin = 0, // margin kanan dalam dots (baru sejak 2.3.6)
autoCenter = false, // true = margin kiri dicerminkan ke kanan (simetris)
lineSpacing = 40 // jarak antar baris dalam dots; 0 = default printer
)- Area printable =
paperWidthDots - leftMargin - rightMargin, jadi isi tidak lagi meluap/terpotong di sisi kanan. centerText(text)dancenterWrapped(text, maxLine)memakai perataan hardware (ESC a 1) β semua baris hasil word-wrap tetap center, termasuk yang lebih dari 2 baris. Untuk perilaku lama (software padding) gunakancenterTextSingleLine(text).lineSpacing > 0diterapkan otomatis saatinitialize()via commandESC 3 n.
| Document | Description |
|---|---|
| Transport Support | Platform-by-platform transport compatibility |
| Printer OS Setup | OS-level driver & permission setup (Linux udev, Windows, macOS) |
| API & Migration | Typed configs, profiles, migration from v1 |
| Releasing | How to publish to GitHub Maven & Maven Central |
| GitHub Pages | Online documentation site |
# Build all platforms
./gradlew build
# Run JVM tests
./gradlew :printer:jvmTest
# Compile specific targets
./gradlew :printer:compileKotlinMetadata
./gradlew :printer:compileKotlinJs
./gradlew :printer:compileDebugKotlinAndroid
# Update version in docs after changing LIB_VERSION in gradle.properties
./gradlew syncDocumentationVersionprinter/
βββ src/
β βββ commonMain/ # Shared platform-independent logic
β β βββ model/ # Printer models, configs, enums
β β βββ manager/ # Connection management, discovery
β β βββ usecase/ # Print, discover, diagnose
β β βββ util/ # ESC/POS commands, rendering, preview
β βββ androidMain/ # Android BLE, USB, Network, Bluetooth
β βββ iosMain/ # iOS BLE, Network
β βββ jvmMain/ # Desktop USB, Serial, Network, BlueZ
β βββ jsMain/ # Web Bluetooth, USB
βββ androidApp/ # Android sample app
βββ desktopApp/ # JVM Desktop sample app
βββ iosApp/ # iOS sample app
We welcome contributions! Please see CONTRIBUTING.md for guidelines on:
- Code style and PR process
- Platform-specific testing
- How to add new transport support
Jika library ini membantu proyek Anda, dukung pengembangan dengan mentraktir kopi:
π https://trakteer.id/ringga_dev/gift
MIT License β see LICENSE.
Copyright (c) 2026 Ringga. Free to use, modify, and distribute in commercial and non-commercial applications.
Built with β€οΈ and Kotlin Multiplatform β β Star on GitHub