react-native-bluetooth-escpos-printer-fork 0.0.18 → 0.0.20
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.
- package/ios/ImageUtils.m +42 -17
- package/ios/PrintImageBleWriteDelegate.m +11 -2
- package/ios/RNBluetoothEscposPrinter.m +13 -1
- package/ios/RNBluetoothManager.m +65 -86
- package/package.json +1 -1
package/ios/ImageUtils.m
CHANGED
|
@@ -190,12 +190,24 @@ int p6[] = { 0, 0x02 };
|
|
|
190
190
|
**/
|
|
191
191
|
+ (NSData *)eachLinePixToCmd:(unsigned char *)src nWidth:(NSInteger) nWidth nHeight:(NSInteger) nHeight nMode:(NSInteger) nMode
|
|
192
192
|
{
|
|
193
|
-
NSLog(@"SIZE OF SRC: %lu",sizeof(&src));
|
|
194
193
|
NSInteger nBytesPerLine = (int)nWidth/8;
|
|
195
194
|
unsigned char * data = malloc(nHeight*(8+nBytesPerLine));
|
|
196
|
-
// const char* srcData = (const char*)[src bytes];
|
|
197
195
|
NSInteger k = 0;
|
|
198
|
-
|
|
196
|
+
|
|
197
|
+
// Log first few lines to see actual pixel distribution
|
|
198
|
+
int totalBlackPixelsInFirstLines = 0;
|
|
199
|
+
for(int line = 0; line < MIN(10, nHeight); line++) {
|
|
200
|
+
int blackInLine = 0;
|
|
201
|
+
for(int x = 0; x < nWidth; x++) {
|
|
202
|
+
if(src[line * nWidth + x] == 1) blackInLine++;
|
|
203
|
+
}
|
|
204
|
+
if(blackInLine > 0) {
|
|
205
|
+
NSLog(@"Line %d: %d/%d pixels are black", line, blackInLine, (int)nWidth);
|
|
206
|
+
}
|
|
207
|
+
totalBlackPixelsInFirstLines += blackInLine;
|
|
208
|
+
}
|
|
209
|
+
NSLog(@"First 10 lines: %d total black pixels", totalBlackPixelsInFirstLines);
|
|
210
|
+
|
|
199
211
|
for(int i=0;i<nHeight;i++){
|
|
200
212
|
NSInteger var10 = i*(8+nBytesPerLine);
|
|
201
213
|
//GS v 0 m xL xH yL yH d1....dk 打印光栅位图
|
|
@@ -207,19 +219,31 @@ int p6[] = { 0, 0x02 };
|
|
|
207
219
|
data[var10 + 5] = (unsigned char)(nBytesPerLine / 256);//xH
|
|
208
220
|
data[var10 + 6] = 1;//yL
|
|
209
221
|
data[var10 + 7] = 0;//yH
|
|
210
|
-
// for(int l=0;l<8;l++){
|
|
211
|
-
// NSInteger d =data[var10 + l];
|
|
212
|
-
// [toLog appendFormat:@"%ld,",(long)d];
|
|
213
|
-
// }
|
|
214
222
|
|
|
215
223
|
for (int j = 0; j < nBytesPerLine; ++j) {
|
|
216
224
|
data[var10 + 8 + j] = (int) (p0[src[k]] + p1[src[k + 1]] + p2[src[k + 2]] + p3[src[k + 3]] + p4[src[k + 4]] + p5[src[k + 5]] + p6[src[k + 6]] + src[k + 7]);
|
|
217
225
|
k =k+8;
|
|
218
|
-
// [toLog appendFormat:@"%ld,",(long)data[var10+8+j]];
|
|
219
226
|
}
|
|
220
|
-
// [toLog appendString:@"\n\r"];
|
|
221
227
|
}
|
|
222
|
-
|
|
228
|
+
|
|
229
|
+
// Find and log first line with actual black pixels
|
|
230
|
+
for(int i = 0; i < nHeight; i++) {
|
|
231
|
+
NSInteger var10 = i*(8+nBytesPerLine);
|
|
232
|
+
BOOL hasBlack = NO;
|
|
233
|
+
for(int j = 0; j < MIN(20, nBytesPerLine); j++) {
|
|
234
|
+
if(data[var10 + 8 + j] != 0) {
|
|
235
|
+
hasBlack = YES;
|
|
236
|
+
break;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
if(hasBlack) {
|
|
240
|
+
NSLog(@"First line with black pixels is line %d, first 10 bytes: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x",
|
|
241
|
+
i, data[var10+8], data[var10+9], data[var10+10], data[var10+11], data[var10+12],
|
|
242
|
+
data[var10+13], data[var10+14], data[var10+15], data[var10+16], data[var10+17]);
|
|
243
|
+
break;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
223
247
|
return [NSData dataWithBytes:data length:nHeight*(8+nBytesPerLine)];
|
|
224
248
|
}
|
|
225
249
|
|
|
@@ -242,24 +266,25 @@ int p6[] = { 0, 0x02 };
|
|
|
242
266
|
}
|
|
243
267
|
|
|
244
268
|
int grayave = graytotal / ysize / xsize;
|
|
269
|
+
NSLog(@"Threshold: average gray = %d (pixels lighter than this become white)", grayave);
|
|
270
|
+
|
|
245
271
|
k = 0;
|
|
246
|
-
|
|
247
|
-
// int oneCount = 0;
|
|
272
|
+
int oneCount = 0;
|
|
248
273
|
for(i = 0; i < ysize; ++i) {
|
|
249
274
|
for(j = 0; j < xsize; ++j) {
|
|
250
275
|
gray = orgpixels[k] & 255;
|
|
251
276
|
if(gray > grayave) {
|
|
252
|
-
despixels[k] = 0;
|
|
277
|
+
despixels[k] = 0; // White (don't print)
|
|
253
278
|
} else {
|
|
254
|
-
despixels[k] = 1;
|
|
255
|
-
|
|
279
|
+
despixels[k] = 1; // Black (print)
|
|
280
|
+
oneCount++;
|
|
256
281
|
}
|
|
257
282
|
|
|
258
283
|
++k;
|
|
259
|
-
// [logStr appendFormat:@"%d,",despixels[k]];
|
|
260
284
|
}
|
|
261
285
|
}
|
|
262
|
-
|
|
286
|
+
NSLog(@"Thresholding complete: %d/%d pixels are black (%.1f%%)",
|
|
287
|
+
oneCount, (int)(xsize*ysize), (oneCount * 100.0 / (xsize*ysize)));
|
|
263
288
|
return despixels;
|
|
264
289
|
}
|
|
265
290
|
+(NSData *)pixToTscCmd:(uint8_t *)src width:(NSInteger) width
|
|
@@ -42,11 +42,20 @@
|
|
|
42
42
|
-(void) print
|
|
43
43
|
{
|
|
44
44
|
@synchronized (self) {
|
|
45
|
-
|
|
45
|
+
// Each line contains: 8 bytes (ESC/POS raster command header) + (width/8) bytes (pixel data)
|
|
46
|
+
NSInteger sizePerLine = 8 + (int)(_width/8);
|
|
46
47
|
NSData *subData = [_toPrint subdataWithRange:NSMakeRange(_now, sizePerLine)];
|
|
48
|
+
|
|
49
|
+
// Log first line to debug
|
|
50
|
+
if(_now == 0) {
|
|
51
|
+
NSUInteger logLen = MIN(20, [subData length]);
|
|
52
|
+
NSData *logData = [subData subdataWithRange:NSMakeRange(0, logLen)];
|
|
53
|
+
NSLog(@"First image line (%lu bytes): %@", (unsigned long)[subData length], logData);
|
|
54
|
+
}
|
|
55
|
+
|
|
47
56
|
[RNBluetoothManager writeValue:subData withDelegate:self];
|
|
48
57
|
_now = _now+sizePerLine;
|
|
49
|
-
[NSThread sleepForTimeInterval:0.
|
|
58
|
+
[NSThread sleepForTimeInterval:0.05f]; // 50ms delay between lines to avoid overwhelming printer buffer
|
|
50
59
|
}
|
|
51
60
|
}
|
|
52
61
|
@end
|
|
@@ -488,7 +488,8 @@ RCT_EXPORT_METHOD(printPic:(NSString *) base64encodeStr withOptions:(NSDictionar
|
|
|
488
488
|
//mBitmap.getHeight() * width / mBitmap.getWidth();
|
|
489
489
|
NSInteger imgHeight = jpgImage.size.height;
|
|
490
490
|
NSInteger imagWidth = jpgImage.size.width;
|
|
491
|
-
|
|
491
|
+
// Round width to multiple of 8 (required for raster image format)
|
|
492
|
+
NSInteger width = ((nWidth + 7) / 8) * 8;
|
|
492
493
|
CGSize size = CGSizeMake(width, imgHeight*width/imagWidth);
|
|
493
494
|
UIImage *scaled = [ImageUtils imageWithImage:jpgImage scaledToFillSize:size];
|
|
494
495
|
if(paddingLeft>0){
|
|
@@ -497,8 +498,16 @@ RCT_EXPORT_METHOD(printPic:(NSString *) base64encodeStr withOptions:(NSDictionar
|
|
|
497
498
|
}
|
|
498
499
|
|
|
499
500
|
unsigned char * graImage = [ImageUtils imageToGreyImage:scaled];
|
|
501
|
+
NSLog(@"Grayscale conversion complete");
|
|
502
|
+
|
|
500
503
|
unsigned char * formatedData = [ImageUtils format_K_threshold:graImage width:size.width height:size.height];
|
|
504
|
+
NSLog(@"Thresholding complete, generating ESC/POS commands");
|
|
505
|
+
|
|
501
506
|
NSData *dataToPrint = [ImageUtils eachLinePixToCmd:formatedData nWidth:size.width nHeight:size.height nMode:0];
|
|
507
|
+
NSLog(@"Image print: %lux%lu pixels, %lu bytes total, %lu bytes per line",
|
|
508
|
+
(unsigned long)size.width, (unsigned long)size.height,
|
|
509
|
+
(unsigned long)[dataToPrint length], (unsigned long)(8 + size.width/8));
|
|
510
|
+
|
|
502
511
|
PrintImageBleWriteDelegate *delegate = [[PrintImageBleWriteDelegate alloc] init];
|
|
503
512
|
delegate.pendingResolve = resolve;
|
|
504
513
|
delegate.pendingReject = reject;
|
|
@@ -543,6 +552,9 @@ RCT_EXPORT_METHOD(printQRCode:(NSString *)content
|
|
|
543
552
|
uint8_t * graImage = [ImageUtils imageToGreyImage:[UIImage imageWithCGImage:image]];
|
|
544
553
|
unsigned char * formatedData = [ImageUtils format_K_threshold:graImage width:size height:size];
|
|
545
554
|
NSData *dataToPrint = [ImageUtils eachLinePixToCmd:formatedData nWidth:size nHeight:size nMode:0];
|
|
555
|
+
NSLog(@"QR code print: %lux%lu pixels, %lu bytes total, %lu bytes per line",
|
|
556
|
+
(unsigned long)size, (unsigned long)size,
|
|
557
|
+
(unsigned long)[dataToPrint length], (unsigned long)(8 + size/8));
|
|
546
558
|
PrintImageBleWriteDelegate *delegate = [[PrintImageBleWriteDelegate alloc] init];
|
|
547
559
|
delegate.pendingResolve=resolve;
|
|
548
560
|
delegate.pendingReject = reject;
|
package/ios/RNBluetoothManager.m
CHANGED
|
@@ -78,7 +78,7 @@ static NSTimer *timer;
|
|
|
78
78
|
|
|
79
79
|
// Small delay between chunks to avoid overwhelming the device
|
|
80
80
|
if(writeType == CBCharacteristicWriteWithoutResponse) {
|
|
81
|
-
[NSThread sleepForTimeInterval:0.
|
|
81
|
+
[NSThread sleepForTimeInterval:0.02]; // 20ms delay
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
@catch(NSException *e) {
|
|
@@ -280,7 +280,7 @@ RCT_EXPORT_METHOD(scanDevices:(RCTPromiseResolveBlock)resolve
|
|
|
280
280
|
[timer invalidate];
|
|
281
281
|
timer = nil;
|
|
282
282
|
}
|
|
283
|
-
timer = [NSTimer scheduledTimerWithTimeInterval:
|
|
283
|
+
timer = [NSTimer scheduledTimerWithTimeInterval:12 target:self selector:@selector(callStop) userInfo:nil repeats:NO];
|
|
284
284
|
|
|
285
285
|
}
|
|
286
286
|
@catch(NSException *exception){
|
|
@@ -421,53 +421,40 @@ RCT_EXPORT_METHOD(unpaire:(NSString *)address
|
|
|
421
421
|
-(void)callStop{
|
|
422
422
|
if(self.centralManager.isScanning){
|
|
423
423
|
[self.centralManager stopScan];
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
name = @"";
|
|
437
|
-
}
|
|
438
|
-
[devices addObject:@{
|
|
439
|
-
@"address":key,
|
|
440
|
-
@"name":name,
|
|
441
|
-
@"deviceClass":@(7936), // Unknown device class
|
|
442
|
-
@"majorDeviceClass":@(7936) // Unknown major device class
|
|
443
|
-
}];
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
// Create result object matching Android format: {"paired": [...], "found": [...]}
|
|
448
|
-
// Empty paired devices array (iOS doesn't have "paired" concept like Android)
|
|
449
|
-
NSMutableDictionary *result = [[NSMutableDictionary alloc] init];
|
|
450
|
-
[result setObject:@[] forKey:@"paired"];
|
|
451
|
-
[result setObject:devices forKey:@"found"];
|
|
452
|
-
|
|
453
|
-
// Convert the entire result to a JSON string (matching Android's behavior)
|
|
454
|
-
NSError *error = nil;
|
|
455
|
-
NSData* resultJsonData = [NSJSONSerialization dataWithJSONObject:result options:0 error:&error];
|
|
456
|
-
NSString *resultJsonStr = [[NSString alloc] initWithData:resultJsonData encoding:NSUTF8StringEncoding];
|
|
457
|
-
|
|
458
|
-
if(hasListeners){
|
|
459
|
-
// For events, send separate paired and found strings
|
|
460
|
-
NSData* foundJsonData = [NSJSONSerialization dataWithJSONObject:devices options:0 error:&error];
|
|
461
|
-
NSString *foundJsonStr = [[NSString alloc] initWithData:foundJsonData encoding:NSUTF8StringEncoding];
|
|
462
|
-
[self sendEventWithName:EVENT_DEVICE_DISCOVER_DONE body:@{@"found":foundJsonStr,@"paired":@"[]"}];
|
|
463
|
-
}
|
|
464
|
-
if(self.scanResolveBlock){
|
|
465
|
-
RCTPromiseResolveBlock rsBlock = self.scanResolveBlock;
|
|
466
|
-
// Return single JSON string: "{\"paired\":[],\"found\":[...]}"
|
|
467
|
-
rsBlock(resultJsonStr);
|
|
468
|
-
self.scanResolveBlock = nil;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
NSMutableArray *devices = [[NSMutableArray alloc] init];
|
|
427
|
+
for(NSString *key in self.foundDevices){
|
|
428
|
+
NSLog(@"insert found devices:%@ =>%@",key,[self.foundDevices objectForKey:key]);
|
|
429
|
+
NSDictionary *deviceInfo = [self.foundDevicesMetadata objectForKey:key];
|
|
430
|
+
if (deviceInfo) {
|
|
431
|
+
[devices addObject:deviceInfo];
|
|
432
|
+
} else {
|
|
433
|
+
NSString *name = [self.foundDevices objectForKey:key].name;
|
|
434
|
+
if(!name) name = @"";
|
|
435
|
+
[devices addObject:@{@"address":key, @"name":name}];
|
|
469
436
|
}
|
|
470
437
|
}
|
|
438
|
+
|
|
439
|
+
NSMutableDictionary *result = [[NSMutableDictionary alloc] init];
|
|
440
|
+
[result setObject:@[] forKey:@"paired"];
|
|
441
|
+
[result setObject:devices forKey:@"found"];
|
|
442
|
+
|
|
443
|
+
NSError *error = nil;
|
|
444
|
+
NSData *resultJsonData = [NSJSONSerialization dataWithJSONObject:result options:0 error:&error];
|
|
445
|
+
NSString *resultJsonStr = [[NSString alloc] initWithData:resultJsonData encoding:NSUTF8StringEncoding];
|
|
446
|
+
|
|
447
|
+
if(hasListeners){
|
|
448
|
+
NSData *foundJsonData = [NSJSONSerialization dataWithJSONObject:devices options:0 error:&error];
|
|
449
|
+
NSString *foundJsonStr = [[NSString alloc] initWithData:foundJsonData encoding:NSUTF8StringEncoding];
|
|
450
|
+
[self sendEventWithName:EVENT_DEVICE_DISCOVER_DONE body:@{@"found":foundJsonStr, @"paired":@"[]"}];
|
|
451
|
+
}
|
|
452
|
+
if(self.scanResolveBlock){
|
|
453
|
+
RCTPromiseResolveBlock rsBlock = self.scanResolveBlock;
|
|
454
|
+
rsBlock(resultJsonStr);
|
|
455
|
+
self.scanResolveBlock = nil;
|
|
456
|
+
}
|
|
457
|
+
|
|
471
458
|
if(timer && timer.isValid){
|
|
472
459
|
[timer invalidate];
|
|
473
460
|
timer = nil;
|
|
@@ -553,52 +540,44 @@ RCT_EXPORT_METHOD(unpaire:(NSString *)address
|
|
|
553
540
|
|
|
554
541
|
// Try to infer device class from advertising data
|
|
555
542
|
// iOS BLE doesn't expose Classic Bluetooth device class, but we can make educated guesses
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
//
|
|
543
|
+
// Build advertisementData dictionary for JS layer
|
|
544
|
+
NSMutableDictionary *advData = [[NSMutableDictionary alloc] init];
|
|
545
|
+
|
|
546
|
+
// Service UUIDs
|
|
560
547
|
NSArray *serviceUUIDs = advertisementData[CBAdvertisementDataServiceUUIDsKey];
|
|
561
|
-
BOOL isPrinter = NO;
|
|
562
|
-
|
|
563
548
|
if (serviceUUIDs && serviceUUIDs.count > 0) {
|
|
549
|
+
NSMutableArray *uuidStrings = [[NSMutableArray alloc] init];
|
|
564
550
|
for (CBUUID *uuid in serviceUUIDs) {
|
|
565
|
-
|
|
566
|
-
// Serial Port Profile (SPP) UUID - common for printers
|
|
567
|
-
// Also check for other printer-related service UUIDs
|
|
568
|
-
if ([uuidString isEqualToString:@"00001101-0000-1000-8000-00805F9B34FB"] ||
|
|
569
|
-
[uuidString isEqualToString:@"49535343-FE7D-4AE5-8FA9-9FAFD205E455"] ||
|
|
570
|
-
[uuidString containsString:@"18F0"]) {
|
|
571
|
-
isPrinter = YES;
|
|
572
|
-
break;
|
|
573
|
-
}
|
|
551
|
+
[uuidStrings addObject:uuid.UUIDString];
|
|
574
552
|
}
|
|
553
|
+
[advData setObject:uuidStrings forKey:@"serviceUUIDs"];
|
|
575
554
|
}
|
|
576
|
-
|
|
577
|
-
//
|
|
578
|
-
NSString *
|
|
579
|
-
if (
|
|
580
|
-
[
|
|
581
|
-
[name containsString:@"ESCPOS"] ||
|
|
582
|
-
[name containsString:@"THERMAL"] ||
|
|
583
|
-
[name containsString:@"RECEIPT"]) {
|
|
584
|
-
isPrinter = YES;
|
|
555
|
+
|
|
556
|
+
// Local name (may differ from peripheral.name)
|
|
557
|
+
NSString *localName = advertisementData[CBAdvertisementDataLocalNameKey];
|
|
558
|
+
if (localName) {
|
|
559
|
+
[advData setObject:localName forKey:@"localName"];
|
|
585
560
|
}
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
majorDeviceClass = 1536; // 0x0600 in decimal
|
|
592
|
-
deviceClass = 1664; // 0x0680 in decimal
|
|
593
|
-
} else {
|
|
594
|
-
// Unknown/Unclassified device
|
|
595
|
-
// Major Device Class: 0x1F00 (Uncategorized)
|
|
596
|
-
majorDeviceClass = 7936; // 0x1F00 in decimal
|
|
597
|
-
deviceClass = 7936;
|
|
561
|
+
|
|
562
|
+
// Manufacturer data as base64
|
|
563
|
+
NSData *manufacturerData = advertisementData[CBAdvertisementDataManufacturerDataKey];
|
|
564
|
+
if (manufacturerData) {
|
|
565
|
+
[advData setObject:[manufacturerData base64EncodedStringWithOptions:0] forKey:@"manufacturerData"];
|
|
598
566
|
}
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
567
|
+
|
|
568
|
+
// TX power level
|
|
569
|
+
NSNumber *txPowerLevel = advertisementData[CBAdvertisementDataTxPowerLevelKey];
|
|
570
|
+
if (txPowerLevel) {
|
|
571
|
+
[advData setObject:txPowerLevel forKey:@"txPowerLevel"];
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
// Connectable flag
|
|
575
|
+
NSNumber *isConnectable = advertisementData[CBAdvertisementDataIsConnectable];
|
|
576
|
+
if (isConnectable) {
|
|
577
|
+
[advData setObject:isConnectable forKey:@"isConnectable"];
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
[deviceInfo setObject:advData forKey:@"advertisementData"];
|
|
602
581
|
|
|
603
582
|
// Store the peripheral object for connection
|
|
604
583
|
NSDictionary *peripheralStored = @{peripheral.identifier.UUIDString:peripheral};
|