@sincpro/printer-expo 0.1.2 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/README.md +589 -415
  2. package/android/.editorconfig +20 -0
  3. package/android/README.md +103 -0
  4. package/android/build.gradle +36 -34
  5. package/android/libs/pdf/Bixolon_pdf.aar +0 -0
  6. package/android/libs/{jniLibs/x86_64/libbxl_common.so → sincpro-printer-sdk.aar} +0 -0
  7. package/android/src/main/java/sincpro/expo/printer/entrypoint/PrinterModule.kt +387 -0
  8. package/build/SincproPrinter.d.ts +191 -0
  9. package/build/SincproPrinter.d.ts.map +1 -0
  10. package/build/SincproPrinter.js +120 -0
  11. package/build/SincproPrinter.js.map +1 -0
  12. package/build/index.d.ts +11 -3
  13. package/build/index.d.ts.map +1 -1
  14. package/build/index.js +13 -3
  15. package/build/index.js.map +1 -1
  16. package/build/types/bluetooth.types.d.ts +19 -0
  17. package/build/types/bluetooth.types.d.ts.map +1 -0
  18. package/build/types/bluetooth.types.js +5 -0
  19. package/build/types/bluetooth.types.js.map +1 -0
  20. package/build/types/index.d.ts +7 -0
  21. package/build/types/index.d.ts.map +1 -0
  22. package/build/types/index.js +7 -0
  23. package/build/types/index.js.map +1 -0
  24. package/build/types/printer.types.d.ts +118 -0
  25. package/build/types/printer.types.d.ts.map +1 -0
  26. package/build/types/printer.types.js +5 -0
  27. package/build/types/printer.types.js.map +1 -0
  28. package/build/types/receipt.types.d.ts +96 -0
  29. package/build/types/receipt.types.d.ts.map +1 -0
  30. package/build/types/receipt.types.js +5 -0
  31. package/build/types/receipt.types.js.map +1 -0
  32. package/expo-module.config.json +2 -5
  33. package/package.json +6 -5
  34. package/src/SincproPrinter.ts +208 -0
  35. package/src/index.ts +15 -3
  36. package/src/types/bluetooth.types.ts +20 -0
  37. package/src/types/index.ts +7 -0
  38. package/src/types/printer.types.ts +141 -0
  39. package/src/types/receipt.types.ts +115 -0
  40. package/android/libs/BixolonLabelPrinterLibrary_V2.0.9.jar +0 -0
  41. package/android/libs/jniLibs/arm64-v8a/libbxl_common.so +0 -0
  42. package/android/libs/jniLibs/armeabi-v7a/libbxl_common.so +0 -0
  43. package/android/libs/jniLibs/x86/libbxl_common.so +0 -0
  44. package/android/libs/libcommon_V1.4.0.jar +0 -0
  45. package/android/src/main/java/expo/sincpro/ExpoBixolonModule.kt +0 -271
  46. package/android/src/main/java/expo/sincpro/bixolon/BixolonQRPrinter.kt +0 -423
  47. package/android/src/main/java/expo/sincpro/managers/BluetoothManager.kt +0 -139
  48. package/android/src/main/java/expo/sincpro/managers/ConnectionManager.kt +0 -124
  49. package/android/src/main/java/expo/sincpro/managers/PermissionManager.kt +0 -122
  50. package/android/src/main/java/expo/sincpro/managers/PrinterManager.kt +0 -396
  51. package/android/src/main/jniLibs/arm64-v8a/libbxl_common.so +0 -0
  52. package/android/src/main/jniLibs/armeabi-v7a/libbxl_common.so +0 -0
  53. package/android/src/main/jniLibs/x86/libbxl_common.so +0 -0
  54. package/android/src/main/jniLibs/x86_64/libbxl_common.so +0 -0
  55. package/build/BixolonPrinter.d.ts +0 -4
  56. package/build/BixolonPrinter.d.ts.map +0 -1
  57. package/build/BixolonPrinter.js +0 -12
  58. package/build/BixolonPrinter.js.map +0 -1
  59. package/build/ExpoBixolon.types.d.ts +0 -45
  60. package/build/ExpoBixolon.types.d.ts.map +0 -1
  61. package/build/ExpoBixolon.types.js +0 -2
  62. package/build/ExpoBixolon.types.js.map +0 -1
  63. package/build/ExpoBixolonModule.d.ts +0 -24
  64. package/build/ExpoBixolonModule.d.ts.map +0 -1
  65. package/build/ExpoBixolonModule.js +0 -3
  66. package/build/ExpoBixolonModule.js.map +0 -1
  67. package/build/QrCodePrinter.d.ts +0 -45
  68. package/build/QrCodePrinter.d.ts.map +0 -1
  69. package/build/QrCodePrinter.js +0 -118
  70. package/build/QrCodePrinter.js.map +0 -1
  71. package/src/BixolonPrinter.ts +0 -16
  72. package/src/ExpoBixolon.types.ts +0 -60
  73. package/src/ExpoBixolonModule.ts +0 -38
  74. package/src/QrCodePrinter.ts +0 -201
@@ -0,0 +1,20 @@
1
+ root = true
2
+
3
+ [*]
4
+ charset = utf-8
5
+ end_of_line = lf
6
+ insert_final_newline = true
7
+ trim_trailing_whitespace = true
8
+
9
+ [*.{kt,kts}]
10
+ indent_size = 4
11
+ indent_style = space
12
+ max_line_length = 120
13
+ ij_kotlin_allow_trailing_comma = true
14
+ ij_kotlin_allow_trailing_comma_on_call_site = true
15
+
16
+ [*.{json,yml,yaml}]
17
+ indent_size = 2
18
+
19
+ [*.md]
20
+ trim_trailing_whitespace = false
@@ -0,0 +1,103 @@
1
+ # Android Development para Módulo Expo
2
+
3
+ ## ⚠️ ERROR CONOCIDO: `debugRuntimeClasspathCopy`
4
+
5
+ Este error **NO tiene solución** y **es normal**. Android Studio intenta sincronizar el módulo como una app Android completa, pero los módulos Expo son bibliotecas.
6
+
7
+ ```
8
+ Cannot select root node 'debugRuntimeClasspathCopy' as a variant
9
+ ```
10
+
11
+ **IGNORA ESTE ERROR.** El módulo funciona correctamente en apps host.
12
+
13
+ ---
14
+
15
+ ## Lo que SÍ funciona en Android Studio:
16
+
17
+ A pesar del error rojo de Gradle sync, puedes desarrollar normalmente:
18
+
19
+ ✅ **Autocompletado de Kotlin** - Funciona
20
+ ✅ **Navegación de código** - Cmd+Click en clases/funciones
21
+ ✅ **Análisis de errores** - Ve errores de sintaxis y tipos
22
+ ✅ **Formateo de código** - Cmd+Opt+L
23
+ ✅ **Refactoring** - Renombrar, extraer métodos, etc.
24
+ ✅ **Git integration** - Commits, diff, branches
25
+ ✅ **Debug** - Puedes poner breakpoints (cuando uses en app host)
26
+
27
+ ---
28
+
29
+ ## Setup rápido
30
+
31
+ ```bash
32
+ # Desde la raíz del proyecto
33
+ make android-env
34
+
35
+ # Abrir Android Studio
36
+ cd android
37
+ open -a "Android Studio" .
38
+ ```
39
+
40
+ **Ignora el banner rojo de Gradle sync y trabaja normalmente.**
41
+
42
+ ---
43
+
44
+ ## ¿Cómo probar el módulo?
45
+
46
+ El módulo se prueba en una **app host**, no standalone:
47
+
48
+ ```bash
49
+ # Crear app de prueba
50
+ cd ..
51
+ npx create-expo-app test-app
52
+ cd test-app
53
+ npm install ../sincpro_printer_expo
54
+
55
+ # Compilar y correr
56
+ npx expo run:android
57
+ ```
58
+
59
+ ---
60
+
61
+ ## Alternativa sin errores: IntelliJ IDEA
62
+
63
+ Si el error te molesta mucho:
64
+
65
+ ```bash
66
+ brew install --cask intellij-idea-ce
67
+ cd android
68
+ open -a "IntelliJ IDEA CE" .
69
+ ```
70
+
71
+ IntelliJ **no intenta** sincronizar como app Android, funciona mejor con bibliotecas.
72
+
73
+ ---
74
+
75
+ ## Comandos útiles
76
+
77
+ ```bash
78
+ make clean-android # Limpia caché
79
+ make android-env # Setup ambiente
80
+ make format # Formatear Kotlin
81
+ npm run lint:kotlin # Ver errores de lint
82
+ ```
83
+
84
+ ---
85
+
86
+ ## Estructura
87
+
88
+ ```
89
+ android-dev-env/ ← Templates versionados
90
+ ├── settings.gradle
91
+ ├── gradle.properties
92
+ └── README.md
93
+
94
+ android/
95
+ ├── build.gradle ← ✅ Commitear
96
+ ├── libs/ ← ✅ Commitear
97
+ ├── src/main/ ← ✅ Commitear
98
+
99
+ ├── settings.gradle ← ❌ Local (copiado)
100
+ ├── gradle.properties ← ❌ Local (copiado)
101
+ ├── .gradle/, .idea/ ← ❌ Ignorados
102
+ └── build/ ← ❌ Ignorado
103
+ ```
@@ -1,52 +1,54 @@
1
- apply plugin: 'com.android.library'
1
+ plugins {
2
+ id 'com.android.library'
3
+ id 'org.jetbrains.kotlin.android'
4
+ }
2
5
 
3
- group = 'expo.sincpro.bixolon'
6
+ group = 'sincpro.expo.printer'
4
7
  version = '0.1.0'
5
8
 
6
- def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
7
- apply from: expoModulesCorePlugin
8
- applyKotlinExpoModulesCorePlugin()
9
- useCoreDependencies()
10
- useExpoPublishing()
11
-
12
- // If you want to use the managed Android SDK versions from expo-modules-core, set this to true.
13
- // The Android SDK versions will be bumped from time to time in SDK releases and may introduce breaking changes in your module code.
14
- // Most of the time, you may like to manage the Android SDK versions yourself.
15
- def useManagedAndroidSdkVersions = false
16
- if (useManagedAndroidSdkVersions) {
17
- useDefaultAndroidSdkVersions()
18
- } else {
19
- buildscript {
20
- // Simple helper that allows the root project to override versions declared by this library.
21
- ext.safeExtGet = { prop, fallback ->
22
- rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
23
- }
24
- }
25
- project.android {
26
- compileSdkVersion safeExtGet("compileSdkVersion", 34)
27
- defaultConfig {
28
- minSdkVersion safeExtGet("minSdkVersion", 21)
29
- targetSdkVersion safeExtGet("targetSdkVersion", 34)
30
- }
31
- }
32
- }
33
-
34
9
  android {
35
- namespace "expo.sincpro.bixolon"
10
+ namespace "sincpro.expo.printer"
11
+ compileSdk 35
12
+
36
13
  defaultConfig {
14
+ minSdk 21
15
+ targetSdk 35
37
16
  versionCode 1
38
17
  versionName "0.1.0"
39
18
  }
19
+
20
+ compileOptions {
21
+ sourceCompatibility JavaVersion.VERSION_17
22
+ targetCompatibility JavaVersion.VERSION_17
23
+ }
24
+
25
+ kotlinOptions {
26
+ jvmTarget = '17'
27
+ }
28
+
40
29
  lintOptions {
41
30
  abortOnError false
42
31
  }
43
32
  }
44
33
 
45
34
  dependencies {
46
- // Bixolon printer libraries
47
- implementation fileTree(dir: 'libs', include: ['*.jar'])
35
+ // Sincpro Printer SDK (incluye Bixolon JARs + jniLibs embebidos)
36
+ implementation files('libs/sincpro-printer-sdk.aar')
48
37
 
49
- // Bluetooth and permissions
38
+ // Bixolon PDF support (AAR no puede embeber otro AAR)
39
+ implementation files('libs/pdf/Bixolon_pdf.aar')
40
+
41
+ // Expo modules (provided by host app)
42
+ compileOnly 'expo.modules:modules-core'
43
+
50
44
  implementation 'androidx.core:core-ktx:1.12.0'
51
45
  implementation 'androidx.appcompat:appcompat:1.6.1'
46
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3'
47
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
48
+ }
49
+ repositories {
50
+ mavenCentral()
51
+ }
52
+ kotlin {
53
+ jvmToolchain(17)
52
54
  }
Binary file
@@ -0,0 +1,387 @@
1
+ package sincpro.expo.printer.entrypoint
2
+
3
+ import android.content.Context
4
+ import com.sincpro.printer.SincproPrinterSdk
5
+ import com.sincpro.printer.domain.Alignment
6
+ import com.sincpro.printer.domain.BarcodeType
7
+ import com.sincpro.printer.domain.FontSize
8
+ import com.sincpro.printer.domain.MediaConfig
9
+ import com.sincpro.printer.domain.Receipt
10
+ import com.sincpro.printer.domain.ReceiptLine
11
+ import expo.modules.kotlin.modules.Module
12
+ import expo.modules.kotlin.modules.ModuleDefinition
13
+
14
+ class PrinterModule : Module() {
15
+ private lateinit var sdk: SincproPrinterSdk
16
+
17
+ override fun definition() =
18
+ ModuleDefinition {
19
+ Name("SincproPrinter")
20
+
21
+ OnCreate {
22
+ val context = appContext.reactContext as Context
23
+ sdk = SincproPrinterSdk(context)
24
+ }
25
+
26
+ // ============================================================
27
+ // BLUETOOTH API
28
+ // ============================================================
29
+
30
+ Function("getPairedDevices") {
31
+ sdk.bixolon.connectivity
32
+ .getPairedDevices()
33
+ .map { devices ->
34
+ devices.map { device ->
35
+ mapOf(
36
+ "name" to device.name,
37
+ "address" to device.address,
38
+ "isPrinter" to device.isPrinter,
39
+ )
40
+ }
41
+ }.getOrElse { emptyList() }
42
+ }
43
+
44
+ Function("getPairedPrinters") {
45
+ sdk.bixolon.connectivity
46
+ .getPairedPrinters()
47
+ .map { printers ->
48
+ printers.map { printer ->
49
+ mapOf(
50
+ "name" to printer.name,
51
+ "address" to printer.address,
52
+ )
53
+ }
54
+ }.getOrElse { emptyList() }
55
+ }
56
+
57
+ // ============================================================
58
+ // CONNECTION API
59
+ // ============================================================
60
+
61
+ AsyncFunction("connectBluetooth") { address: String, timeoutMs: Double? ->
62
+ sdk.bixolon.connectivity
63
+ .connectBluetooth(
64
+ address = address,
65
+ timeoutMs = timeoutMs?.toLong() ?: 10000,
66
+ ).getOrThrow()
67
+ }
68
+
69
+ AsyncFunction("connectWifi") { ip: String, port: Int?, timeoutMs: Double? ->
70
+ sdk.bixolon.connectivity
71
+ .connectWifi(
72
+ ip = ip,
73
+ port = port ?: 9100,
74
+ timeoutMs = timeoutMs?.toLong() ?: 10000,
75
+ ).getOrThrow()
76
+ }
77
+
78
+ AsyncFunction("connectUsb") {
79
+ sdk.bixolon.connectivity
80
+ .connectUsb()
81
+ .getOrThrow()
82
+ }
83
+
84
+ AsyncFunction("disconnect") {
85
+ sdk.bixolon.connectivity
86
+ .disconnect()
87
+ .getOrThrow()
88
+ }
89
+
90
+ Function("isConnected") {
91
+ sdk.bixolon.connectivity.isConnected()
92
+ }
93
+
94
+ AsyncFunction("getStatus") {
95
+ val status =
96
+ sdk.bixolon.connectivity
97
+ .getStatus()
98
+ .getOrThrow()
99
+ mapOf(
100
+ "connectionState" to status.connectionState.name,
101
+ "hasPaper" to status.hasPaper,
102
+ "isCoverOpen" to status.isCoverOpen,
103
+ "isOverheated" to status.isOverheated,
104
+ "hasError" to status.hasError,
105
+ "errorMessage" to status.errorMessage,
106
+ )
107
+ }
108
+
109
+ AsyncFunction("getInfo") {
110
+ val info =
111
+ sdk.bixolon.connectivity
112
+ .getInfo()
113
+ .getOrThrow()
114
+ mapOf(
115
+ "model" to info.model,
116
+ "firmware" to info.firmware,
117
+ "serial" to info.serial,
118
+ "dpi" to info.dpi,
119
+ )
120
+ }
121
+
122
+ Function("getDpi") {
123
+ sdk.bixolon.connectivity.getDpi()
124
+ }
125
+
126
+ // ============================================================
127
+ // PRINT API - Text
128
+ // ============================================================
129
+
130
+ AsyncFunction("printText") { text: String, options: Map<String, Any?>? ->
131
+ val fontSize = parseFontSize(options?.get("fontSize") as? String)
132
+ val alignment = parseAlignment(options?.get("alignment") as? String)
133
+ val bold = options?.get("bold") as? Boolean ?: false
134
+ val media = parseMediaConfig(options?.get("media") as? Map<String, Any?>)
135
+
136
+ sdk.bixolon.print
137
+ .printText(text, fontSize, alignment, bold, media)
138
+ .getOrThrow()
139
+ }
140
+
141
+ AsyncFunction("printTexts") { texts: List<String>, options: Map<String, Any?>? ->
142
+ val fontSize = parseFontSize(options?.get("fontSize") as? String)
143
+ val media = parseMediaConfig(options?.get("media") as? Map<String, Any?>)
144
+
145
+ sdk.bixolon.print
146
+ .printTexts(texts, fontSize, media)
147
+ .getOrThrow()
148
+ }
149
+
150
+ // ============================================================
151
+ // PRINT API - QR & Barcode
152
+ // ============================================================
153
+
154
+ AsyncFunction("printQR") { data: String, options: Map<String, Any?>? ->
155
+ val size = (options?.get("size") as? Number)?.toInt() ?: 5
156
+ val alignment = parseAlignment(options?.get("alignment") as? String)
157
+ val media = parseMediaConfig(options?.get("media") as? Map<String, Any?>)
158
+
159
+ sdk.bixolon.print
160
+ .printQR(data, size, alignment, media)
161
+ .getOrThrow()
162
+ }
163
+
164
+ AsyncFunction("printBarcode") { data: String, options: Map<String, Any?>? ->
165
+ val type = parseBarcodeType(options?.get("type") as? String)
166
+ val height = (options?.get("height") as? Number)?.toInt() ?: 60
167
+ val alignment = parseAlignment(options?.get("alignment") as? String)
168
+ val media = parseMediaConfig(options?.get("media") as? Map<String, Any?>)
169
+
170
+ sdk.bixolon.print
171
+ .printBarcode(data, type, height, alignment, media)
172
+ .getOrThrow()
173
+ }
174
+
175
+ // ============================================================
176
+ // PRINT API - Images & PDF
177
+ // ============================================================
178
+
179
+ AsyncFunction("printImageBase64") { base64Data: String, options: Map<String, Any?>? ->
180
+ val alignment = parseAlignment(options?.get("alignment") as? String)
181
+ val media = parseMediaConfig(options?.get("media") as? Map<String, Any?>)
182
+
183
+ sdk.bixolon.print
184
+ .printImageBase64(base64Data, alignment, media)
185
+ .getOrThrow()
186
+ }
187
+
188
+ AsyncFunction("printPdfBase64") { base64Data: String, options: Map<String, Any?>? ->
189
+ val page = (options?.get("page") as? Number)?.toInt() ?: 1
190
+ val alignment = parseAlignment(options?.get("alignment") as? String)
191
+ val media = parseMediaConfig(options?.get("media") as? Map<String, Any?>)
192
+
193
+ sdk.bixolon.print
194
+ .printPdfBase64(base64Data, page, alignment, media)
195
+ .getOrThrow()
196
+ }
197
+
198
+ Function("getPdfPageCount") { base64Data: String ->
199
+ sdk.bixolon.print.getPdfPageCount(base64Data)
200
+ }
201
+
202
+ // ============================================================
203
+ // PRINT API - Receipt (High Level)
204
+ // ============================================================
205
+
206
+ AsyncFunction("printReceipt") { receiptData: Map<String, Any?>, options: Map<String, Any?>? ->
207
+ val receipt = parseReceipt(receiptData)
208
+ val media = parseMediaConfig(options?.get("media") as? Map<String, Any?>)
209
+ val copies = (options?.get("copies") as? Number)?.toInt() ?: 1
210
+
211
+ sdk.bixolon.print
212
+ .printReceipt(receipt, media, copies)
213
+ .getOrThrow()
214
+ }
215
+
216
+ // ============================================================
217
+ // PRINT API - Columns (Key-Value style)
218
+ // ============================================================
219
+
220
+ AsyncFunction("printKeyValue") { key: String, value: String, options: Map<String, Any?>? ->
221
+ val fontSize = parseFontSize(options?.get("fontSize") as? String)
222
+ val bold = options?.get("bold") as? Boolean ?: false
223
+ val media = parseMediaConfig(options?.get("media") as? Map<String, Any?>)
224
+
225
+ sdk.bixolon.print
226
+ .printKeyValue(key, value, fontSize, bold, media)
227
+ .getOrThrow()
228
+ }
229
+ }
230
+
231
+ // ============================================================
232
+ // PARSERS
233
+ // ============================================================
234
+
235
+ private fun parseFontSize(value: String?): FontSize =
236
+ when (value?.lowercase()) {
237
+ "small" -> FontSize.SMALL
238
+ "medium" -> FontSize.MEDIUM
239
+ "large" -> FontSize.LARGE
240
+ "xlarge" -> FontSize.XLARGE
241
+ else -> FontSize.MEDIUM
242
+ }
243
+
244
+ private fun parseAlignment(value: String?): Alignment =
245
+ when (value?.lowercase()) {
246
+ "left" -> Alignment.LEFT
247
+ "center" -> Alignment.CENTER
248
+ "right" -> Alignment.RIGHT
249
+ else -> Alignment.LEFT
250
+ }
251
+
252
+ private fun parseBarcodeType(value: String?): BarcodeType =
253
+ when (value?.uppercase()) {
254
+ "CODE128" -> BarcodeType.CODE128
255
+ "CODE39" -> BarcodeType.CODE39
256
+ "EAN13" -> BarcodeType.EAN13
257
+ "EAN8" -> BarcodeType.EAN8
258
+ "UPCA" -> BarcodeType.UPCA
259
+ "UPCE" -> BarcodeType.UPCE
260
+ "CODE93" -> BarcodeType.CODE93
261
+ "CODABAR" -> BarcodeType.CODABAR
262
+ else -> BarcodeType.CODE128
263
+ }
264
+
265
+ private fun parseMediaConfig(data: Map<String, Any?>?): MediaConfig {
266
+ if (data == null) return MediaConfig.continuous80mm()
267
+
268
+ val preset = data["preset"] as? String
269
+ if (preset != null) {
270
+ return when (preset) {
271
+ "continuous58mm" -> MediaConfig.continuous58mm()
272
+ "continuous80mm" -> MediaConfig.continuous80mm()
273
+ else -> MediaConfig.continuous80mm()
274
+ }
275
+ }
276
+
277
+ val widthDots = (data["widthDots"] as? Number)?.toInt() ?: 640
278
+ val heightDots = (data["heightDots"] as? Number)?.toInt() ?: 0
279
+
280
+ return MediaConfig(widthDots, heightDots)
281
+ }
282
+
283
+ private fun parseReceipt(data: Map<String, Any?>): Receipt {
284
+ val header = parseReceiptLines(data["header"] as? List<*>)
285
+ val body = parseReceiptLines(data["body"] as? List<*>)
286
+ val footer = parseReceiptLines(data["footer"] as? List<*>)
287
+
288
+ return Receipt(header, body, footer)
289
+ }
290
+
291
+ private fun parseReceiptLines(data: List<*>?): List<ReceiptLine> {
292
+ if (data == null) return emptyList()
293
+
294
+ return data.mapNotNull { item ->
295
+ val lineData = item as? Map<String, Any?> ?: return@mapNotNull null
296
+ parseReceiptLine(lineData)
297
+ }
298
+ }
299
+
300
+ private fun parseReceiptLine(data: Map<String, Any?>): ReceiptLine? {
301
+ val type = data["type"] as? String ?: return null
302
+
303
+ return when (type) {
304
+ "text" -> {
305
+ ReceiptLine.Text(
306
+ content = data["content"] as? String ?: "",
307
+ fontSize = parseFontSize(data["fontSize"] as? String),
308
+ bold = data["bold"] as? Boolean ?: false,
309
+ alignment = parseAlignment(data["alignment"] as? String),
310
+ )
311
+ }
312
+
313
+ "keyValue" -> {
314
+ ReceiptLine.KeyValue(
315
+ key = data["key"] as? String ?: "",
316
+ value = data["value"] as? String ?: "",
317
+ fontSize = parseFontSize(data["fontSize"] as? String),
318
+ bold = data["bold"] as? Boolean ?: false,
319
+ )
320
+ }
321
+
322
+ "qr" -> {
323
+ ReceiptLine.QR(
324
+ data = data["data"] as? String ?: "",
325
+ size = (data["size"] as? Number)?.toInt() ?: 5,
326
+ alignment = parseAlignment(data["alignment"] as? String),
327
+ )
328
+ }
329
+
330
+ "barcode" -> {
331
+ ReceiptLine.Barcode(
332
+ data = data["data"] as? String ?: "",
333
+ type = parseBarcodeType(data["barcodeType"] as? String),
334
+ height = (data["height"] as? Number)?.toInt() ?: 60,
335
+ alignment = parseAlignment(data["alignment"] as? String),
336
+ )
337
+ }
338
+
339
+ "image" -> {
340
+ val base64 = data["base64"] as? String ?: return null
341
+ val bitmap =
342
+ com.sincpro.printer.infrastructure.BinaryConverter
343
+ .base64ToBitmap(base64)
344
+ ?: return null
345
+ ReceiptLine.Image(
346
+ bitmap = bitmap,
347
+ alignment = parseAlignment(data["alignment"] as? String),
348
+ )
349
+ }
350
+
351
+ "separator" -> {
352
+ ReceiptLine.Separator(
353
+ char = (data["char"] as? String)?.firstOrNull() ?: '-',
354
+ length = (data["length"] as? Number)?.toInt() ?: 48,
355
+ )
356
+ }
357
+
358
+ "space" -> {
359
+ ReceiptLine.Space(
360
+ lines = (data["lines"] as? Number)?.toInt() ?: 1,
361
+ )
362
+ }
363
+
364
+ "columns" -> {
365
+ val columnsData = data["columns"] as? List<*> ?: return null
366
+ val columns =
367
+ columnsData.mapNotNull { col ->
368
+ val colData = col as? Map<String, Any?> ?: return@mapNotNull null
369
+ ReceiptLine.Column(
370
+ text = colData["text"] as? String ?: "",
371
+ widthRatio = (colData["widthRatio"] as? Number)?.toFloat() ?: 0.5f,
372
+ alignment = parseAlignment(colData["alignment"] as? String),
373
+ )
374
+ }
375
+ ReceiptLine.Columns(
376
+ columns = columns,
377
+ fontSize = parseFontSize(data["fontSize"] as? String),
378
+ bold = data["bold"] as? Boolean ?: false,
379
+ )
380
+ }
381
+
382
+ else -> {
383
+ null
384
+ }
385
+ }
386
+ }
387
+ }