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 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
- // NSMutableString *toLog = [[NSMutableString alloc] init];
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
- // NSLog(@"line datas: %@",toLog);
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
- // NSMutableString *logStr = [[NSMutableString alloc]init];
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
- // oneCount++;
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
- // NSLog(@"despixels [with 1 count:%d]: %@",oneCount,logStr);
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
- NSInteger sizePerLine = (int)(_width/8);
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.01f];
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
- NSInteger width = nWidth;//((int)(((nWidth*0.86)+7)/8))*8-7;
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;
@@ -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.01]; // 10ms delay
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:30 target:self selector:@selector(callStop) userInfo:nil repeats:NO];
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
- NSMutableArray *devices = [[NSMutableArray alloc] init];
425
- for(NSString *key in self.foundDevices){
426
- NSLog(@"insert found devices:%@ =>%@",key,[self.foundDevices objectForKey:key]);
427
-
428
- // Get stored metadata if available, otherwise create basic info
429
- NSDictionary *deviceInfo = [self.foundDevicesMetadata objectForKey:key];
430
- if (deviceInfo) {
431
- [devices addObject:deviceInfo];
432
- } else {
433
- // Fallback if metadata wasn't stored (shouldn't happen with new code)
434
- NSString *name = [self.foundDevices objectForKey:key].name;
435
- if(!name){
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
- NSInteger deviceClass = 0;
557
- NSInteger majorDeviceClass = 0;
558
-
559
- // Check if device advertises printer-related services
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
- NSString *uuidString = uuid.UUIDString.uppercaseString;
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
- // Check device name for printer keywords
578
- NSString *name = peripheral.name ? peripheral.name.uppercaseString : @"";
579
- if ([name containsString:@"PRINT"] ||
580
- [name containsString:@"POS"] ||
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
- if (isPrinter) {
588
- // Android Bluetooth Class values for printers:
589
- // Major Device Class: 0x0600 (Imaging - printers, scanners, cameras, etc.)
590
- // Device Class: 0x000680 (Imaging/Printer)
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
- [deviceInfo setObject:@(deviceClass) forKey:@"deviceClass"];
601
- [deviceInfo setObject:@(majorDeviceClass) forKey:@"majorDeviceClass"];
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};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-bluetooth-escpos-printer-fork",
3
- "version": "0.0.18",
3
+ "version": "0.0.20",
4
4
  "description": "React-Native plugin for the bluetooth ESC/POS printers.",
5
5
  "main": "index.js",
6
6
  "scripts": {