├── .idea ├── .name ├── .gitignore ├── codeStyles │ ├── codeStyleConfig.xml │ └── Project.xml ├── compiler.xml ├── vcs.xml ├── misc.xml ├── gradle.xml └── jarRepositories.xml ├── app ├── .gitignore ├── src │ ├── main │ │ ├── res │ │ │ ├── drawable │ │ │ │ ├── logo.png │ │ │ │ ├── logo2.png │ │ │ │ └── ic_launcher_background.xml │ │ │ ├── mipmap-hdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ ├── ic_launcher.xml │ │ │ │ └── ic_launcher_round.xml │ │ │ ├── values │ │ │ │ ├── colors.xml │ │ │ │ ├── strings.xml │ │ │ │ └── themes.xml │ │ │ ├── values-night │ │ │ │ └── themes.xml │ │ │ ├── drawable-v24 │ │ │ │ └── ic_launcher_foreground.xml │ │ │ └── layout │ │ │ │ └── activity_main.xml │ │ ├── AndroidManifest.xml │ │ └── java │ │ │ └── com │ │ │ └── khairo │ │ │ └── printer │ │ │ ├── utils │ │ │ └── Extensions.kt │ │ │ └── ui │ │ │ └── MainActivity.kt │ ├── test │ │ └── java │ │ │ └── com │ │ │ └── khairo │ │ │ └── printer │ │ │ └── ExampleUnitTest.kt │ └── androidTest │ │ └── java │ │ └── com │ │ └── khairo │ │ └── printer │ │ └── ExampleInstrumentedTest.kt ├── proguard-rules.pro └── build.gradle ├── escposprinter ├── consumer-rules.pro ├── .gitignore ├── src │ ├── main │ │ ├── res │ │ │ └── values │ │ │ │ └── strings.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── khairo │ │ │ │ ├── async │ │ │ │ ├── AsyncTcpEscPosPrint.kt │ │ │ │ ├── AsyncUsbEscPosPrint.kt │ │ │ │ ├── AsyncEscPosPrinter.kt │ │ │ │ ├── AsyncBluetoothEscPosPrint.kt │ │ │ │ └── AsyncEscPosPrint.kt │ │ │ │ ├── escposprinter │ │ │ │ ├── exceptions │ │ │ │ │ ├── EscPosParserException.kt │ │ │ │ │ ├── EscPosBarcodeException.kt │ │ │ │ │ ├── EscPosEncodingException.kt │ │ │ │ │ └── EscPosConnectionException.kt │ │ │ │ ├── EscPosCharsetEncoding.kt │ │ │ │ ├── textparser │ │ │ │ │ ├── IPrinterTextParserElement.kt │ │ │ │ │ ├── CoroutinesIPrinterTextParserElement.kt │ │ │ │ │ ├── PrinterTextParserQRCode.kt │ │ │ │ │ ├── CoroutinesPrinterTextParserQRCode.kt │ │ │ │ │ ├── PrinterTextParserTag.kt │ │ │ │ │ ├── PrinterTextParserLine.kt │ │ │ │ │ ├── PrinterTextParserString.kt │ │ │ │ │ ├── CoroutinesPrinterTextParserLine.kt │ │ │ │ │ ├── CoroutinesPrinterTextParserString.kt │ │ │ │ │ ├── PrinterTextParserBarcode.kt │ │ │ │ │ ├── CoroutinesPrinterTextParserBarcode.kt │ │ │ │ │ ├── PrinterTextParserImg.java │ │ │ │ │ ├── CoroutinesPrinterTextParserImg.kt │ │ │ │ │ ├── PrinterTextParser.kt │ │ │ │ │ ├── CoroutinesPrinterTextParser.kt │ │ │ │ │ └── PrinterTextParserColumn.kt │ │ │ │ ├── barcode │ │ │ │ │ ├── BarcodeEAN8.kt │ │ │ │ │ ├── BarcodeUPCA.kt │ │ │ │ │ ├── BarcodeEAN13.kt │ │ │ │ │ ├── Barcode128.kt │ │ │ │ │ ├── BarcodeUPCE.kt │ │ │ │ │ ├── Barcode.kt │ │ │ │ │ └── BarcodeNumber.kt │ │ │ │ ├── connection │ │ │ │ │ ├── usb │ │ │ │ │ │ ├── UsbConnections.kt │ │ │ │ │ │ ├── UsbDeviceHelper.kt │ │ │ │ │ │ ├── UsbPrintersConnections.kt │ │ │ │ │ │ ├── UsbConnection.kt │ │ │ │ │ │ └── UsbOutputStream.kt │ │ │ │ │ ├── bluetooth │ │ │ │ │ │ ├── BluetoothConnections.kt │ │ │ │ │ │ ├── BluetoothPrintersConnections.kt │ │ │ │ │ │ └── BluetoothConnection.kt │ │ │ │ │ ├── DeviceConnection.kt │ │ │ │ │ └── tcp │ │ │ │ │ │ ├── TcpConnection.kt │ │ │ │ │ │ └── TcpDeviceConnection.kt │ │ │ │ ├── EscPosPrinterSize.kt │ │ │ │ ├── CoroutinesEscPosPrinter.kt │ │ │ │ └── EscPosPrinter.java │ │ │ │ ├── common │ │ │ │ └── App.kt │ │ │ │ ├── coroutines │ │ │ │ ├── CoroutinesEscPosPrinter.kt │ │ │ │ └── CoroutinesEscPosPrint.kt │ │ │ │ └── exeption │ │ │ │ └── PrintingException.kt │ │ └── AndroidManifest.xml │ ├── test │ │ └── java │ │ │ └── com │ │ │ └── dantsu │ │ │ └── escposprinter │ │ │ └── ExampleUnitTest.java │ └── androidTest │ │ └── java │ │ └── com │ │ └── dantsu │ │ └── escposprinter │ │ └── ExampleInstrumentedTest.java ├── proguard-rules.pro ├── LICENSE └── build.gradle ├── settings.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .gitignore ├── gradle.properties ├── gradlew.bat └── gradlew /.idea/.name: -------------------------------------------------------------------------------- 1 | Printer -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /escposprinter/consumer-rules.pro: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /escposprinter/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':escposprinter' 2 | include ':app' 3 | rootProject.name = "Printer" -------------------------------------------------------------------------------- /app/src/main/res/drawable/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KhairoHumsi/Printer-ktx/HEAD/app/src/main/res/drawable/logo.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KhairoHumsi/Printer-ktx/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/src/main/res/drawable/logo2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KhairoHumsi/Printer-ktx/HEAD/app/src/main/res/drawable/logo2.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KhairoHumsi/Printer-ktx/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KhairoHumsi/Printer-ktx/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KhairoHumsi/Printer-ktx/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KhairoHumsi/Printer-ktx/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KhairoHumsi/Printer-ktx/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /escposprinter/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | ThermalPrinter ESC POS Bluetooth 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KhairoHumsi/Printer-ktx/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KhairoHumsi/Printer-ktx/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KhairoHumsi/Printer-ktx/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KhairoHumsi/Printer-ktx/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KhairoHumsi/Printer-ktx/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /escposprinter/src/main/java/com/khairo/async/AsyncTcpEscPosPrint.kt: -------------------------------------------------------------------------------- 1 | package com.khairo.async 2 | 3 | import android.content.Context 4 | 5 | class AsyncTcpEscPosPrint(context: Context) : AsyncEscPosPrint(context) 6 | -------------------------------------------------------------------------------- /escposprinter/src/main/java/com/khairo/async/AsyncUsbEscPosPrint.kt: -------------------------------------------------------------------------------- 1 | package com.khairo.async 2 | 3 | import android.content.Context 4 | 5 | class AsyncUsbEscPosPrint(context: Context?) : AsyncEscPosPrint(context) 6 | -------------------------------------------------------------------------------- /escposprinter/src/main/java/com/khairo/escposprinter/exceptions/EscPosParserException.kt: -------------------------------------------------------------------------------- 1 | package com.khairo.escposprinter.exceptions 2 | 3 | class EscPosParserException(errorMessage: String?) : Exception(errorMessage) 4 | -------------------------------------------------------------------------------- /escposprinter/src/main/java/com/khairo/escposprinter/exceptions/EscPosBarcodeException.kt: -------------------------------------------------------------------------------- 1 | package com.khairo.escposprinter.exceptions 2 | 3 | class EscPosBarcodeException(errorMessage: String?) : Exception(errorMessage) 4 | -------------------------------------------------------------------------------- /escposprinter/src/main/java/com/khairo/escposprinter/exceptions/EscPosEncodingException.kt: -------------------------------------------------------------------------------- 1 | package com.khairo.escposprinter.exceptions 2 | 3 | class EscPosEncodingException(errorMessage: String?) : Exception(errorMessage) 4 | -------------------------------------------------------------------------------- /escposprinter/src/main/java/com/khairo/escposprinter/exceptions/EscPosConnectionException.kt: -------------------------------------------------------------------------------- 1 | package com.khairo.escposprinter.exceptions 2 | 3 | class EscPosConnectionException(errorMessage: String?) : Exception(errorMessage) 4 | 5 | 6 | -------------------------------------------------------------------------------- /escposprinter/src/main/java/com/khairo/escposprinter/EscPosCharsetEncoding.kt: -------------------------------------------------------------------------------- 1 | package com.khairo.escposprinter 2 | 3 | class EscPosCharsetEncoding(val name: String, escPosCharsetId: Int) { 4 | val command: ByteArray = byteArrayOf(0x1B, 0x74, escPosCharsetId.toByte()) 5 | 6 | } 7 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Dec 17 14:55:25 EET 2020 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /escposprinter/src/main/java/com/khairo/common/App.kt: -------------------------------------------------------------------------------- 1 | package com.khairo.common 2 | 3 | import android.app.Application 4 | 5 | class App: Application() { 6 | override fun onCreate() { 7 | super.onCreate() 8 | 9 | instance = this 10 | } 11 | 12 | companion object { 13 | lateinit var instance: App 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /escposprinter/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | -------------------------------------------------------------------------------- /app/src/test/java/com/khairo/printer/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.khairo.printer 2 | 3 | import org.junit.Assert.assertEquals 4 | import org.junit.Test 5 | 6 | /** 7 | * Example local unit test, which will execute on the development machine (host). 8 | * 9 | * See [testing documentation](http://d.android.com/tools/testing). 10 | */ 11 | class ExampleUnitTest { 12 | @Test 13 | fun addition_isCorrect() { 14 | assertEquals(4, 2 + 2) 15 | } 16 | } -------------------------------------------------------------------------------- /escposprinter/src/main/java/com/khairo/escposprinter/textparser/IPrinterTextParserElement.kt: -------------------------------------------------------------------------------- 1 | package com.khairo.escposprinter.textparser 2 | 3 | import com.khairo.escposprinter.EscPosPrinterCommands 4 | import com.khairo.escposprinter.exceptions.EscPosEncodingException 5 | 6 | interface IPrinterTextParserElement { 7 | @Throws(EscPosEncodingException::class) 8 | fun length(): Int 9 | 10 | @Throws(EscPosEncodingException::class) 11 | fun print(printerSocket: EscPosPrinterCommands?): IPrinterTextParserElement? 12 | } 13 | -------------------------------------------------------------------------------- /escposprinter/src/test/java/com/dantsu/escposprinter/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.khairo.escposprinter; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.assertEquals; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Printer 3 | Click to print ! 4 | Print by bluetooth ! 5 | Print by USB ! 6 | IP : 7 | 192.168.1.160 8 | Port : 9 | 9100 10 | Print by TCP ! 11 | -------------------------------------------------------------------------------- /escposprinter/src/main/java/com/khairo/escposprinter/textparser/CoroutinesIPrinterTextParserElement.kt: -------------------------------------------------------------------------------- 1 | package com.khairo.escposprinter.textparser 2 | 3 | import com.khairo.escposprinter.CoroutinesEscPosPrinterCommands 4 | import com.khairo.escposprinter.exceptions.EscPosEncodingException 5 | 6 | interface CoroutinesIPrinterTextParserElement { 7 | @Throws(EscPosEncodingException::class) 8 | fun length(): Int 9 | 10 | @Throws(EscPosEncodingException::class) 11 | suspend fun print(printerSocket: CoroutinesEscPosPrinterCommands?): CoroutinesIPrinterTextParserElement? 12 | } 13 | -------------------------------------------------------------------------------- /escposprinter/src/main/java/com/khairo/escposprinter/barcode/BarcodeEAN8.kt: -------------------------------------------------------------------------------- 1 | package com.khairo.escposprinter.barcode 2 | 3 | import com.khairo.escposprinter.EscPosPrinterCommands 4 | import com.khairo.escposprinter.EscPosPrinterSize 5 | 6 | class BarcodeEAN8( 7 | printerSize: EscPosPrinterSize, 8 | code: String, 9 | widthMM: Float, 10 | heightMM: Float, 11 | textPosition: Int 12 | ) : BarcodeNumber( 13 | printerSize, 14 | EscPosPrinterCommands.BARCODE_TYPE_EAN8, 15 | code, 16 | widthMM, 17 | heightMM, 18 | textPosition 19 | ) { 20 | override fun getCodeLength(): Int = 8 21 | } -------------------------------------------------------------------------------- /escposprinter/src/main/java/com/khairo/async/AsyncEscPosPrinter.kt: -------------------------------------------------------------------------------- 1 | package com.khairo.async 2 | 3 | import com.khairo.escposprinter.EscPosPrinterSize 4 | import com.khairo.escposprinter.connection.DeviceConnection 5 | 6 | class AsyncEscPosPrinter(val printerConnection: DeviceConnection, printerDpi: Int, printerWidthMM: Float, printerNbrCharactersPerLine: Int) : EscPosPrinterSize(printerDpi, printerWidthMM, printerNbrCharactersPerLine) { 7 | var textToPrint = "" 8 | 9 | fun setTextToPrint(textToPrint: String): AsyncEscPosPrinter { 10 | this.textToPrint = textToPrint 11 | return this 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /escposprinter/src/main/java/com/khairo/escposprinter/barcode/BarcodeUPCA.kt: -------------------------------------------------------------------------------- 1 | package com.khairo.escposprinter.barcode 2 | 3 | import com.khairo.escposprinter.EscPosPrinterCommands 4 | import com.khairo.escposprinter.EscPosPrinterSize 5 | 6 | class BarcodeUPCA( 7 | printerSize: EscPosPrinterSize, 8 | code: String, 9 | widthMM: Float, 10 | heightMM: Float, 11 | textPosition: Int 12 | ) : BarcodeNumber( 13 | printerSize, 14 | EscPosPrinterCommands.BARCODE_TYPE_UPCA, 15 | code, 16 | widthMM, 17 | heightMM, 18 | textPosition 19 | ) { 20 | override fun getCodeLength(): Int { 21 | return 12 22 | } 23 | } -------------------------------------------------------------------------------- /escposprinter/src/main/java/com/khairo/escposprinter/barcode/BarcodeEAN13.kt: -------------------------------------------------------------------------------- 1 | package com.khairo.escposprinter.barcode 2 | 3 | import com.khairo.escposprinter.EscPosPrinterCommands 4 | import com.khairo.escposprinter.EscPosPrinterSize 5 | 6 | class BarcodeEAN13( 7 | printerSize: EscPosPrinterSize, 8 | code: String, 9 | widthMM: Float, 10 | heightMM: Float, 11 | textPosition: Int 12 | ) : BarcodeNumber( 13 | printerSize, 14 | EscPosPrinterCommands.BARCODE_TYPE_EAN13, 15 | code, 16 | widthMM, 17 | heightMM, 18 | textPosition 19 | ) { 20 | override fun getCodeLength(): Int { 21 | return 13 22 | } 23 | } -------------------------------------------------------------------------------- /escposprinter/src/main/java/com/khairo/coroutines/CoroutinesEscPosPrinter.kt: -------------------------------------------------------------------------------- 1 | package com.khairo.coroutines 2 | 3 | import com.khairo.escposprinter.EscPosPrinterSize 4 | import com.khairo.escposprinter.connection.tcp.TcpDeviceConnection 5 | 6 | class CoroutinesEscPosPrinter( 7 | val printerConnection: TcpDeviceConnection, 8 | printerDpi: Int, 9 | printerWidthMM: Float, 10 | printerNbrCharactersPerLine: Int 11 | ) : EscPosPrinterSize(printerDpi, printerWidthMM, printerNbrCharactersPerLine) { 12 | var textToPrint = "" 13 | 14 | fun setTextToPrint(textToPrint: String): CoroutinesEscPosPrinter { 15 | this.textToPrint = textToPrint 16 | return this 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /escposprinter/src/main/java/com/khairo/escposprinter/barcode/Barcode128.kt: -------------------------------------------------------------------------------- 1 | package com.khairo.escposprinter.barcode 2 | 3 | import com.khairo.escposprinter.EscPosPrinterCommands 4 | import com.khairo.escposprinter.EscPosPrinterCommands.* 5 | import com.khairo.escposprinter.EscPosPrinterSize 6 | 7 | class Barcode128( 8 | printerSize: EscPosPrinterSize, 9 | code: String, 10 | widthMM: Float, 11 | heightMM: Float, 12 | textPosition: Int 13 | ) : Barcode( 14 | printerSize, 15 | BARCODE_TYPE_128, 16 | code, 17 | widthMM, 18 | heightMM, 19 | textPosition 20 | ) { 21 | 22 | override fun getCodeLength(): Int { 23 | return code.length 24 | } 25 | 26 | override fun getColsCount(): Int { 27 | return (this.getCodeLength() + 5) * 11 28 | } 29 | } -------------------------------------------------------------------------------- /app/src/androidTest/java/com/khairo/printer/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.khairo.printer 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4 4 | import androidx.test.platform.app.InstrumentationRegistry 5 | import org.junit.Assert.assertEquals 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | /** 10 | * Instrumented test, which will execute on an Android device. 11 | * 12 | * See [testing documentation](http://d.android.com/tools/testing). 13 | */ 14 | @RunWith(AndroidJUnit4::class) 15 | class ExampleInstrumentedTest { 16 | @Test 17 | fun useAppContext() { 18 | // Context of the app under test. 19 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 20 | assertEquals("com.khairo.printer", appContext.packageName) 21 | } 22 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /escposprinter/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /escposprinter/src/main/java/com/khairo/async/AsyncBluetoothEscPosPrint.kt: -------------------------------------------------------------------------------- 1 | package com.khairo.async 2 | 3 | import android.content.Context 4 | import com.khairo.escposprinter.connection.bluetooth.BluetoothPrintersConnections 5 | 6 | class AsyncBluetoothEscPosPrint(context: Context?) : AsyncEscPosPrint(context) { 7 | override fun doInBackground(vararg printersData: AsyncEscPosPrinter): Int { 8 | if (printersData.isEmpty()) return FINISH_NO_PRINTER 9 | 10 | var printerData: AsyncEscPosPrinter = printersData[0] 11 | 12 | printerData = AsyncEscPosPrinter( 13 | BluetoothPrintersConnections.selectFirstPaired()!!, 14 | printerData.printerDpi, 15 | printerData.printerWidthMM, 16 | printerData.printerNbrCharactersPerLine 17 | ) 18 | printerData.textToPrint = printerData.textToPrint 19 | return super.doInBackground(*printersData) 20 | } 21 | } -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 21 | 22 | -------------------------------------------------------------------------------- /escposprinter/src/androidTest/java/com/dantsu/escposprinter/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.khairo.escposprinter; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.test.ext.junit.runners.AndroidJUnit4; 6 | import androidx.test.platform.app.InstrumentationRegistry; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | import static org.junit.Assert.assertEquals; 12 | 13 | /** 14 | * Instrumented test, which will execute on an Android usbDevice. 15 | * 16 | * @see Testing documentation 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class ExampleInstrumentedTest { 20 | @Test 21 | public void useAppContext() { 22 | // Context of the app under test. 23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 24 | 25 | assertEquals("com.khairo.thermalprinter_escpos.test", appContext.getPackageName()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /escposprinter/src/main/java/com/khairo/escposprinter/connection/usb/UsbConnections.kt: -------------------------------------------------------------------------------- 1 | package com.khairo.escposprinter.connection.usb 2 | 3 | import android.content.Context 4 | import android.hardware.usb.UsbDevice 5 | import android.hardware.usb.UsbManager 6 | 7 | open class UsbConnections(context: Context) { 8 | protected var usbManager: UsbManager = 9 | context.getSystemService(Context.USB_SERVICE) as UsbManager 10 | 11 | /** 12 | * Get a list of USB devices available. 13 | * 14 | * @return Return an array of UsbConnection instance 15 | */ 16 | open fun getList(): Array? { 17 | val devicesList: Collection = usbManager.deviceList.values 18 | val usbDevices = arrayOfNulls(devicesList.size) 19 | if (devicesList.isNotEmpty()) { 20 | var i = 0 21 | for (device in devicesList) { 22 | usbDevices[i++] = UsbConnection(usbManager, device) 23 | } 24 | } 25 | return usbDevices 26 | } 27 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /escposprinter/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Franck ALARY 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /escposprinter/src/main/java/com/khairo/escposprinter/connection/bluetooth/BluetoothConnections.kt: -------------------------------------------------------------------------------- 1 | package com.khairo.escposprinter.connection.bluetooth 2 | 3 | import android.bluetooth.BluetoothAdapter 4 | 5 | open class BluetoothConnections { 6 | private var bluetoothAdapter: BluetoothAdapter? = BluetoothAdapter.getDefaultAdapter() 7 | 8 | /** 9 | * Get a list of bluetooth devices available. 10 | * 11 | * @return Return an array of BluetoothConnection instance 12 | */ 13 | open fun getList(): Array? { 14 | if (bluetoothAdapter == null) { 15 | return null 16 | } 17 | if (!bluetoothAdapter!!.isEnabled) { 18 | return null 19 | } 20 | val bluetoothDevicesList = bluetoothAdapter!!.bondedDevices 21 | val bluetoothDevices = arrayOfNulls(bluetoothDevicesList.size) 22 | if (bluetoothDevicesList.size > 0) { 23 | var i = 0 24 | for (device in bluetoothDevicesList) { 25 | bluetoothDevices[i++] = BluetoothConnection(device) 26 | } 27 | } 28 | return bluetoothDevices 29 | } 30 | 31 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app"s APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Automatically convert third-party libraries to use AndroidX 19 | android.enableJetifier=true 20 | # Kotlin code style for this project: "official" or "obsolete": 21 | kotlin.code.style=official -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | 29 | 30 | -------------------------------------------------------------------------------- /escposprinter/src/main/java/com/khairo/escposprinter/barcode/BarcodeUPCE.kt: -------------------------------------------------------------------------------- 1 | package com.khairo.escposprinter.barcode 2 | 3 | import com.khairo.escposprinter.EscPosPrinterCommands 4 | import com.khairo.escposprinter.EscPosPrinterSize 5 | import com.khairo.escposprinter.exceptions.EscPosBarcodeException 6 | 7 | class BarcodeUPCE( 8 | printerSize: EscPosPrinterSize, 9 | code: String, 10 | widthMM: Float, 11 | heightMM: Float, 12 | textPosition: Int 13 | ) : Barcode( 14 | printerSize, 15 | EscPosPrinterCommands.BARCODE_TYPE_UPCE, 16 | code, 17 | widthMM, 18 | heightMM, 19 | textPosition 20 | ) { 21 | override fun getCodeLength(): Int = 6 22 | 23 | override fun getColsCount(): Int = this.getCodeLength() * 7 + 16 24 | 25 | @Throws(EscPosBarcodeException::class) 26 | private fun checkCode() { 27 | val codeLength = this.getCodeLength() 28 | if (code.length < codeLength) { 29 | throw EscPosBarcodeException("Code is too short for the barcode type.") 30 | } 31 | try { 32 | code = code.substring(0, codeLength) 33 | for (i in 0 until codeLength) { 34 | code.substring(i, i + 1).toInt(10) 35 | } 36 | } catch (e: NumberFormatException) { 37 | e.printStackTrace() 38 | throw EscPosBarcodeException("Invalid barcode number") 39 | } 40 | } 41 | 42 | init { 43 | checkCode() 44 | } 45 | } -------------------------------------------------------------------------------- /escposprinter/src/main/java/com/khairo/coroutines/CoroutinesEscPosPrint.kt: -------------------------------------------------------------------------------- 1 | package com.khairo.coroutines 2 | 3 | import android.content.Context 4 | import com.khairo.escposprinter.EscPosCharsetEncoding 5 | import com.khairo.escposprinter.connection.tcp.TcpDeviceConnection 6 | import com.khairo.exeption.PrintingException.FINISH_NO_PRINTER 7 | import com.khairo.exeption.onException 8 | import java.lang.ref.WeakReference 9 | 10 | class CoroutinesEscPosPrint( 11 | private val context: Context 12 | ) { 13 | private var weakContext: WeakReference = WeakReference(context) 14 | 15 | suspend fun execute(vararg printersData: CoroutinesEscPosPrinter) { 16 | if (printersData.isEmpty()) 17 | return onException(context, FINISH_NO_PRINTER) 18 | 19 | val printerData = printersData[0] 20 | 21 | var deviceConnection: TcpDeviceConnection? = printerData.printerConnection 22 | // if (deviceConnection == null) 23 | // deviceConnection = BluetoothPrintersConnections.selectFirstPaired() 24 | 25 | if (deviceConnection == null) return onException(context, FINISH_NO_PRINTER) 26 | 27 | val context = weakContext.get() ?: return 28 | val printer = com.khairo.escposprinter.CoroutinesEscPosPrinter( 29 | deviceConnection, 30 | printerData.printerDpi, 31 | printerData.printerWidthMM, 32 | printerData.printerNbrCharactersPerLine, 33 | EscPosCharsetEncoding("Arabic", 22) 34 | ) 35 | 36 | printer.printFormattedTextAndCut(context, printerData.textToPrint) 37 | .apply { disconnectPrinter() } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /escposprinter/src/main/java/com/khairo/escposprinter/barcode/Barcode.kt: -------------------------------------------------------------------------------- 1 | package com.khairo.escposprinter.barcode 2 | 3 | import com.khairo.escposprinter.EscPosPrinterSize 4 | import com.khairo.escposprinter.exceptions.EscPosBarcodeException 5 | import kotlin.math.roundToInt 6 | 7 | abstract class Barcode internal constructor( 8 | printerSize: EscPosPrinterSize, 9 | barcodeType: Int, 10 | code: String, 11 | widthMM: Float, 12 | heightMM: Float, 13 | textPosition: Int 14 | ) { 15 | var barcodeType: Int 16 | protected set 17 | var code: String 18 | protected set 19 | var colWidth: Int 20 | protected set 21 | var height: Int 22 | protected set 23 | var textPosition: Int 24 | protected set 25 | abstract fun getCodeLength(): Int 26 | abstract fun getColsCount(): Int 27 | 28 | init { 29 | var widthMM = widthMM 30 | this.barcodeType = barcodeType 31 | this.code = code 32 | height = printerSize.mmToPx(heightMM) 33 | this.textPosition = textPosition 34 | if (widthMM == 0f) { 35 | widthMM = printerSize.printerWidthMM * 0.7f 36 | } 37 | val wantedPxWidth = 38 | if (widthMM > printerSize.printerWidthMM) printerSize.printerWidthPx else printerSize.mmToPx( 39 | widthMM 40 | ) 41 | var colWidth = (wantedPxWidth.toDouble() / getColsCount().toDouble()).roundToInt() 42 | if (colWidth * getColsCount() > printerSize.printerWidthPx) { 43 | --colWidth 44 | } 45 | if (colWidth == 0) { 46 | throw EscPosBarcodeException("Barcode is too long for the paper size.") 47 | } 48 | this.colWidth = colWidth 49 | } 50 | } -------------------------------------------------------------------------------- /escposprinter/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'kotlin-android' 3 | 4 | android { 5 | compileSdkVersion 30 6 | 7 | defaultConfig { 8 | minSdkVersion 16 9 | targetSdkVersion 30 10 | versionCode 1 11 | versionName "1.0" 12 | 13 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 14 | consumerProguardFiles 'consumer-rules.pro' 15 | } 16 | 17 | buildTypes { 18 | release { 19 | minifyEnabled false 20 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 21 | } 22 | } 23 | 24 | } 25 | 26 | dependencies { 27 | implementation fileTree(dir: 'libs', include: ['*.jar']) 28 | implementation 'androidx.appcompat:appcompat:1.2.0' 29 | testImplementation 'junit:junit:4.13.1' 30 | androidTestImplementation 'androidx.test.ext:junit:1.1.2' 31 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' 32 | implementation 'com.google.zxing:core:3.4.0' 33 | implementation "androidx.core:core-ktx:1.3.2" 34 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 35 | 36 | //coroutines 37 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines" 38 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines" 39 | 40 | //lifecycle 41 | implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version" 42 | implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version" 43 | implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version" 44 | implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version" 45 | } 46 | repositories { 47 | mavenCentral() 48 | } 49 | -------------------------------------------------------------------------------- /escposprinter/src/main/java/com/khairo/escposprinter/connection/usb/UsbDeviceHelper.kt: -------------------------------------------------------------------------------- 1 | package com.khairo.escposprinter.connection.usb 2 | 3 | import android.hardware.usb.UsbConstants 4 | import android.hardware.usb.UsbDevice 5 | import android.hardware.usb.UsbEndpoint 6 | import android.hardware.usb.UsbInterface 7 | 8 | object UsbDeviceHelper { 9 | /** 10 | * Find the correct USB interface for printing 11 | * 12 | * @param usbDevice USB device 13 | * @return correct USB interface for printing, null if not found 14 | */ 15 | fun findPrinterInterface(usbDevice: UsbDevice?): UsbInterface? { 16 | if (usbDevice == null) { 17 | return null 18 | } 19 | val interfacesCount = usbDevice.interfaceCount 20 | for (i in 0 until interfacesCount) { 21 | val usbInterface = usbDevice.getInterface(i) 22 | if (usbInterface.interfaceClass == UsbConstants.USB_CLASS_PRINTER) { 23 | return usbInterface 24 | } 25 | } 26 | return usbDevice.getInterface(0) 27 | } 28 | 29 | /** 30 | * Find the USB endpoint for device input 31 | * 32 | * @param usbInterface USB interface 33 | * @return Input endpoint or null if not found 34 | */ 35 | fun findEndpointIn(usbInterface: UsbInterface?): UsbEndpoint? { 36 | if (usbInterface != null) { 37 | val endpointsCount = usbInterface.endpointCount 38 | for (i in 0 until endpointsCount) { 39 | val endpoint = usbInterface.getEndpoint(i) 40 | if (endpoint.type == UsbConstants.USB_ENDPOINT_XFER_BULK && endpoint.direction == UsbConstants.USB_DIR_OUT) { 41 | return endpoint 42 | } 43 | } 44 | } 45 | return null 46 | } 47 | } -------------------------------------------------------------------------------- /escposprinter/src/main/java/com/khairo/escposprinter/barcode/BarcodeNumber.kt: -------------------------------------------------------------------------------- 1 | package com.khairo.escposprinter.barcode 2 | 3 | import com.khairo.escposprinter.EscPosPrinterSize 4 | import com.khairo.escposprinter.exceptions.EscPosBarcodeException 5 | 6 | abstract class BarcodeNumber( 7 | printerSize: EscPosPrinterSize, 8 | barcodeType: Int, 9 | code: String, 10 | widthMM: Float, 11 | heightMM: Float, 12 | textPosition: Int 13 | ) : Barcode(printerSize, barcodeType, code, widthMM, heightMM, textPosition) { 14 | override fun getColsCount(): Int { 15 | return this.getCodeLength() * 7 + 11 16 | } 17 | 18 | @Throws(EscPosBarcodeException::class) 19 | private fun checkCode() { 20 | val codeLength = this.getCodeLength() - 1 21 | if (code.length < codeLength) { 22 | throw EscPosBarcodeException("Code is too short for the barcode type.") 23 | } 24 | try { 25 | val code = code.substring(0, codeLength) 26 | var totalBarcodeKey = 0 27 | for (i in 0 until codeLength) { 28 | val pos = codeLength - 1 - i 29 | var intCode = code.substring(pos, pos + 1).toInt(10) 30 | if (i % 2 == 0) { 31 | intCode = 3 * intCode 32 | } 33 | totalBarcodeKey += intCode 34 | } 35 | var barcodeKey = (10 - totalBarcodeKey % 10).toString() 36 | if (barcodeKey.length == 2) { 37 | barcodeKey = "0" 38 | } 39 | this.code = code + barcodeKey 40 | } catch (e: NumberFormatException) { 41 | e.printStackTrace() 42 | throw EscPosBarcodeException("Invalid barcode number") 43 | } 44 | } 45 | 46 | init { 47 | checkCode() 48 | } 49 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /escposprinter/src/main/java/com/khairo/escposprinter/connection/usb/UsbPrintersConnections.kt: -------------------------------------------------------------------------------- 1 | package com.khairo.escposprinter.connection.usb 2 | 3 | import android.content.Context 4 | import android.hardware.usb.UsbConstants 5 | 6 | class UsbPrintersConnections 7 | /** 8 | * Create a new instance of UsbPrintersConnections 9 | * 10 | * @param context Application context 11 | */ 12 | (context: Context) : UsbConnections(context) { 13 | 14 | override fun getList(): Array { 15 | val usbConnections = super.getList() 16 | var i = 0 17 | val printersTmp = arrayOfNulls(usbConnections!!.size) 18 | for (usbConnection in usbConnections) { 19 | val device = usbConnection!!.device 20 | var usbClass = device.deviceClass 21 | if (usbClass == UsbConstants.USB_CLASS_PER_INTERFACE && UsbDeviceHelper.findPrinterInterface( 22 | device 23 | ) != null 24 | ) { 25 | usbClass = UsbConstants.USB_CLASS_PRINTER 26 | } 27 | if (usbClass == UsbConstants.USB_CLASS_PRINTER) { 28 | printersTmp[i++] = UsbConnection(usbManager, device) 29 | } 30 | } 31 | val usbPrinters = arrayOfNulls(i) 32 | System.arraycopy(printersTmp, 0, usbPrinters, 0, i) 33 | return usbPrinters 34 | } 35 | 36 | companion object { 37 | /** 38 | * Easy way to get the first USB printer paired / connected. 39 | * 40 | * @return a UsbConnection instance 41 | */ 42 | fun selectFirstConnected(context: Context): UsbConnection? { 43 | val printers = UsbPrintersConnections(context) 44 | val bluetoothPrinters = printers.getList() 45 | return if (bluetoothPrinters.isEmpty()) { 46 | null 47 | } else bluetoothPrinters[0] 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /escposprinter/src/main/java/com/khairo/escposprinter/textparser/PrinterTextParserQRCode.kt: -------------------------------------------------------------------------------- 1 | package com.khairo.escposprinter.textparser 2 | 3 | 4 | import com.khairo.escposprinter.EscPosPrinterCommands 5 | import com.khairo.escposprinter.exceptions.EscPosBarcodeException 6 | import com.khairo.escposprinter.exceptions.EscPosParserException 7 | import java.util.* 8 | 9 | class PrinterTextParserQRCode( 10 | printerTextParserColumn: PrinterTextParserColumn, textAlign: String?, 11 | qrCodeAttributes: Hashtable, data: String 12 | ) : PrinterTextParserImg( 13 | printerTextParserColumn, 14 | textAlign, 15 | initConstructor(printerTextParserColumn, qrCodeAttributes, data) 16 | ) { 17 | companion object { 18 | @Throws(EscPosParserException::class, EscPosBarcodeException::class) 19 | private fun initConstructor( 20 | printerTextParserColumn: PrinterTextParserColumn, 21 | qrCodeAttributes: Hashtable, 22 | oldData: String 23 | ): ByteArray { 24 | var data = oldData 25 | val printer = printerTextParserColumn.line.textParser.printer 26 | data = data.trim { it <= ' ' } 27 | var size = printer.mmToPx(20f) 28 | if (qrCodeAttributes.containsKey(PrinterTextParser.ATTR_QRCODE_SIZE)) { 29 | val qrCodeAttribute = qrCodeAttributes[PrinterTextParser.ATTR_QRCODE_SIZE] 30 | ?: throw EscPosParserException("Invalid QR code attribute : " + PrinterTextParser.ATTR_QRCODE_SIZE) 31 | size = try { 32 | printer.mmToPx(qrCodeAttribute.toFloat()) 33 | } catch (nfe: NumberFormatException) { 34 | throw EscPosParserException("Invalid QR code " + PrinterTextParser.ATTR_QRCODE_SIZE + " value") 35 | } 36 | } 37 | return EscPosPrinterCommands.QRCodeDataToBytes(data, size) 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /escposprinter/src/main/java/com/khairo/escposprinter/textparser/CoroutinesPrinterTextParserQRCode.kt: -------------------------------------------------------------------------------- 1 | package com.khairo.escposprinter.textparser 2 | 3 | import com.khairo.escposprinter.EscPosPrinterCommands 4 | import com.khairo.escposprinter.exceptions.EscPosBarcodeException 5 | import com.khairo.escposprinter.exceptions.EscPosParserException 6 | import java.util.* 7 | 8 | class CoroutinesPrinterTextParserQRCode( 9 | printerTextParserColumn: CoroutinesPrinterTextParserColumn, textAlign: String?, 10 | qrCodeAttributes: Hashtable, data: String 11 | ) : CoroutinesPrinterTextParserImg( 12 | printerTextParserColumn, 13 | textAlign, 14 | initConstructor(printerTextParserColumn, qrCodeAttributes, data) 15 | ) { 16 | companion object { 17 | @Throws(EscPosParserException::class, EscPosBarcodeException::class) 18 | private fun initConstructor( 19 | printerTextParserColumn: CoroutinesPrinterTextParserColumn, 20 | qrCodeAttributes: Hashtable, 21 | oldData: String 22 | ): ByteArray { 23 | var data = oldData 24 | val printer = printerTextParserColumn.line.textParser.printer 25 | data = data.trim { it <= ' ' } 26 | var size = printer.mmToPx(20f) 27 | if (qrCodeAttributes.containsKey(PrinterTextParser.ATTR_QRCODE_SIZE)) { 28 | val qrCodeAttribute = qrCodeAttributes[PrinterTextParser.ATTR_QRCODE_SIZE] 29 | ?: throw EscPosParserException("Invalid QR code attribute : " + PrinterTextParser.ATTR_QRCODE_SIZE) 30 | size = try { 31 | printer.mmToPx(qrCodeAttribute.toFloat()) 32 | } catch (nfe: NumberFormatException) { 33 | throw EscPosParserException("Invalid QR code " + PrinterTextParser.ATTR_QRCODE_SIZE + " value") 34 | } 35 | } 36 | return EscPosPrinterCommands.QRCodeDataToBytes(data, size) 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /escposprinter/src/main/java/com/khairo/escposprinter/textparser/PrinterTextParserTag.kt: -------------------------------------------------------------------------------- 1 | package com.khairo.escposprinter.textparser 2 | 3 | import java.util.* 4 | 5 | class PrinterTextParserTag(oldTag: String) { 6 | var tagName = "" 7 | val attributes = Hashtable() 8 | var length = 0 9 | var isCloseTag = false 10 | fun getAttribute(key: String): String? = attributes[key] 11 | 12 | fun hasAttribute(key: String): Boolean = attributes.containsKey(key) 13 | 14 | init { 15 | var tag = oldTag 16 | tag = tag.trim { it <= ' ' } 17 | // if (!tag.startsWith("<") || !tag.endsWith(">")) { 18 | // return 19 | // } 20 | length = tag.length 21 | val openTagIndex = tag.indexOf("<") 22 | val closeTagIndex = tag.indexOf(">") 23 | val nextSpaceIndex = tag.indexOf(" ") 24 | if (nextSpaceIndex != -1 && nextSpaceIndex < closeTagIndex) { 25 | tagName = tag.substring(openTagIndex + 1, nextSpaceIndex).toLowerCase(Locale.ROOT) 26 | var attributesString = tag.substring(nextSpaceIndex, closeTagIndex).trim { it <= ' ' } 27 | while (attributesString.contains("='")) { 28 | val egalPos = attributesString.indexOf("='") 29 | val endPos = attributesString.indexOf("'", egalPos + 2) 30 | val attributeName = attributesString.substring(0, egalPos) 31 | val attributeValue = attributesString.substring(egalPos + 2, endPos) 32 | if (attributeName != "") { 33 | attributes[attributeName] = attributeValue 34 | } 35 | attributesString = attributesString.substring(endPos + 1).trim { it <= ' ' } 36 | } 37 | 38 | } else tagName = tag.substring(openTagIndex + 1, closeTagIndex).toLowerCase(Locale.ROOT) 39 | 40 | if (tagName.startsWith("/")) { 41 | tagName = tagName.substring(1) 42 | isCloseTag = true 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /escposprinter/src/main/java/com/khairo/escposprinter/textparser/PrinterTextParserLine.kt: -------------------------------------------------------------------------------- 1 | package com.khairo.escposprinter.textparser 2 | 3 | import java.util.* 4 | import java.util.regex.Pattern 5 | import kotlin.math.floor 6 | 7 | class PrinterTextParserLine(val textParser: PrinterTextParser, textLine: String) { 8 | val nbrColumns: Int 9 | var nbrCharColumn: Int 10 | private set 11 | var nbrCharForgetted: Int 12 | private set 13 | var nbrCharColumnExceeded: Int 14 | private set 15 | val columns: Array 16 | fun setNbrCharColumn(newValue: Int): PrinterTextParserLine { 17 | nbrCharColumn = newValue 18 | return this 19 | } 20 | 21 | fun setNbrCharForgetted(newValue: Int): PrinterTextParserLine { 22 | nbrCharForgetted = newValue 23 | return this 24 | } 25 | 26 | fun setNbrCharColumnExceeded(newValue: Int): PrinterTextParserLine { 27 | nbrCharColumnExceeded = newValue 28 | return this 29 | } 30 | 31 | init { 32 | val nbrCharactersPerLine = textParser.printer.printerNbrCharactersPerLine 33 | val pattern = Pattern.compile(PrinterTextParser.regexAlignTags) 34 | val matcher = pattern.matcher(textLine) 35 | val columnsList = ArrayList() 36 | var lastPosition = 0 37 | while (matcher.find()) { 38 | val startPosition = matcher.start() 39 | if (startPosition > 0) { 40 | columnsList.add(textLine.substring(lastPosition, startPosition)) 41 | } 42 | lastPosition = startPosition 43 | } 44 | columnsList.add(textLine.substring(lastPosition)) 45 | nbrColumns = columnsList.size 46 | nbrCharColumn = 47 | floor((nbrCharactersPerLine.toFloat() / nbrColumns.toFloat()).toDouble()) 48 | .toInt() 49 | nbrCharForgetted = nbrCharactersPerLine - nbrCharColumn * nbrColumns 50 | nbrCharColumnExceeded = 0 51 | columns = arrayOfNulls(nbrColumns) 52 | var i = 0 53 | for (column in columnsList) { 54 | columns[i++] = PrinterTextParserColumn(this, column) 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /escposprinter/src/main/java/com/khairo/escposprinter/textparser/PrinterTextParserString.kt: -------------------------------------------------------------------------------- 1 | package com.khairo.escposprinter.textparser 2 | 3 | import com.khairo.escposprinter.EscPosPrinter 4 | import com.khairo.escposprinter.EscPosPrinterCommands 5 | import com.khairo.escposprinter.exceptions.EscPosEncodingException 6 | import java.io.UnsupportedEncodingException 7 | import java.util.* 8 | 9 | class PrinterTextParserString( 10 | printerTextParserColumn: PrinterTextParserColumn, 11 | var text: String, 12 | var textSize: ByteArray, 13 | var textColor: ByteArray, 14 | private var textReverseColor: ByteArray, 15 | private var textBold: ByteArray, 16 | private var textUnderline: ByteArray, 17 | private var textDoubleStrike: ByteArray 18 | ) : IPrinterTextParserElement { 19 | private val printer: EscPosPrinter = printerTextParserColumn.line.textParser.printer 20 | 21 | @Throws(EscPosEncodingException::class) 22 | override fun length(): Int { 23 | val charsetEncoding = printer.encoding 24 | val coef = if (Arrays.equals( 25 | textSize, 26 | EscPosPrinterCommands.TEXT_SIZE_DOUBLE_WIDTH 27 | ) || Arrays.equals( 28 | textSize, EscPosPrinterCommands.TEXT_SIZE_BIG 29 | ) 30 | ) 2 else 1 31 | return if (charsetEncoding != null) { 32 | try { 33 | text.toByteArray(charset(charsetEncoding.name)).size * coef 34 | } catch (e: UnsupportedEncodingException) { 35 | throw EscPosEncodingException(e.message) 36 | } 37 | } else text.length * coef 38 | } 39 | 40 | /** 41 | * Print text 42 | * 43 | * @param printerSocket Instance of EscPosPrinterCommands 44 | * @return this Fluent method 45 | */ 46 | @Throws(EscPosEncodingException::class) 47 | override fun print(printerSocket: EscPosPrinterCommands?): PrinterTextParserString { 48 | printerSocket!!.printText( 49 | text, 50 | textSize, 51 | textColor, 52 | textReverseColor, 53 | textBold, 54 | textUnderline, 55 | textDoubleStrike 56 | ) 57 | return this 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /escposprinter/src/main/java/com/khairo/escposprinter/textparser/CoroutinesPrinterTextParserLine.kt: -------------------------------------------------------------------------------- 1 | package com.khairo.escposprinter.textparser 2 | 3 | import java.util.* 4 | import java.util.regex.Pattern 5 | import kotlin.math.floor 6 | 7 | class CoroutinesPrinterTextParserLine(val textParser: CoroutinesPrinterTextParser, textLine: String) { 8 | val nbrColumns: Int 9 | var nbrCharColumn: Int 10 | private set 11 | var nbrCharForgetted: Int 12 | private set 13 | var nbrCharColumnExceeded: Int 14 | private set 15 | val columns: Array 16 | fun setNbrCharColumn(newValue: Int): CoroutinesPrinterTextParserLine { 17 | nbrCharColumn = newValue 18 | return this 19 | } 20 | 21 | fun setNbrCharForgetted(newValue: Int): CoroutinesPrinterTextParserLine { 22 | nbrCharForgetted = newValue 23 | return this 24 | } 25 | 26 | fun setNbrCharColumnExceeded(newValue: Int): CoroutinesPrinterTextParserLine { 27 | nbrCharColumnExceeded = newValue 28 | return this 29 | } 30 | 31 | init { 32 | val nbrCharactersPerLine = textParser.printer.printerNbrCharactersPerLine 33 | val pattern = Pattern.compile(PrinterTextParser.regexAlignTags) 34 | val matcher = pattern.matcher(textLine) 35 | val columnsList = ArrayList() 36 | var lastPosition = 0 37 | while (matcher.find()) { 38 | val startPosition = matcher.start() 39 | if (startPosition > 0) { 40 | columnsList.add(textLine.substring(lastPosition, startPosition)) 41 | } 42 | lastPosition = startPosition 43 | } 44 | columnsList.add(textLine.substring(lastPosition)) 45 | nbrColumns = columnsList.size 46 | nbrCharColumn = 47 | floor((nbrCharactersPerLine.toFloat() / nbrColumns.toFloat()).toDouble()) 48 | .toInt() 49 | nbrCharForgetted = nbrCharactersPerLine - nbrCharColumn * nbrColumns 50 | nbrCharColumnExceeded = 0 51 | columns = arrayOfNulls(nbrColumns) 52 | var i = 0 53 | for (column in columnsList) { 54 | columns[i++] = CoroutinesPrinterTextParserColumn(this, column) 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /escposprinter/src/main/java/com/khairo/escposprinter/connection/bluetooth/BluetoothPrintersConnections.kt: -------------------------------------------------------------------------------- 1 | package com.khairo.escposprinter.connection.bluetooth 2 | 3 | import android.annotation.SuppressLint 4 | import android.bluetooth.BluetoothClass 5 | import com.khairo.escposprinter.exceptions.EscPosConnectionException 6 | 7 | class BluetoothPrintersConnections : BluetoothConnections() { 8 | /** 9 | * Get a list of bluetooth printers. 10 | * 11 | * @return an array of EscPosPrinterCommands 12 | */ 13 | @SuppressLint("MissingPermission") 14 | override fun getList(): Array { 15 | val bluetoothDevicesList = super.getList() 16 | var i = 0 17 | val printersTmp = arrayOfNulls(bluetoothDevicesList!!.size) 18 | for (bluetoothConnection in bluetoothDevicesList) { 19 | val device = bluetoothConnection!!.device 20 | val majDeviceCl = device!!.bluetoothClass.majorDeviceClass 21 | val deviceCl = device.bluetoothClass.deviceClass 22 | if (majDeviceCl == BluetoothClass.Device.Major.IMAGING && (deviceCl == 1664 || deviceCl == BluetoothClass.Device.Major.IMAGING)) { 23 | printersTmp[i++] = BluetoothConnection(device) 24 | } 25 | } 26 | val bluetoothPrinters = arrayOfNulls(i) 27 | System.arraycopy(printersTmp, 0, bluetoothPrinters, 0, i) 28 | return bluetoothPrinters 29 | } 30 | 31 | companion object { 32 | /** 33 | * Easy way to get the first bluetooth printer paired / connected. 34 | * 35 | * @return a EscPosPrinterCommands instance 36 | */ 37 | fun selectFirstPaired(): BluetoothConnection? { 38 | val printers = BluetoothPrintersConnections() 39 | val bluetoothPrinters = printers.getList() 40 | if (bluetoothPrinters.isNotEmpty()) { 41 | for (printer in bluetoothPrinters) { 42 | try { 43 | return printer!!.connect() 44 | } catch (e: EscPosConnectionException) { 45 | e.printStackTrace() 46 | } 47 | } 48 | } 49 | return null 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | id 'kotlin-android' 4 | id 'kotlin-kapt' 5 | } 6 | 7 | android { 8 | compileSdkVersion 30 9 | buildToolsVersion "30.0.1" 10 | 11 | defaultConfig { 12 | applicationId "com.khairo.printer" 13 | minSdkVersion 21 14 | targetSdkVersion 30 15 | versionCode 1 16 | versionName "1.0" 17 | 18 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 19 | } 20 | 21 | buildTypes { 22 | release { 23 | minifyEnabled false 24 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 25 | } 26 | } 27 | compileOptions { 28 | sourceCompatibility JavaVersion.VERSION_1_8 29 | targetCompatibility JavaVersion.VERSION_1_8 30 | } 31 | kotlinOptions { 32 | jvmTarget = '1.8' 33 | } 34 | 35 | buildFeatures { 36 | dataBinding = true 37 | viewBinding true 38 | } 39 | } 40 | 41 | dependencies { 42 | 43 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" 44 | implementation 'androidx.core:core-ktx:1.3.2' 45 | implementation 'androidx.appcompat:appcompat:1.2.0' 46 | 47 | //material 48 | implementation 'com.google.android.material:material:1.2.1' 49 | implementation 'androidx.constraintlayout:constraintlayout:2.0.4' 50 | 51 | //testing unit 52 | testImplementation 'junit:junit:4.13.1' 53 | androidTestImplementation 'androidx.test.ext:junit:1.1.2' 54 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' 55 | 56 | //databinding 57 | kapt "com.android.databinding:compiler:3.2.1" 58 | 59 | //coroutines 60 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines" 61 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines" 62 | 63 | //lifecycle 64 | implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version" 65 | implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version" 66 | implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version" 67 | implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version" 68 | 69 | //Thermal Printer 70 | implementation project(path: ':escposprinter') 71 | } 72 | -------------------------------------------------------------------------------- /escposprinter/src/main/java/com/khairo/escposprinter/connection/DeviceConnection.kt: -------------------------------------------------------------------------------- 1 | package com.khairo.escposprinter.connection 2 | 3 | import com.khairo.escposprinter.exceptions.EscPosConnectionException 4 | import java.io.IOException 5 | import java.io.OutputStream 6 | import kotlin.math.floor 7 | 8 | abstract class DeviceConnection { 9 | @JvmField 10 | protected var stream: OutputStream? = null 11 | 12 | @JvmField 13 | protected var data: ByteArray 14 | 15 | @Throws(EscPosConnectionException::class) 16 | abstract fun connect(): DeviceConnection? 17 | abstract fun disconnect(): DeviceConnection? 18 | 19 | /** 20 | * Check if OutputStream is open. 21 | * 22 | * @return true if is connected 23 | */ 24 | // open val isConnected: Boolean 25 | // get() = stream != null 26 | open fun isConnected(): Boolean = this.stream != null 27 | 28 | /** 29 | * Add data to send. 30 | */ 31 | fun write(bytes: ByteArray) { 32 | val data = ByteArray(bytes.size + data.size) 33 | System.arraycopy(this.data, 0, data, 0, this.data.size) 34 | System.arraycopy(bytes, 0, data, this.data.size, bytes.size) 35 | this.data = data 36 | } 37 | 38 | /** 39 | * Send data to the device. 40 | */ 41 | @Throws(EscPosConnectionException::class) 42 | open fun send() { 43 | this.send(0) 44 | } 45 | 46 | /** 47 | * Send data to the device. 48 | */ 49 | @Throws(EscPosConnectionException::class) 50 | open fun send(addWaitingTime: Int) { 51 | if (!isConnected()) { 52 | throw EscPosConnectionException("Unable to send data to device.") 53 | } 54 | try { 55 | stream!!.write(data) 56 | stream!!.flush() 57 | data = ByteArray(0) 58 | val waitingTime = addWaitingTime + floor((data.size / 16f).toDouble()) 59 | .toInt() 60 | if (waitingTime > 0) { 61 | Thread.sleep(waitingTime.toLong()) 62 | } 63 | } catch (e: IOException) { 64 | e.printStackTrace() 65 | throw EscPosConnectionException(e.message) 66 | } catch (e: InterruptedException) { 67 | e.printStackTrace() 68 | throw EscPosConnectionException(e.message) 69 | } 70 | } 71 | 72 | init { 73 | data = ByteArray(0) 74 | } 75 | } -------------------------------------------------------------------------------- /escposprinter/src/main/java/com/khairo/escposprinter/textparser/CoroutinesPrinterTextParserString.kt: -------------------------------------------------------------------------------- 1 | package com.khairo.escposprinter.textparser 2 | 3 | import com.khairo.escposprinter.CoroutinesEscPosPrinter 4 | import com.khairo.escposprinter.CoroutinesEscPosPrinterCommands 5 | import com.khairo.escposprinter.EscPosPrinter 6 | import com.khairo.escposprinter.EscPosPrinterCommands 7 | import com.khairo.escposprinter.exceptions.EscPosEncodingException 8 | import java.io.UnsupportedEncodingException 9 | import java.util.* 10 | 11 | class CoroutinesPrinterTextParserString( 12 | printerTextParserColumn: CoroutinesPrinterTextParserColumn, 13 | var text: String, 14 | var textSize: ByteArray, 15 | var textColor: ByteArray, 16 | private var textReverseColor: ByteArray, 17 | private var textBold: ByteArray, 18 | private var textUnderline: ByteArray, 19 | private var textDoubleStrike: ByteArray 20 | ) : CoroutinesIPrinterTextParserElement { 21 | private val printer: CoroutinesEscPosPrinter = printerTextParserColumn.line.textParser.printer 22 | 23 | @Throws(EscPosEncodingException::class) 24 | override fun length(): Int { 25 | val charsetEncoding = printer.encoding 26 | val coef = if (Arrays.equals( 27 | textSize, 28 | EscPosPrinterCommands.TEXT_SIZE_DOUBLE_WIDTH 29 | ) || Arrays.equals( 30 | textSize, EscPosPrinterCommands.TEXT_SIZE_BIG 31 | ) 32 | ) 2 else 1 33 | return if (charsetEncoding != null) { 34 | try { 35 | text.toByteArray(charset(charsetEncoding.name)).size * coef 36 | } catch (e: UnsupportedEncodingException) { 37 | throw EscPosEncodingException(e.message) 38 | } 39 | } else text.length * coef 40 | } 41 | 42 | /** 43 | * Print text 44 | * 45 | * @param printerSocket Instance of EscPosPrinterCommands 46 | * @return this Fluent method 47 | */ 48 | @Throws(EscPosEncodingException::class) 49 | override suspend fun print(printerSocket: CoroutinesEscPosPrinterCommands?): CoroutinesPrinterTextParserString { 50 | printerSocket!!.printText( 51 | text, 52 | textSize, 53 | textColor, 54 | textReverseColor, 55 | textBold, 56 | textUnderline, 57 | textDoubleStrike 58 | ) 59 | return this 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /escposprinter/src/main/java/com/khairo/escposprinter/connection/tcp/TcpConnection.kt: -------------------------------------------------------------------------------- 1 | package com.khairo.escposprinter.connection.tcp 2 | 3 | import android.content.Context 4 | import com.khairo.exeption.PrintingException.FINISH_PRINTER_DISCONNECTED 5 | import com.khairo.exeption.onException 6 | import java.io.IOException 7 | import java.net.InetAddress 8 | import java.net.InetSocketAddress 9 | import java.net.Socket 10 | 11 | /** 12 | * Create un instance of TcpConnection. 13 | * 14 | * @param address IP address of the device 15 | * @param port Port of the device 16 | */ 17 | class TcpConnection(private val address: String, private val port: Int) : TcpDeviceConnection() { 18 | private var socket: Socket? = null 19 | 20 | /** 21 | * Check if the TCP device is connected by socket. 22 | * 23 | * @return true if is connected 24 | */ 25 | 26 | override fun isConnected(): Boolean = 27 | socket != null && socket!!.isConnected && super.isConnected() 28 | 29 | /** 30 | * Start socket connection with the TCP device. 31 | */ 32 | override suspend fun connect(context: Context): TcpConnection { 33 | if (this.isConnected()) return this 34 | 35 | try { 36 | socket = Socket() 37 | socket!!.connect(InetSocketAddress(InetAddress.getByName(address), port)) 38 | stream = socket!!.getOutputStream() 39 | data = ByteArray(0) 40 | 41 | } catch (e: IOException) { 42 | e.printStackTrace() 43 | socket = null 44 | stream = null 45 | onException(context, FINISH_PRINTER_DISCONNECTED) 46 | // throw EscPosConnectionException("Unable to connect to TCP device.") 47 | } 48 | return this 49 | } 50 | 51 | /** 52 | * Close the socket connection with the TCP device. 53 | */ 54 | override suspend fun disconnect(): TcpConnection { 55 | data = ByteArray(0) 56 | if (stream != null) { 57 | try { 58 | stream!!.close() 59 | stream = null 60 | } catch (e: IOException) { 61 | e.printStackTrace() 62 | } 63 | } 64 | if (socket != null) { 65 | try { 66 | socket!!.close() 67 | socket = null 68 | } catch (e: IOException) { 69 | e.printStackTrace() 70 | } 71 | } 72 | return this 73 | } 74 | } -------------------------------------------------------------------------------- /escposprinter/src/main/java/com/khairo/escposprinter/connection/tcp/TcpDeviceConnection.kt: -------------------------------------------------------------------------------- 1 | package com.khairo.escposprinter.connection.tcp 2 | 3 | import android.content.Context 4 | import com.khairo.exeption.PrintingException.FINISH_PRINTER_DISCONNECTED 5 | import com.khairo.exeption.onException 6 | import java.io.IOException 7 | import java.io.OutputStream 8 | import kotlin.math.floor 9 | 10 | abstract class TcpDeviceConnection { 11 | @JvmField 12 | protected var stream: OutputStream? = null 13 | 14 | @JvmField 15 | protected var data: ByteArray 16 | 17 | abstract suspend fun connect(context: Context): TcpDeviceConnection? 18 | abstract suspend fun disconnect(): TcpDeviceConnection? 19 | 20 | /** 21 | * Check if OutputStream is open. 22 | * 23 | * @return true if is connected 24 | */ 25 | // open val isConnected: Boolean 26 | // get() = stream != null 27 | open fun isConnected(): Boolean = this.stream != null 28 | 29 | /** 30 | * Add data to send. 31 | */ 32 | suspend fun write(bytes: ByteArray) { 33 | val data = ByteArray(bytes.size + data.size) 34 | System.arraycopy(this.data, 0, data, 0, this.data.size) 35 | System.arraycopy(bytes, 0, data, this.data.size, bytes.size) 36 | this.data = data 37 | } 38 | 39 | /** 40 | * Send data to the device. 41 | */ 42 | open suspend fun send(context: Context) { 43 | this.send(context, 0) 44 | } 45 | 46 | /** 47 | * Send data to the device. 48 | */ 49 | open fun send(context: Context, addWaitingTime: Int) { 50 | if (!isConnected()) { 51 | onException(context, FINISH_PRINTER_DISCONNECTED) 52 | return 53 | } 54 | try { 55 | stream!!.write(data) 56 | stream!!.flush() 57 | data = ByteArray(0) 58 | val waitingTime = addWaitingTime + floor((data.size / 16f).toDouble()) 59 | .toInt() 60 | if (waitingTime > 0) { 61 | Thread.sleep(waitingTime.toLong()) 62 | } 63 | } catch (e: IOException) { 64 | e.printStackTrace() 65 | onException(context, FINISH_PRINTER_DISCONNECTED) 66 | } catch (e: InterruptedException) { 67 | e.printStackTrace() 68 | onException(context, FINISH_PRINTER_DISCONNECTED) 69 | } 70 | } 71 | 72 | init { 73 | data = ByteArray(0) 74 | } 75 | } -------------------------------------------------------------------------------- /escposprinter/src/main/java/com/khairo/escposprinter/connection/usb/UsbConnection.kt: -------------------------------------------------------------------------------- 1 | package com.khairo.escposprinter.connection.usb 2 | 3 | import android.hardware.usb.UsbDevice 4 | import android.hardware.usb.UsbManager 5 | import com.khairo.escposprinter.connection.DeviceConnection 6 | import com.khairo.escposprinter.exceptions.EscPosConnectionException 7 | import java.io.IOException 8 | 9 | class UsbConnection 10 | /** 11 | * Create un instance of UsbConnection. 12 | * 13 | * @param usbManager an instance of UsbManager 14 | * @param device an instance of UsbDevice 15 | */( 16 | private val usbManager: UsbManager, 17 | /** 18 | * Get the instance UsbDevice connected. 19 | * 20 | * @return an instance of UsbDevice 21 | */ 22 | val device: UsbDevice 23 | ) : DeviceConnection() { 24 | 25 | /** 26 | * Start socket connection with the usbDevice. 27 | */ 28 | @Throws(EscPosConnectionException::class) 29 | override fun connect(): UsbConnection { 30 | if (isConnected()) { 31 | return this 32 | } 33 | try { 34 | stream = UsbOutputStream(usbManager, device) 35 | data = ByteArray(0) 36 | } catch (e: IOException) { 37 | e.printStackTrace() 38 | stream = null 39 | throw EscPosConnectionException("Unable to connect to USB device.") 40 | } 41 | return this 42 | } 43 | 44 | /** 45 | * Close the socket connection with the usbDevice. 46 | */ 47 | override fun disconnect(): UsbConnection { 48 | data = ByteArray(0) 49 | if (isConnected()) { 50 | try { 51 | stream!!.close() 52 | } catch (e: IOException) { 53 | e.printStackTrace() 54 | } 55 | stream = null 56 | } 57 | return this 58 | } 59 | 60 | /** 61 | * Send data to the device. 62 | */ 63 | @Throws(EscPosConnectionException::class) 64 | override fun send() { 65 | this.send(0) 66 | } 67 | 68 | /** 69 | * Send data to the device. 70 | */ 71 | @Throws(EscPosConnectionException::class) 72 | override fun send(addWaitingTime: Int) { 73 | try { 74 | stream!!.write(data) 75 | data = ByteArray(0) 76 | } catch (e: IOException) { 77 | e.printStackTrace() 78 | throw EscPosConnectionException(e.message) 79 | } 80 | } 81 | } -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /escposprinter/src/main/java/com/khairo/escposprinter/connection/usb/UsbOutputStream.kt: -------------------------------------------------------------------------------- 1 | package com.khairo.escposprinter.connection.usb 2 | 3 | import android.hardware.usb.* 4 | import java.io.IOException 5 | import java.io.OutputStream 6 | import java.nio.ByteBuffer 7 | 8 | class UsbOutputStream(usbManager: UsbManager, usbDevice: UsbDevice?) : OutputStream() { 9 | private var usbConnection: UsbDeviceConnection? 10 | private var usbInterface: UsbInterface? 11 | private var usbEndpoint: UsbEndpoint? 12 | 13 | @Throws(IOException::class) 14 | override fun write(i: Int) { 15 | this.write(byteArrayOf(i.toByte())) 16 | } 17 | 18 | @Throws(IOException::class) 19 | override fun write(bytes: ByteArray) { 20 | this.write(bytes, 0, bytes.size) 21 | } 22 | 23 | @Throws(IOException::class) 24 | override fun write(bytes: ByteArray, offset: Int, length: Int) { 25 | if (usbInterface == null || usbEndpoint == null || usbConnection == null) { 26 | throw IOException("Unable to connect to USB device.") 27 | } 28 | if (!usbConnection!!.claimInterface(usbInterface, true)) { 29 | throw IOException("Error during claim USB interface.") 30 | } 31 | val buffer = ByteBuffer.wrap(bytes) 32 | val usbRequest = UsbRequest() 33 | try { 34 | usbRequest.initialize(usbConnection, usbEndpoint) 35 | if (!usbRequest.queue(buffer, bytes.size)) { 36 | throw IOException("Error queueing USB request.") 37 | } 38 | usbConnection!!.requestWait() 39 | } finally { 40 | usbRequest.close() 41 | } 42 | } 43 | 44 | @Throws(IOException::class) 45 | override fun flush() { 46 | } 47 | 48 | @Throws(IOException::class) 49 | override fun close() { 50 | if (usbConnection != null) { 51 | usbConnection!!.close() 52 | usbInterface = null 53 | usbEndpoint = null 54 | usbConnection = null 55 | } 56 | } 57 | 58 | init { 59 | usbInterface = UsbDeviceHelper.findPrinterInterface(usbDevice) 60 | if (usbInterface == null) { 61 | throw IOException("Unable to find USB interface.") 62 | } 63 | usbEndpoint = UsbDeviceHelper.findEndpointIn(usbInterface) 64 | if (usbEndpoint == null) { 65 | throw IOException("Unable to find USB endpoint.") 66 | } 67 | usbConnection = usbManager.openDevice(usbDevice) 68 | if (usbConnection == null) { 69 | throw IOException("Unable to open USB connection.") 70 | } 71 | } 72 | } -------------------------------------------------------------------------------- /escposprinter/src/main/java/com/khairo/escposprinter/EscPosPrinterSize.kt: -------------------------------------------------------------------------------- 1 | package com.khairo.escposprinter 2 | 3 | import android.graphics.Bitmap 4 | import kotlin.math.roundToInt 5 | 6 | open class EscPosPrinterSize constructor( 7 | /** 8 | * Get the printer DPI 9 | * 10 | * @return int 11 | */ 12 | var printerDpi: Int, 13 | /** 14 | * Get the printing width in millimeters 15 | * 16 | * @return float 17 | */ 18 | var printerWidthMM: Float, 19 | /** 20 | * Get the maximum number of characters that can be printed on a line. 21 | * 22 | * @return int 23 | */ 24 | var printerNbrCharactersPerLine: Int 25 | ) { 26 | 27 | /** 28 | * Get the printing width in dot 29 | * 30 | * @return int 31 | */ 32 | var printerWidthPx: Int 33 | protected set 34 | 35 | /** 36 | * Get the number of dot that a printed character contain 37 | * 38 | * @return int 39 | */ 40 | var printerCharSizeWidthPx: Int 41 | protected set 42 | 43 | /** 44 | * Convert from millimeters to dot the mmSize variable. 45 | * 46 | * @param mmSize Distance in millimeters to be converted 47 | * @return int 48 | */ 49 | fun mmToPx(mmSize: Float): Int = (mmSize * printerDpi.toFloat() / INCH_TO_MM).roundToInt() 50 | 51 | /** 52 | * Convert Bitmap object to ESC/POS image. 53 | * 54 | * @param oldBitmap Instance of Bitmap 55 | * @return Bytes contain the image in ESC/POS command 56 | */ 57 | fun bitmapToBytes(oldBitmap: Bitmap): ByteArray { 58 | var bitmap = oldBitmap 59 | var isSizeEdit = false 60 | var bitmapWidth = bitmap.width 61 | var bitmapHeight = bitmap.height 62 | val maxWidth = printerWidthPx 63 | val maxHeight = 256 64 | if (bitmapWidth > maxWidth) { 65 | bitmapHeight = 66 | (bitmapHeight.toFloat() * maxWidth.toFloat() / bitmapWidth.toFloat()).roundToInt() 67 | bitmapWidth = maxWidth 68 | isSizeEdit = true 69 | } 70 | if (bitmapHeight > maxHeight) { 71 | bitmapWidth = 72 | (bitmapWidth.toFloat() * maxHeight.toFloat() / bitmapHeight.toFloat()).roundToInt() 73 | bitmapHeight = maxHeight 74 | isSizeEdit = true 75 | } 76 | if (isSizeEdit) 77 | bitmap = Bitmap.createScaledBitmap(bitmap, bitmapWidth, bitmapHeight, false) 78 | 79 | return EscPosPrinterCommands.bitmapToBytes(bitmap) 80 | } 81 | 82 | companion object { 83 | const val INCH_TO_MM = 25.4f 84 | } 85 | 86 | init { 87 | val printingWidthPx = mmToPx(printerWidthMM) 88 | printerWidthPx = printingWidthPx + printingWidthPx % 8 89 | printerCharSizeWidthPx = printingWidthPx / printerNbrCharactersPerLine 90 | } 91 | } -------------------------------------------------------------------------------- /escposprinter/src/main/java/com/khairo/escposprinter/connection/bluetooth/BluetoothConnection.kt: -------------------------------------------------------------------------------- 1 | package com.khairo.escposprinter.connection.bluetooth 2 | 3 | import android.annotation.SuppressLint 4 | import android.bluetooth.BluetoothDevice 5 | import android.bluetooth.BluetoothSocket 6 | import com.khairo.escposprinter.connection.DeviceConnection 7 | import com.khairo.escposprinter.exceptions.EscPosConnectionException 8 | import java.io.IOException 9 | import java.util.* 10 | 11 | class BluetoothConnection 12 | /** 13 | * Create un instance of BluetoothConnection. 14 | * 15 | * @param device an instance of BluetoothDevice 16 | */( 17 | /** 18 | * Get the instance BluetoothDevice connected. 19 | * 20 | * @return an instance of BluetoothDevice 21 | */ 22 | val device: BluetoothDevice? 23 | ) : DeviceConnection() { 24 | private var socket: BluetoothSocket? = null 25 | 26 | /** 27 | * Check if OutputStream is open. 28 | * 29 | * @return true if is connected 30 | */ 31 | override fun isConnected(): Boolean { 32 | return socket != null && socket!!.isConnected && super.isConnected() 33 | } 34 | 35 | /** 36 | * Start socket connection with the bluetooth device. 37 | */ 38 | @SuppressLint("MissingPermission") 39 | @Throws(EscPosConnectionException::class) 40 | override fun connect(): BluetoothConnection { 41 | if (isConnected()) { 42 | return this 43 | } 44 | if (device == null) { 45 | throw EscPosConnectionException("Bluetooth device is not connected.") 46 | } 47 | val uuids = device.uuids 48 | val uuid = if (uuids != null && uuids.isNotEmpty()) uuids[0].uuid else UUID.randomUUID() 49 | try { 50 | socket = device.createRfcommSocketToServiceRecord(uuid) 51 | socket!!.connect() 52 | stream = socket!!.outputStream 53 | data = ByteArray(0) 54 | } catch (e: IOException) { 55 | e.printStackTrace() 56 | socket = null 57 | stream = null 58 | throw EscPosConnectionException("Unable to connect to bluetooth device.") 59 | } 60 | return this 61 | } 62 | 63 | /** 64 | * Close the socket connection with the bluetooth device. 65 | */ 66 | override fun disconnect(): BluetoothConnection { 67 | data = ByteArray(0) 68 | if (stream != null) { 69 | try { 70 | stream!!.close() 71 | } catch (e: IOException) { 72 | e.printStackTrace() 73 | } 74 | stream = null 75 | } 76 | if (socket != null) { 77 | try { 78 | socket!!.close() 79 | } catch (e: IOException) { 80 | e.printStackTrace() 81 | } 82 | socket = null 83 | } 84 | return this 85 | } 86 | } -------------------------------------------------------------------------------- /app/src/main/java/com/khairo/printer/utils/Extensions.kt: -------------------------------------------------------------------------------- 1 | package com.khairo.printer.utils 2 | 3 | import android.annotation.SuppressLint 4 | import android.content.Context 5 | import com.khairo.coroutines.CoroutinesEscPosPrinter 6 | import com.khairo.escposprinter.textparser.PrinterTextParserImg 7 | import com.khairo.printer.R 8 | import java.text.SimpleDateFormat 9 | import java.util.* 10 | 11 | fun String.replaceNonstandardDigits(): String { 12 | if (this.isEmpty()) { 13 | return this 14 | } 15 | val builder = StringBuilder() 16 | for (element in this) { 17 | if (element.isNonstandardDigit()) { 18 | val numericValue = Character.getNumericValue(element) 19 | if (numericValue >= 0) { 20 | builder.append(numericValue) 21 | } 22 | } else { 23 | builder.append(element) 24 | } 25 | } 26 | return builder.toString() 27 | } 28 | 29 | fun Char.isNonstandardDigit(): Boolean { 30 | return Character.isDigit(this) && this !in '0'..'9' 31 | } 32 | 33 | @SuppressLint("SimpleDateFormat") 34 | fun String.getDateTime(): String = SimpleDateFormat(this).format(Date()).replaceNonstandardDigits() 35 | 36 | 37 | @SuppressLint("UseCompatLoadingForDrawables") 38 | fun Context.printViaWifi( 39 | printer: CoroutinesEscPosPrinter, 40 | orderId: Int, 41 | body: String, 42 | totalBill: Float, 43 | tax: Int, 44 | customer: String = "", 45 | barcode: String 46 | ): CoroutinesEscPosPrinter { 47 | 48 | var test = 49 | "[C]" + PrinterTextParserImg.bitmapToHexadecimalString( 50 | printer, 51 | getDrawable(R.drawable.logo) 52 | ) + "\n" + 53 | "[L]\n" + 54 | "[C]ORDER N°$orderId\n" + 55 | "[L]\n" + 56 | "[C]${"'on' yyyy-MM-dd 'at' HH:mm:ss".getDateTime()}\n" + 57 | "[C]================================\n" + 58 | "[L]\n" + 59 | "[L] Items[R][R]Qty[R][R]Price\n" + 60 | "[L][R]\n" + 61 | "$body\n" + 62 | "[L][R]\n" + 63 | "[C]--------------------------------\n" + 64 | "[R] TOTAL :[R]${totalBill} $\n" 65 | 66 | test += if (tax != 0) "[R] TAX :[R]${tax} %\n" + "[R] GRAND TOTAL :[R]${totalBill * (tax / 100f) + totalBill} $\n" else "" 67 | 68 | test += "[L][R]\n" + 69 | "$customer\n" + 70 | "[C]$barcode\n" + 71 | "[L]\n" + 72 | "[C]VISIT HIS SITE\n" + 73 | "[L]\n" + 74 | "[L]\n" + 75 | "[C]http://www.developpeur-web.khairo.com/\n" + 76 | "[L]\n" + 77 | "[L]\n" + 78 | "[L]\n" + 79 | "[L]\n" + 80 | "[L]\n" 81 | 82 | return printer.setTextToPrint(test) 83 | } 84 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 9 | 10 | 15 | 16 | 21 | 22 | 28 | 29 | 35 | 36 | 41 | 42 | 47 | 48 | 55 | 56 | 62 | 63 | 69 | 70 | 71 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /escposprinter/src/main/java/com/khairo/exeption/PrintingException.kt: -------------------------------------------------------------------------------- 1 | package com.khairo.exeption 2 | 3 | import android.app.AlertDialog 4 | import android.content.Context 5 | import com.khairo.exeption.PrintingException.FINISH_BARCODE_ERROR 6 | import com.khairo.exeption.PrintingException.FINISH_ENCODING_ERROR 7 | import com.khairo.exeption.PrintingException.FINISH_NO_PRINTER 8 | import com.khairo.exeption.PrintingException.FINISH_PARSER_ERROR 9 | import com.khairo.exeption.PrintingException.FINISH_PRINTER_DISCONNECTED 10 | import com.khairo.exeption.PrintingException.FINISH_SUCCESS 11 | import kotlinx.coroutines.Dispatchers 12 | import kotlinx.coroutines.runBlocking 13 | import java.lang.ref.WeakReference 14 | 15 | //try { 16 | // var deviceConnection: TcpDeviceConnection? = printerData.printerConnection 17 | //// if (deviceConnection == null) 18 | //// deviceConnection = BluetoothPrintersConnections.selectFirstPaired() 19 | // 20 | // if (deviceConnection == null) return onPostExecute(CoroutinesEscPosPrint.FINISH_NO_PRINTER) 21 | // 22 | // val context = weakContext.get() ?: return 23 | // val printer = com.khairo.escposprinter.CoroutinesEscPosPrinter( 24 | // context, 25 | // deviceConnection, 26 | // printerData.printerDpi, 27 | // printerData.printerWidthMM, 28 | // printerData.printerNbrCharactersPerLine, 29 | // EscPosCharsetEncoding("Arabic", 22) 30 | // ) 31 | // 32 | // printer.printFormattedTextAndCut(printerData.textToPrint).apply { disconnectPrinter() } 33 | // 34 | //} catch (e: EscPosConnectionException) { 35 | // e.printStackTrace() 36 | // return onPostExecute(CoroutinesEscPosPrint.FINISH_PRINTER_DISCONNECTED) 37 | //} catch (e: EscPosParserException) { 38 | // e.printStackTrace() 39 | // return onPostExecute(CoroutinesEscPosPrint.FINISH_PARSER_ERROR) 40 | //} catch (e: EscPosEncodingException) { 41 | // e.printStackTrace() 42 | // return onPostExecute(CoroutinesEscPosPrint.FINISH_ENCODING_ERROR) 43 | //} catch (e: EscPosBarcodeException) { 44 | // e.printStackTrace() 45 | // return onPostExecute(CoroutinesEscPosPrint.FINISH_BARCODE_ERROR) 46 | //} 47 | //return onPostExecute(FINISH_SUCCESS) 48 | 49 | fun onException(context: Context, result: Int) { 50 | val weakContext: WeakReference = WeakReference(context) 51 | 52 | val newContext = weakContext.get() ?: return 53 | runBlocking(Dispatchers.Main) { 54 | when (result) { 55 | FINISH_SUCCESS -> AlertDialog.Builder(newContext) 56 | .setTitle("Success") 57 | .setMessage("Congratulation ! The text is printed !") 58 | .show() 59 | FINISH_NO_PRINTER -> AlertDialog.Builder(newContext) 60 | .setTitle("No printer") 61 | .setMessage("The application can't find any printer connected.") 62 | .show() 63 | FINISH_PRINTER_DISCONNECTED -> AlertDialog.Builder(newContext) 64 | .setTitle("Broken connection") 65 | .setMessage("Unable to connect the printer.") 66 | .show() 67 | FINISH_PARSER_ERROR -> AlertDialog.Builder(newContext) 68 | .setTitle("Invalid formatted text") 69 | .setMessage("It seems to be an invalid syntax problem.") 70 | .show() 71 | FINISH_ENCODING_ERROR -> AlertDialog.Builder(newContext) 72 | .setTitle("Bad selected encoding") 73 | .setMessage("The selected encoding character returning an error.") 74 | .show() 75 | FINISH_BARCODE_ERROR -> AlertDialog.Builder(newContext) 76 | .setTitle("Invalid barcode") 77 | .setMessage("Data send to be converted to barcode or QR code seems to be invalid.") 78 | .show() 79 | else -> AlertDialog.Builder(newContext) 80 | .setTitle("Unknown error") 81 | .setMessage("Unknown error.") 82 | .show() 83 | } 84 | } 85 | } 86 | 87 | object PrintingException { 88 | 89 | const val FINISH_SUCCESS = 1 90 | const val FINISH_NO_PRINTER = 2 91 | const val FINISH_PRINTER_DISCONNECTED = 3 92 | const val FINISH_PARSER_ERROR = 4 93 | const val FINISH_ENCODING_ERROR = 5 94 | const val FINISH_BARCODE_ERROR = 6 95 | } -------------------------------------------------------------------------------- /escposprinter/src/main/java/com/khairo/async/AsyncEscPosPrint.kt: -------------------------------------------------------------------------------- 1 | package com.khairo.async 2 | 3 | import android.app.AlertDialog 4 | import android.content.Context 5 | import android.os.AsyncTask 6 | import com.khairo.escposprinter.EscPosCharsetEncoding 7 | import com.khairo.escposprinter.EscPosPrinter 8 | import com.khairo.escposprinter.connection.DeviceConnection 9 | import com.khairo.escposprinter.connection.bluetooth.BluetoothPrintersConnections 10 | import com.khairo.escposprinter.exceptions.EscPosBarcodeException 11 | import com.khairo.escposprinter.exceptions.EscPosConnectionException 12 | import com.khairo.escposprinter.exceptions.EscPosEncodingException 13 | import com.khairo.escposprinter.exceptions.EscPosParserException 14 | import java.lang.ref.WeakReference 15 | 16 | abstract class AsyncEscPosPrint(context: Context?) : AsyncTask() { 17 | private var weakContext: WeakReference = WeakReference(context) 18 | 19 | override fun doInBackground(vararg printersData: AsyncEscPosPrinter): Int { 20 | if (printersData.isEmpty()) 21 | return FINISH_NO_PRINTER 22 | 23 | val printerData = printersData[0] 24 | try { 25 | var deviceConnection: DeviceConnection? = printerData.printerConnection 26 | if (deviceConnection == null) 27 | deviceConnection = BluetoothPrintersConnections.selectFirstPaired() 28 | 29 | if (deviceConnection == null) return FINISH_NO_PRINTER 30 | 31 | val printer = EscPosPrinter( 32 | deviceConnection, 33 | printerData.printerDpi, 34 | printerData.printerWidthMM, 35 | printerData.printerNbrCharactersPerLine, 36 | EscPosCharsetEncoding("Arabic", 22) 37 | ) 38 | 39 | printer.printFormattedTextAndCut(printerData.textToPrint).apply { disconnectPrinter() } 40 | 41 | } catch (e: EscPosConnectionException) { 42 | e.printStackTrace() 43 | return FINISH_PRINTER_DISCONNECTED 44 | } catch (e: EscPosParserException) { 45 | e.printStackTrace() 46 | return FINISH_PARSER_ERROR 47 | } catch (e: EscPosEncodingException) { 48 | e.printStackTrace() 49 | return FINISH_ENCODING_ERROR 50 | } catch (e: EscPosBarcodeException) { 51 | e.printStackTrace() 52 | return FINISH_BARCODE_ERROR 53 | } 54 | return FINISH_SUCCESS 55 | } 56 | 57 | override fun onPostExecute(result: Int) { 58 | val context = weakContext.get() ?: return 59 | when (result) { 60 | FINISH_SUCCESS -> 61 | AlertDialog.Builder(context) 62 | .setTitle("Success") 63 | .setMessage("Congratulation ! The text is printed !") 64 | .show() 65 | FINISH_NO_PRINTER -> AlertDialog.Builder(context) 66 | .setTitle("No printer") 67 | .setMessage("The application can't find any printer connected.") 68 | .show() 69 | FINISH_PRINTER_DISCONNECTED -> AlertDialog.Builder(context) 70 | .setTitle("Broken connection") 71 | .setMessage("Unable to connect the printer.") 72 | .show() 73 | FINISH_PARSER_ERROR -> AlertDialog.Builder(context) 74 | .setTitle("Invalid formatted text") 75 | .setMessage("It seems to be an invalid syntax problem.") 76 | .show() 77 | FINISH_ENCODING_ERROR -> AlertDialog.Builder(context) 78 | .setTitle("Bad selected encoding") 79 | .setMessage("The selected encoding character returning an error.") 80 | .show() 81 | FINISH_BARCODE_ERROR -> AlertDialog.Builder(context) 82 | .setTitle("Invalid barcode") 83 | .setMessage("Data send to be converted to barcode or QR code seems to be invalid.") 84 | .show() 85 | } 86 | } 87 | 88 | companion object { 89 | const val FINISH_SUCCESS = 1 90 | const val FINISH_NO_PRINTER = 2 91 | const val FINISH_PRINTER_DISCONNECTED = 3 92 | const val FINISH_PARSER_ERROR = 4 93 | const val FINISH_ENCODING_ERROR = 5 94 | const val FINISH_BARCODE_ERROR = 6 95 | } 96 | 97 | } -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 20 | 22 | 23 | 135 | 136 | 138 | 139 | -------------------------------------------------------------------------------- /escposprinter/src/main/java/com/khairo/escposprinter/textparser/PrinterTextParserBarcode.kt: -------------------------------------------------------------------------------- 1 | package com.khairo.escposprinter.textparser 2 | 3 | import com.khairo.escposprinter.EscPosPrinterCommands 4 | import com.khairo.escposprinter.barcode.* 5 | import com.khairo.escposprinter.exceptions.EscPosEncodingException 6 | import com.khairo.escposprinter.exceptions.EscPosParserException 7 | import java.util.* 8 | 9 | class PrinterTextParserBarcode( 10 | printerTextParserColumn: PrinterTextParserColumn, 11 | textAlign: String?, 12 | barcodeAttributes: Hashtable, 13 | var code: String 14 | ) : IPrinterTextParserElement { 15 | private var barcode: Barcode? = null 16 | private val length: Int 17 | private var align: ByteArray 18 | 19 | /** 20 | * Get the barcode width in char length. 21 | * 22 | * @return int 23 | */ 24 | @Throws(EscPosEncodingException::class) 25 | override fun length(): Int { 26 | return length 27 | } 28 | 29 | /** 30 | * Print barcode 31 | * 32 | * @param printerSocket Instance of EscPosPrinterCommands 33 | * @return this Fluent method 34 | */ 35 | override fun print(printerSocket: EscPosPrinterCommands?): PrinterTextParserBarcode { 36 | printerSocket!!.setAlign(align).printBarcode(barcode!!) 37 | return this 38 | } 39 | 40 | init { 41 | val printer = printerTextParserColumn.line.textParser.printer 42 | code = code.trim { it <= ' ' } 43 | align = EscPosPrinterCommands.TEXT_ALIGN_LEFT 44 | when (textAlign) { 45 | PrinterTextParser.TAGS_ALIGN_CENTER -> align = EscPosPrinterCommands.TEXT_ALIGN_CENTER 46 | PrinterTextParser.TAGS_ALIGN_RIGHT -> align = EscPosPrinterCommands.TEXT_ALIGN_RIGHT 47 | } 48 | length = printer.printerNbrCharactersPerLine 49 | var height = 10f 50 | if (barcodeAttributes.containsKey(PrinterTextParser.ATTR_BARCODE_HEIGHT)) { 51 | val barCodeAttribute = barcodeAttributes[PrinterTextParser.ATTR_BARCODE_HEIGHT] 52 | ?: throw EscPosParserException("Invalid barcode attribute: " + PrinterTextParser.ATTR_BARCODE_HEIGHT) 53 | height = try { 54 | barCodeAttribute.toFloat() 55 | } catch (nfe: NumberFormatException) { 56 | throw EscPosParserException("Invalid barcode " + PrinterTextParser.ATTR_BARCODE_HEIGHT + " value") 57 | } 58 | } 59 | var width = 0f 60 | if (barcodeAttributes.containsKey(PrinterTextParser.ATTR_BARCODE_WIDTH)) { 61 | val barCodeAttribute = barcodeAttributes[PrinterTextParser.ATTR_BARCODE_WIDTH] 62 | ?: throw EscPosParserException("Invalid barcode attribute: " + PrinterTextParser.ATTR_BARCODE_WIDTH) 63 | width = try { 64 | barCodeAttribute.toFloat() 65 | } catch (nfe: NumberFormatException) { 66 | throw EscPosParserException("Invalid barcode " + PrinterTextParser.ATTR_BARCODE_WIDTH + " value") 67 | } 68 | } 69 | var textPosition = EscPosPrinterCommands.BARCODE_TEXT_POSITION_BELOW 70 | if (barcodeAttributes.containsKey(PrinterTextParser.ATTR_BARCODE_TEXT_POSITION)) { 71 | val barCodeAttribute = barcodeAttributes[PrinterTextParser.ATTR_BARCODE_TEXT_POSITION] 72 | ?: throw EscPosParserException("Invalid barcode attribute: " + PrinterTextParser.ATTR_BARCODE_TEXT_POSITION) 73 | when (barCodeAttribute) { 74 | PrinterTextParser.ATTR_BARCODE_TEXT_POSITION_NONE -> textPosition = 75 | EscPosPrinterCommands.BARCODE_TEXT_POSITION_NONE 76 | PrinterTextParser.ATTR_BARCODE_TEXT_POSITION_ABOVE -> textPosition = 77 | EscPosPrinterCommands.BARCODE_TEXT_POSITION_ABOVE 78 | } 79 | } 80 | var barcodeType: String? = PrinterTextParser.ATTR_BARCODE_TYPE_EAN13 81 | if (barcodeAttributes.containsKey(PrinterTextParser.ATTR_BARCODE_TYPE)) { 82 | barcodeType = barcodeAttributes[PrinterTextParser.ATTR_BARCODE_TYPE] 83 | if (barcodeType == null) { 84 | throw EscPosParserException("Invalid barcode attribute : " + PrinterTextParser.ATTR_BARCODE_TYPE) 85 | } 86 | } 87 | barcode = when (barcodeType) { 88 | PrinterTextParser.ATTR_BARCODE_TYPE_EAN8 -> BarcodeEAN8( 89 | printer, 90 | code, 91 | width, 92 | height, 93 | textPosition 94 | ) 95 | PrinterTextParser.ATTR_BARCODE_TYPE_EAN13 -> BarcodeEAN13( 96 | printer, 97 | code, 98 | width, 99 | height, 100 | textPosition 101 | ) 102 | PrinterTextParser.ATTR_BARCODE_TYPE_UPCA -> BarcodeUPCA( 103 | printer, 104 | code, 105 | width, 106 | height, 107 | textPosition 108 | ) 109 | PrinterTextParser.ATTR_BARCODE_TYPE_UPCE -> BarcodeUPCE( 110 | printer, 111 | code, 112 | width, 113 | height, 114 | textPosition 115 | ) 116 | PrinterTextParser.ATTR_BARCODE_TYPE_128 -> Barcode128( 117 | printer, 118 | code, 119 | width, 120 | height, 121 | textPosition 122 | ) 123 | else -> throw EscPosParserException("Invalid barcode attribute : " + PrinterTextParser.ATTR_BARCODE_TYPE) 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /escposprinter/src/main/java/com/khairo/escposprinter/textparser/CoroutinesPrinterTextParserBarcode.kt: -------------------------------------------------------------------------------- 1 | package com.khairo.escposprinter.textparser 2 | 3 | import com.khairo.escposprinter.CoroutinesEscPosPrinterCommands 4 | import com.khairo.escposprinter.EscPosPrinterCommands 5 | import com.khairo.escposprinter.barcode.* 6 | import com.khairo.escposprinter.exceptions.EscPosEncodingException 7 | import com.khairo.escposprinter.exceptions.EscPosParserException 8 | import java.util.* 9 | 10 | class CoroutinesPrinterTextParserBarcode( 11 | printerTextParserColumn: CoroutinesPrinterTextParserColumn, 12 | textAlign: String?, 13 | barcodeAttributes: Hashtable, 14 | var code: String 15 | ) : CoroutinesIPrinterTextParserElement { 16 | private var barcode: Barcode? = null 17 | private val length: Int 18 | private var align: ByteArray 19 | 20 | /** 21 | * Get the barcode width in char length. 22 | * 23 | * @return int 24 | */ 25 | @Throws(EscPosEncodingException::class) 26 | override fun length(): Int { 27 | return length 28 | } 29 | 30 | /** 31 | * Print barcode 32 | * 33 | * @param printerSocket Instance of EscPosPrinterCommands 34 | * @return this Fluent method 35 | */ 36 | override suspend fun print(printerSocket: CoroutinesEscPosPrinterCommands?): CoroutinesPrinterTextParserBarcode { 37 | printerSocket!!.setAlign(align).printBarcode(barcode!!) 38 | return this 39 | } 40 | 41 | init { 42 | val printer = printerTextParserColumn.line.textParser.printer 43 | code = code.trim { it <= ' ' } 44 | align = EscPosPrinterCommands.TEXT_ALIGN_LEFT 45 | when (textAlign) { 46 | PrinterTextParser.TAGS_ALIGN_CENTER -> align = EscPosPrinterCommands.TEXT_ALIGN_CENTER 47 | PrinterTextParser.TAGS_ALIGN_RIGHT -> align = EscPosPrinterCommands.TEXT_ALIGN_RIGHT 48 | } 49 | length = printer.printerNbrCharactersPerLine 50 | var height = 10f 51 | if (barcodeAttributes.containsKey(PrinterTextParser.ATTR_BARCODE_HEIGHT)) { 52 | val barCodeAttribute = barcodeAttributes[PrinterTextParser.ATTR_BARCODE_HEIGHT] 53 | ?: throw EscPosParserException("Invalid barcode attribute: " + PrinterTextParser.ATTR_BARCODE_HEIGHT) 54 | height = try { 55 | barCodeAttribute.toFloat() 56 | } catch (nfe: NumberFormatException) { 57 | throw EscPosParserException("Invalid barcode " + PrinterTextParser.ATTR_BARCODE_HEIGHT + " value") 58 | } 59 | } 60 | var width = 0f 61 | if (barcodeAttributes.containsKey(PrinterTextParser.ATTR_BARCODE_WIDTH)) { 62 | val barCodeAttribute = barcodeAttributes[PrinterTextParser.ATTR_BARCODE_WIDTH] 63 | ?: throw EscPosParserException("Invalid barcode attribute: " + PrinterTextParser.ATTR_BARCODE_WIDTH) 64 | width = try { 65 | barCodeAttribute.toFloat() 66 | } catch (nfe: NumberFormatException) { 67 | throw EscPosParserException("Invalid barcode " + PrinterTextParser.ATTR_BARCODE_WIDTH + " value") 68 | } 69 | } 70 | var textPosition = EscPosPrinterCommands.BARCODE_TEXT_POSITION_BELOW 71 | if (barcodeAttributes.containsKey(PrinterTextParser.ATTR_BARCODE_TEXT_POSITION)) { 72 | val barCodeAttribute = barcodeAttributes[PrinterTextParser.ATTR_BARCODE_TEXT_POSITION] 73 | ?: throw EscPosParserException("Invalid barcode attribute: " + PrinterTextParser.ATTR_BARCODE_TEXT_POSITION) 74 | when (barCodeAttribute) { 75 | PrinterTextParser.ATTR_BARCODE_TEXT_POSITION_NONE -> textPosition = 76 | EscPosPrinterCommands.BARCODE_TEXT_POSITION_NONE 77 | PrinterTextParser.ATTR_BARCODE_TEXT_POSITION_ABOVE -> textPosition = 78 | EscPosPrinterCommands.BARCODE_TEXT_POSITION_ABOVE 79 | } 80 | } 81 | var barcodeType: String? = PrinterTextParser.ATTR_BARCODE_TYPE_EAN13 82 | if (barcodeAttributes.containsKey(PrinterTextParser.ATTR_BARCODE_TYPE)) { 83 | barcodeType = barcodeAttributes[PrinterTextParser.ATTR_BARCODE_TYPE] 84 | if (barcodeType == null) { 85 | throw EscPosParserException("Invalid barcode attribute : " + PrinterTextParser.ATTR_BARCODE_TYPE) 86 | } 87 | } 88 | barcode = when (barcodeType) { 89 | PrinterTextParser.ATTR_BARCODE_TYPE_EAN8 -> BarcodeEAN8( 90 | printer, 91 | code, 92 | width, 93 | height, 94 | textPosition 95 | ) 96 | PrinterTextParser.ATTR_BARCODE_TYPE_EAN13 -> BarcodeEAN13( 97 | printer, 98 | code, 99 | width, 100 | height, 101 | textPosition 102 | ) 103 | PrinterTextParser.ATTR_BARCODE_TYPE_UPCA -> BarcodeUPCA( 104 | printer, 105 | code, 106 | width, 107 | height, 108 | textPosition 109 | ) 110 | PrinterTextParser.ATTR_BARCODE_TYPE_UPCE -> BarcodeUPCE( 111 | printer, 112 | code, 113 | width, 114 | height, 115 | textPosition 116 | ) 117 | PrinterTextParser.ATTR_BARCODE_TYPE_128 -> Barcode128( 118 | printer, 119 | code, 120 | width, 121 | height, 122 | textPosition 123 | ) 124 | else -> throw EscPosParserException("Invalid barcode attribute : " + PrinterTextParser.ATTR_BARCODE_TYPE) 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /escposprinter/src/main/java/com/khairo/escposprinter/textparser/PrinterTextParserImg.java: -------------------------------------------------------------------------------- 1 | package com.khairo.escposprinter.textparser; 2 | 3 | import android.graphics.Bitmap; 4 | import android.graphics.drawable.BitmapDrawable; 5 | import android.graphics.drawable.Drawable; 6 | 7 | import com.khairo.escposprinter.EscPosPrinter; 8 | import com.khairo.escposprinter.EscPosPrinterCommands; 9 | import com.khairo.escposprinter.EscPosPrinterSize; 10 | import com.khairo.escposprinter.exceptions.EscPosEncodingException; 11 | 12 | 13 | public class PrinterTextParserImg implements IPrinterTextParserElement { 14 | 15 | /** 16 | * Convert Drawable instance to a hexadecimal string of the image data. 17 | * 18 | * @param printerSize A EscPosPrinterSize instance that will print the image. 19 | * @param drawable Drawable instance to be converted. 20 | * @return A hexadecimal string of the image data. Empty string if Drawable cannot be cast to BitmapDrawable. 21 | */ 22 | public static String bitmapToHexadecimalString(EscPosPrinterSize printerSize, Drawable drawable) { 23 | if (drawable instanceof BitmapDrawable) { 24 | return PrinterTextParserImg.bitmapToHexadecimalString(printerSize, (BitmapDrawable) drawable); 25 | } 26 | return ""; 27 | } 28 | 29 | /** 30 | * Convert BitmapDrawable instance to a hexadecimal string of the image data. 31 | * 32 | * @param printerSize A EscPosPrinterSize instance that will print the image. 33 | * @param bitmapDrawable BitmapDrawable instance to be converted. 34 | * @return A hexadecimal string of the image data. 35 | */ 36 | public static String bitmapToHexadecimalString(EscPosPrinterSize printerSize, BitmapDrawable bitmapDrawable) { 37 | return PrinterTextParserImg.bitmapToHexadecimalString(printerSize, bitmapDrawable.getBitmap()); 38 | } 39 | 40 | /** 41 | * Convert Bitmap instance to a hexadecimal string of the image data. 42 | * 43 | * @param printerSize A EscPosPrinterSize instance that will print the image. 44 | * @param bitmap Bitmap instance to be converted. 45 | * @return A hexadecimal string of the image data. 46 | */ 47 | public static String bitmapToHexadecimalString(EscPosPrinterSize printerSize, Bitmap bitmap) { 48 | return PrinterTextParserImg.bytesToHexadecimalString(printerSize.bitmapToBytes(bitmap)); 49 | } 50 | 51 | /** 52 | * Convert byte array to a hexadecimal string of the image data. 53 | * 54 | * @param bytes Bytes contain the image in ESC/POS command. 55 | * @return A hexadecimal string of the image data. 56 | */ 57 | public static String bytesToHexadecimalString(byte[] bytes) { 58 | StringBuilder imageHexString = new StringBuilder(); 59 | for (byte aByte : bytes) { 60 | String hexString = Integer.toHexString(aByte & 0xFF); 61 | if (hexString.length() == 1) { 62 | hexString = "0" + hexString; 63 | } 64 | imageHexString.append(hexString); 65 | } 66 | return imageHexString.toString(); 67 | } 68 | 69 | /** 70 | * Convert hexadecimal string of the image data to bytes ESC/POS command. 71 | * 72 | * @param hexString Hexadecimal string of the image data. 73 | * @return Bytes contain the image in ESC/POS command. 74 | */ 75 | public static byte[] hexadecimalStringToBytes(String hexString) throws NumberFormatException { 76 | byte[] bytes = new byte[hexString.length() / 2]; 77 | for (int i = 0; i < bytes.length; i++) { 78 | int pos = i * 2; 79 | bytes[i] = (byte) Integer.parseInt(hexString.substring(pos, pos + 2), 16); 80 | } 81 | 82 | return bytes; 83 | } 84 | 85 | 86 | private int length; 87 | private byte[] image; 88 | 89 | /** 90 | * Create new instance of PrinterTextParserImg. 91 | * 92 | * @param printerTextParserColumn Parent PrinterTextParserColumn instance. 93 | * @param textAlign Set the image alignment. Use PrinterTextParser.TAGS_ALIGN_... constants. 94 | * @param hexadecimalString Hexadecimal string of the image data. 95 | */ 96 | public PrinterTextParserImg(PrinterTextParserColumn printerTextParserColumn, String textAlign, String hexadecimalString) { 97 | this(printerTextParserColumn, textAlign, PrinterTextParserImg.hexadecimalStringToBytes(hexadecimalString)); 98 | } 99 | 100 | /** 101 | * Create new instance of PrinterTextParserImg. 102 | * 103 | * @param printerTextParserColumn Parent PrinterTextParserColumn instance. 104 | * @param textAlign Set the image alignment. Use PrinterTextParser.TAGS_ALIGN_... constants. 105 | * @param image Bytes contain the image in ESC/POS command. 106 | */ 107 | public PrinterTextParserImg(PrinterTextParserColumn printerTextParserColumn, String textAlign, byte[] image) { 108 | EscPosPrinter printer = printerTextParserColumn.getLine().getTextParser().getPrinter(); 109 | 110 | int 111 | byteWidth = ((int) image[4] & 0xFF) + ((int) image[5] & 0xFF) * 256, 112 | width = byteWidth * 8, 113 | height = ((int) image[6] & 0xFF) + ((int) image[7] & 0xFF) * 256, 114 | nbrByteDiff = (int) Math.floor(((float) (printer.getPrinterWidthPx() - width)) / 8f), 115 | nbrWhiteByteToInsert = 0; 116 | 117 | switch (textAlign) { 118 | case PrinterTextParser.TAGS_ALIGN_CENTER: 119 | nbrWhiteByteToInsert = Math.round(((float) nbrByteDiff) / 2f); 120 | break; 121 | case PrinterTextParser.TAGS_ALIGN_RIGHT: 122 | nbrWhiteByteToInsert = nbrByteDiff; 123 | break; 124 | } 125 | 126 | if (nbrWhiteByteToInsert > 0) { 127 | int newByteWidth = byteWidth + nbrWhiteByteToInsert; 128 | byte[] newImage = EscPosPrinterCommands.initImageCommand(newByteWidth, height); 129 | for (int i = 0; i < height; i++) { 130 | System.arraycopy(image, (byteWidth * i + 8), newImage, (newByteWidth * i + nbrWhiteByteToInsert + 8), byteWidth); 131 | } 132 | image = newImage; 133 | } 134 | 135 | this.length = (int) Math.ceil(((float) byteWidth * 8) / ((float) printer.getPrinterCharSizeWidthPx())); 136 | this.image = image; 137 | } 138 | 139 | /** 140 | * Get the image width in char length. 141 | * 142 | * @return int 143 | */ 144 | @Override 145 | public int length() throws EscPosEncodingException { 146 | return this.length; 147 | } 148 | 149 | /** 150 | * Print image 151 | * 152 | * @param printerSocket Instance of EscPosPrinterCommands 153 | * @return this Fluent method 154 | */ 155 | @Override 156 | public PrinterTextParserImg print(EscPosPrinterCommands printerSocket) { 157 | printerSocket.printImage(this.image); 158 | return this; 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /escposprinter/src/main/java/com/khairo/escposprinter/textparser/CoroutinesPrinterTextParserImg.kt: -------------------------------------------------------------------------------- 1 | package com.khairo.escposprinter.textparser 2 | 3 | import android.graphics.Bitmap 4 | import android.graphics.drawable.BitmapDrawable 5 | import android.graphics.drawable.Drawable 6 | import com.khairo.escposprinter.CoroutinesEscPosPrinterCommands 7 | import com.khairo.escposprinter.EscPosPrinterCommands 8 | import com.khairo.escposprinter.EscPosPrinterSize 9 | import com.khairo.escposprinter.exceptions.EscPosEncodingException 10 | import kotlin.experimental.and 11 | 12 | open class CoroutinesPrinterTextParserImg( 13 | printerTextParserColumn: CoroutinesPrinterTextParserColumn, 14 | textAlign: String?, 15 | image: ByteArray 16 | ) : CoroutinesIPrinterTextParserElement { 17 | private val length: Int 18 | private val image: ByteArray 19 | 20 | /** 21 | * Create new instance of PrinterTextParserImg. 22 | * 23 | * @param printerTextParserColumn Parent PrinterTextParserColumn instance. 24 | * @param textAlign Set the image alignment. Use PrinterTextParser.TAGS_ALIGN_... constants. 25 | * @param hexadecimalString Hexadecimal string of the image data. 26 | */ 27 | constructor( 28 | printerTextParserColumn: CoroutinesPrinterTextParserColumn, 29 | textAlign: String?, 30 | hexadecimalString: String 31 | ) : this(printerTextParserColumn, textAlign, hexadecimalStringToBytes(hexadecimalString)) { 32 | } 33 | 34 | /** 35 | * Get the image width in char length. 36 | * 37 | * @return int 38 | */ 39 | @Throws(EscPosEncodingException::class) 40 | override fun length(): Int { 41 | return length 42 | } 43 | 44 | 45 | /** 46 | * Print image 47 | * 48 | * @param printerSocket Instance of EscPosPrinterCommands 49 | * @return this Fluent method 50 | */ 51 | override suspend fun print(printerSocket: CoroutinesEscPosPrinterCommands?): CoroutinesIPrinterTextParserElement? { 52 | printerSocket!!.printImage(image) 53 | return this 54 | } 55 | 56 | companion object { 57 | /** 58 | * Convert Drawable instance to a hexadecimal string of the image data. 59 | * 60 | * @param printerSize A EscPosPrinterSize instance that will print the image. 61 | * @param drawable Drawable instance to be converted. 62 | * @return A hexadecimal string of the image data. Empty string if Drawable cannot be cast to BitmapDrawable. 63 | */ 64 | fun bitmapToHexadecimalString(printerSize: EscPosPrinterSize, drawable: Drawable?): String { 65 | return if (drawable is BitmapDrawable) { 66 | bitmapToHexadecimalString( 67 | printerSize, 68 | drawable 69 | ) 70 | } else "" 71 | } 72 | 73 | /** 74 | * Convert BitmapDrawable instance to a hexadecimal string of the image data. 75 | * 76 | * @param printerSize A EscPosPrinterSize instance that will print the image. 77 | * @param bitmapDrawable BitmapDrawable instance to be converted. 78 | * @return A hexadecimal string of the image data. 79 | */ 80 | fun bitmapToHexadecimalString( 81 | printerSize: EscPosPrinterSize, 82 | bitmapDrawable: BitmapDrawable 83 | ): String { 84 | return bitmapToHexadecimalString(printerSize, bitmapDrawable.bitmap) 85 | } 86 | 87 | /** 88 | * Convert Bitmap instance to a hexadecimal string of the image data. 89 | * 90 | * @param printerSize A EscPosPrinterSize instance that will print the image. 91 | * @param bitmap Bitmap instance to be converted. 92 | * @return A hexadecimal string of the image data. 93 | */ 94 | fun bitmapToHexadecimalString(printerSize: EscPosPrinterSize, bitmap: Bitmap?): String { 95 | return bytesToHexadecimalString( 96 | printerSize.bitmapToBytes( 97 | bitmap!! 98 | ) 99 | ) 100 | } 101 | 102 | /** 103 | * Convert byte array to a hexadecimal string of the image data. 104 | * 105 | * @param bytes Bytes contain the image in ESC/POS command. 106 | * @return A hexadecimal string of the image data. 107 | */ 108 | fun bytesToHexadecimalString(bytes: ByteArray): String { 109 | val imageHexString = StringBuilder() 110 | for (aByte in bytes) { 111 | var hexString = Integer.toHexString((aByte and 0xFF.toByte()).toInt()) 112 | if (hexString.length == 1) { 113 | hexString = "0$hexString" 114 | } 115 | imageHexString.append(hexString) 116 | } 117 | return imageHexString.toString() 118 | } 119 | 120 | /** 121 | * Convert hexadecimal string of the image data to bytes ESC/POS command. 122 | * 123 | * @param hexString Hexadecimal string of the image data. 124 | * @return Bytes contain the image in ESC/POS command. 125 | */ 126 | @Throws(NumberFormatException::class) 127 | fun hexadecimalStringToBytes(hexString: String): ByteArray { 128 | val bytes = ByteArray(hexString.length / 2) 129 | for (i in bytes.indices) { 130 | val pos = i * 2 131 | bytes[i] = hexString.substring(pos, pos + 2).toInt(16).toByte() 132 | } 133 | return bytes 134 | } 135 | } 136 | 137 | /** 138 | * Create new instance of PrinterTextParserImg. 139 | * 140 | * @param printerTextParserColumn Parent PrinterTextParserColumn instance. 141 | * @param textAlign Set the image alignment. Use PrinterTextParser.TAGS_ALIGN_... constants. 142 | * @param image Bytes contain the image in ESC/POS command. 143 | */ 144 | init { 145 | var image = image 146 | val printer = printerTextParserColumn.line.textParser.printer 147 | val byteWidth = (image[4].toInt() and 0xFF) + (image[5].toInt() and 0xFF) * 256 148 | val width = byteWidth * 8 149 | val height = (image[6].toInt() and 0xFF) + (image[7].toInt() and 0xFF) * 256 150 | val nbrByteDiff = Math.floor(((printer.printerWidthPx - width).toFloat() / 8f).toDouble()) 151 | .toInt() 152 | var nbrWhiteByteToInsert = 0 153 | when (textAlign) { 154 | PrinterTextParser.TAGS_ALIGN_CENTER -> nbrWhiteByteToInsert = Math.round( 155 | nbrByteDiff.toFloat() / 2f 156 | ) 157 | PrinterTextParser.TAGS_ALIGN_RIGHT -> nbrWhiteByteToInsert = nbrByteDiff 158 | } 159 | if (nbrWhiteByteToInsert > 0) { 160 | val newByteWidth = byteWidth + nbrWhiteByteToInsert 161 | val newImage = EscPosPrinterCommands.initImageCommand(newByteWidth, height) 162 | for (i in 0 until height) { 163 | System.arraycopy( 164 | image, 165 | byteWidth * i + 8, 166 | newImage, 167 | newByteWidth * i + nbrWhiteByteToInsert + 8, 168 | byteWidth 169 | ) 170 | } 171 | image = newImage 172 | } 173 | length = 174 | Math.ceil((byteWidth.toFloat() * 8 / printer.printerCharSizeWidthPx.toFloat()).toDouble()) 175 | .toInt() 176 | this.image = image 177 | } 178 | } -------------------------------------------------------------------------------- /escposprinter/src/main/java/com/khairo/escposprinter/CoroutinesEscPosPrinter.kt: -------------------------------------------------------------------------------- 1 | package com.khairo.escposprinter 2 | 3 | import android.content.Context 4 | import com.khairo.escposprinter.connection.tcp.TcpDeviceConnection 5 | import com.khairo.escposprinter.exceptions.EscPosBarcodeException 6 | import com.khairo.escposprinter.exceptions.EscPosEncodingException 7 | import com.khairo.escposprinter.exceptions.EscPosParserException 8 | import com.khairo.escposprinter.textparser.* 9 | import com.khairo.exeption.PrintingException.FINISH_NO_PRINTER 10 | import com.khairo.exeption.onException 11 | 12 | class CoroutinesEscPosPrinter( 13 | var printer: CoroutinesEscPosPrinterCommands?, 14 | printerDpi: Int, 15 | printerWidthMM: Float, 16 | printerNbrCharactersPerLine: Int 17 | ) : EscPosPrinterSize(printerDpi, printerWidthMM, printerNbrCharactersPerLine) { 18 | 19 | /** 20 | * Create new instance of EscPosPrinter. 21 | * 22 | * @param printerConnection Instance of class which implement DeviceConnection 23 | * @param printerDpi DPI of the connected printer 24 | * @param printerWidthMM Printing width in millimeters 25 | * @param printerNbrCharactersPerLine The maximum number of characters that can be printed on a line. 26 | */ 27 | constructor( 28 | printerConnection: TcpDeviceConnection?, 29 | printerDpi: Int, 30 | printerWidthMM: Float, 31 | printerNbrCharactersPerLine: Int 32 | ) : this( 33 | printerConnection?.let { CoroutinesEscPosPrinterCommands(it) }, 34 | printerDpi, 35 | printerWidthMM, 36 | printerNbrCharactersPerLine 37 | ) 38 | 39 | /** 40 | * Create new instance of EscPosPrinter. 41 | * 42 | * @param printerConnection Instance of class which implement DeviceConnection 43 | * @param printerDpi DPI of the connected printer 44 | * @param printerWidthMM Printing width in millimeters 45 | * @param printerNbrCharactersPerLine The maximum number of characters that can be printed on a line. 46 | * @param charsetEncoding Set the charset encoding. 47 | */ 48 | constructor( 49 | printerConnection: TcpDeviceConnection?, 50 | printerDpi: Int, 51 | printerWidthMM: Float, 52 | printerNbrCharactersPerLine: Int, 53 | charsetEncoding: EscPosCharsetEncoding? 54 | ) : this( 55 | printerConnection?.let { CoroutinesEscPosPrinterCommands(it, charsetEncoding) }, 56 | printerDpi, 57 | printerWidthMM, 58 | printerNbrCharactersPerLine 59 | ) 60 | 61 | /** 62 | * Close the connection with the printer. 63 | * 64 | * @return Fluent interface 65 | */ 66 | suspend fun disconnectPrinter(): CoroutinesEscPosPrinter { 67 | if (printer != null) { 68 | printer!!.disconnect() 69 | printer = null 70 | } 71 | return this 72 | } 73 | /** 74 | * Print a formatted text. Read the README.md for more information about text formatting options. 75 | * 76 | * @param text Formatted text to be printed. 77 | * @param mmFeedPaper millimeter distance feed paper at the end. 78 | * @return Fluent interface 79 | */ 80 | /** 81 | * Print a formatted text. Read the README.md for more information about text formatting options. 82 | * 83 | * @param text Formatted text to be printed. 84 | * @return Fluent interface 85 | */ 86 | @JvmOverloads 87 | @Throws( 88 | EscPosParserException::class, 89 | EscPosEncodingException::class, 90 | EscPosBarcodeException::class 91 | ) 92 | suspend fun printFormattedText( 93 | context: Context, 94 | text: String?, 95 | mmFeedPaper: Float = 20f 96 | ): CoroutinesEscPosPrinter { 97 | return this.printFormattedText(context, text, mmToPx(mmFeedPaper)) 98 | } 99 | 100 | /** 101 | * Print a formatted text. Read the README.md for more information about text formatting options. 102 | * 103 | * @param text Formatted text to be printed. 104 | * @param dotsFeedPaper distance feed paper at the end. 105 | * @return Fluent interface 106 | */ 107 | @Throws( 108 | EscPosParserException::class, 109 | EscPosEncodingException::class, 110 | EscPosBarcodeException::class 111 | ) 112 | suspend fun printFormattedText( 113 | context: Context, 114 | text: String?, 115 | dotsFeedPaper: Int 116 | ): CoroutinesEscPosPrinter { 117 | if (printer == null || printerNbrCharactersPerLine == 0) { 118 | return this 119 | } 120 | 121 | val textParser = CoroutinesPrinterTextParser(this) 122 | val linesParsed = textParser 123 | .setFormattedText(text!!) 124 | .parse() 125 | printer!!.reset() 126 | for (line in linesParsed) { 127 | val columns = line!!.columns 128 | var lastElement: CoroutinesIPrinterTextParserElement? = null 129 | for (column in columns) { 130 | val elements = column!!.elements 131 | for (element in elements) { 132 | element!!.print(printer) 133 | lastElement = element 134 | } 135 | } 136 | if (lastElement is CoroutinesPrinterTextParserString) { 137 | printer!!.newLine(context) 138 | } 139 | } 140 | printer!!.feedPaper(context, dotsFeedPaper) 141 | return this 142 | } 143 | /** 144 | * Print a formatted text and cut the paper. Read the README.md for more information about text formatting options. 145 | * 146 | * @param text Formatted text to be printed. 147 | * @param mmFeedPaper millimeter distance feed paper at the end. 148 | * @return Fluent interface 149 | */ 150 | /** 151 | * Print a formatted text and cut the paper. Read the README.md for more information about text formatting options. 152 | * 153 | * @param text Formatted text to be printed. 154 | * @return Fluent interface 155 | */ 156 | @JvmOverloads 157 | @Throws( 158 | EscPosParserException::class, 159 | EscPosEncodingException::class, 160 | EscPosBarcodeException::class 161 | ) 162 | suspend fun printFormattedTextAndCut( 163 | context: Context, 164 | text: String?, 165 | mmFeedPaper: Float = 20f 166 | ): CoroutinesEscPosPrinter { 167 | return this.printFormattedTextAndCut(context, text, mmToPx(mmFeedPaper)) 168 | } 169 | 170 | /** 171 | * Print a formatted text and cut the paper. Read the README.md for more information about text formatting options. 172 | * 173 | * @param text Formatted text to be printed. 174 | * @param dotsFeedPaper distance feed paper at the end. 175 | * @return Fluent interface 176 | */ 177 | @Throws( 178 | EscPosParserException::class, 179 | EscPosEncodingException::class, 180 | EscPosBarcodeException::class 181 | ) 182 | suspend fun printFormattedTextAndCut( 183 | context: Context, 184 | text: String?, 185 | dotsFeedPaper: Int 186 | ): CoroutinesEscPosPrinter { 187 | if (printer == null || printerNbrCharactersPerLine == 0) { 188 | onException(context, FINISH_NO_PRINTER) 189 | return this 190 | } 191 | 192 | this.printFormattedText(context, text, dotsFeedPaper) 193 | printer!!.cutPaper(context) 194 | return this 195 | } 196 | 197 | /** 198 | * @return Charset encoding 199 | */ 200 | val encoding: EscPosCharsetEncoding 201 | get() = printer!!.getCharsetEncoding()!! 202 | 203 | } 204 | -------------------------------------------------------------------------------- /escposprinter/src/main/java/com/khairo/escposprinter/EscPosPrinter.java: -------------------------------------------------------------------------------- 1 | package com.khairo.escposprinter; 2 | 3 | import com.khairo.escposprinter.connection.DeviceConnection; 4 | import com.khairo.escposprinter.exceptions.EscPosBarcodeException; 5 | import com.khairo.escposprinter.exceptions.EscPosConnectionException; 6 | import com.khairo.escposprinter.exceptions.EscPosEncodingException; 7 | import com.khairo.escposprinter.exceptions.EscPosParserException; 8 | import com.khairo.escposprinter.textparser.IPrinterTextParserElement; 9 | import com.khairo.escposprinter.textparser.PrinterTextParser; 10 | import com.khairo.escposprinter.textparser.PrinterTextParserColumn; 11 | import com.khairo.escposprinter.textparser.PrinterTextParserLine; 12 | import com.khairo.escposprinter.textparser.PrinterTextParserString; 13 | import com.khairo.escposprinter.connection.DeviceConnection; 14 | import com.khairo.escposprinter.exceptions.EscPosBarcodeException; 15 | import com.khairo.escposprinter.exceptions.EscPosConnectionException; 16 | import com.khairo.escposprinter.exceptions.EscPosEncodingException; 17 | import com.khairo.escposprinter.exceptions.EscPosParserException; 18 | import com.khairo.escposprinter.textparser.IPrinterTextParserElement; 19 | import com.khairo.escposprinter.textparser.PrinterTextParser; 20 | import com.khairo.escposprinter.textparser.PrinterTextParserColumn; 21 | import com.khairo.escposprinter.textparser.PrinterTextParserLine; 22 | import com.khairo.escposprinter.textparser.PrinterTextParserString; 23 | 24 | public class EscPosPrinter extends EscPosPrinterSize { 25 | 26 | private EscPosPrinterCommands printer = null; 27 | 28 | /** 29 | * Create new instance of EscPosPrinter. 30 | * 31 | * @param printerConnection Instance of class which implement DeviceConnection 32 | * @param printerDpi DPI of the connected printer 33 | * @param printerWidthMM Printing width in millimeters 34 | * @param printerNbrCharactersPerLine The maximum number of characters that can be printed on a line. 35 | */ 36 | public EscPosPrinter(DeviceConnection printerConnection, int printerDpi, float printerWidthMM, int printerNbrCharactersPerLine) throws EscPosConnectionException { 37 | this(printerConnection != null ? new EscPosPrinterCommands(printerConnection) : null, printerDpi, printerWidthMM, printerNbrCharactersPerLine); 38 | } 39 | 40 | /** 41 | * Create new instance of EscPosPrinter. 42 | * 43 | * @param printerConnection Instance of class which implement DeviceConnection 44 | * @param printerDpi DPI of the connected printer 45 | * @param printerWidthMM Printing width in millimeters 46 | * @param printerNbrCharactersPerLine The maximum number of characters that can be printed on a line. 47 | * @param charsetEncoding Set the charset encoding. 48 | */ 49 | public EscPosPrinter(DeviceConnection printerConnection, int printerDpi, float printerWidthMM, int printerNbrCharactersPerLine, EscPosCharsetEncoding charsetEncoding) throws EscPosConnectionException { 50 | this(printerConnection != null ? new EscPosPrinterCommands(printerConnection, charsetEncoding) : null, printerDpi, printerWidthMM, printerNbrCharactersPerLine); 51 | } 52 | 53 | /** 54 | * Create new instance of EscPosPrinter. 55 | * 56 | * @param printer Instance of EscPosPrinterCommands 57 | * @param printerDpi DPI of the connected printer 58 | * @param printerWidthMM Printing width in millimeters 59 | * @param printerNbrCharactersPerLine The maximum number of characters that can be printed on a line. 60 | */ 61 | public EscPosPrinter(EscPosPrinterCommands printer, int printerDpi, float printerWidthMM, int printerNbrCharactersPerLine) throws EscPosConnectionException { 62 | super(printerDpi, printerWidthMM, printerNbrCharactersPerLine); 63 | if (printer != null) { 64 | this.printer = printer.connect(); 65 | } 66 | } 67 | 68 | /** 69 | * Close the connection with the printer. 70 | * 71 | * @return Fluent interface 72 | */ 73 | public EscPosPrinter disconnectPrinter() { 74 | if (this.printer != null) { 75 | this.printer.disconnect(); 76 | this.printer = null; 77 | } 78 | return this; 79 | } 80 | 81 | /** 82 | * Print a formatted text. Read the README.md for more information about text formatting options. 83 | * 84 | * @param text Formatted text to be printed. 85 | * @return Fluent interface 86 | */ 87 | public EscPosPrinter printFormattedText(String text) throws EscPosConnectionException, EscPosParserException, EscPosEncodingException, EscPosBarcodeException { 88 | return this.printFormattedText(text, 20f); 89 | } 90 | 91 | /** 92 | * Print a formatted text. Read the README.md for more information about text formatting options. 93 | * 94 | * @param text Formatted text to be printed. 95 | * @param mmFeedPaper millimeter distance feed paper at the end. 96 | * @return Fluent interface 97 | */ 98 | public EscPosPrinter printFormattedText(String text, float mmFeedPaper) throws EscPosConnectionException, EscPosParserException, EscPosEncodingException, EscPosBarcodeException { 99 | return this.printFormattedText(text, this.mmToPx(mmFeedPaper)); 100 | } 101 | 102 | /** 103 | * Print a formatted text. Read the README.md for more information about text formatting options. 104 | * 105 | * @param text Formatted text to be printed. 106 | * @param dotsFeedPaper distance feed paper at the end. 107 | * @return Fluent interface 108 | */ 109 | public EscPosPrinter printFormattedText(String text, int dotsFeedPaper) throws EscPosConnectionException, EscPosParserException, EscPosEncodingException, EscPosBarcodeException { 110 | if (this.printer == null || this.getPrinterNbrCharactersPerLine() == 0) { 111 | return this; 112 | } 113 | 114 | PrinterTextParser textParser = new PrinterTextParser(this); 115 | PrinterTextParserLine[] linesParsed = textParser 116 | .setFormattedText(text) 117 | .parse(); 118 | 119 | this.printer.reset(); 120 | 121 | for (PrinterTextParserLine line : linesParsed) { 122 | PrinterTextParserColumn[] columns = line.getColumns(); 123 | 124 | IPrinterTextParserElement lastElement = null; 125 | for (PrinterTextParserColumn column : columns) { 126 | IPrinterTextParserElement[] elements = column.getElements(); 127 | for (IPrinterTextParserElement element : elements) { 128 | element.print(this.printer); 129 | lastElement = element; 130 | } 131 | } 132 | 133 | if (lastElement instanceof PrinterTextParserString) { 134 | this.printer.newLine(); 135 | } 136 | } 137 | 138 | this.printer.feedPaper(dotsFeedPaper); 139 | return this; 140 | } 141 | 142 | /** 143 | * Print a formatted text and cut the paper. Read the README.md for more information about text formatting options. 144 | * 145 | * @param text Formatted text to be printed. 146 | * @return Fluent interface 147 | */ 148 | public EscPosPrinter printFormattedTextAndCut(String text) throws EscPosConnectionException, EscPosParserException, EscPosEncodingException, EscPosBarcodeException { 149 | return this.printFormattedTextAndCut(text, 20f); 150 | } 151 | 152 | /** 153 | * Print a formatted text and cut the paper. Read the README.md for more information about text formatting options. 154 | * 155 | * @param text Formatted text to be printed. 156 | * @param mmFeedPaper millimeter distance feed paper at the end. 157 | * @return Fluent interface 158 | */ 159 | public EscPosPrinter printFormattedTextAndCut(String text, float mmFeedPaper) throws EscPosConnectionException, EscPosParserException, EscPosEncodingException, EscPosBarcodeException { 160 | return this.printFormattedTextAndCut(text, this.mmToPx(mmFeedPaper)); 161 | } 162 | 163 | /** 164 | * Print a formatted text and cut the paper. Read the README.md for more information about text formatting options. 165 | * 166 | * @param text Formatted text to be printed. 167 | * @param dotsFeedPaper distance feed paper at the end. 168 | * @return Fluent interface 169 | */ 170 | public EscPosPrinter printFormattedTextAndCut(String text, int dotsFeedPaper) throws EscPosConnectionException, EscPosParserException, EscPosEncodingException, EscPosBarcodeException { 171 | if (this.printer == null || this.getPrinterNbrCharactersPerLine() == 0) { 172 | return this; 173 | } 174 | 175 | this.printFormattedText(text, dotsFeedPaper); 176 | this.printer.cutPaper(); 177 | 178 | return this; 179 | } 180 | 181 | /** 182 | * @return Charset encoding 183 | */ 184 | public EscPosCharsetEncoding getEncoding() { 185 | return this.printer.getCharsetEncoding(); 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /escposprinter/src/main/java/com/khairo/escposprinter/textparser/PrinterTextParser.kt: -------------------------------------------------------------------------------- 1 | package com.khairo.escposprinter.textparser 2 | 3 | import com.khairo.escposprinter.EscPosPrinter 4 | import com.khairo.escposprinter.EscPosPrinterCommands 5 | import com.khairo.escposprinter.exceptions.EscPosBarcodeException 6 | import com.khairo.escposprinter.exceptions.EscPosEncodingException 7 | import com.khairo.escposprinter.exceptions.EscPosParserException 8 | 9 | class PrinterTextParser(val printer: EscPosPrinter) { 10 | private var textSize = arrayOf(EscPosPrinterCommands.TEXT_SIZE_NORMAL) 11 | private var textColor = arrayOf(EscPosPrinterCommands.TEXT_COLOR_BLACK) 12 | private var textReverseColor = arrayOf(EscPosPrinterCommands.TEXT_COLOR_REVERSE_OFF) 13 | private var textBold = arrayOf(EscPosPrinterCommands.TEXT_WEIGHT_NORMAL) 14 | private var textUnderline = arrayOf(EscPosPrinterCommands.TEXT_UNDERLINE_OFF) 15 | private var textDoubleStrike = arrayOf(EscPosPrinterCommands.TEXT_DOUBLE_STRIKE_OFF) 16 | private var text = "" 17 | fun setFormattedText(text: String): PrinterTextParser { 18 | this.text = text 19 | return this 20 | } 21 | 22 | val lastTextSize: ByteArray 23 | get() = textSize[textSize.size - 1] 24 | 25 | fun addTextSize(newTextSize: ByteArray): PrinterTextParser { 26 | textSize = arrayBytePush(textSize, newTextSize).toTypedArray() 27 | return this 28 | } 29 | 30 | fun dropLastTextSize(): PrinterTextParser { 31 | if (textSize.size > 1) { 32 | textSize = arrayByteDropLast(textSize).toTypedArray() 33 | } 34 | return this 35 | } 36 | 37 | val lastTextColor: ByteArray 38 | get() = textColor[textColor.size - 1] 39 | 40 | fun addTextColor(newTextColor: ByteArray): PrinterTextParser { 41 | textColor = arrayBytePush(textColor, newTextColor).toTypedArray() 42 | return this 43 | } 44 | 45 | fun dropLastTextColor(): PrinterTextParser { 46 | if (textColor.size > 1) { 47 | textColor = arrayByteDropLast(textColor).toTypedArray() 48 | } 49 | return this 50 | } 51 | 52 | val lastTextReverseColor: ByteArray 53 | get() = textReverseColor[textReverseColor.size - 1] 54 | 55 | fun addTextReverseColor(newTextReverseColor: ByteArray): PrinterTextParser { 56 | textReverseColor = arrayBytePush(textReverseColor, newTextReverseColor).toTypedArray() 57 | return this 58 | } 59 | 60 | fun dropLastTextReverseColor(): PrinterTextParser { 61 | if (textReverseColor.size > 1) { 62 | textReverseColor = arrayByteDropLast(textReverseColor).toTypedArray() 63 | } 64 | return this 65 | } 66 | 67 | val lastTextBold: ByteArray 68 | get() = textBold[textBold.size - 1] 69 | 70 | fun addTextBold(newTextBold: ByteArray): PrinterTextParser { 71 | textBold = arrayBytePush(textBold, newTextBold).toTypedArray() 72 | return this 73 | } 74 | 75 | fun dropTextBold(): PrinterTextParser { 76 | if (textBold.size > 1) { 77 | textBold = arrayByteDropLast(textBold).toTypedArray() 78 | } 79 | return this 80 | } 81 | 82 | val lastTextUnderline: ByteArray 83 | get() = textUnderline[textUnderline.size - 1] 84 | 85 | fun addTextUnderline(newTextUnderline: ByteArray): PrinterTextParser { 86 | textUnderline = arrayBytePush(textUnderline, newTextUnderline).toTypedArray() 87 | return this 88 | } 89 | 90 | fun dropLastTextUnderline(): PrinterTextParser { 91 | if (textUnderline.size > 1) { 92 | textUnderline = arrayByteDropLast(textUnderline).toTypedArray() 93 | } 94 | return this 95 | } 96 | 97 | val lastTextDoubleStrike: ByteArray 98 | get() = textDoubleStrike[textDoubleStrike.size - 1] 99 | 100 | fun addTextDoubleStrike(newTextDoubleStrike: ByteArray): PrinterTextParser { 101 | textDoubleStrike = arrayBytePush(textDoubleStrike, newTextDoubleStrike).toTypedArray() 102 | return this 103 | } 104 | 105 | fun dropLastTextDoubleStrike(): PrinterTextParser { 106 | if (textDoubleStrike.size > 1) { 107 | textDoubleStrike = arrayByteDropLast(textDoubleStrike).toTypedArray() 108 | } 109 | return this 110 | } 111 | 112 | @Throws( 113 | EscPosParserException::class, 114 | EscPosBarcodeException::class, 115 | EscPosEncodingException::class 116 | ) 117 | fun parse(): Array { 118 | val stringLines = text.split("\n|\r\n".toRegex()).toTypedArray() 119 | val lines = arrayOfNulls(stringLines.size) 120 | var i = 0 121 | for (line in stringLines) { 122 | lines[i++] = PrinterTextParserLine(this, line) 123 | } 124 | return lines 125 | } 126 | 127 | companion object { 128 | const val TAGS_ALIGN_LEFT = "L" 129 | const val TAGS_ALIGN_CENTER = "C" 130 | const val TAGS_ALIGN_RIGHT = "R" 131 | val TAGS_ALIGN = arrayOf(TAGS_ALIGN_LEFT, TAGS_ALIGN_CENTER, TAGS_ALIGN_RIGHT) 132 | const val TAGS_IMAGE = "img" 133 | const val TAGS_BARCODE = "barcode" 134 | const val TAGS_QRCODE = "qrcode" 135 | const val ATTR_BARCODE_WIDTH = "width" 136 | const val ATTR_BARCODE_HEIGHT = "height" 137 | const val ATTR_BARCODE_TYPE = "type" 138 | const val ATTR_BARCODE_TYPE_EAN8 = "ean8" 139 | const val ATTR_BARCODE_TYPE_EAN13 = "ean13" 140 | const val ATTR_BARCODE_TYPE_UPCA = "upca" 141 | const val ATTR_BARCODE_TYPE_UPCE = "upce" 142 | const val ATTR_BARCODE_TYPE_128 = "128" 143 | const val ATTR_BARCODE_TEXT_POSITION = "text" 144 | const val ATTR_BARCODE_TEXT_POSITION_NONE = "none" 145 | const val ATTR_BARCODE_TEXT_POSITION_ABOVE = "above" 146 | const val ATTR_BARCODE_TEXT_POSITION_BELOW = "below" 147 | const val TAGS_FORMAT_TEXT_FONT = "font" 148 | const val TAGS_FORMAT_TEXT_BOLD = "b" 149 | const val TAGS_FORMAT_TEXT_UNDERLINE = "u" 150 | val TAGS_FORMAT_TEXT = 151 | arrayOf(TAGS_FORMAT_TEXT_FONT, TAGS_FORMAT_TEXT_BOLD, TAGS_FORMAT_TEXT_UNDERLINE) 152 | const val ATTR_FORMAT_TEXT_UNDERLINE_TYPE = "type" 153 | const val ATTR_FORMAT_TEXT_UNDERLINE_TYPE_NORMAL = "normal" 154 | const val ATTR_FORMAT_TEXT_UNDERLINE_TYPE_DOUBLE = "double" 155 | const val ATTR_FORMAT_TEXT_FONT_SIZE = "size" 156 | const val ATTR_FORMAT_TEXT_FONT_SIZE_BIG = "big" 157 | const val ATTR_FORMAT_TEXT_FONT_SIZE_TALL = "tall" 158 | const val ATTR_FORMAT_TEXT_FONT_SIZE_WIDE = "wide" 159 | const val ATTR_FORMAT_TEXT_FONT_SIZE_NORMAL = "normal" 160 | const val ATTR_FORMAT_TEXT_FONT_COLOR = "color" 161 | const val ATTR_FORMAT_TEXT_FONT_COLOR_BLACK = "black" 162 | const val ATTR_FORMAT_TEXT_FONT_COLOR_BG_BLACK = "bg-black" 163 | const val ATTR_FORMAT_TEXT_FONT_COLOR_RED = "red" 164 | const val ATTR_FORMAT_TEXT_FONT_COLOR_BG_RED = "bg-red" 165 | const val ATTR_QRCODE_SIZE = "size" 166 | var regexAlignTags: String? = null 167 | get() { 168 | if (field == null) { 169 | val regexAlignTags = StringBuilder() 170 | for (i in TAGS_ALIGN.indices) { 171 | regexAlignTags.append("|\\[").append(TAGS_ALIGN[i]).append("\\]") 172 | } 173 | field = regexAlignTags.toString().substring(1) 174 | } 175 | return field 176 | } 177 | private set 178 | 179 | @JvmStatic 180 | fun isTagTextFormat(oldTagName: String): Boolean { 181 | var tagName = oldTagName 182 | if (tagName.startsWith("/")) tagName = tagName.substring(1) 183 | 184 | for (tag in TAGS_FORMAT_TEXT) { 185 | if (tag == tagName) return true 186 | } 187 | return false 188 | } 189 | 190 | // fun arrayByteDropLast(arr: Array): Array { 191 | // if (arr.isEmpty()) return arr 192 | // System.arraycopy(arr, 0, arr, 0, arr.size) 193 | // return arr 194 | // } 195 | 196 | // fun arrayBytePush(arr: Array, add: ByteArray): Array { 197 | // var newArr = ArrayList() 198 | // 199 | // for (i in arr.indices) { 200 | // newArr[i] = arr[i] 201 | // } 202 | // System.arraycopy(arr, 0, arr, 0, arr.size) 203 | // Log.d("asdadada", "size: ${arr.size}") 204 | // arr[arr.size] = add 205 | // return newArr 206 | // } 207 | 208 | fun arrayByteDropLast(arr: Array): List { 209 | if (arr.isEmpty()) { 210 | return arr.toList() 211 | } 212 | val newArr = arrayOfNulls(arr.size - 1) 213 | System.arraycopy(arr, 0, newArr, 0, newArr.size) 214 | return newArr.filterNotNull() 215 | } 216 | 217 | // 218 | fun arrayBytePush(arr: Array, add: ByteArray): List { 219 | val newArr = arrayOfNulls(arr.size + 1) 220 | System.arraycopy(arr, 0, newArr, 0, arr.size) 221 | newArr[arr.size] = add 222 | return newArr.filterNotNull() 223 | } 224 | } 225 | } -------------------------------------------------------------------------------- /escposprinter/src/main/java/com/khairo/escposprinter/textparser/CoroutinesPrinterTextParser.kt: -------------------------------------------------------------------------------- 1 | package com.khairo.escposprinter.textparser 2 | 3 | import com.khairo.escposprinter.CoroutinesEscPosPrinter 4 | import com.khairo.escposprinter.CoroutinesEscPosPrinterCommands 5 | import com.khairo.escposprinter.exceptions.EscPosBarcodeException 6 | import com.khairo.escposprinter.exceptions.EscPosEncodingException 7 | import com.khairo.escposprinter.exceptions.EscPosParserException 8 | 9 | class CoroutinesPrinterTextParser(val printer: CoroutinesEscPosPrinter) { 10 | private var textSize = arrayOf(CoroutinesEscPosPrinterCommands.TEXT_SIZE_NORMAL) 11 | private var textColor = arrayOf(CoroutinesEscPosPrinterCommands.TEXT_COLOR_BLACK) 12 | private var textReverseColor = arrayOf(CoroutinesEscPosPrinterCommands.TEXT_COLOR_REVERSE_OFF) 13 | private var textBold = arrayOf(CoroutinesEscPosPrinterCommands.TEXT_WEIGHT_NORMAL) 14 | private var textUnderline = arrayOf(CoroutinesEscPosPrinterCommands.TEXT_UNDERLINE_OFF) 15 | private var textDoubleStrike = arrayOf(CoroutinesEscPosPrinterCommands.TEXT_DOUBLE_STRIKE_OFF) 16 | private var text = "" 17 | fun setFormattedText(text: String): CoroutinesPrinterTextParser { 18 | this.text = text 19 | return this 20 | } 21 | 22 | val lastTextSize: ByteArray 23 | get() = textSize[textSize.size - 1] 24 | 25 | fun addTextSize(newTextSize: ByteArray): CoroutinesPrinterTextParser { 26 | textSize = arrayBytePush(textSize, newTextSize).toTypedArray() 27 | return this 28 | } 29 | 30 | fun dropLastTextSize(): CoroutinesPrinterTextParser { 31 | if (textSize.size > 1) { 32 | textSize = arrayByteDropLast(textSize).toTypedArray() 33 | } 34 | return this 35 | } 36 | 37 | val lastTextColor: ByteArray 38 | get() = textColor[textColor.size - 1] 39 | 40 | fun addTextColor(newTextColor: ByteArray): CoroutinesPrinterTextParser { 41 | textColor = arrayBytePush(textColor, newTextColor).toTypedArray() 42 | return this 43 | } 44 | 45 | fun dropLastTextColor(): CoroutinesPrinterTextParser { 46 | if (textColor.size > 1) { 47 | textColor = arrayByteDropLast(textColor).toTypedArray() 48 | } 49 | return this 50 | } 51 | 52 | val lastTextReverseColor: ByteArray 53 | get() = textReverseColor[textReverseColor.size - 1] 54 | 55 | fun addTextReverseColor(newTextReverseColor: ByteArray): CoroutinesPrinterTextParser { 56 | textReverseColor = arrayBytePush(textReverseColor, newTextReverseColor).toTypedArray() 57 | return this 58 | } 59 | 60 | fun dropLastTextReverseColor(): CoroutinesPrinterTextParser { 61 | if (textReverseColor.size > 1) { 62 | textReverseColor = arrayByteDropLast(textReverseColor).toTypedArray() 63 | } 64 | return this 65 | } 66 | 67 | val lastTextBold: ByteArray 68 | get() = textBold[textBold.size - 1] 69 | 70 | fun addTextBold(newTextBold: ByteArray): CoroutinesPrinterTextParser { 71 | textBold = arrayBytePush(textBold, newTextBold).toTypedArray() 72 | return this 73 | } 74 | 75 | fun dropTextBold(): CoroutinesPrinterTextParser { 76 | if (textBold.size > 1) { 77 | textBold = arrayByteDropLast(textBold).toTypedArray() 78 | } 79 | return this 80 | } 81 | 82 | val lastTextUnderline: ByteArray 83 | get() = textUnderline[textUnderline.size - 1] 84 | 85 | fun addTextUnderline(newTextUnderline: ByteArray): CoroutinesPrinterTextParser { 86 | textUnderline = arrayBytePush(textUnderline, newTextUnderline).toTypedArray() 87 | return this 88 | } 89 | 90 | fun dropLastTextUnderline(): CoroutinesPrinterTextParser { 91 | if (textUnderline.size > 1) { 92 | textUnderline = arrayByteDropLast(textUnderline).toTypedArray() 93 | } 94 | return this 95 | } 96 | 97 | val lastTextDoubleStrike: ByteArray 98 | get() = textDoubleStrike[textDoubleStrike.size - 1] 99 | 100 | fun addTextDoubleStrike(newTextDoubleStrike: ByteArray): CoroutinesPrinterTextParser { 101 | textDoubleStrike = arrayBytePush(textDoubleStrike, newTextDoubleStrike).toTypedArray() 102 | return this 103 | } 104 | 105 | fun dropLastTextDoubleStrike(): CoroutinesPrinterTextParser { 106 | if (textDoubleStrike.size > 1) { 107 | textDoubleStrike = arrayByteDropLast(textDoubleStrike).toTypedArray() 108 | } 109 | return this 110 | } 111 | 112 | @Throws( 113 | EscPosParserException::class, 114 | EscPosBarcodeException::class, 115 | EscPosEncodingException::class 116 | ) 117 | fun parse(): Array { 118 | val stringLines = text.split("\n|\r\n".toRegex()).toTypedArray() 119 | val lines = arrayOfNulls(stringLines.size) 120 | var i = 0 121 | for (line in stringLines) { 122 | lines[i++] = CoroutinesPrinterTextParserLine(this, line) 123 | } 124 | return lines 125 | } 126 | 127 | companion object { 128 | const val TAGS_ALIGN_LEFT = "L" 129 | const val TAGS_ALIGN_CENTER = "C" 130 | const val TAGS_ALIGN_RIGHT = "R" 131 | val TAGS_ALIGN = arrayOf(TAGS_ALIGN_LEFT, TAGS_ALIGN_CENTER, TAGS_ALIGN_RIGHT) 132 | const val TAGS_IMAGE = "img" 133 | const val TAGS_BARCODE = "barcode" 134 | const val TAGS_QRCODE = "qrcode" 135 | const val ATTR_BARCODE_WIDTH = "width" 136 | const val ATTR_BARCODE_HEIGHT = "height" 137 | const val ATTR_BARCODE_TYPE = "type" 138 | const val ATTR_BARCODE_TYPE_EAN8 = "ean8" 139 | const val ATTR_BARCODE_TYPE_EAN13 = "ean13" 140 | const val ATTR_BARCODE_TYPE_UPCA = "upca" 141 | const val ATTR_BARCODE_TYPE_UPCE = "upce" 142 | const val ATTR_BARCODE_TYPE_128 = "128" 143 | const val ATTR_BARCODE_TEXT_POSITION = "text" 144 | const val ATTR_BARCODE_TEXT_POSITION_NONE = "none" 145 | const val ATTR_BARCODE_TEXT_POSITION_ABOVE = "above" 146 | const val ATTR_BARCODE_TEXT_POSITION_BELOW = "below" 147 | const val TAGS_FORMAT_TEXT_FONT = "font" 148 | const val TAGS_FORMAT_TEXT_BOLD = "b" 149 | const val TAGS_FORMAT_TEXT_UNDERLINE = "u" 150 | val TAGS_FORMAT_TEXT = 151 | arrayOf(TAGS_FORMAT_TEXT_FONT, TAGS_FORMAT_TEXT_BOLD, TAGS_FORMAT_TEXT_UNDERLINE) 152 | const val ATTR_FORMAT_TEXT_UNDERLINE_TYPE = "type" 153 | const val ATTR_FORMAT_TEXT_UNDERLINE_TYPE_NORMAL = "normal" 154 | const val ATTR_FORMAT_TEXT_UNDERLINE_TYPE_DOUBLE = "double" 155 | const val ATTR_FORMAT_TEXT_FONT_SIZE = "size" 156 | const val ATTR_FORMAT_TEXT_FONT_SIZE_BIG = "big" 157 | const val ATTR_FORMAT_TEXT_FONT_SIZE_TALL = "tall" 158 | const val ATTR_FORMAT_TEXT_FONT_SIZE_WIDE = "wide" 159 | const val ATTR_FORMAT_TEXT_FONT_SIZE_NORMAL = "normal" 160 | const val ATTR_FORMAT_TEXT_FONT_COLOR = "color" 161 | const val ATTR_FORMAT_TEXT_FONT_COLOR_BLACK = "black" 162 | const val ATTR_FORMAT_TEXT_FONT_COLOR_BG_BLACK = "bg-black" 163 | const val ATTR_FORMAT_TEXT_FONT_COLOR_RED = "red" 164 | const val ATTR_FORMAT_TEXT_FONT_COLOR_BG_RED = "bg-red" 165 | const val ATTR_QRCODE_SIZE = "size" 166 | var regexAlignTags: String? = null 167 | get() { 168 | if (field == null) { 169 | val regexAlignTags = StringBuilder() 170 | for (i in TAGS_ALIGN.indices) { 171 | regexAlignTags.append("|\\[").append(TAGS_ALIGN[i]).append("\\]") 172 | } 173 | field = regexAlignTags.toString().substring(1) 174 | } 175 | return field 176 | } 177 | private set 178 | 179 | @JvmStatic 180 | fun isTagTextFormat(oldTagName: String): Boolean { 181 | var tagName = oldTagName 182 | if (tagName.startsWith("/")) tagName = tagName.substring(1) 183 | 184 | for (tag in TAGS_FORMAT_TEXT) { 185 | if (tag == tagName) return true 186 | } 187 | return false 188 | } 189 | 190 | // fun arrayByteDropLast(arr: Array): Array { 191 | // if (arr.isEmpty()) return arr 192 | // System.arraycopy(arr, 0, arr, 0, arr.size) 193 | // return arr 194 | // } 195 | 196 | // fun arrayBytePush(arr: Array, add: ByteArray): Array { 197 | // var newArr = ArrayList() 198 | // 199 | // for (i in arr.indices) { 200 | // newArr[i] = arr[i] 201 | // } 202 | // System.arraycopy(arr, 0, arr, 0, arr.size) 203 | // Log.d("asdadada", "size: ${arr.size}") 204 | // arr[arr.size] = add 205 | // return newArr 206 | // } 207 | 208 | fun arrayByteDropLast(arr: Array): List { 209 | if (arr.isEmpty()) { 210 | return arr.toList() 211 | } 212 | val newArr = arrayOfNulls(arr.size - 1) 213 | System.arraycopy(arr, 0, newArr, 0, newArr.size) 214 | return newArr.filterNotNull() 215 | } 216 | 217 | // 218 | fun arrayBytePush(arr: Array, add: ByteArray): List { 219 | val newArr = arrayOfNulls(arr.size + 1) 220 | System.arraycopy(arr, 0, newArr, 0, arr.size) 221 | newArr[arr.size] = add 222 | return newArr.filterNotNull() 223 | } 224 | } 225 | } -------------------------------------------------------------------------------- /app/src/main/java/com/khairo/printer/ui/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.khairo.printer.ui 2 | 3 | import android.Manifest 4 | import android.annotation.SuppressLint 5 | import android.app.AlertDialog 6 | import android.app.PendingIntent 7 | import android.content.BroadcastReceiver 8 | import android.content.Context 9 | import android.content.Intent 10 | import android.content.IntentFilter 11 | import android.content.pm.PackageManager 12 | import android.hardware.usb.UsbDevice 13 | import android.hardware.usb.UsbManager 14 | import android.os.AsyncTask 15 | import android.os.Bundle 16 | import android.os.Parcelable 17 | import android.util.DisplayMetrics 18 | import androidx.appcompat.app.AppCompatActivity 19 | import androidx.core.app.ActivityCompat 20 | import androidx.core.content.ContextCompat 21 | import androidx.databinding.DataBindingUtil 22 | import androidx.lifecycle.lifecycleScope 23 | import com.khairo.async.* 24 | import com.khairo.coroutines.CoroutinesEscPosPrint 25 | import com.khairo.coroutines.CoroutinesEscPosPrinter 26 | import com.khairo.escposprinter.EscPosPrinter 27 | import com.khairo.escposprinter.connection.DeviceConnection 28 | import com.khairo.escposprinter.connection.tcp.TcpConnection 29 | import com.khairo.escposprinter.connection.usb.UsbConnection 30 | import com.khairo.escposprinter.connection.usb.UsbPrintersConnections 31 | import com.khairo.escposprinter.exceptions.EscPosBarcodeException 32 | import com.khairo.escposprinter.exceptions.EscPosConnectionException 33 | import com.khairo.escposprinter.exceptions.EscPosEncodingException 34 | import com.khairo.escposprinter.exceptions.EscPosParserException 35 | import com.khairo.escposprinter.textparser.PrinterTextParserImg 36 | import com.khairo.printer.R 37 | import com.khairo.printer.databinding.ActivityMainBinding 38 | import com.khairo.printer.utils.printViaWifi 39 | import kotlinx.coroutines.Dispatchers 40 | import kotlinx.coroutines.launch 41 | import java.text.SimpleDateFormat 42 | import java.util.* 43 | 44 | class MainActivity : AppCompatActivity() { 45 | 46 | private lateinit var binding: ActivityMainBinding 47 | 48 | private var printer: CoroutinesEscPosPrinter? = null 49 | 50 | override fun onCreate(savedInstanceState: Bundle?) { 51 | super.onCreate(savedInstanceState) 52 | binding = DataBindingUtil.setContentView(this, R.layout.activity_main) 53 | 54 | binding.apply { 55 | buttonTcp.setOnClickListener { 56 | lifecycleScope.launch(Dispatchers.IO) { 57 | printTcp() 58 | } 59 | } 60 | 61 | buttonBluetooth.setOnClickListener { 62 | printBluetooth() 63 | } 64 | 65 | buttonUsb.setOnClickListener { 66 | printUsb() 67 | } 68 | } 69 | } 70 | 71 | /*============================================================================================== 72 | ======================================BLUETOOTH PART============================================ 73 | ==============================================================================================*/ 74 | private val PERMISSION_BLUETOOTH = 1 75 | 76 | private fun printBluetooth() { 77 | if (ContextCompat.checkSelfPermission( 78 | this, 79 | Manifest.permission.BLUETOOTH 80 | ) != PackageManager.PERMISSION_GRANTED 81 | ) { 82 | ActivityCompat.requestPermissions( 83 | this, 84 | arrayOf(Manifest.permission.BLUETOOTH), 85 | PERMISSION_BLUETOOTH 86 | ) 87 | } else { 88 | // this.printIt(BluetoothPrintersConnections.selectFirstPaired()); 89 | AsyncBluetoothEscPosPrint(this).execute(this.getAsyncEscPosPrinter(null)) 90 | } 91 | } 92 | 93 | override fun onRequestPermissionsResult( 94 | requestCode: Int, 95 | permissions: Array, 96 | grantResults: IntArray 97 | ) { 98 | if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { 99 | when (requestCode) { 100 | PERMISSION_BLUETOOTH -> printBluetooth() 101 | } 102 | } 103 | } 104 | 105 | /*============================================================================================== 106 | ===========================================USB PART============================================= 107 | ==============================================================================================*/ 108 | private val ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION" 109 | private val usbReceiver: BroadcastReceiver = object : BroadcastReceiver() { 110 | override fun onReceive(context: Context, intent: Intent) { 111 | val action = intent.action 112 | if (ACTION_USB_PERMISSION == action) { 113 | synchronized(this) { 114 | val usbManager = getSystemService(USB_SERVICE) as UsbManager 115 | val usbDevice = 116 | intent.getParcelableExtra(UsbManager.EXTRA_DEVICE) as UsbDevice? 117 | if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) { 118 | if (usbDevice != null) { 119 | // printIt(new UsbConnection(usbManager, usbDevice)); 120 | AsyncUsbEscPosPrint(context) 121 | .execute( 122 | getAsyncEscPosPrinter( 123 | UsbConnection( 124 | usbManager, 125 | usbDevice 126 | ) 127 | ) 128 | ) 129 | } 130 | } 131 | } 132 | } 133 | } 134 | } 135 | 136 | fun printUsb() { 137 | val usbConnection = UsbPrintersConnections.selectFirstConnected(this) 138 | val usbManager = this.getSystemService(USB_SERVICE) as UsbManager 139 | if (usbConnection == null) { 140 | AlertDialog.Builder(this) 141 | .setTitle("USB Connection") 142 | .setMessage("No USB printer found.") 143 | .show() 144 | return 145 | } 146 | val permissionIntent = PendingIntent.getBroadcast(this, 0, Intent(ACTION_USB_PERMISSION), 0) 147 | val filter = IntentFilter(ACTION_USB_PERMISSION) 148 | registerReceiver(usbReceiver, filter) 149 | usbManager.requestPermission(usbConnection.device, permissionIntent) 150 | } 151 | 152 | /*============================================================================================== 153 | ===================================ESC/POS PRINTER PART========================================= 154 | ==============================================================================================*/ 155 | /** 156 | * Synchronous printing 157 | */ 158 | @SuppressLint("SimpleDateFormat") 159 | fun printIt(printerConnection: DeviceConnection?) { 160 | AsyncTask.execute { 161 | try { 162 | val format = SimpleDateFormat("'on' yyyy-MM-dd 'at' HH:mm:ss") 163 | val printer = EscPosPrinter( 164 | printerConnection, 165 | 203, 166 | 48f, 167 | 32 168 | ) 169 | printer 170 | .printFormattedText( 171 | "[C]" + PrinterTextParserImg.bitmapToHexadecimalString( 172 | printer, 173 | applicationContext.resources.getDrawableForDensity( 174 | R.drawable.logo, 175 | DisplayMetrics.DENSITY_MEDIUM 176 | ) 177 | ) + "\n" + 178 | "[L]\n" + 179 | "[C]ORDER N°045\n" + 180 | "[C]" + format.format(Date()) + "\n" + 181 | "[L]\n" + 182 | "[C]==================عربي تيست هههههههه==============\n" + 183 | "[L]\n" + 184 | "[L]BEAUTIFUL SHIRT[R]9.99e\n" + 185 | "[L] + Size : S\n" + 186 | "[L]\n" + 187 | "[L]AWESOME HAT[R]24.99e\n" + 188 | "[L] + Size : 57/58\n" + 189 | "[L]\n" + 190 | "[C]--------------------------------\n" + 191 | "[R]TOTAL PRICE :[R]34.98e\n" + 192 | "[R]TAX :[R]4.23e\n" + 193 | "[L]\n" + 194 | "[C]================================\n" + 195 | "[L]\n" + 196 | "[L]Customer :\n" + 197 | "[L]Raymond DUPONT\n" + 198 | "[L]5 rue des girafes\n" + 199 | "[L]31547 PERPETES\n" + 200 | "[L]Tel : +33801201456\n" + 201 | "[L]\n" + 202 | "[C]83125478455134567890\n" + 203 | "[C]http://www.developpeur-web.khairo.com/" + 204 | "[L]\n" + 205 | "[L]\n" + 206 | "[L]\n" + 207 | "[L]\n" + 208 | "[L]\n" + 209 | "[L]\n" 210 | ) 211 | } catch (e: EscPosConnectionException) { 212 | e.printStackTrace() 213 | AlertDialog.Builder(this@MainActivity) 214 | .setTitle("Broken connection") 215 | .setMessage(e.message) 216 | .show() 217 | } catch (e: EscPosParserException) { 218 | e.printStackTrace() 219 | AlertDialog.Builder(this@MainActivity) 220 | .setTitle("Invalid formatted text") 221 | .setMessage(e.message) 222 | .show() 223 | } catch (e: EscPosEncodingException) { 224 | e.printStackTrace() 225 | AlertDialog.Builder(this@MainActivity) 226 | .setTitle("Bad selected encoding") 227 | .setMessage(e.message) 228 | .show() 229 | } catch (e: EscPosBarcodeException) { 230 | e.printStackTrace() 231 | AlertDialog.Builder(this@MainActivity) 232 | .setTitle("Invalid barcode") 233 | .setMessage(e.message) 234 | .show() 235 | } 236 | } 237 | } 238 | 239 | /** 240 | * Asynchronous printing 241 | */ 242 | @SuppressLint("SimpleDateFormat") 243 | fun getAsyncEscPosPrinter(printerConnection: DeviceConnection?): AsyncEscPosPrinter { 244 | val format = SimpleDateFormat("'on' yyyy-MM-dd 'at' HH:mm:ss") 245 | val printer = AsyncEscPosPrinter(printerConnection!!, 203, 48f, 32) 246 | return printer.setTextToPrint( 247 | "[C]" + PrinterTextParserImg.bitmapToHexadecimalString( 248 | printer, 249 | this.applicationContext.resources.getDrawableForDensity( 250 | R.drawable.logo, 251 | DisplayMetrics.DENSITY_MEDIUM 252 | ) 253 | ) + "\n" + 254 | "[L]\n" + 255 | "[C]ORDER N°045\n" + 256 | "[L]\n" + 257 | "[C]" + format.format(Date()) + "\n" + 258 | "[C]\n" + 259 | "[C]================================\n" + 260 | "[L]\n" + 261 | "[L]BEAUTIFUL SHIRT[R]9.99e\n" + 262 | "[L] + Size : S\n" + 263 | "[L]\n" + 264 | "[L]AWESOME HAT[R]24.99e\n" + 265 | "[L] + Size : 57/58\n" + 266 | "[L]\n" + 267 | "[C]--------------------------------\n" + 268 | "[R]TOTAL PRICE :[R]34.98e\n" + 269 | "[R]TAX :[R]4.23e\n" + 270 | "[L]\n" + 271 | "[C]================================\n" + 272 | "[L]\n" + 273 | "[L]Customer :\n" + 274 | "[L]Raymond DUPONT\n" + 275 | "[L]5 rue des girafes\n" + 276 | "[L]31547 PERPETES\n" + 277 | "[L]Tel : +33801201456\n" + 278 | "\n" + 279 | "[C]83125478455134567890\n" + 280 | "[L]\n" + 281 | "[C]http://www.developpeur-web.khairo.com/\n" + 282 | "[L]\n" + 283 | "[L]\n" + 284 | "[L]\n" + 285 | "[L]\n" + 286 | "[L]\n" + 287 | "[L]\n" 288 | ) 289 | } 290 | 291 | /*============================================================================================== 292 | =========================================TCP PART=============================================== 293 | ==============================================================================================*/ 294 | private suspend fun printTcp() { 295 | try { 296 | printer = 297 | CoroutinesEscPosPrinter( 298 | TcpConnection( 299 | binding.tcpIp.text.toString(), 300 | binding.tcpPort.text.toString().toInt() 301 | ).apply { connect(this@MainActivity) }, 203, 48f, 32 302 | ) 303 | 304 | // this.printIt(new TcpConnection(ipAddress.getText().toString(), Integer.parseInt(portAddress.getText().toString()))); 305 | // AsyncTcpEscPosPrint(this).execute(printer.setTextToPrint(test)) 306 | 307 | CoroutinesEscPosPrint(this).execute( 308 | printViaWifi( 309 | printer!!, 310 | 45, 311 | body, 312 | 34.98f, 313 | 4, 314 | customer, 315 | "83125478455134567890" 316 | ) 317 | ).apply { printer = null } 318 | 319 | } catch (e: NumberFormatException) { 320 | AlertDialog.Builder(this) 321 | .setTitle("Invalid TCP port address") 322 | .setMessage("Port field must be a number.") 323 | .show() 324 | e.printStackTrace() 325 | } 326 | } 327 | 328 | private val body: String 329 | get() = "[L]\n" + 330 | "[L] Pizza[R][R]3[R][R]55 $\n" + 331 | "[L] + Olive[R][R]1 $\n" + 332 | "[L] + Cheese[R][R]5 $\n" + 333 | "[L] + Mushroom[R][R]7 $\n" + 334 | "[L]\n" + 335 | "[L] Burger[R][R]7[R][R]43.54 $\n" + 336 | "[L] + Cheese[R][R]3 $\n" + 337 | "[L]\n" + 338 | "[L] Shawarma[R][R]2[R][R]4 $\n" + 339 | "[L] + Garlic[R][R]0.5 $\n" + 340 | "[L]\n" + 341 | "[L] Steak[R][R]3[R][R]75 $\n" + 342 | "[L]\n" + 343 | "[R] PAYMENT METHOD :[R]Visa\n" 344 | 345 | private val customer: String 346 | get() = 347 | "[C]================================\n" + 348 | "[L]\n" + 349 | "[L]Delivery[R]5 $\n" + 350 | "[L]\n" + 351 | "[L]Customer :\n" + 352 | "[L]Name : Mohammad khair\n" + 353 | "[L]Phone : 00962787144627\n" + 354 | "[L]Area : Khalda\n" + 355 | "[L]street : testing street\n" + 356 | "[L]building : 9\n" + 357 | "[L]Floor : 2\n" + 358 | "[L]Apartment : 1\n" + 359 | "[L]Note : This order is just for testing\n" 360 | } 361 | -------------------------------------------------------------------------------- /escposprinter/src/main/java/com/khairo/escposprinter/textparser/PrinterTextParserColumn.kt: -------------------------------------------------------------------------------- 1 | package com.khairo.escposprinter.textparser 2 | 3 | import com.khairo.escposprinter.EscPosPrinterCommands 4 | import com.khairo.escposprinter.exceptions.EscPosBarcodeException 5 | import com.khairo.escposprinter.exceptions.EscPosParserException 6 | import com.khairo.escposprinter.textparser.PrinterTextParser.Companion.isTagTextFormat 7 | import java.util.* 8 | import kotlin.math.floor 9 | 10 | /** 11 | * Create a new instance of PrinterTextParserColumn. 12 | * 13 | * @param textParserLine Parent PrinterTextParserLine instance 14 | * @param oldTextColumn Text that the column contain 15 | */ 16 | class PrinterTextParserColumn(textParserLine: PrinterTextParserLine, oldTextColumn: String) { 17 | val line: PrinterTextParserLine 18 | var elements = arrayOfNulls(0) 19 | private set 20 | 21 | private fun prependString(text: String): PrinterTextParserColumn { 22 | val textParser = line.textParser 23 | return this.prependString( 24 | text, 25 | textParser.lastTextSize, 26 | textParser.lastTextColor, 27 | textParser.lastTextReverseColor, 28 | textParser.lastTextBold, 29 | textParser.lastTextUnderline, 30 | textParser.lastTextDoubleStrike 31 | ) 32 | } 33 | 34 | private fun prependString( 35 | text: String, 36 | textSize: ByteArray?, 37 | textColor: ByteArray?, 38 | textReverseColor: ByteArray?, 39 | textBold: ByteArray?, 40 | textUnderline: ByteArray?, 41 | textDoubleStrike: ByteArray? 42 | ): PrinterTextParserColumn { 43 | return prependElement( 44 | PrinterTextParserString( 45 | this, 46 | text, 47 | textSize!!, 48 | textColor!!, 49 | textReverseColor!!, 50 | textBold!!, 51 | textUnderline!!, 52 | textDoubleStrike!! 53 | ) 54 | ) 55 | } 56 | 57 | private fun appendString(text: String): PrinterTextParserColumn { 58 | val textParser = line.textParser 59 | return this.appendString( 60 | text, 61 | textParser.lastTextSize, 62 | textParser.lastTextColor, 63 | textParser.lastTextReverseColor, 64 | textParser.lastTextBold, 65 | textParser.lastTextUnderline, 66 | textParser.lastTextDoubleStrike 67 | ) 68 | } 69 | 70 | private fun appendString( 71 | text: String, 72 | textSize: ByteArray?, 73 | textColor: ByteArray?, 74 | textReverseColor: ByteArray?, 75 | textBold: ByteArray?, 76 | textUnderline: ByteArray?, 77 | textDoubleStrike: ByteArray? 78 | ): PrinterTextParserColumn { 79 | val printer = line.textParser.printer 80 | return this.appendElement( 81 | PrinterTextParserString( 82 | this, 83 | text, 84 | textSize!!, 85 | textColor!!, 86 | textReverseColor!!, 87 | textBold!!, 88 | textUnderline!!, 89 | textDoubleStrike!! 90 | ) 91 | ) 92 | } 93 | 94 | private fun prependImage(textAlign: String, hexString: String): PrinterTextParserColumn { 95 | return prependElement( 96 | PrinterTextParserImg( 97 | this, 98 | textAlign, 99 | hexString 100 | ) 101 | ) 102 | } 103 | 104 | private fun appendImage(textAlign: String, hexString: String): PrinterTextParserColumn { 105 | return this.appendElement( 106 | PrinterTextParserImg( 107 | this, 108 | textAlign, 109 | hexString 110 | ) 111 | ) 112 | } 113 | 114 | @Throws(EscPosParserException::class, EscPosBarcodeException::class) 115 | private fun prependBarcode( 116 | textAlign: String, 117 | barcodeAttributes: Hashtable, 118 | code: String 119 | ): PrinterTextParserColumn { 120 | return prependElement(PrinterTextParserBarcode(this, textAlign, barcodeAttributes, code)) 121 | } 122 | 123 | @Throws(EscPosParserException::class, EscPosBarcodeException::class) 124 | private fun appendBarcode( 125 | textAlign: String, 126 | barcodeAttributes: Hashtable, 127 | code: String 128 | ): PrinterTextParserColumn { 129 | return this.appendElement( 130 | PrinterTextParserBarcode( 131 | this, 132 | textAlign, 133 | barcodeAttributes, 134 | code 135 | ) 136 | ) 137 | } 138 | 139 | @Throws(EscPosParserException::class, EscPosBarcodeException::class) 140 | private fun prependQRCode( 141 | textAlign: String, 142 | qrCodeAttributes: Hashtable, 143 | data: String 144 | ): PrinterTextParserColumn { 145 | return prependElement(PrinterTextParserBarcode(this, textAlign, qrCodeAttributes, data)) 146 | } 147 | 148 | @Throws(EscPosParserException::class, EscPosBarcodeException::class) 149 | private fun appendQRCode( 150 | textAlign: String, 151 | qrCodeAttributes: Hashtable, 152 | data: String 153 | ): PrinterTextParserColumn { 154 | return this.appendElement(PrinterTextParserQRCode(this, textAlign, qrCodeAttributes, data)) 155 | } 156 | 157 | private fun prependElement(element: IPrinterTextParserElement): PrinterTextParserColumn { 158 | val elementsTmp = arrayOfNulls(elements.size + 1) 159 | elementsTmp[0] = element 160 | System.arraycopy(elements, 0, elementsTmp, 1, elements.size) 161 | elements = elementsTmp 162 | return this 163 | } 164 | 165 | private fun appendElement(element: IPrinterTextParserElement): PrinterTextParserColumn { 166 | val elementsTmp = arrayOfNulls(elements.size + 1) 167 | System.arraycopy(elements, 0, elementsTmp, 0, elements.size) 168 | elementsTmp[elements.size] = element 169 | elements = elementsTmp 170 | return this 171 | } 172 | 173 | companion object { 174 | private fun generateSpace(nbrSpace: Int): String { 175 | val str = StringBuilder() 176 | for (i in 0 until nbrSpace) { 177 | str.append(" ") 178 | } 179 | return str.toString() 180 | } 181 | } 182 | 183 | init { 184 | var textColumn = oldTextColumn 185 | line = textParserLine 186 | val textParser = line.textParser 187 | var textAlign = PrinterTextParser.TAGS_ALIGN_LEFT 188 | val textUnderlineStartColumn = textParser.lastTextUnderline 189 | val textDoubleStrikeStartColumn = textParser.lastTextDoubleStrike 190 | val textColorStartColumn = textParser.lastTextColor 191 | val textReverseColorStartColumn = textParser.lastTextReverseColor 192 | 193 | 194 | // ================================================================= 195 | // Check the column alignment 196 | if (textColumn.length > 2) { 197 | when (textColumn.substring(0, 3).toUpperCase(Locale.ROOT)) { 198 | "[" + PrinterTextParser.TAGS_ALIGN_LEFT + "]", "[" + PrinterTextParser.TAGS_ALIGN_CENTER + "]", "[" + PrinterTextParser.TAGS_ALIGN_RIGHT + "]" -> { 199 | textAlign = textColumn.substring(1, 2).toUpperCase(Locale.ROOT) 200 | textColumn = textColumn.substring(3) 201 | } 202 | } 203 | } 204 | val trimmedTextColumn = textColumn.trim { it <= ' ' } 205 | var isImgOrBarcodeLine = false 206 | if (line.nbrColumns == 1 && trimmedTextColumn.indexOf("<") == 0) { 207 | // ================================================================= 208 | // Image or Barcode Lines 209 | val openTagIndex = trimmedTextColumn.indexOf("<") 210 | val openTagEndIndex = trimmedTextColumn.indexOf(">", openTagIndex + 1) + 1 211 | if (openTagIndex < openTagEndIndex) { 212 | val textParserTag = 213 | PrinterTextParserTag(trimmedTextColumn.substring(openTagIndex, openTagEndIndex)) 214 | when (textParserTag.tagName) { 215 | PrinterTextParser.TAGS_IMAGE, PrinterTextParser.TAGS_BARCODE, PrinterTextParser.TAGS_QRCODE -> { 216 | val closeTag = "" 217 | val closeTagPosition = trimmedTextColumn.length - closeTag.length 218 | if (trimmedTextColumn.substring(closeTagPosition) == closeTag) { 219 | when (textParserTag.tagName) { 220 | PrinterTextParser.TAGS_IMAGE -> appendImage( 221 | textAlign, 222 | trimmedTextColumn.substring(openTagEndIndex, closeTagPosition) 223 | ) 224 | PrinterTextParser.TAGS_BARCODE -> appendBarcode( 225 | textAlign, 226 | textParserTag.attributes, 227 | trimmedTextColumn.substring(openTagEndIndex, closeTagPosition) 228 | ) 229 | PrinterTextParser.TAGS_QRCODE -> appendQRCode( 230 | textAlign, 231 | textParserTag.attributes, 232 | trimmedTextColumn.substring(openTagEndIndex, closeTagPosition) 233 | ) 234 | } 235 | isImgOrBarcodeLine = true 236 | } 237 | } 238 | } 239 | } 240 | } 241 | if (!isImgOrBarcodeLine) { 242 | // ================================================================= 243 | // If the tag is for format text 244 | var offset = 0 245 | while (true) { 246 | var openTagIndex = textColumn.indexOf("<", offset) 247 | var closeTagIndex = -1 248 | if (openTagIndex != -1) { 249 | closeTagIndex = textColumn.indexOf(">", openTagIndex) 250 | } else { 251 | openTagIndex = textColumn.length 252 | } 253 | this.appendString(textColumn.substring(offset, openTagIndex)) 254 | if (closeTagIndex == -1) { 255 | break 256 | } 257 | closeTagIndex++ 258 | val textParserTag = 259 | PrinterTextParserTag(textColumn.substring(openTagIndex, closeTagIndex)) 260 | offset = if (isTagTextFormat(textParserTag.tagName)) { 261 | if (textParserTag.isCloseTag) { 262 | when (textParserTag.tagName) { 263 | PrinterTextParser.TAGS_FORMAT_TEXT_BOLD -> textParser.dropTextBold() 264 | PrinterTextParser.TAGS_FORMAT_TEXT_UNDERLINE -> { 265 | textParser.dropLastTextUnderline() 266 | textParser.dropLastTextDoubleStrike() 267 | } 268 | PrinterTextParser.TAGS_FORMAT_TEXT_FONT -> { 269 | textParser.dropLastTextSize() 270 | textParser.dropLastTextColor() 271 | textParser.dropLastTextReverseColor() 272 | } 273 | } 274 | } else { 275 | when (textParserTag.tagName) { 276 | PrinterTextParser.TAGS_FORMAT_TEXT_BOLD -> textParser.addTextBold( 277 | EscPosPrinterCommands.TEXT_WEIGHT_BOLD 278 | ) 279 | PrinterTextParser.TAGS_FORMAT_TEXT_UNDERLINE -> if (textParserTag.hasAttribute( 280 | PrinterTextParser.ATTR_FORMAT_TEXT_UNDERLINE_TYPE 281 | ) 282 | ) { 283 | when (textParserTag.getAttribute(PrinterTextParser.ATTR_FORMAT_TEXT_UNDERLINE_TYPE)) { 284 | PrinterTextParser.ATTR_FORMAT_TEXT_UNDERLINE_TYPE_NORMAL -> { 285 | textParser.addTextUnderline(EscPosPrinterCommands.TEXT_UNDERLINE_LARGE) 286 | textParser.addTextDoubleStrike(textParser.lastTextDoubleStrike) 287 | } 288 | PrinterTextParser.ATTR_FORMAT_TEXT_UNDERLINE_TYPE_DOUBLE -> { 289 | textParser.addTextUnderline(textParser.lastTextUnderline) 290 | textParser.addTextDoubleStrike(EscPosPrinterCommands.TEXT_DOUBLE_STRIKE_ON) 291 | } 292 | } 293 | } else { 294 | textParser.addTextUnderline(EscPosPrinterCommands.TEXT_UNDERLINE_LARGE) 295 | textParser.addTextDoubleStrike(textParser.lastTextDoubleStrike) 296 | } 297 | PrinterTextParser.TAGS_FORMAT_TEXT_FONT -> { 298 | if (textParserTag.hasAttribute(PrinterTextParser.ATTR_FORMAT_TEXT_FONT_SIZE)) { 299 | when (textParserTag.getAttribute(PrinterTextParser.ATTR_FORMAT_TEXT_FONT_SIZE)) { 300 | PrinterTextParser.ATTR_FORMAT_TEXT_FONT_SIZE_NORMAL -> textParser.addTextSize( 301 | EscPosPrinterCommands.TEXT_SIZE_NORMAL 302 | ) 303 | PrinterTextParser.ATTR_FORMAT_TEXT_FONT_SIZE_TALL -> textParser.addTextSize( 304 | EscPosPrinterCommands.TEXT_SIZE_DOUBLE_HEIGHT 305 | ) 306 | PrinterTextParser.ATTR_FORMAT_TEXT_FONT_SIZE_WIDE -> textParser.addTextSize( 307 | EscPosPrinterCommands.TEXT_SIZE_DOUBLE_WIDTH 308 | ) 309 | PrinterTextParser.ATTR_FORMAT_TEXT_FONT_SIZE_BIG -> textParser.addTextSize( 310 | EscPosPrinterCommands.TEXT_SIZE_BIG 311 | ) 312 | else -> textParser.addTextSize(EscPosPrinterCommands.TEXT_SIZE_NORMAL) 313 | } 314 | } else { 315 | textParser.addTextSize(textParser.lastTextSize) 316 | } 317 | if (textParserTag.hasAttribute(PrinterTextParser.ATTR_FORMAT_TEXT_FONT_COLOR)) { 318 | when (textParserTag.getAttribute(PrinterTextParser.ATTR_FORMAT_TEXT_FONT_COLOR)) { 319 | PrinterTextParser.ATTR_FORMAT_TEXT_FONT_COLOR_BLACK -> { 320 | textParser.addTextColor(EscPosPrinterCommands.TEXT_COLOR_BLACK) 321 | textParser.addTextReverseColor(EscPosPrinterCommands.TEXT_COLOR_REVERSE_OFF) 322 | } 323 | PrinterTextParser.ATTR_FORMAT_TEXT_FONT_COLOR_BG_BLACK -> { 324 | textParser.addTextColor(EscPosPrinterCommands.TEXT_COLOR_BLACK) 325 | textParser.addTextReverseColor(EscPosPrinterCommands.TEXT_COLOR_REVERSE_ON) 326 | } 327 | PrinterTextParser.ATTR_FORMAT_TEXT_FONT_COLOR_RED -> { 328 | textParser.addTextColor(EscPosPrinterCommands.TEXT_COLOR_RED) 329 | textParser.addTextReverseColor(EscPosPrinterCommands.TEXT_COLOR_REVERSE_OFF) 330 | } 331 | PrinterTextParser.ATTR_FORMAT_TEXT_FONT_COLOR_BG_RED -> { 332 | textParser.addTextColor(EscPosPrinterCommands.TEXT_COLOR_RED) 333 | textParser.addTextReverseColor(EscPosPrinterCommands.TEXT_COLOR_REVERSE_ON) 334 | } 335 | else -> { 336 | textParser.addTextColor(EscPosPrinterCommands.TEXT_COLOR_BLACK) 337 | textParser.addTextReverseColor(EscPosPrinterCommands.TEXT_COLOR_REVERSE_OFF) 338 | } 339 | } 340 | } else { 341 | textParser.addTextColor(textParser.lastTextColor) 342 | textParser.addTextReverseColor(textParser.lastTextReverseColor) 343 | } 344 | } 345 | } 346 | } 347 | closeTagIndex 348 | } else { 349 | this.appendString("<") 350 | openTagIndex + 1 351 | } 352 | } 353 | 354 | // ================================================================= 355 | // Define the number of spaces required for the different alignments 356 | val nbrCharColumn = line.nbrCharColumn 357 | var nbrCharForgetted = line.nbrCharForgetted 358 | var nbrCharColumnExceeded = line.nbrCharColumnExceeded 359 | var nbrCharTextWithoutTag = 0 360 | var leftSpace = 0 361 | var rightSpace = 0 362 | for (textParserElement in elements) { 363 | nbrCharTextWithoutTag += textParserElement!!.length() 364 | } 365 | when (textAlign) { 366 | PrinterTextParser.TAGS_ALIGN_LEFT -> rightSpace = 367 | nbrCharColumn - nbrCharTextWithoutTag 368 | PrinterTextParser.TAGS_ALIGN_CENTER -> { 369 | leftSpace = 370 | floor(((nbrCharColumn.toFloat() - nbrCharTextWithoutTag.toFloat()) / 2f).toDouble()) 371 | .toInt() 372 | rightSpace = nbrCharColumn - nbrCharTextWithoutTag - leftSpace 373 | } 374 | PrinterTextParser.TAGS_ALIGN_RIGHT -> leftSpace = 375 | nbrCharColumn - nbrCharTextWithoutTag 376 | } 377 | if (nbrCharForgetted > 0) { 378 | nbrCharForgetted -= 1 379 | rightSpace++ 380 | } 381 | if (nbrCharColumnExceeded < 0) { 382 | leftSpace += nbrCharColumnExceeded 383 | nbrCharColumnExceeded = 0 384 | if (leftSpace < 1) { 385 | rightSpace += leftSpace - 1 386 | leftSpace = 1 387 | } 388 | } 389 | if (leftSpace < 0) { 390 | nbrCharColumnExceeded += leftSpace 391 | leftSpace = 0 392 | } 393 | if (rightSpace < 0) { 394 | nbrCharColumnExceeded += rightSpace 395 | rightSpace = 0 396 | } 397 | if (leftSpace > 0) { 398 | this.prependString( 399 | generateSpace(leftSpace), 400 | EscPosPrinterCommands.TEXT_SIZE_NORMAL, 401 | textColorStartColumn, 402 | textReverseColorStartColumn, 403 | EscPosPrinterCommands.TEXT_WEIGHT_NORMAL, 404 | textUnderlineStartColumn, 405 | textDoubleStrikeStartColumn 406 | ) 407 | } 408 | if (rightSpace > 0) { 409 | this.appendString( 410 | generateSpace(rightSpace), 411 | EscPosPrinterCommands.TEXT_SIZE_NORMAL, 412 | textParser.lastTextColor, 413 | textParser.lastTextReverseColor, 414 | EscPosPrinterCommands.TEXT_WEIGHT_NORMAL, 415 | textParser.lastTextUnderline, 416 | textParser.lastTextDoubleStrike 417 | ) 418 | } 419 | 420 | // ================================================================================================= 421 | // nbrCharForgetted and nbrCharColumnExceeded is use to define number of spaces for the next columns 422 | line 423 | .setNbrCharForgetted(nbrCharForgetted) 424 | .setNbrCharColumnExceeded(nbrCharColumnExceeded) 425 | } 426 | } 427 | } --------------------------------------------------------------------------------