diff --git a/android/src/main/java/com/tscprinter/TscCommonModule.kt b/android/src/main/java/com/tscprinter/TscCommonModule.kt new file mode 100644 index 0000000..4e1c1a7 --- /dev/null +++ b/android/src/main/java/com/tscprinter/TscCommonModule.kt @@ -0,0 +1,55 @@ +package com.tscprinter + +import com.facebook.react.bridge.ReactApplicationContext +import com.facebook.react.module.annotations.ReactModule +import com.facebook.react.bridge.ReactMethod +import com.facebook.react.bridge.Promise +import com.facebook.react.bridge.Arguments +import com.facebook.react.bridge.WritableMap +import com.facebook.react.bridge.WritableArray + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import android.util.Base64 + +import java.net.URL + +@ReactModule(name = TscCommonModule.NAME) +class TscCommonModule(reactContext: ReactApplicationContext) : + TscCommonSpec(reactContext) { + + override fun getName(): String { + return NAME + } + + @ReactMethod + override fun loadImage(uri: String, width: Double, height: Double, promise: Promise) { + try { + val url = URL(uri) + val bitmap = BitmapFactory.decodeStream(url.openStream()) + val resizedBitmap = if (width == 0 || height == 0) { + bitmap + } else { + Bitmap.createScaledBitmap(bitmap, width.toInt(), height.toInt(), true) + } + val rawData = IntArray(resizedBitmap.width * resizedBitmap.height) + resizedBitmap.getPixels(rawData, 0, resizedBitmap.width, 0, 0, resizedBitmap.width, resizedBitmap.height) + val data = IntArray(rawData.size) + for (i in rawData.indices) { + val color = rawData[i] + val r = (color shr 16) and 0xFF + val g = (color shr 8) and 0xFF + val b = color and 0xFF + data[i] = (0.299 * r + 0.587 * g + 0.114 * b).toInt() + } + promise.resolve(Arguments.fromArray(data)) + } catch (e: Exception) { + promise.reject(e) + } + } + + companion object { + const val NAME = "TscCommon" + } +} diff --git a/android/src/newarch/TscCommonSpec.kt b/android/src/newarch/TscCommonSpec.kt new file mode 100644 index 0000000..e41323f --- /dev/null +++ b/android/src/newarch/TscCommonSpec.kt @@ -0,0 +1,7 @@ +package com.tscprinter + +import com.facebook.react.bridge.ReactApplicationContext + +abstract class TscCommonSpec internal constructor(context: ReactApplicationContext) : + NativeTscCommonSpec(context) { +} diff --git a/android/src/oldarch/TscCommonSpec.kt b/android/src/oldarch/TscCommonSpec.kt new file mode 100644 index 0000000..792c730 --- /dev/null +++ b/android/src/oldarch/TscCommonSpec.kt @@ -0,0 +1,11 @@ +package com.tscprinter + +import com.facebook.react.bridge.ReactApplicationContext +import com.facebook.react.bridge.ReactContextBaseJavaModule +import com.facebook.react.bridge.Promise + +abstract class TscCommonSpec internal constructor(context: ReactApplicationContext) : + ReactContextBaseJavaModule(context) { + + abstract fun loadImage(uri: String, width: Double, height: Double, promise: Promise) +} diff --git a/ios/TscCommon.h b/ios/TscCommon.h new file mode 100644 index 0000000..f85ebeb --- /dev/null +++ b/ios/TscCommon.h @@ -0,0 +1,11 @@ +#ifdef RCT_NEW_ARCH_ENABLED +#import "generated/RNTscPrinterSpec/RNTscPrinterSpec.h" + +@interface TscCommon : NSObject +#else +#import + +@interface TscCommon : NSObject +#endif + +@end diff --git a/ios/TscCommon.mm b/ios/TscCommon.mm new file mode 100644 index 0000000..28ae155 --- /dev/null +++ b/ios/TscCommon.mm @@ -0,0 +1,88 @@ +#import "TscCommon.h" +#import + +@implementation TscCommon +RCT_EXPORT_MODULE() + +RCT_EXPORT_METHOD(loadImage:(NSString *)uri + width:(double)width + height:(double)height + resolver:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) +{ + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + @try { + NSURL *url = [NSURL URLWithString:uri]; + NSData *data; + if ([uri hasPrefix:@"data:"]) { + NSString *base64String = [uri componentsSeparatedByString:@","][1]; + data = [[NSData alloc] initWithBase64EncodedString:base64String options:0]; + } else { + data = [NSData dataWithContentsOfURL:url]; + } + + if (!data) { + reject(@"E_LOAD_IMAGE", @"Failed to load image data", nil); + return; + } + + UIImage *image = [UIImage imageWithData:data]; + if (!image) { + reject(@"E_LOAD_IMAGE", @"Failed to create image from data", nil); + return; + } + + // Resize image to target size + UIImage *resizedImage; + if (width == 0 || height == 0) { + resizedImage = image; + } else { + UIGraphicsBeginImageContext(CGSizeMake(width, height)); + [image drawInRect:CGRectMake(0, 0, width, height)]; + resizedImage = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + } + + // Convert to grayscale bitmap + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray(); + CGContextRef context = CGBitmapContextCreate(nil, + resizedImage.size.width, + resizedImage.size.height, + 8, + resizedImage.size.width, + colorSpace, + kCGImageAlphaNone); + CGColorSpaceRelease(colorSpace); + + CGContextDrawImage(context, + CGRectMake(0, 0, resizedImage.size.width, resizedImage.size.height), + resizedImage.CGImage); + + NSData *bitmapData = [NSData dataWithBytes:CGBitmapContextGetData(context) + length:CGBitmapContextGetHeight(context) * CGBitmapContextGetBytesPerRow(context)]; + CGContextRelease(context); + + resolve(@[ + @(resizedImage.size.width), + @(resizedImage.size.height), + bitmapData + ]); + } @catch (NSException *exception) { + reject(@"E_LOAD_IMAGE", exception.reason, nil); + } + }); +} + +#pragma mark - TurboModule + +#ifdef RCT_NEW_ARCH_ENABLED + +- (std::shared_ptr)getTurboModule: + (const facebook::react::ObjCTurboModule::InitParams &)params +{ + return std::make_shared(params); +} + +#endif + +@end \ No newline at end of file diff --git a/react-native-tsc-printer.podspec b/react-native-tsc-printer.podspec index e1edde4..5d8db27 100644 --- a/react-native-tsc-printer.podspec +++ b/react-native-tsc-printer.podspec @@ -16,7 +16,7 @@ Pod::Spec.new do |s| s.source_files = "ios/**/*.{h,m,mm,cpp}" - s.frameworks = "CoreBluetooth", "ExternalAccessory" + s.frameworks = "CoreBluetooth", "ExternalAccessory", "UIKit" # Use install_modules_dependencies helper to install the dependencies if React Native version >=0.71.0. # See https://github.com/facebook/react-native/blob/febf6b7f33fdb4904669f99d795eba4c0f95d7bf/scripts/cocoapods/new_architecture.rb#L79. diff --git a/src/NativeTscCommon.ts b/src/NativeTscCommon.ts new file mode 100644 index 0000000..f58ba3c --- /dev/null +++ b/src/NativeTscCommon.ts @@ -0,0 +1,8 @@ +import type { TurboModule } from 'react-native'; +import { TurboModuleRegistry } from 'react-native'; + +export interface Spec extends TurboModule { + loadImage(uri: string, width: number, height: number): Promise; +} + +export default TurboModuleRegistry.getEnforcing('TscCommon'); diff --git a/src/index.tsx b/src/index.tsx index a220801..fa3a35b 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,6 +1,7 @@ import net from 'react-native-tcp-socket'; import UsbConnection from './UsbConnection'; import BlueConnection from './BlueConnection'; +import Common from './NativeTscCommon'; import { BARCODE_DEFAULT_WIDE, STATUS_MAP } from './constants'; import { ConnectionType, @@ -316,6 +317,17 @@ class Printer { return this.sendCommand(buildCommand('SET TEAR OFF')); } + async addImage( + x: number, + y: number, + uri: string, + width: number = 0, + height: number = 0 + ): Promise { + const data = await Common.loadImage(uri, width, height); + return this.addBitmap(x, y, width, height, new Uint8Array(data)); + } + async addBitmap( x: number, y: number,