@mleonard9/vin-scanner 0.1.1

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 (47) hide show
  1. package/README.md +110 -0
  2. package/android/build.gradle +102 -0
  3. package/android/gradle.properties +5 -0
  4. package/android/src/main/AndroidManifest.xml +8 -0
  5. package/android/src/main/AndroidManifestNew.xml +2 -0
  6. package/android/src/main/java/com/visioncamerabarcodescanner/VisionCameraBarcodeScannerModule.kt +93 -0
  7. package/android/src/main/java/com/visioncamerabarcodescanner/VisionCameraBarcodeScannerPackage.kt +27 -0
  8. package/android/src/main/java/com/visioncameratextrecognition/VisionCameraTextRecognitionModule.kt +130 -0
  9. package/ios/VisionCameraBarcodeScanner.m +175 -0
  10. package/ios/VisionCameraTextRecognition.m +114 -0
  11. package/lib/commonjs/index.js +87 -0
  12. package/lib/commonjs/index.js.map +1 -0
  13. package/lib/commonjs/scanBarcodes.js +46 -0
  14. package/lib/commonjs/scanBarcodes.js.map +1 -0
  15. package/lib/commonjs/scanText.js +29 -0
  16. package/lib/commonjs/scanText.js.map +1 -0
  17. package/lib/commonjs/types.js +6 -0
  18. package/lib/commonjs/types.js.map +1 -0
  19. package/lib/commonjs/vinUtils.js +240 -0
  20. package/lib/commonjs/vinUtils.js.map +1 -0
  21. package/lib/module/index.js +80 -0
  22. package/lib/module/index.js.map +1 -0
  23. package/lib/module/scanBarcodes.js +40 -0
  24. package/lib/module/scanBarcodes.js.map +1 -0
  25. package/lib/module/scanText.js +23 -0
  26. package/lib/module/scanText.js.map +1 -0
  27. package/lib/module/types.js +2 -0
  28. package/lib/module/types.js.map +1 -0
  29. package/lib/module/vinUtils.js +230 -0
  30. package/lib/module/vinUtils.js.map +1 -0
  31. package/lib/typescript/src/index.d.ts +6 -0
  32. package/lib/typescript/src/index.d.ts.map +1 -0
  33. package/lib/typescript/src/scanBarcodes.d.ts +3 -0
  34. package/lib/typescript/src/scanBarcodes.d.ts.map +1 -0
  35. package/lib/typescript/src/scanText.d.ts +3 -0
  36. package/lib/typescript/src/scanText.d.ts.map +1 -0
  37. package/lib/typescript/src/types.d.ts +104 -0
  38. package/lib/typescript/src/types.d.ts.map +1 -0
  39. package/lib/typescript/src/vinUtils.d.ts +19 -0
  40. package/lib/typescript/src/vinUtils.d.ts.map +1 -0
  41. package/package.json +168 -0
  42. package/src/index.tsx +131 -0
  43. package/src/scanBarcodes.ts +76 -0
  44. package/src/scanText.ts +36 -0
  45. package/src/types.ts +145 -0
  46. package/src/vinUtils.ts +368 -0
  47. package/vin-scanner.podspec +28 -0
package/README.md ADDED
@@ -0,0 +1,110 @@
1
+
2
+ <div align="center">
3
+
4
+ # @mleonard9/vin-scanner
5
+
6
+ High-performance VIN detection for React Native powered by Google ML Kit barcode + text recognition and `react-native-vision-camera`.
7
+
8
+ </div>
9
+
10
+ ## Requirements
11
+
12
+ - `react-native-vision-camera` >= 3.9.0
13
+ - `react-native-worklets-core` >= 0.4.0
14
+ - iOS 13+ / Android 21+
15
+
16
+ ## Installation
17
+
18
+ ```sh
19
+ yarn add @mleonard9/vin-scanner
20
+ # or
21
+ npm install @mleonard9/vin-scanner
22
+
23
+ # iOS
24
+ cd ios && pod install
25
+ ```
26
+
27
+ ## Usage
28
+
29
+ ```tsx
30
+ import React, { useMemo, useState } from 'react';
31
+ import { StyleSheet, View } from 'react-native';
32
+ import { useCameraDevice } from 'react-native-vision-camera';
33
+ import { Camera as VinScannerCamera } from '@mleonard9/vin-scanner';
34
+
35
+ export function VinScannerExample(): JSX.Element {
36
+ const device = useCameraDevice('back');
37
+ const [results, setResults] = useState([]);
38
+
39
+ const options = useMemo(
40
+ () => ({
41
+ barcode: { formats: ['code-39', 'code-128', 'pdf-417'] },
42
+ text: { language: 'latin' },
43
+ detection: { resultMode: 'all' as const },
44
+ }),
45
+ []
46
+ );
47
+
48
+ if (device == null) {
49
+ return null;
50
+ }
51
+
52
+ return (
53
+ <View style={StyleSheet.absoluteFill}>
54
+ <VinScannerCamera
55
+ style={StyleSheet.absoluteFill}
56
+ device={device}
57
+ options={options}
58
+ callback={(event) => {
59
+ setResults(event.candidates);
60
+ }}
61
+ />
62
+ </View>
63
+ );
64
+ }
65
+ ```
66
+
67
+ Every frame, the camera runs ML Kit barcode + text recognition, extracts 17-character VIN candidates, validates them (including the optional checksum), and routes a payload to `callback`.
68
+
69
+ ### Callback payload
70
+
71
+ ```ts
72
+ type VinScannerEvent = {
73
+ mode: 'best' | 'all';
74
+ timestamp: number;
75
+ best?: VinCandidate | null;
76
+ candidates: VinCandidate[];
77
+ raw: {
78
+ barcodes: BarcodeDetection[];
79
+ textBlocks: TextDetection[];
80
+ };
81
+ };
82
+ ```
83
+
84
+ `VinCandidate` contains `{ value, source: 'barcode' | 'text', confidence, boundingBox }`.
85
+ `resultMode === 'best'` returns at most one candidate per frame, while `'all'` returns every candidate so you can render overlays/selectors.
86
+
87
+ ### Options
88
+
89
+ | Path | Type | Description | Default |
90
+ | --- | --- | --- | --- |
91
+ | `options.barcode.enabled` | boolean | Enable barcode scanning | `true` |
92
+ | `options.barcode.formats` | `BarcodeFormat[]` | Restrict ML Kit formats (`'code-39'`, `'code-128'`, `'pdf-417'`, etc.) | `['all']` |
93
+ | `options.text.enabled` | boolean | Enable text recognition | `true` |
94
+ | `options.text.language` | `'latin' \| 'chinese' \| 'devanagari' \| 'japanese' \| 'korean'` | ML Kit language pack | `'latin'` |
95
+ | `options.detection.resultMode` | `'best' \| 'all'` | Emit a single best VIN or every candidate | `'best'` |
96
+ | `options.detection.preferBarcode` | boolean | Prefer barcode hits when selecting the best VIN | `true` |
97
+ | `options.detection.validateChecksum` | boolean | Run VIN checksum validation | `true` |
98
+ | `options.detection.emitDuplicates` | boolean | Emit unchanged payloads every frame | `false` for `'best'`, `true` for `'all'` |
99
+
100
+ ## Publishing (internal use)
101
+
102
+ This package is scoped (`@mleonard9/vin-scanner`) and marked as `access: restricted`. To release a new build:
103
+
104
+ ```sh
105
+ yarn prepare # builds /lib via bob
106
+ npm publish --access restricted
107
+ ```
108
+
109
+ Ensure the authenticated npm user has access to the `@mleonard9` scope.
110
+
@@ -0,0 +1,102 @@
1
+ buildscript {
2
+ // Buildscript is evaluated before everything else so we can't use getExtOrDefault
3
+ def kotlin_version = rootProject.ext.has("kotlinVersion") ? rootProject.ext.get("kotlinVersion") : project.properties["VisionCameraBarcodeScanner_kotlinVersion"]
4
+
5
+ repositories {
6
+ google()
7
+ mavenCentral()
8
+ }
9
+
10
+ dependencies {
11
+ classpath "com.android.tools.build:gradle:7.2.1"
12
+ // noinspection DifferentKotlinGradleVersion
13
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
14
+ }
15
+ }
16
+
17
+ def isNewArchitectureEnabled() {
18
+ return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true"
19
+ }
20
+
21
+ apply plugin: "com.android.library"
22
+ apply plugin: "kotlin-android"
23
+
24
+ if (isNewArchitectureEnabled()) {
25
+ apply plugin: "com.facebook.react"
26
+ }
27
+
28
+ def getExtOrDefault(name) {
29
+ return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["VisionCameraBarcodeScanner_" + name]
30
+ }
31
+
32
+ def getExtOrIntegerDefault(name) {
33
+ return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["VisionCameraBarcodeScanner_" + name]).toInteger()
34
+ }
35
+
36
+ def supportsNamespace() {
37
+ def parsed = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION.tokenize('.')
38
+ def major = parsed[0].toInteger()
39
+ def minor = parsed[1].toInteger()
40
+
41
+ // Namespace support was added in 7.3.0
42
+ return (major == 7 && minor >= 3) || major >= 8
43
+ }
44
+
45
+ android {
46
+ if (supportsNamespace()) {
47
+ namespace "com.visioncamerabarcodescanner"
48
+
49
+ sourceSets {
50
+ main {
51
+ manifest.srcFile "src/main/AndroidManifestNew.xml"
52
+ }
53
+ }
54
+ }
55
+
56
+ compileSdkVersion getExtOrIntegerDefault("compileSdkVersion")
57
+
58
+ defaultConfig {
59
+ minSdkVersion getExtOrIntegerDefault("minSdkVersion")
60
+ targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
61
+
62
+ }
63
+
64
+ buildTypes {
65
+ release {
66
+ minifyEnabled false
67
+ }
68
+ }
69
+
70
+ lintOptions {
71
+ disable "GradleCompatible"
72
+ }
73
+
74
+ compileOptions {
75
+ sourceCompatibility JavaVersion.VERSION_1_8
76
+ targetCompatibility JavaVersion.VERSION_1_8
77
+ }
78
+ }
79
+
80
+ repositories {
81
+ mavenCentral()
82
+ google()
83
+ }
84
+
85
+ def kotlin_version = getExtOrDefault("kotlinVersion")
86
+
87
+ dependencies {
88
+ // For < 0.71, this will be from the local maven repo
89
+ // For > 0.71, this will be replaced by `com.facebook.react:react-android:$version` by react gradle plugin
90
+ //noinspection GradleDynamicVersion
91
+ implementation "com.facebook.react:react-native:+"
92
+ implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
93
+ implementation 'com.google.mlkit:barcode-scanning:17.2.0'
94
+ implementation 'com.google.mlkit:text-recognition:16.0.0'
95
+ implementation 'com.google.mlkit:text-recognition-chinese:16.0.0'
96
+ implementation 'com.google.mlkit:text-recognition-devanagari:16.0.0'
97
+ implementation 'com.google.mlkit:text-recognition-japanese:16.0.0'
98
+ implementation 'com.google.mlkit:text-recognition-korean:16.0.0'
99
+ implementation project(':react-native-vision-camera')
100
+ implementation "androidx.camera:camera-core:1.1.0"
101
+ }
102
+
@@ -0,0 +1,5 @@
1
+ VisionCameraBarcodeScanner_kotlinVersion=1.7.0
2
+ VisionCameraBarcodeScanner_minSdkVersion=21
3
+ VisionCameraBarcodeScanner_targetSdkVersion=31
4
+ VisionCameraBarcodeScanner_compileSdkVersion=31
5
+ VisionCameraBarcodeScanner_ndkversion=21.4.7075529
@@ -0,0 +1,8 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
2
+ package="com.visioncamerabarcodescanner">
3
+ <application>
4
+ <meta-data
5
+ android:name="com.google.mlkit.vision.DEPENDENCIES"
6
+ android:value="barcode" />
7
+ </application>
8
+ </manifest>
@@ -0,0 +1,2 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
+ </manifest>
@@ -0,0 +1,93 @@
1
+ package com.visioncamerabarcodescanner
2
+
3
+ import android.media.Image
4
+ import com.facebook.react.bridge.WritableNativeArray
5
+ import com.facebook.react.bridge.WritableNativeMap
6
+ import com.google.android.gms.tasks.Task
7
+ import com.google.android.gms.tasks.Tasks
8
+ import com.google.mlkit.vision.barcode.BarcodeScannerOptions
9
+ import com.google.mlkit.vision.barcode.BarcodeScanning
10
+ import com.google.mlkit.vision.barcode.common.Barcode
11
+ import com.google.mlkit.vision.barcode.common.Barcode.FORMAT_ALL_FORMATS
12
+ import com.google.mlkit.vision.barcode.common.Barcode.FORMAT_AZTEC
13
+ import com.google.mlkit.vision.barcode.common.Barcode.FORMAT_CODABAR
14
+ import com.google.mlkit.vision.barcode.common.Barcode.FORMAT_CODE_128
15
+ import com.google.mlkit.vision.barcode.common.Barcode.FORMAT_CODE_39
16
+ import com.google.mlkit.vision.barcode.common.Barcode.FORMAT_CODE_93
17
+ import com.google.mlkit.vision.barcode.common.Barcode.FORMAT_DATA_MATRIX
18
+ import com.google.mlkit.vision.barcode.common.Barcode.FORMAT_ITF
19
+ import com.google.mlkit.vision.barcode.common.Barcode.FORMAT_PDF417
20
+ import com.google.mlkit.vision.barcode.common.Barcode.FORMAT_QR_CODE
21
+ import com.google.mlkit.vision.barcode.common.Barcode.FORMAT_UPC_A
22
+ import com.google.mlkit.vision.barcode.common.Barcode.FORMAT_UPC_E
23
+ import com.google.mlkit.vision.barcode.common.Barcode.FORMAT_EAN_8
24
+ import com.google.mlkit.vision.barcode.common.Barcode.FORMAT_EAN_13
25
+ import com.google.mlkit.vision.common.InputImage
26
+ import com.mrousavy.camera.frameprocessors.Frame
27
+ import com.mrousavy.camera.frameprocessors.FrameProcessorPlugin
28
+ import com.mrousavy.camera.frameprocessors.VisionCameraProxy
29
+
30
+ class VisionCameraBarcodeScannerModule(proxy : VisionCameraProxy, options: Map<String, Any>?): FrameProcessorPlugin() {
31
+
32
+ override fun callback(frame: Frame, arguments: Map<String, Any>?): Any {
33
+ try {
34
+ val optionsBuilder = BarcodeScannerOptions.Builder()
35
+ if (arguments?.get("code-128").toString().toBoolean()) optionsBuilder.setBarcodeFormats(FORMAT_CODE_128)
36
+ else if (arguments?.get("code-39").toString().toBoolean()) optionsBuilder.setBarcodeFormats(FORMAT_CODE_39)
37
+ else if (arguments?.get("code-93").toString().toBoolean()) optionsBuilder.setBarcodeFormats(FORMAT_CODE_93)
38
+ else if (arguments?.get("codabar").toString().toBoolean()) optionsBuilder.setBarcodeFormats(FORMAT_CODABAR)
39
+ else if (arguments?.get("ean-13").toString().toBoolean()) optionsBuilder.setBarcodeFormats(FORMAT_EAN_13)
40
+ else if (arguments?.get("ean-8").toString().toBoolean()) optionsBuilder.setBarcodeFormats(FORMAT_EAN_8)
41
+ else if (arguments?.get("itf").toString().toBoolean()) optionsBuilder.setBarcodeFormats(FORMAT_ITF)
42
+ else if (arguments?.get("upc-e").toString().toBoolean()) optionsBuilder.setBarcodeFormats(FORMAT_UPC_E)
43
+ else if (arguments?.get("upc-a").toString().toBoolean()) optionsBuilder.setBarcodeFormats(FORMAT_UPC_A)
44
+ else if (arguments?.get("qr").toString().toBoolean()) optionsBuilder.setBarcodeFormats(FORMAT_QR_CODE)
45
+ else if (arguments?.get("pdf-417").toString().toBoolean()) optionsBuilder.setBarcodeFormats(FORMAT_PDF417)
46
+ else if (arguments?.get("aztec").toString().toBoolean()) optionsBuilder.setBarcodeFormats(FORMAT_AZTEC)
47
+ else if (arguments?.get("data-matrix").toString().toBoolean()) optionsBuilder.setBarcodeFormats(FORMAT_DATA_MATRIX)
48
+ else if (arguments?.get("all").toString().toBoolean()) optionsBuilder.setBarcodeFormats(FORMAT_ALL_FORMATS)
49
+ else optionsBuilder.setBarcodeFormats(FORMAT_ALL_FORMATS)
50
+
51
+ val scanner = BarcodeScanning.getClient(optionsBuilder.build())
52
+ val mediaImage: Image = frame.image
53
+ val image = InputImage.fromMediaImage(mediaImage, frame.imageProxy.imageInfo.rotationDegrees)
54
+ val task: Task<List<Barcode>> = scanner.process(image)
55
+ val barcodes: List<Barcode> = Tasks.await(task)
56
+ val array = WritableNativeArray()
57
+ for (barcode in barcodes) {
58
+ val map = WritableNativeMap()
59
+ val bounds = barcode.boundingBox
60
+ if (bounds != null) {
61
+ map.putInt("width",bounds.width())
62
+ map.putInt("height",bounds.height())
63
+ map.putInt("top",bounds.top)
64
+ map.putInt("bottom",bounds.bottom)
65
+ map.putInt("left",bounds.left)
66
+ map.putInt("right",bounds.right)
67
+ }
68
+ val rawValue = barcode.rawValue
69
+ map.putString("rawValue",rawValue)
70
+ val valueType = barcode.valueType
71
+ when (valueType) {
72
+ Barcode.TYPE_WIFI -> {
73
+ val ssid = barcode.wifi!!.ssid
74
+ map.putString("ssid",ssid)
75
+ val password = barcode.wifi!!.password
76
+ map.putString("password",password)
77
+ }
78
+ Barcode.TYPE_URL -> {
79
+ val title = barcode.url!!.title
80
+ map.putString("title",title)
81
+ val url = barcode.url!!.url
82
+ map.putString("url",url)
83
+ }
84
+ }
85
+ array.pushMap(map)
86
+ }
87
+ return array.toArrayList()
88
+ } catch (e: Exception) {
89
+ throw Exception("Error processing barcode scanner: $e ")
90
+ }
91
+ }
92
+
93
+ }
@@ -0,0 +1,27 @@
1
+ package com.visioncamerabarcodescanner
2
+
3
+ import com.facebook.react.ReactPackage
4
+ import com.facebook.react.bridge.NativeModule
5
+ import com.facebook.react.bridge.ReactApplicationContext
6
+ import com.facebook.react.uimanager.ViewManager
7
+ import com.mrousavy.camera.frameprocessors.FrameProcessorPluginRegistry
8
+ import com.visioncameratextrecognition.VisionCameraTextRecognitionModule
9
+
10
+ class VisionCameraBarcodeScannerPackage : ReactPackage {
11
+ companion object {
12
+ init {
13
+ FrameProcessorPluginRegistry.addFrameProcessorPlugin("scanBarcodes") {proxy,options ->
14
+ VisionCameraBarcodeScannerModule(proxy,options)
15
+ }
16
+ FrameProcessorPluginRegistry.addFrameProcessorPlugin("scanText") { proxy, options ->
17
+ VisionCameraTextRecognitionModule(proxy, options)
18
+ }
19
+ }
20
+ }
21
+ override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
22
+ return emptyList()
23
+ }
24
+ override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
25
+ return emptyList()
26
+ }
27
+ }
@@ -0,0 +1,130 @@
1
+ package com.visioncameratextrecognition
2
+
3
+ import android.media.Image
4
+ import com.facebook.react.bridge.WritableNativeArray
5
+ import com.facebook.react.bridge.WritableNativeMap
6
+ import com.google.android.gms.tasks.Task
7
+ import com.google.android.gms.tasks.Tasks
8
+ import com.google.mlkit.vision.common.InputImage
9
+ import com.google.mlkit.vision.text.Text
10
+ import com.google.mlkit.vision.text.TextRecognition
11
+ import com.google.mlkit.vision.text.TextRecognizer
12
+ import com.google.mlkit.vision.text.chinese.ChineseTextRecognizerOptions
13
+ import com.google.mlkit.vision.text.devanagari.DevanagariTextRecognizerOptions
14
+ import com.google.mlkit.vision.text.japanese.JapaneseTextRecognizerOptions
15
+ import com.google.mlkit.vision.text.korean.KoreanTextRecognizerOptions
16
+ import com.google.mlkit.vision.text.latin.TextRecognizerOptions
17
+ import com.mrousavy.camera.frameprocessor.Frame
18
+ import com.mrousavy.camera.frameprocessor.FrameProcessorPlugin
19
+ import com.mrousavy.camera.frameprocessor.VisionCameraProxy
20
+ import com.mrousavy.camera.types.Orientation
21
+ import java.util.ArrayList
22
+
23
+
24
+ class VisionCameraTextRecognitionModule(proxy : VisionCameraProxy, options: Map<String, Any>?): FrameProcessorPlugin() {
25
+ private var recognizer: TextRecognizer = TextRecognition.getClient(TextRecognizerOptions.DEFAULT_OPTIONS)
26
+ private var language = options?.get("language").toString()
27
+
28
+ init {
29
+ recognizer = when (language) {
30
+ "latin" -> TextRecognition.getClient(TextRecognizerOptions.DEFAULT_OPTIONS)
31
+ "chinese" -> TextRecognition.getClient(ChineseTextRecognizerOptions.Builder().build())
32
+ "devanagari" -> TextRecognition.getClient(DevanagariTextRecognizerOptions.Builder().build())
33
+ "japanese" -> TextRecognition.getClient(JapaneseTextRecognizerOptions.Builder().build())
34
+ "korean" -> TextRecognition.getClient(KoreanTextRecognizerOptions.Builder().build())
35
+ else -> TextRecognition.getClient(TextRecognizerOptions.DEFAULT_OPTIONS)
36
+ }
37
+ }
38
+ override fun callback(frame: Frame, arguments: Map<String, Any>?): ArrayList<Any> {
39
+ try {
40
+ val mediaImage: Image = frame.image
41
+ val orientation : Orientation = frame.orientation
42
+ val array = WritableNativeArray()
43
+ val image = InputImage.fromMediaImage(mediaImage, orientation.toDegrees())
44
+ val task: Task<Text> = recognizer.process(image)
45
+ val result: Text? = Tasks.await(task)
46
+ val resultText = result?.text
47
+ for (block in result?.textBlocks!!) {
48
+ val map = WritableNativeMap()
49
+ map.putString("resultText",resultText)
50
+ val blockText = block.text
51
+ map.putString("blockText",blockText)
52
+ val blockCornerPoints = block.cornerPoints?.size
53
+ if (blockCornerPoints != null) {
54
+ map.putInt("size",blockCornerPoints)
55
+ }
56
+ val blockFrameBottom = block.boundingBox?.bottom
57
+ val blockFrameTop = block.boundingBox?.top
58
+ val blockFrameLeft = block.boundingBox?.left
59
+ val blockFrameRight = block.boundingBox?.right
60
+ if (blockFrameBottom != null) {
61
+ map.putInt("blockFrameBottom",blockFrameBottom)
62
+ }
63
+ if (blockFrameLeft != null) {
64
+ map.putInt("blockFrameLeft",blockFrameLeft)
65
+ }
66
+ if (blockFrameTop != null) {
67
+ map.putInt("blockFrameTop",blockFrameTop)
68
+ }
69
+ if (blockFrameRight != null) {
70
+ map.putInt("blockFrameRight",blockFrameRight)
71
+ }
72
+ for (line in block.lines) {
73
+ val lineText = line.text
74
+ map.putString("lineText",lineText)
75
+ val lineCornerPoints = line.cornerPoints?.size
76
+ if (lineCornerPoints != null) {
77
+ map.putInt("size",lineCornerPoints)
78
+ }
79
+ val lineFrameBottom = line.boundingBox?.bottom
80
+ val lineFrameTop = line.boundingBox?.top
81
+ val lineFrameLeft = line.boundingBox?.left
82
+ val lineFrameRight = line.boundingBox?.right
83
+ if (lineFrameBottom != null) {
84
+ map.putInt("lineFrameBottom",lineFrameBottom)
85
+ }
86
+ if (lineFrameLeft != null) {
87
+ map.putInt("lineFrameLeft",lineFrameLeft)
88
+ }
89
+ if (lineFrameTop != null) {
90
+ map.putInt("lineFrameTop",lineFrameTop)
91
+ }
92
+ if (lineFrameRight != null) {
93
+ map.putInt("lineFrameRight",lineFrameRight)
94
+ }
95
+ for (element in line.elements) {
96
+ val elementText = element.text
97
+ map.putString("elementText",elementText)
98
+
99
+ val elementCornerPoints = line.cornerPoints?.size
100
+ if (elementCornerPoints != null) {
101
+ map.putInt("size",elementCornerPoints)
102
+ }
103
+ val elementFrameBottom = line.boundingBox?.bottom
104
+ val elementFrameTop = line.boundingBox?.top
105
+ val elementFrameLeft = line.boundingBox?.left
106
+ val elementFrameRight = line.boundingBox?.right
107
+ if (elementFrameBottom != null) {
108
+ map.putInt("elementFrameBottom",elementFrameBottom)
109
+ }
110
+ if (elementFrameLeft != null) {
111
+ map.putInt("elementFrameLeft",elementFrameLeft)
112
+ }
113
+ if (elementFrameTop != null) {
114
+ map.putInt("elementFrameTop",elementFrameTop)
115
+ }
116
+ if (elementFrameRight != null) {
117
+ map.putInt("elementFrameRight",elementFrameRight)
118
+ }
119
+ }
120
+ array.pushMap(map)
121
+
122
+ }
123
+ }
124
+ return array.toArrayList()
125
+ } catch (e: Exception) {
126
+ throw Exception("Error processing text recognition: $e ")
127
+ }
128
+ }
129
+ }
130
+
@@ -0,0 +1,175 @@
1
+ #import <MLKitBarcodeScanning/MLKitBarcodeScanning.h>
2
+ #import <MLKitBarcodeScanning/MLKBarcodeScannerOptions.h>
3
+ #import <VisionCamera/FrameProcessorPlugin.h>
4
+ #import <VisionCamera/FrameProcessorPluginRegistry.h>
5
+ #import <VisionCamera/Frame.h>
6
+ #import <React/RCTBridgeModule.h>
7
+ @import MLKitVision;
8
+
9
+ @interface VisionCameraBarcodeScannerPlugin : FrameProcessorPlugin
10
+
11
+ @property(nonatomic, strong) NSDictionary *configuration;
12
+
13
+ @end
14
+
15
+ @implementation VisionCameraBarcodeScannerPlugin {
16
+ MLKBarcodeScannerOptions *options;
17
+ }
18
+
19
+ - (instancetype _Nonnull)initWithProxy:(VisionCameraProxyHolder*)proxy
20
+ withOptions:(NSDictionary* _Nullable)options {
21
+ self = [super initWithProxy:proxy withOptions:options];
22
+ if (self) {
23
+ _configuration = options ?: @{};
24
+ }
25
+ return self;
26
+ }
27
+
28
+ - (id _Nullable)callback:(Frame* _Nonnull)frame
29
+ withArguments:(NSDictionary* _Nullable)arguments {
30
+ NSDictionary *config = arguments ?: self.configuration ?: @{};
31
+ MLKBarcodeFormat formatMask = [self formatMaskFromConfig:config];
32
+ options = [[MLKBarcodeScannerOptions alloc] initWithFormats:formatMask];
33
+
34
+ MLKBarcodeScanner *barcodeScanner = [MLKBarcodeScanner barcodeScannerWithOptions:options];
35
+
36
+ CMSampleBufferRef buffer = frame.buffer;
37
+ UIImageOrientation orientation = frame.orientation;
38
+ MLKVisionImage *image = [[MLKVisionImage alloc] initWithBuffer:buffer];
39
+ image.orientation = orientation;
40
+ NSMutableArray *data = [NSMutableArray array];
41
+ dispatch_group_t dispatchGroup = dispatch_group_create();
42
+ dispatch_group_enter(dispatchGroup);
43
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
44
+ [barcodeScanner processImage:image
45
+ completion:^(NSArray<MLKBarcode *> *_Nullable barcodes,
46
+ NSError *_Nullable error) {
47
+ if (error != nil) {
48
+ RCTResponseErrorBlock error;
49
+ return;
50
+ }
51
+ if (barcodes.count > 0) {
52
+ for (MLKBarcode *barcode in barcodes) {
53
+ NSMutableDictionary *obj = [[NSMutableDictionary alloc] init];
54
+
55
+ NSString *displayValue = barcode.displayValue;
56
+ obj[@"displayValue"] = displayValue;
57
+ NSString *rawValue = barcode.rawValue;
58
+ obj[@"rawValue"] = rawValue;
59
+ obj[@"format"] = [self stringFromFormat:barcode.format];
60
+ obj[@"left"] = @(CGRectGetMinX(barcode.frame));
61
+ obj[@"top"] = @(CGRectGetMinY(barcode.frame));
62
+ obj[@"right"] = @(CGRectGetMaxX(barcode.frame));
63
+ obj[@"bottom"] = @(CGRectGetMaxY(barcode.frame));
64
+ obj[@"width"] = @(barcode.frame.size.width);
65
+ obj[@"height"] = @(barcode.frame.size.height);
66
+
67
+ MLKBarcodeValueType valueType = barcode.valueType;
68
+
69
+ switch (valueType) {
70
+ case MLKBarcodeValueTypeWiFi:
71
+ obj[@"ssid"] = barcode.wifi.ssid;
72
+ obj[@"password"] = barcode.wifi.password;
73
+ break;
74
+ case MLKBarcodeValueTypeURL:
75
+ obj[@"url"] = barcode.URL.url;
76
+ obj[@"title"] = barcode.URL.title;
77
+ break;
78
+ default:
79
+ break;
80
+ }
81
+ [data addObject:obj];
82
+
83
+ }
84
+
85
+ }
86
+
87
+ dispatch_group_leave(dispatchGroup);
88
+ }];
89
+ });
90
+
91
+ dispatch_group_wait(dispatchGroup, DISPATCH_TIME_FOREVER);
92
+ return data;
93
+ }
94
+
95
+ VISION_EXPORT_FRAME_PROCESSOR(VisionCameraBarcodeScannerPlugin, scanBarcodes)
96
+
97
+ @end
98
+
99
+ static BOOL optionEnabled(NSDictionary *config, NSString *key) {
100
+ id value = config[key];
101
+ if (!value) {
102
+ return NO;
103
+ }
104
+ if ([value isKindOfClass:[NSNumber class]]) {
105
+ return [(NSNumber *)value boolValue];
106
+ }
107
+ if ([value isKindOfClass:[NSString class]]) {
108
+ return [value boolValue];
109
+ }
110
+ return NO;
111
+ }
112
+
113
+ @implementation VisionCameraBarcodeScannerPlugin (Configuration)
114
+
115
+ - (MLKBarcodeFormat)formatMaskFromConfig:(NSDictionary *)config {
116
+ if (optionEnabled(config, @"all")) {
117
+ return MLKBarcodeFormatAll;
118
+ }
119
+ MLKBarcodeFormat mask = 0;
120
+ if (optionEnabled(config, @"aztec")) mask |= MLKBarcodeFormatAztec;
121
+ if (optionEnabled(config, @"code-128")) mask |= MLKBarcodeFormatCode128;
122
+ if (optionEnabled(config, @"code-39")) mask |= MLKBarcodeFormatCode39;
123
+ if (optionEnabled(config, @"code-93")) mask |= MLKBarcodeFormatCode93;
124
+ if (optionEnabled(config, @"codabar")) mask |= MLKBarcodeFormatCodaBar;
125
+ if (optionEnabled(config, @"data-matrix")) mask |= MLKBarcodeFormatDataMatrix;
126
+ if (optionEnabled(config, @"ean-13")) mask |= MLKBarcodeFormatEAN13;
127
+ if (optionEnabled(config, @"ean-8")) mask |= MLKBarcodeFormatEAN8;
128
+ if (optionEnabled(config, @"itf")) mask |= MLKBarcodeFormatITF;
129
+ if (optionEnabled(config, @"pdf-417")) mask |= MLKBarcodeFormatPDF417;
130
+ if (optionEnabled(config, @"qr")) mask |= MLKBarcodeFormatQRCode;
131
+ if (optionEnabled(config, @"upc-a")) mask |= MLKBarcodeFormatUPCA;
132
+ if (optionEnabled(config, @"upc-e")) mask |= MLKBarcodeFormatUPCE;
133
+
134
+ if (mask == 0) {
135
+ mask = MLKBarcodeFormatAll;
136
+ }
137
+ return mask;
138
+ }
139
+
140
+ - (NSString *)stringFromFormat:(MLKBarcodeFormat)format {
141
+ switch (format) {
142
+ case MLKBarcodeFormatAztec:
143
+ return @"aztec";
144
+ case MLKBarcodeFormatCode128:
145
+ return @"code-128";
146
+ case MLKBarcodeFormatCode39:
147
+ return @"code-39";
148
+ case MLKBarcodeFormatCode93:
149
+ return @"code-93";
150
+ case MLKBarcodeFormatCodaBar:
151
+ return @"codabar";
152
+ case MLKBarcodeFormatDataMatrix:
153
+ return @"data-matrix";
154
+ case MLKBarcodeFormatEAN13:
155
+ return @"ean-13";
156
+ case MLKBarcodeFormatEAN8:
157
+ return @"ean-8";
158
+ case MLKBarcodeFormatITF:
159
+ return @"itf";
160
+ case MLKBarcodeFormatPDF417:
161
+ return @"pdf-417";
162
+ case MLKBarcodeFormatQRCode:
163
+ return @"qr";
164
+ case MLKBarcodeFormatUPCA:
165
+ return @"upc-a";
166
+ case MLKBarcodeFormatUPCE:
167
+ return @"upc-e";
168
+ case MLKBarcodeFormatAll:
169
+ return @"all";
170
+ default:
171
+ return @"unknown";
172
+ }
173
+ }
174
+
175
+ @end