Commit ca4ab0f779b01bd40535f2a27f1c1493e720fd84

Authored by 杨鑫
2 parents 364df3ef 817a562b

Merge branch 'main' of http://39.98.150.180/wangming/Food-Labeling-Management-Platform

美国版/Food Labeling Management App UniApp/src/pages/labels/bluetooth.vue
@@ -163,7 +163,7 @@ @@ -163,7 +163,7 @@
163 <text class="tips-item">2. On Android: enable Location (required for Bluetooth scan)</text> 163 <text class="tips-item">2. On Android: enable Location (required for Bluetooth scan)</text>
164 <text class="tips-item">3. Place the printer within 10 m and tap Scan again</text> 164 <text class="tips-item">3. Place the printer within 10 m and tap Scan again</text>
165 <text class="tips-item">4. Devices with no name show as "Unknown Device"—you can still connect</text> 165 <text class="tips-item">4. Devices with no name show as "Unknown Device"—you can still connect</text>
166 - <text class="tips-item">5. D320FAX (d320fax_295c): use Bluetooth mode, tap Scan—shows paired + nearby devices, no filtering</text> 166 + <text class="tips-item">5. GP-D320FX (d320fx_xxxx): use Bluetooth mode, tap Scan—shows paired + nearby devices, no filtering</text>
167 <text class="tips-item tips-item-last">6. Restart the printer or app if not visible</text> 167 <text class="tips-item tips-item-last">6. Restart the printer or app if not visible</text>
168 </view> 168 </view>
169 </view> 169 </view>
@@ -260,8 +260,21 @@ async function refreshNativeDebug () { @@ -260,8 +260,21 @@ async function refreshNativeDebug () {
260 function hasPreferredClassicDeviceInList () { 260 function hasPreferredClassicDeviceInList () {
261 return devices.value.some((item: any) => { 261 return devices.value.some((item: any) => {
262 const name = String(item?.name || '').toLowerCase() 262 const name = String(item?.name || '').toLowerCase()
263 - const type = String(item?.type || '').toLowerCase()  
264 - return name.includes('virtual bt printer') || type === 'classic' || type === 'dual' 263 + const driverKey = String(item?.driverKey || '').toLowerCase()
  264 + return name.includes('virtual bt printer') || driverKey === 'd320fax'
  265 + })
  266 +}
  267 +
  268 +function upsertDevice (device: any) {
  269 + const described = describeDiscoveredPrinter(device)
  270 + const existing = devices.value.find(item => item.deviceId === described.deviceId)
  271 + if (!existing) {
  272 + discoveredIds.add(described.deviceId)
  273 + devices.value.push(described)
  274 + return
  275 + }
  276 + Object.assign(existing, described, {
  277 + RSSI: described.RSSI != null ? described.RSSI : existing.RSSI,
265 }) 278 })
266 } 279 }
267 280
@@ -343,16 +356,13 @@ const stopDiscovery = () =&gt; { @@ -343,16 +356,13 @@ const stopDiscovery = () =&gt; {
343 const onDeviceFound = (res: any) => { 356 const onDeviceFound = (res: any) => {
344 const foundDevices: any[] = res.devices || [] 357 const foundDevices: any[] = res.devices || []
345 for (const d of foundDevices) { 358 for (const d of foundDevices) {
346 - if (discoveredIds.has(d.deviceId)) continue  
347 const name = (d.localName || d.name || '').trim() 359 const name = (d.localName || d.name || '').trim()
348 - const displayName = name || 'Unknown Device'  
349 - discoveredIds.add(d.deviceId)  
350 - devices.value.push(describeDiscoveredPrinter({ 360 + upsertDevice({
351 deviceId: d.deviceId, 361 deviceId: d.deviceId,
352 - name: displayName, 362 + name: name || 'Unknown Device',
353 RSSI: d.RSSI, 363 RSSI: d.RSSI,
354 type: 'ble', 364 type: 'ble',
355 - })) 365 + })
356 } 366 }
357 devices.value.sort((a, b) => (b.RSSI || -100) - (a.RSSI || -100)) 367 devices.value.sort((a, b) => (b.RSSI || -100) - (a.RSSI || -100))
358 } 368 }
@@ -363,15 +373,13 @@ function mergeCachedBleDevices () { @@ -363,15 +373,13 @@ function mergeCachedBleDevices () {
363 success: (res: any) => { 373 success: (res: any) => {
364 const list = res.devices || [] 374 const list = res.devices || []
365 for (const d of list) { 375 for (const d of list) {
366 - if (discoveredIds.has(d.deviceId)) continue  
367 const name = (d.localName || d.name || '').trim() 376 const name = (d.localName || d.name || '').trim()
368 - discoveredIds.add(d.deviceId)  
369 - devices.value.push(describeDiscoveredPrinter({ 377 + upsertDevice({
370 deviceId: d.deviceId, 378 deviceId: d.deviceId,
371 name: name || 'Unknown Device', 379 name: name || 'Unknown Device',
372 RSSI: d.RSSI, 380 RSSI: d.RSSI,
373 type: 'ble', 381 type: 'ble',
374 - })) 382 + })
375 } 383 }
376 if (list.length > 0) devices.value.sort((a, b) => (b.RSSI || -100) - (a.RSSI || -100)) 384 if (list.length > 0) devices.value.sort((a, b) => (b.RSSI || -100) - (a.RSSI || -100))
377 }, 385 },
@@ -387,13 +395,11 @@ function addPairedDevices () { @@ -387,13 +395,11 @@ function addPairedDevices () {
387 debugInfo.value.pairedCount = (paired || []).length 395 debugInfo.value.pairedCount = (paired || []).length
388 debugInfo.value.foundVirtualPrinter = (paired || []).some((item: any) => String(item?.name || '').toLowerCase().includes('virtual bt printer')) 396 debugInfo.value.foundVirtualPrinter = (paired || []).some((item: any) => String(item?.name || '').toLowerCase().includes('virtual bt printer'))
389 for (const p of paired) { 397 for (const p of paired) {
390 - if (discoveredIds.has(p.deviceId)) continue  
391 - discoveredIds.add(p.deviceId)  
392 - devices.value.push(describeDiscoveredPrinter({ 398 + upsertDevice({
393 deviceId: p.deviceId, 399 deviceId: p.deviceId,
394 name: p.name || 'Unknown Device', 400 name: p.name || 'Unknown Device',
395 type: p.type || 'classic', 401 type: p.type || 'classic',
396 - })) 402 + })
397 } 403 }
398 if (paired.length > 0) { 404 if (paired.length > 0) {
399 debugInfo.value.lastClassicEvent = 'paired devices loaded' 405 debugInfo.value.lastClassicEvent = 'paired devices loaded'
@@ -416,13 +422,11 @@ function startClassicScan () { @@ -416,13 +422,11 @@ function startClassicScan () {
416 classic.startClassicDiscovery( 422 classic.startClassicDiscovery(
417 (dev: { name: string; deviceId: string; type: string }) => { 423 (dev: { name: string; deviceId: string; type: string }) => {
418 debugInfo.value.lastClassicEvent = 'device found' 424 debugInfo.value.lastClassicEvent = 'device found'
419 - if (discoveredIds.has(dev.deviceId)) return  
420 - discoveredIds.add(dev.deviceId)  
421 - devices.value.push(describeDiscoveredPrinter({ 425 + upsertDevice({
422 deviceId: dev.deviceId, 426 deviceId: dev.deviceId,
423 name: dev.name || 'Unknown Device', 427 name: dev.name || 'Unknown Device',
424 type: (dev.type as BtDevice['type']) || 'classic', 428 type: (dev.type as BtDevice['type']) || 'classic',
425 - })) 429 + })
426 devices.value.sort((a, b) => (b.RSSI || -100) - (a.RSSI || -100)) 430 devices.value.sort((a, b) => (b.RSSI || -100) - (a.RSSI || -100))
427 }, 431 },
428 () => { 432 () => {
@@ -542,7 +546,7 @@ const handleTestPrint = async () =&gt; { @@ -542,7 +546,7 @@ const handleTestPrint = async () =&gt; {
542 if (msg === 'BUILTIN_PLUGIN_NOT_FOUND') { 546 if (msg === 'BUILTIN_PLUGIN_NOT_FOUND') {
543 uni.showModal({ 547 uni.showModal({
544 title: 'Use Bluetooth Mode', 548 title: 'Use Bluetooth Mode',
545 - content: 'For GP-D320FAX, switch to Bluetooth mode and tap Scan to connect (e.g. d320fax_295c or Virtual BT Printer). Built-in mode needs custom app packaging.', 549 + content: 'For GP-D320FX, switch to Bluetooth mode and tap Scan to connect (for example d320fx_xxxx). Built-in mode needs custom app packaging.',
546 showCancel: false, 550 showCancel: false,
547 success: () => { switchType('bluetooth') }, 551 success: () => { switchType('bluetooth') },
548 }) 552 })
美国版/Food Labeling Management App UniApp/src/pages/more/printers.vue
@@ -218,8 +218,8 @@ const normalizeDeviceName = (device: any) =&gt; { @@ -218,8 +218,8 @@ const normalizeDeviceName = (device: any) =&gt; {
218 const hasPreferredClassicDevice = () => { 218 const hasPreferredClassicDevice = () => {
219 return pairedDevices.value.some((item: any) => { 219 return pairedDevices.value.some((item: any) => {
220 const name = String(item?.name || '').toLowerCase() 220 const name = String(item?.name || '').toLowerCase()
221 - const type = String(item?.type || '').toLowerCase()  
222 - return name.includes('virtual bt printer') || type === 'classic' || type === 'dual' 221 + const driverKey = String(item?.driverKey || '').toLowerCase()
  222 + return name.includes('virtual bt printer') || driverKey === 'd320fax'
223 }) 223 })
224 } 224 }
225 225
美国版/Food Labeling Management App UniApp/src/utils/print/drivers/d320fax.ts
@@ -3,7 +3,6 @@ import type { PrinterCandidate, PrinterDriver } from &#39;../types/printer&#39; @@ -3,7 +3,6 @@ import type { PrinterCandidate, PrinterDriver } from &#39;../types/printer&#39;
3 3
4 const KEYWORDS = [ 4 const KEYWORDS = [
5 'd320fax', 5 'd320fax',
6 - 'd320fx',  
7 'virtual bt printer', 6 'virtual bt printer',
8 'gp-d320fax', 7 'gp-d320fax',
9 ] 8 ]
@@ -20,7 +19,7 @@ function score (device: PrinterCandidate): number { @@ -20,7 +19,7 @@ function score (device: PrinterCandidate): number {
20 export const d320faxDriver: PrinterDriver = { 19 export const d320faxDriver: PrinterDriver = {
21 key: 'd320fax', 20 key: 'd320fax',
22 brand: 'Gprinter', 21 brand: 'Gprinter',
23 - model: 'D320FAX/D320FX', 22 + model: 'D320FAX',
24 displayName: 'Gprinter D320FAX', 23 displayName: 'Gprinter D320FAX',
25 protocol: 'tsc', 24 protocol: 'tsc',
26 preferredConnection: 'classic', 25 preferredConnection: 'classic',
美国版/Food Labeling Management App UniApp/src/utils/print/drivers/gpD320fx.ts 0 → 100644
  1 +import { buildTscLabelData, buildTscTestPrintData } from '../protocols/tscProtocol'
  2 +import type { PrinterCandidate, PrinterDriver } from '../types/printer'
  3 +
  4 +const KEYWORDS = [
  5 + 'd320fx',
  6 + 'gp-d320fx',
  7 +]
  8 +
  9 +function score (device: PrinterCandidate): number {
  10 + const text = `${device.name || ''} ${device.deviceId || ''}`.toLowerCase()
  11 + let total = 0
  12 + KEYWORDS.forEach((keyword) => {
  13 + if (text.includes(keyword)) total += 80
  14 + })
  15 + if (text.includes('gprinter')) total += 10
  16 + return total
  17 +}
  18 +
  19 +export const gpD320fxDriver: PrinterDriver = {
  20 + key: 'gp-d320fx',
  21 + brand: 'Gprinter',
  22 + model: 'GP-D320FX',
  23 + displayName: 'Gprinter GP-D320FX',
  24 + protocol: 'tsc',
  25 + preferredConnection: 'ble',
  26 + preferredBleMtu: 512,
  27 + imageMaxWidthDots: 800,
  28 + imageDpi: 203,
  29 + keywords: KEYWORDS,
  30 + matches (device) {
  31 + return score(device)
  32 + },
  33 + resolveConnectionType (device) {
  34 + return device.type === 'classic' ? 'classic' : 'ble'
  35 + },
  36 + buildTestPrintData () {
  37 + return buildTscTestPrintData()
  38 + },
  39 + buildLabelData (payload) {
  40 + return buildTscLabelData(payload)
  41 + },
  42 +}
美国版/Food Labeling Management App UniApp/src/utils/print/manager/driverRegistry.ts
1 import { d320faxDriver } from '../drivers/d320fax' 1 import { d320faxDriver } from '../drivers/d320fax'
2 import { genericTscDriver } from '../drivers/genericTsc' 2 import { genericTscDriver } from '../drivers/genericTsc'
  3 +import { gpD320fxDriver } from '../drivers/gpD320fx'
3 import { gpR3Driver } from '../drivers/gpR3' 4 import { gpR3Driver } from '../drivers/gpR3'
4 import type { PrinterCandidate, PrinterDriver, ResolvedPrinterCandidate } from '../types/printer' 5 import type { PrinterCandidate, PrinterDriver, ResolvedPrinterCandidate } from '../types/printer'
5 6
6 const printerDrivers: PrinterDriver[] = [ 7 const printerDrivers: PrinterDriver[] = [
7 gpR3Driver, 8 gpR3Driver,
  9 + gpD320fxDriver,
8 d320faxDriver, 10 d320faxDriver,
9 genericTscDriver, 11 genericTscDriver,
10 ] 12 ]
美国版/Food Labeling Management App UniApp/src/utils/print/manager/printerManager.ts
@@ -41,7 +41,38 @@ function getPrinterTypeDisplayName (type: &#39;&#39; | &#39;bluetooth&#39; | &#39;builtin&#39;): string @@ -41,7 +41,38 @@ function getPrinterTypeDisplayName (type: &#39;&#39; | &#39;bluetooth&#39; | &#39;builtin&#39;): string
41 function connectClassicBluetooth (device: PrinterCandidate, driver: PrinterDriver): Promise<void> { 41 function connectClassicBluetooth (device: PrinterCandidate, driver: PrinterDriver): Promise<void> {
42 return new Promise((resolve, reject) => { 42 return new Promise((resolve, reject) => {
43 // #ifdef APP-PLUS 43 // #ifdef APP-PLUS
44 - if (isNativeFastPrinterAvailable()) { 44 + const shouldUseGenericClassicOnly = driver.key === 'gp-d320fx'
  45 +
  46 + const connectClassicSocketFallback = () => {
  47 + try {
  48 + if (!classicBluetooth || typeof classicBluetooth.connDevice !== 'function') {
  49 + reject(new Error('Classic Bluetooth fallback is not available.'))
  50 + return
  51 + }
  52 + classicBluetooth.connDevice(device.deviceId, (ok: boolean) => {
  53 + if (ok) {
  54 + setBluetoothConnection({
  55 + deviceId: device.deviceId,
  56 + deviceName: device.name || 'Bluetooth Printer',
  57 + deviceType: 'classic',
  58 + transportMode: 'generic',
  59 + driverKey: driver.key,
  60 + mtu: driver.preferredBleMtu || 20,
  61 + })
  62 + resolve()
  63 + return
  64 + }
  65 + const message = typeof classicBluetooth.getLastError === 'function'
  66 + ? classicBluetooth.getLastError()
  67 + : ''
  68 + reject(new Error(message || 'Classic Bluetooth connection failed.'))
  69 + })
  70 + } catch (error: any) {
  71 + reject(error instanceof Error ? error : new Error(String(error || 'Classic Bluetooth connection failed.')))
  72 + }
  73 + }
  74 +
  75 + if (!shouldUseGenericClassicOnly && isNativeFastPrinterAvailable()) {
45 connectNativeFastPrinterPlugin({ 76 connectNativeFastPrinterPlugin({
46 deviceId: device.deviceId, 77 deviceId: device.deviceId,
47 deviceName: device.name || 'Bluetooth Printer', 78 deviceName: device.name || 'Bluetooth Printer',
@@ -50,15 +81,24 @@ function connectClassicBluetooth (device: PrinterCandidate, driver: PrinterDrive @@ -50,15 +81,24 @@ function connectClassicBluetooth (device: PrinterCandidate, driver: PrinterDrive
50 deviceId: device.deviceId, 81 deviceId: device.deviceId,
51 deviceName: device.name || 'Bluetooth Printer', 82 deviceName: device.name || 'Bluetooth Printer',
52 deviceType: 'classic', 83 deviceType: 'classic',
  84 + transportMode: 'native-plugin',
53 driverKey: driver.key, 85 driverKey: driver.key,
54 mtu: driver.preferredBleMtu || 20, 86 mtu: driver.preferredBleMtu || 20,
55 }) 87 })
56 resolve() 88 resolve()
57 }).catch((error: any) => { 89 }).catch((error: any) => {
  90 + if (driver.key === 'd320fax') {
  91 + connectClassicSocketFallback()
  92 + return
  93 + }
58 reject(error instanceof Error ? error : new Error(String(error || 'Classic Bluetooth connection failed.'))) 94 reject(error instanceof Error ? error : new Error(String(error || 'Classic Bluetooth connection failed.')))
59 }) 95 })
60 return 96 return
61 } 97 }
  98 + if (driver.key === 'd320fax' || shouldUseGenericClassicOnly) {
  99 + connectClassicSocketFallback()
  100 + return
  101 + }
62 reject(new Error('NATIVE_FAST_PRINTER_PLUGIN_NOT_FOUND. Please rebuild the custom base with native-fast-printer.')) 102 reject(new Error('NATIVE_FAST_PRINTER_PLUGIN_NOT_FOUND. Please rebuild the custom base with native-fast-printer.'))
63 // #endif 103 // #endif
64 // #ifndef APP-PLUS 104 // #ifndef APP-PLUS
@@ -103,19 +143,44 @@ function findBleWriteCharacteristic (deviceId: string): Promise&lt;{ serviceId: str @@ -103,19 +143,44 @@ function findBleWriteCharacteristic (deviceId: string): Promise&lt;{ serviceId: str
103 }) 143 })
104 } 144 }
105 145
  146 +function requestBleMtu (deviceId: string, preferredMtu: number): Promise<number> {
  147 + return new Promise((resolve) => {
  148 + const targetMtu = Math.max(20, Math.min(512, Math.round(preferredMtu || 20)))
  149 + if (targetMtu <= 20 || typeof (uni as any).setBLEMTU !== 'function') {
  150 + resolve(20)
  151 + return
  152 + }
  153 + let settled = false
  154 + const done = (value: number) => {
  155 + if (settled) return
  156 + settled = true
  157 + clearTimeout(timer)
  158 + resolve(Math.max(20, Math.round(value || 20)))
  159 + }
  160 + const timer = setTimeout(() => done(20), 3000)
  161 + ;(uni as any).setBLEMTU({
  162 + deviceId,
  163 + mtu: targetMtu,
  164 + success: (res: any) => done(Number(res?.mtu) || targetMtu),
  165 + fail: () => done(20),
  166 + })
  167 + })
  168 +}
  169 +
106 function connectBlePrinter (device: PrinterCandidate, driver: PrinterDriver): Promise<void> { 170 function connectBlePrinter (device: PrinterCandidate, driver: PrinterDriver): Promise<void> {
107 const finalizeExistingBleConnection = async () => { 171 const finalizeExistingBleConnection = async () => {
108 const write = await findBleWriteCharacteristic(device.deviceId) 172 const write = await findBleWriteCharacteristic(device.deviceId)
109 if (!write) { 173 if (!write) {
110 throw new Error('No writable characteristic found. This device may not support printing.') 174 throw new Error('No writable characteristic found. This device may not support printing.')
111 } 175 }
  176 + const negotiatedMtu = await requestBleMtu(device.deviceId, driver.preferredBleMtu || 20)
112 setBluetoothConnection({ 177 setBluetoothConnection({
113 deviceId: device.deviceId, 178 deviceId: device.deviceId,
114 deviceName: device.name || 'Bluetooth Printer', 179 deviceName: device.name || 'Bluetooth Printer',
115 serviceId: write.serviceId, 180 serviceId: write.serviceId,
116 characteristicId: write.characteristicId, 181 characteristicId: write.characteristicId,
117 deviceType: 'ble', 182 deviceType: 'ble',
118 - mtu: driver.preferredBleMtu || 20, 183 + mtu: negotiatedMtu,
119 driverKey: driver.key, 184 driverKey: driver.key,
120 }) 185 })
121 } 186 }
@@ -145,6 +210,14 @@ function connectBlePrinter (device: PrinterCandidate, driver: PrinterDriver): Pr @@ -145,6 +210,14 @@ function connectBlePrinter (device: PrinterCandidate, driver: PrinterDriver): Pr
145 210
146 export async function connectBluetoothPrinter (device: PrinterCandidate): Promise<PrinterDriver> { 211 export async function connectBluetoothPrinter (device: PrinterCandidate): Promise<PrinterDriver> {
147 const driver = resolvePrinterDriver(device) 212 const driver = resolvePrinterDriver(device)
  213 + if (driver.key === 'gp-d320fx') {
  214 + try {
  215 + await connectBlePrinter(device, driver)
  216 + } catch (_) {
  217 + await connectClassicBluetooth(device, driver)
  218 + }
  219 + return driver
  220 + }
148 const resolvedType = driver.resolveConnectionType(device) 221 const resolvedType = driver.resolveConnectionType(device)
149 if (resolvedType === 'classic') { 222 if (resolvedType === 'classic') {
150 await connectClassicBluetooth(device, driver) 223 await connectClassicBluetooth(device, driver)
@@ -218,19 +291,25 @@ function canUseNativeFastTemplatePrint (driver: PrinterDriver): boolean { @@ -218,19 +291,25 @@ function canUseNativeFastTemplatePrint (driver: PrinterDriver): boolean {
218 const connection = getBluetoothConnection() 291 const connection = getBluetoothConnection()
219 return driver.protocol === 'tsc' 292 return driver.protocol === 'tsc'
220 && connection?.deviceType === 'classic' 293 && connection?.deviceType === 'classic'
  294 + && connection?.transportMode === 'native-plugin'
221 && isNativeFastPrinterAvailable() 295 && isNativeFastPrinterAvailable()
222 } 296 }
223 297
224 function getNativeClassicConnection () { 298 function getNativeClassicConnection () {
225 const connection = getBluetoothConnection() 299 const connection = getBluetoothConnection()
226 - if (!connection || connection.deviceType !== 'classic') return null 300 + if (!connection || connection.deviceType !== 'classic' || connection.transportMode !== 'native-plugin') return null
227 return connection 301 return connection
228 } 302 }
229 303
230 export async function testPrintCurrentPrinter (onProgress?: (percent: number) => void): Promise<PrinterDriver> { 304 export async function testPrintCurrentPrinter (onProgress?: (percent: number) => void): Promise<PrinterDriver> {
231 const driver = getCurrentPrinterDriver() 305 const driver = getCurrentPrinterDriver()
232 const connection = getBluetoothConnection() 306 const connection = getBluetoothConnection()
233 - if (driver.protocol === 'tsc' && connection?.deviceType === 'classic' && !isNativeFastPrinterAvailable()) { 307 + if (
  308 + driver.protocol === 'tsc'
  309 + && connection?.deviceType === 'classic'
  310 + && connection?.transportMode === 'native-plugin'
  311 + && !isNativeFastPrinterAvailable()
  312 + ) {
234 throw new Error('NATIVE_FAST_PRINTER_PLUGIN_NOT_FOUND. Please rebuild the custom base with native-fast-printer.') 313 throw new Error('NATIVE_FAST_PRINTER_PLUGIN_NOT_FOUND. Please rebuild the custom base with native-fast-printer.')
235 } 314 }
236 if (canUseNativeFastTemplatePrint(driver)) { 315 if (canUseNativeFastTemplatePrint(driver)) {
@@ -319,7 +398,12 @@ export async function printSystemTemplateForCurrentPrinter ( @@ -319,7 +398,12 @@ export async function printSystemTemplateForCurrentPrinter (
319 ): Promise<PrinterDriver> { 398 ): Promise<PrinterDriver> {
320 const driver = getCurrentPrinterDriver() 399 const driver = getCurrentPrinterDriver()
321 const connection = getBluetoothConnection() 400 const connection = getBluetoothConnection()
322 - if (driver.protocol === 'tsc' && connection?.deviceType === 'classic' && !isNativeFastPrinterAvailable()) { 401 + if (
  402 + driver.protocol === 'tsc'
  403 + && connection?.deviceType === 'classic'
  404 + && connection?.transportMode === 'native-plugin'
  405 + && !isNativeFastPrinterAvailable()
  406 + ) {
323 throw new Error('NATIVE_FAST_PRINTER_PLUGIN_NOT_FOUND. Please rebuild the custom base with native-fast-printer.') 407 throw new Error('NATIVE_FAST_PRINTER_PLUGIN_NOT_FOUND. Please rebuild the custom base with native-fast-printer.')
324 } 408 }
325 if (canUseNativeFastTemplatePrint(driver)) { 409 if (canUseNativeFastTemplatePrint(driver)) {
@@ -341,6 +425,7 @@ export async function printSystemTemplateForCurrentPrinter ( @@ -341,6 +425,7 @@ export async function printSystemTemplateForCurrentPrinter (
341 const structuredTemplate = adaptSystemLabelTemplate(template, data, { 425 const structuredTemplate = adaptSystemLabelTemplate(template, data, {
342 dpi: driver.imageDpi || 203, 426 dpi: driver.imageDpi || 203,
343 printQty: options.printQty || 1, 427 printQty: options.printQty || 1,
  428 + disableBitmapText: driver.key === 'gp-d320fx',
344 }) 429 })
345 const bytes = driver.protocol === 'esc' 430 const bytes = driver.protocol === 'esc'
346 ? buildEscPosTemplateData(structuredTemplate) 431 ? buildEscPosTemplateData(structuredTemplate)
@@ -360,7 +445,7 @@ export function disconnectCurrentPrinter (): Promise&lt;void&gt; { @@ -360,7 +445,7 @@ export function disconnectCurrentPrinter (): Promise&lt;void&gt; {
360 445
361 if (type === 'bluetooth' && connection?.deviceType === 'classic') { 446 if (type === 'bluetooth' && connection?.deviceType === 'classic') {
362 // #ifdef APP-PLUS 447 // #ifdef APP-PLUS
363 - if (isNativeFastPrinterAvailable()) { 448 + if (connection.transportMode === 'native-plugin' && isNativeFastPrinterAvailable()) {
364 disconnectNativeFastPrinterPlugin().catch((e: any) => { 449 disconnectNativeFastPrinterPlugin().catch((e: any) => {
365 console.error('Disconnect native fast printer failed', e) 450 console.error('Disconnect native fast printer failed', e)
366 }).finally(() => { 451 }).finally(() => {
美国版/Food Labeling Management App UniApp/src/utils/print/nativeBitmapPatch.ts
@@ -10,6 +10,14 @@ const DESIGN_DPI = 96 @@ -10,6 +10,14 @@ const DESIGN_DPI = 96
10 const DEFAULT_THRESHOLD = 180 10 const DEFAULT_THRESHOLD = 180
11 const TEXT_PADDING_DOTS = 6 11 const TEXT_PADDING_DOTS = 6
12 12
  13 +function normalizePrinterLikeText (str: string): string {
  14 + return String(str || '')
  15 + .normalize('NFKC')
  16 + .replace(/[\u2018\u2019]/g, '\'')
  17 + .replace(/[\u201C\u201D]/g, '"')
  18 + .replace(/[\u2013\u2014]/g, '-')
  19 +}
  20 +
13 type BitmapPatchItem = { 21 type BitmapPatchItem = {
14 type: 'bitmap' 22 type: 'bitmap'
15 x: number 23 x: number
@@ -144,10 +152,11 @@ function splitTextLines (text: string, paint: any, maxWidth: number): string[] { @@ -144,10 +152,11 @@ function splitTextLines (text: string, paint: any, maxWidth: number): string[] {
144 152
145 export function shouldRasterizeTextElement (text: string, type: string): boolean { 153 export function shouldRasterizeTextElement (text: string, type: string): boolean {
146 const normalizedType = String(type || '').toUpperCase() 154 const normalizedType = String(type || '').toUpperCase()
147 - if (!text) return false  
148 - if (normalizedType === 'TEXT_PRICE') return true  
149 - if (/[€£¥¥$éÉáàâäãåæçèêëìíîïñòóôöõøùúûüýÿœšž]/.test(text)) return true  
150 - return /[^\x20-\x7E]/.test(text) 155 + const normalizedText = normalizePrinterLikeText(text)
  156 + if (!normalizedText) return false
  157 + if (normalizedType === 'TEXT_PRICE' && /[€£¥¥]/.test(normalizedText)) return true
  158 + if (/[€£¥¥éÉáàâäãåæçèêëìíîïñòóôöõøùúûüýÿœšž]/.test(normalizedText)) return true
  159 + return /[^\x20-\x7E]/.test(normalizedText)
151 } 160 }
152 161
153 export function createTextBitmapPatch (params: { 162 export function createTextBitmapPatch (params: {
美国版/Food Labeling Management App UniApp/src/utils/print/printerConnection.ts
@@ -11,6 +11,7 @@ const STORAGE_BT_DEVICE_NAME = &#39;btDeviceName&#39; @@ -11,6 +11,7 @@ const STORAGE_BT_DEVICE_NAME = &#39;btDeviceName&#39;
11 const STORAGE_BT_SERVICE_ID = 'btServiceId' 11 const STORAGE_BT_SERVICE_ID = 'btServiceId'
12 const STORAGE_BT_CHARACTERISTIC_ID = 'btCharacteristicId' 12 const STORAGE_BT_CHARACTERISTIC_ID = 'btCharacteristicId'
13 const STORAGE_BT_DEVICE_TYPE = 'btDeviceType' // 'ble' | 'classic' 13 const STORAGE_BT_DEVICE_TYPE = 'btDeviceType' // 'ble' | 'classic'
  14 +const STORAGE_BT_TRANSPORT_MODE = 'btTransportMode' // 'native-plugin' | 'generic'
14 const STORAGE_BLE_MTU = 'bleMTU' 15 const STORAGE_BLE_MTU = 'bleMTU'
15 const STORAGE_BUILTIN_PORT = 'builtinPort' 16 const STORAGE_BUILTIN_PORT = 'builtinPort'
16 const STORAGE_PRINTER_DRIVER_KEY = 'printerDriverKey' 17 const STORAGE_PRINTER_DRIVER_KEY = 'printerDriverKey'
@@ -29,6 +30,7 @@ export const PrinterStorageKeys = { @@ -29,6 +30,7 @@ export const PrinterStorageKeys = {
29 btServiceId: STORAGE_BT_SERVICE_ID, 30 btServiceId: STORAGE_BT_SERVICE_ID,
30 btCharacteristicId: STORAGE_BT_CHARACTERISTIC_ID, 31 btCharacteristicId: STORAGE_BT_CHARACTERISTIC_ID,
31 btDeviceType: STORAGE_BT_DEVICE_TYPE, 32 btDeviceType: STORAGE_BT_DEVICE_TYPE,
  33 + btTransportMode: STORAGE_BT_TRANSPORT_MODE,
32 bleMTU: STORAGE_BLE_MTU, 34 bleMTU: STORAGE_BLE_MTU,
33 driverKey: STORAGE_PRINTER_DRIVER_KEY, 35 driverKey: STORAGE_PRINTER_DRIVER_KEY,
34 } as const 36 } as const
@@ -43,6 +45,7 @@ export function setBluetoothConnection (info: { @@ -43,6 +45,7 @@ export function setBluetoothConnection (info: {
43 serviceId?: string 45 serviceId?: string
44 characteristicId?: string 46 characteristicId?: string
45 deviceType?: BtDeviceType 47 deviceType?: BtDeviceType
  48 + transportMode?: 'native-plugin' | 'generic'
46 mtu?: number 49 mtu?: number
47 driverKey?: string 50 driverKey?: string
48 }) { 51 }) {
@@ -52,6 +55,10 @@ export function setBluetoothConnection (info: { @@ -52,6 +55,10 @@ export function setBluetoothConnection (info: {
52 uni.setStorageSync(STORAGE_BT_SERVICE_ID, info.serviceId || '') 55 uni.setStorageSync(STORAGE_BT_SERVICE_ID, info.serviceId || '')
53 uni.setStorageSync(STORAGE_BT_CHARACTERISTIC_ID, info.characteristicId || '') 56 uni.setStorageSync(STORAGE_BT_CHARACTERISTIC_ID, info.characteristicId || '')
54 uni.setStorageSync(STORAGE_BT_DEVICE_TYPE, info.deviceType || 'ble') 57 uni.setStorageSync(STORAGE_BT_DEVICE_TYPE, info.deviceType || 'ble')
  58 + uni.setStorageSync(
  59 + STORAGE_BT_TRANSPORT_MODE,
  60 + info.transportMode || (info.deviceType === 'classic' ? 'native-plugin' : 'generic')
  61 + )
55 uni.setStorageSync(STORAGE_BLE_MTU, info.mtu != null ? info.mtu : BLE_MTU_DEFAULT) 62 uni.setStorageSync(STORAGE_BLE_MTU, info.mtu != null ? info.mtu : BLE_MTU_DEFAULT)
56 uni.setStorageSync(STORAGE_PRINTER_DRIVER_KEY, info.driverKey || '') 63 uni.setStorageSync(STORAGE_PRINTER_DRIVER_KEY, info.driverKey || '')
57 } 64 }
@@ -68,6 +75,7 @@ export function clearPrinter () { @@ -68,6 +75,7 @@ export function clearPrinter () {
68 uni.removeStorageSync(STORAGE_BT_SERVICE_ID) 75 uni.removeStorageSync(STORAGE_BT_SERVICE_ID)
69 uni.removeStorageSync(STORAGE_BT_CHARACTERISTIC_ID) 76 uni.removeStorageSync(STORAGE_BT_CHARACTERISTIC_ID)
70 uni.removeStorageSync(STORAGE_BT_DEVICE_TYPE) 77 uni.removeStorageSync(STORAGE_BT_DEVICE_TYPE)
  78 + uni.removeStorageSync(STORAGE_BT_TRANSPORT_MODE)
71 uni.removeStorageSync(STORAGE_BLE_MTU) 79 uni.removeStorageSync(STORAGE_BLE_MTU)
72 uni.removeStorageSync(STORAGE_BUILTIN_PORT) 80 uni.removeStorageSync(STORAGE_BUILTIN_PORT)
73 uni.removeStorageSync(STORAGE_PRINTER_DRIVER_KEY) 81 uni.removeStorageSync(STORAGE_PRINTER_DRIVER_KEY)
@@ -93,10 +101,12 @@ export function getBluetoothConnection (): { @@ -93,10 +101,12 @@ export function getBluetoothConnection (): {
93 serviceId: string 101 serviceId: string
94 characteristicId: string 102 characteristicId: string
95 deviceType: BtDeviceType 103 deviceType: BtDeviceType
  104 + transportMode: 'native-plugin' | 'generic'
96 mtu: number 105 mtu: number
97 } | null { 106 } | null {
98 const deviceId = uni.getStorageSync(STORAGE_BT_DEVICE_ID) 107 const deviceId = uni.getStorageSync(STORAGE_BT_DEVICE_ID)
99 const deviceType = (uni.getStorageSync(STORAGE_BT_DEVICE_TYPE) as BtDeviceType) || 'ble' 108 const deviceType = (uni.getStorageSync(STORAGE_BT_DEVICE_TYPE) as BtDeviceType) || 'ble'
  109 + const transportMode = (uni.getStorageSync(STORAGE_BT_TRANSPORT_MODE) as 'native-plugin' | 'generic') || 'generic'
100 if (!deviceId) return null 110 if (!deviceId) return null
101 if (deviceType === 'classic') { 111 if (deviceType === 'classic') {
102 return { 112 return {
@@ -105,6 +115,7 @@ export function getBluetoothConnection (): { @@ -105,6 +115,7 @@ export function getBluetoothConnection (): {
105 serviceId: '', 115 serviceId: '',
106 characteristicId: '', 116 characteristicId: '',
107 deviceType: 'classic', 117 deviceType: 'classic',
  118 + transportMode,
108 mtu: BLE_MTU_DEFAULT, 119 mtu: BLE_MTU_DEFAULT,
109 } 120 }
110 } 121 }
@@ -117,6 +128,7 @@ export function getBluetoothConnection (): { @@ -117,6 +128,7 @@ export function getBluetoothConnection (): {
117 serviceId, 128 serviceId,
118 characteristicId, 129 characteristicId,
119 deviceType: 'ble', 130 deviceType: 'ble',
  131 + transportMode,
120 mtu: Number(uni.getStorageSync(STORAGE_BLE_MTU)) || BLE_MTU_DEFAULT, 132 mtu: Number(uni.getStorageSync(STORAGE_BLE_MTU)) || BLE_MTU_DEFAULT,
121 } 133 }
122 } 134 }
@@ -209,15 +221,33 @@ function sendViaBle ( @@ -209,15 +221,33 @@ function sendViaBle (
209 return Promise.reject(new Error('Bluetooth printer not connected.')) 221 return Promise.reject(new Error('Bluetooth printer not connected.'))
210 } 222 }
211 const { deviceId, serviceId, characteristicId, mtu } = conn 223 const { deviceId, serviceId, characteristicId, mtu } = conn
  224 + const payloadSize = Math.max(20, Math.round((mtu || BLE_MTU_DEFAULT) > 23 ? (mtu || BLE_MTU_DEFAULT) - 3 : (mtu || BLE_MTU_DEFAULT)))
212 const chunks: number[][] = [] 225 const chunks: number[][] = []
213 - for (let i = 0; i < data.length; i += mtu) {  
214 - chunks.push(data.slice(i, i + mtu)) 226 + for (let i = 0; i < data.length; i += payloadSize) {
  227 + chunks.push(data.slice(i, i + payloadSize))
215 } 228 }
216 const total = chunks.length 229 const total = chunks.length
217 let sent = 0 230 let sent = 0
  231 + let completed = false
  232 + let timeoutId: ReturnType<typeof setTimeout> | null = setTimeout(() => {}, 0)
  233 + const writeDelayMs = payloadSize >= 180 ? 0 : (payloadSize > 20 ? 1 : 8)
  234 +
  235 + const resetTimeout = (reject: (reason?: any) => void) => {
  236 + if (timeoutId) clearTimeout(timeoutId)
  237 + timeoutId = setTimeout(() => {
  238 + if (completed) return
  239 + completed = true
  240 + reject(new Error('BLE write timeout'))
  241 + }, 15000)
  242 + }
218 243
219 function sendNext (): Promise<void> { 244 function sendNext (): Promise<void> {
  245 + if (completed) {
  246 + return Promise.reject(new Error('BLE write timeout'))
  247 + }
220 if (sent >= total) { 248 if (sent >= total) {
  249 + completed = true
  250 + if (timeoutId) clearTimeout(timeoutId)
221 if (onProgress) onProgress(100) 251 if (onProgress) onProgress(100)
222 return Promise.resolve() 252 return Promise.resolve()
223 } 253 }
@@ -228,17 +258,28 @@ function sendViaBle ( @@ -228,17 +258,28 @@ function sendViaBle (
228 view.setUint8(j, chunk[j] & 0xff) 258 view.setUint8(j, chunk[j] & 0xff)
229 } 259 }
230 return new Promise((resolve, reject) => { 260 return new Promise((resolve, reject) => {
  261 + resetTimeout(reject)
231 uni.writeBLECharacteristicValue({ 262 uni.writeBLECharacteristicValue({
232 deviceId, 263 deviceId,
233 serviceId, 264 serviceId,
234 characteristicId, 265 characteristicId,
235 value: buffer, 266 value: buffer,
236 success: () => { 267 success: () => {
  268 + if (completed) return
237 sent++ 269 sent++
238 if (onProgress) onProgress(Math.round((sent / total) * 100)) 270 if (onProgress) onProgress(Math.round((sent / total) * 100))
239 - setTimeout(() => sendNext().then(resolve).catch(reject), 10) 271 + if (writeDelayMs <= 0) {
  272 + sendNext().then(resolve).catch(reject)
  273 + return
  274 + }
  275 + setTimeout(() => sendNext().then(resolve).catch(reject), writeDelayMs)
  276 + },
  277 + fail: (err: any) => {
  278 + if (completed) return
  279 + completed = true
  280 + if (timeoutId) clearTimeout(timeoutId)
  281 + reject(new Error(err.errMsg || 'BLE write failed'))
240 }, 282 },
241 - fail: (err: any) => reject(new Error(err.errMsg || 'BLE write failed')),  
242 }) 283 })
243 }) 284 })
244 } 285 }
@@ -256,9 +297,22 @@ function sendViaClassic ( @@ -256,9 +297,22 @@ function sendViaClassic (
256 return Promise.reject(new Error('Classic Bluetooth printer not connected.')) 297 return Promise.reject(new Error('Classic Bluetooth printer not connected.'))
257 } 298 }
258 return new Promise((resolve, reject) => { 299 return new Promise((resolve, reject) => {
  300 + let settled = false
  301 + const finish = (fn: () => void) => {
  302 + if (settled) return
  303 + settled = true
  304 + clearTimeout(timeoutId)
  305 + fn()
  306 + }
  307 + const timeoutId = setTimeout(() => {
  308 + finish(() => {
  309 + reject(buildClassicBluetoothError('Classic Bluetooth send timeout', conn.deviceId))
  310 + })
  311 + }, 15000)
  312 +
259 try { 313 try {
260 if (!classicBluetooth) { 314 if (!classicBluetooth) {
261 - reject(new Error('Classic Bluetooth not available')) 315 + finish(() => reject(new Error('Classic Bluetooth not available')))
262 return 316 return
263 } 317 }
264 const debugState = typeof classicBluetooth.getDebugState === 'function' 318 const debugState = typeof classicBluetooth.getDebugState === 'function'
@@ -272,7 +326,7 @@ function sendViaClassic ( @@ -272,7 +326,7 @@ function sendViaClassic (
272 const errorMessage = typeof classicBluetooth.getLastError === 'function' 326 const errorMessage = typeof classicBluetooth.getLastError === 'function'
273 ? classicBluetooth.getLastError() 327 ? classicBluetooth.getLastError()
274 : '' 328 : ''
275 - reject(buildClassicBluetoothError(errorMessage || 'Classic Bluetooth connection is not ready', conn.deviceId)) 329 + finish(() => reject(buildClassicBluetoothError(errorMessage || 'Classic Bluetooth connection is not ready', conn.deviceId)))
276 return 330 return
277 } 331 }
278 332
@@ -283,31 +337,35 @@ function sendViaClassic ( @@ -283,31 +337,35 @@ function sendViaClassic (
283 337
284 if (typeof classicBluetooth.sendByteDataAsync === 'function') { 338 if (typeof classicBluetooth.sendByteDataAsync === 'function') {
285 classicBluetooth.sendByteDataAsync(sendData, (ok: boolean, errorMessage?: string) => { 339 classicBluetooth.sendByteDataAsync(sendData, (ok: boolean, errorMessage?: string) => {
286 - if (onProgress) onProgress(100)  
287 - if (ok) {  
288 - resolve()  
289 - return  
290 - }  
291 - reject(buildClassicBluetoothError(  
292 - errorMessage || classicBluetooth.getLastError?.() || 'Classic Bluetooth send failed',  
293 - conn.deviceId  
294 - )) 340 + finish(() => {
  341 + if (onProgress) onProgress(100)
  342 + if (ok) {
  343 + resolve()
  344 + return
  345 + }
  346 + reject(buildClassicBluetoothError(
  347 + errorMessage || classicBluetooth.getLastError?.() || 'Classic Bluetooth send failed',
  348 + conn.deviceId
  349 + ))
  350 + })
295 }) 351 })
296 return 352 return
297 } 353 }
298 354
299 const ok = classicBluetooth.sendByteData(sendData) 355 const ok = classicBluetooth.sendByteData(sendData)
300 - if (onProgress) onProgress(100)  
301 - if (ok) {  
302 - resolve()  
303 - return  
304 - }  
305 - const errorMessage = typeof classicBluetooth.getLastError === 'function'  
306 - ? classicBluetooth.getLastError()  
307 - : ''  
308 - reject(buildClassicBluetoothError(errorMessage || 'Classic Bluetooth send failed', conn.deviceId)) 356 + finish(() => {
  357 + if (onProgress) onProgress(100)
  358 + if (ok) {
  359 + resolve()
  360 + return
  361 + }
  362 + const errorMessage = typeof classicBluetooth.getLastError === 'function'
  363 + ? classicBluetooth.getLastError()
  364 + : ''
  365 + reject(buildClassicBluetoothError(errorMessage || 'Classic Bluetooth send failed', conn.deviceId))
  366 + })
309 } catch (e: any) { 367 } catch (e: any) {
310 - reject(buildClassicBluetoothError(e?.message || String(e || 'Classic Bluetooth send exception'), conn.deviceId)) 368 + finish(() => reject(buildClassicBluetoothError(e?.message || String(e || 'Classic Bluetooth send exception'), conn.deviceId)))
311 } 369 }
312 }) 370 })
313 // #endif 371 // #endif
美国版/Food Labeling Management App UniApp/src/utils/print/systemTemplateAdapter.ts
@@ -297,7 +297,10 @@ function buildTscTemplate ( @@ -297,7 +297,10 @@ function buildTscTemplate (
297 template: SystemLabelTemplate, 297 template: SystemLabelTemplate,
298 data: LabelTemplateData, 298 data: LabelTemplateData,
299 dpi: number, 299 dpi: number,
300 - printQty: number 300 + printQty: number,
  301 + options: {
  302 + disableBitmapText?: boolean
  303 + } = {}
301 ): StructuredTscTemplate { 304 ): StructuredTscTemplate {
302 const widthMm = roundNumber(toMillimeter(template.width, template.unit || 'inch')) 305 const widthMm = roundNumber(toMillimeter(template.width, template.unit || 'inch'))
303 const heightMm = roundNumber(toMillimeter(template.height, template.unit || 'inch')) 306 const heightMm = roundNumber(toMillimeter(template.height, template.unit || 'inch'))
@@ -324,7 +327,7 @@ function buildTscTemplate ( @@ -324,7 +327,7 @@ function buildTscTemplate (
324 const scale = resolveTextScale(getConfigNumber(config, ['fontSize'], 14), dpi) 327 const scale = resolveTextScale(getConfigNumber(config, ['fontSize'], 14), dpi)
325 const align = resolveElementAlign(element, pageWidth) 328 const align = resolveElementAlign(element, pageWidth)
326 329
327 - if (shouldRasterizeTextElement(text, type)) { 330 + if (!options.disableBitmapText && shouldRasterizeTextElement(text, type)) {
328 const bitmapPatch = createTextBitmapPatch({ 331 const bitmapPatch = createTextBitmapPatch({
329 element, 332 element,
330 text, 333 text,
@@ -510,13 +513,16 @@ export function adaptSystemLabelTemplate ( @@ -510,13 +513,16 @@ export function adaptSystemLabelTemplate (
510 options: { 513 options: {
511 dpi?: number 514 dpi?: number
512 printQty?: number 515 printQty?: number
  516 + disableBitmapText?: boolean
513 } = {} 517 } = {}
514 ): StructuredLabelTemplate { 518 ): StructuredLabelTemplate {
515 const dpi = options.dpi || 203 519 const dpi = options.dpi || 203
516 const printQty = Math.max(1, Math.round(options.printQty || 1)) 520 const printQty = Math.max(1, Math.round(options.printQty || 1))
517 return { 521 return {
518 key: template.id || template.name || 'system-label-template', 522 key: template.id || template.name || 'system-label-template',
519 - tsc: buildTscTemplate(template, data, dpi, printQty), 523 + tsc: buildTscTemplate(template, data, dpi, printQty, {
  524 + disableBitmapText: options.disableBitmapText,
  525 + }),
520 esc: buildEscTemplate(template, data, printQty), 526 esc: buildEscTemplate(template, data, printQty),
521 } 527 }
522 } 528 }
美国版/Food Labeling Management App UniApp/src/utils/print/tscLabelBuilder.ts
@@ -220,7 +220,7 @@ export function buildTscTemplateLabel (template: StructuredTscTemplate): number[ @@ -220,7 +220,7 @@ export function buildTscTemplateLabel (template: StructuredTscTemplate): number[
220 if (item.type === 'bitmap') { 220 if (item.type === 'bitmap') {
221 const bytesPerRow = item.image.width / 8 221 const bytesPerRow = item.image.width / 8
222 const bitmapBytes = pixelsToTscBitmapBytes(item.image) 222 const bitmapBytes = pixelsToTscBitmapBytes(item.image)
223 - add(`BITMAP ${item.x},${item.y},${bytesPerRow},${item.image.height},0,`) 223 + addCommandBytes(out, `BITMAP ${item.x},${item.y},${bytesPerRow},${item.image.height},0,`)
224 for (let i = 0; i < bitmapBytes.length; i++) out.push(bitmapBytes[i]) 224 for (let i = 0; i < bitmapBytes.length; i++) out.push(bitmapBytes[i])
225 out.push(0x0d, 0x0a) 225 out.push(0x0d, 0x0a)
226 return 226 return
@@ -275,7 +275,7 @@ export function buildTscImageLabel ( @@ -275,7 +275,7 @@ export function buildTscImageLabel (
275 add('DENSITY 14') 275 add('DENSITY 14')
276 add('SPEED 5') 276 add('SPEED 5')
277 add('CLS') 277 add('CLS')
278 - add(`BITMAP ${x},${y},${bytesPerRow},${image.height},0,`) 278 + addCommandBytes(out, `BITMAP ${x},${y},${bytesPerRow},${image.height},0,`)
279 for (let i = 0; i < bitmapBytes.length; i++) out.push(bitmapBytes[i]) 279 for (let i = 0; i < bitmapBytes.length; i++) out.push(bitmapBytes[i])
280 out.push(0x0d, 0x0a) 280 out.push(0x0d, 0x0a)
281 add(`PRINT 1,${printQty}`) 281 add(`PRINT 1,${printQty}`)