petrus-react-native 0.1.0
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/PetrusReactNative.podspec +20 -0
- package/android/build.gradle +64 -0
- package/android/src/main/AndroidManifest.xml +4 -0
- package/android/src/main/java/com/petrusreactnative/PDF417ScanSession.kt +86 -0
- package/android/src/main/java/com/petrusreactnative/PassportScanSession.kt +137 -0
- package/android/src/main/java/com/petrusreactnative/PetrusCameraView.kt +146 -0
- package/android/src/main/java/com/petrusreactnative/PetrusCameraViewManager.kt +93 -0
- package/android/src/main/java/com/petrusreactnative/PetrusOverlayView.kt +87 -0
- package/android/src/main/java/com/petrusreactnative/PetrusReactNativeModule.kt +253 -0
- package/android/src/main/java/com/petrusreactnative/PetrusReactNativePackage.kt +38 -0
- package/android/src/main/java/com/petrusreactnative/VehicleRegistrationScanSession.kt +119 -0
- package/android/src/main/java/com/petrusreactnative/events/CapturedEvent.kt +30 -0
- package/android/src/main/java/com/petrusreactnative/events/DocumentDetectedEvent.kt +49 -0
- package/android/src/main/java/com/petrusreactnative/events/FrameMetadataEvent.kt +24 -0
- package/android/src/main/java/com/petrusreactnative/mrz/PassportResultMapping.kt +54 -0
- package/android/src/main/java/com/petrusreactnative/ocr/OCRResultMapping.kt +52 -0
- package/android/src/main/java/com/petrusreactnative/pdf417/SouthAfricanDriversLicenseResultMapping.kt +80 -0
- package/android/src/main/java/com/petrusreactnative/sa/SouthAfricanIDResultMapping.kt +50 -0
- package/android/src/main/java/com/petrusreactnative/vehicle/VehicleRegistrationResultMapping.kt +72 -0
- package/ios/PetrusReactNative.h +5 -0
- package/ios/PetrusReactNative.mm +21 -0
- package/lib/module/NativePetrusReactNative.js +5 -0
- package/lib/module/NativePetrusReactNative.js.map +1 -0
- package/lib/module/PetrusCameraView.js +6 -0
- package/lib/module/PetrusCameraView.js.map +1 -0
- package/lib/module/PetrusCameraView.native.js +24 -0
- package/lib/module/PetrusCameraView.native.js.map +1 -0
- package/lib/module/PetrusCameraViewNativeComponent.ts +47 -0
- package/lib/module/cameraPermissions.js +9 -0
- package/lib/module/cameraPermissions.js.map +1 -0
- package/lib/module/cameraPermissions.native.js +10 -0
- package/lib/module/cameraPermissions.native.js.map +1 -0
- package/lib/module/getPipelineVersion.js +6 -0
- package/lib/module/getPipelineVersion.js.map +1 -0
- package/lib/module/getPipelineVersion.native.js +7 -0
- package/lib/module/getPipelineVersion.native.js.map +1 -0
- package/lib/module/index.js +45 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/parseMRZ.js +6 -0
- package/lib/module/parseMRZ.js.map +1 -0
- package/lib/module/parseMRZ.native.js +12 -0
- package/lib/module/parseMRZ.native.js.map +1 -0
- package/lib/module/parseSouthAfricanDriversLicense.js +6 -0
- package/lib/module/parseSouthAfricanDriversLicense.js.map +1 -0
- package/lib/module/parseSouthAfricanDriversLicense.native.js +12 -0
- package/lib/module/parseSouthAfricanDriversLicense.native.js.map +1 -0
- package/lib/module/parseSouthAfricanID.js +6 -0
- package/lib/module/parseSouthAfricanID.js.map +1 -0
- package/lib/module/parseSouthAfricanID.native.js +8 -0
- package/lib/module/parseSouthAfricanID.native.js.map +1 -0
- package/lib/module/parseVehicleRegistration.js +6 -0
- package/lib/module/parseVehicleRegistration.js.map +1 -0
- package/lib/module/parseVehicleRegistration.native.js +8 -0
- package/lib/module/parseVehicleRegistration.native.js.map +1 -0
- package/lib/module/recognizeText.js +6 -0
- package/lib/module/recognizeText.js.map +1 -0
- package/lib/module/recognizeText.native.js +8 -0
- package/lib/module/recognizeText.native.js.map +1 -0
- package/lib/module/scanPDF417.js +6 -0
- package/lib/module/scanPDF417.js.map +1 -0
- package/lib/module/scanPDF417.native.js +8 -0
- package/lib/module/scanPDF417.native.js.map +1 -0
- package/lib/module/scanPassport.js +6 -0
- package/lib/module/scanPassport.js.map +1 -0
- package/lib/module/scanPassport.native.js +13 -0
- package/lib/module/scanPassport.native.js.map +1 -0
- package/lib/module/scanSouthAfricanDriversLicense.js +6 -0
- package/lib/module/scanSouthAfricanDriversLicense.js.map +1 -0
- package/lib/module/scanSouthAfricanDriversLicense.native.js +12 -0
- package/lib/module/scanSouthAfricanDriversLicense.native.js.map +1 -0
- package/lib/module/scanVehicleRegistration.js +6 -0
- package/lib/module/scanVehicleRegistration.js.map +1 -0
- package/lib/module/scanVehicleRegistration.native.js +13 -0
- package/lib/module/scanVehicleRegistration.native.js.map +1 -0
- package/lib/module/types.js +2 -0
- package/lib/module/types.js.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/NativePetrusReactNative.d.ts +271 -0
- package/lib/typescript/src/NativePetrusReactNative.d.ts.map +1 -0
- package/lib/typescript/src/PetrusCameraView.d.ts +10 -0
- package/lib/typescript/src/PetrusCameraView.d.ts.map +1 -0
- package/lib/typescript/src/PetrusCameraView.native.d.ts +4 -0
- package/lib/typescript/src/PetrusCameraView.native.d.ts.map +1 -0
- package/lib/typescript/src/PetrusCameraViewNativeComponent.d.ts +38 -0
- package/lib/typescript/src/PetrusCameraViewNativeComponent.d.ts.map +1 -0
- package/lib/typescript/src/cameraPermissions.d.ts +4 -0
- package/lib/typescript/src/cameraPermissions.d.ts.map +1 -0
- package/lib/typescript/src/cameraPermissions.native.d.ts +4 -0
- package/lib/typescript/src/cameraPermissions.native.d.ts.map +1 -0
- package/lib/typescript/src/getPipelineVersion.d.ts +2 -0
- package/lib/typescript/src/getPipelineVersion.d.ts.map +1 -0
- package/lib/typescript/src/getPipelineVersion.native.d.ts +2 -0
- package/lib/typescript/src/getPipelineVersion.native.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +45 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/parseMRZ.d.ts +3 -0
- package/lib/typescript/src/parseMRZ.d.ts.map +1 -0
- package/lib/typescript/src/parseMRZ.native.d.ts +8 -0
- package/lib/typescript/src/parseMRZ.native.d.ts.map +1 -0
- package/lib/typescript/src/parseSouthAfricanDriversLicense.d.ts +3 -0
- package/lib/typescript/src/parseSouthAfricanDriversLicense.d.ts.map +1 -0
- package/lib/typescript/src/parseSouthAfricanDriversLicense.native.d.ts +8 -0
- package/lib/typescript/src/parseSouthAfricanDriversLicense.native.d.ts.map +1 -0
- package/lib/typescript/src/parseSouthAfricanID.d.ts +3 -0
- package/lib/typescript/src/parseSouthAfricanID.d.ts.map +1 -0
- package/lib/typescript/src/parseSouthAfricanID.native.d.ts +4 -0
- package/lib/typescript/src/parseSouthAfricanID.native.d.ts.map +1 -0
- package/lib/typescript/src/parseVehicleRegistration.d.ts +3 -0
- package/lib/typescript/src/parseVehicleRegistration.d.ts.map +1 -0
- package/lib/typescript/src/parseVehicleRegistration.native.d.ts +4 -0
- package/lib/typescript/src/parseVehicleRegistration.native.d.ts.map +1 -0
- package/lib/typescript/src/recognizeText.d.ts +3 -0
- package/lib/typescript/src/recognizeText.d.ts.map +1 -0
- package/lib/typescript/src/recognizeText.native.d.ts +4 -0
- package/lib/typescript/src/recognizeText.native.d.ts.map +1 -0
- package/lib/typescript/src/scanPDF417.d.ts +3 -0
- package/lib/typescript/src/scanPDF417.d.ts.map +1 -0
- package/lib/typescript/src/scanPDF417.native.d.ts +4 -0
- package/lib/typescript/src/scanPDF417.native.d.ts.map +1 -0
- package/lib/typescript/src/scanPassport.d.ts +3 -0
- package/lib/typescript/src/scanPassport.d.ts.map +1 -0
- package/lib/typescript/src/scanPassport.native.d.ts +9 -0
- package/lib/typescript/src/scanPassport.native.d.ts.map +1 -0
- package/lib/typescript/src/scanSouthAfricanDriversLicense.d.ts +3 -0
- package/lib/typescript/src/scanSouthAfricanDriversLicense.d.ts.map +1 -0
- package/lib/typescript/src/scanSouthAfricanDriversLicense.native.d.ts +8 -0
- package/lib/typescript/src/scanSouthAfricanDriversLicense.native.d.ts.map +1 -0
- package/lib/typescript/src/scanVehicleRegistration.d.ts +3 -0
- package/lib/typescript/src/scanVehicleRegistration.d.ts.map +1 -0
- package/lib/typescript/src/scanVehicleRegistration.native.d.ts +9 -0
- package/lib/typescript/src/scanVehicleRegistration.native.d.ts.map +1 -0
- package/lib/typescript/src/types.d.ts +237 -0
- package/lib/typescript/src/types.d.ts.map +1 -0
- package/package.json +124 -0
- package/src/NativePetrusReactNative.ts +303 -0
- package/src/PetrusCameraView.native.tsx +44 -0
- package/src/PetrusCameraView.tsx +19 -0
- package/src/PetrusCameraViewNativeComponent.ts +47 -0
- package/src/cameraPermissions.native.tsx +10 -0
- package/src/cameraPermissions.tsx +13 -0
- package/src/getPipelineVersion.native.tsx +5 -0
- package/src/getPipelineVersion.tsx +5 -0
- package/src/index.tsx +83 -0
- package/src/parseMRZ.native.tsx +11 -0
- package/src/parseMRZ.tsx +7 -0
- package/src/parseSouthAfricanDriversLicense.native.tsx +13 -0
- package/src/parseSouthAfricanDriversLicense.tsx +9 -0
- package/src/parseSouthAfricanID.native.tsx +7 -0
- package/src/parseSouthAfricanID.tsx +7 -0
- package/src/parseVehicleRegistration.native.tsx +9 -0
- package/src/parseVehicleRegistration.tsx +9 -0
- package/src/recognizeText.native.tsx +7 -0
- package/src/recognizeText.tsx +7 -0
- package/src/scanPDF417.native.tsx +7 -0
- package/src/scanPDF417.tsx +7 -0
- package/src/scanPassport.native.tsx +12 -0
- package/src/scanPassport.tsx +7 -0
- package/src/scanSouthAfricanDriversLicense.native.tsx +11 -0
- package/src/scanSouthAfricanDriversLicense.tsx +7 -0
- package/src/scanVehicleRegistration.native.tsx +12 -0
- package/src/scanVehicleRegistration.tsx +7 -0
- package/src/types.ts +272 -0
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
import { TurboModuleRegistry, type TurboModule } from 'react-native';
|
|
2
|
+
|
|
3
|
+
export interface Spec extends TurboModule {
|
|
4
|
+
// Phase 0 placeholder: proves the JS <-> native TurboModule <-> petrus-core
|
|
5
|
+
// wiring works end to end. Replaced by the real capture/detect/OCR/parse API
|
|
6
|
+
// surface in later phases.
|
|
7
|
+
getPipelineVersion(): string;
|
|
8
|
+
|
|
9
|
+
// Phase 1: camera runtime permission (Android only -- see PetrusReactNativeModule.kt).
|
|
10
|
+
checkPermission(): Promise<'granted' | 'denied' | 'blocked'>;
|
|
11
|
+
requestPermission(): Promise<'granted' | 'denied' | 'blocked'>;
|
|
12
|
+
|
|
13
|
+
// Phase 3: ML Kit-based OCR (Android only). Raw text/boxes/confidence -- no
|
|
14
|
+
// document-specific field parsing. `imagePath` may be a bare filesystem path or a
|
|
15
|
+
// URI (file://, content://, ...).
|
|
16
|
+
recognizeText(imagePath: string): Promise<{
|
|
17
|
+
fullText: string;
|
|
18
|
+
confidence: number;
|
|
19
|
+
blocks: Array<{
|
|
20
|
+
text: string;
|
|
21
|
+
boundingBox: {
|
|
22
|
+
left: number;
|
|
23
|
+
top: number;
|
|
24
|
+
right: number;
|
|
25
|
+
bottom: number;
|
|
26
|
+
};
|
|
27
|
+
lines: Array<{
|
|
28
|
+
text: string;
|
|
29
|
+
confidence: number;
|
|
30
|
+
boundingBox: {
|
|
31
|
+
left: number;
|
|
32
|
+
top: number;
|
|
33
|
+
right: number;
|
|
34
|
+
bottom: number;
|
|
35
|
+
};
|
|
36
|
+
words: Array<{
|
|
37
|
+
text: string;
|
|
38
|
+
confidence: number;
|
|
39
|
+
boundingBox: {
|
|
40
|
+
left: number;
|
|
41
|
+
top: number;
|
|
42
|
+
right: number;
|
|
43
|
+
bottom: number;
|
|
44
|
+
};
|
|
45
|
+
}>;
|
|
46
|
+
}>;
|
|
47
|
+
}>;
|
|
48
|
+
}>;
|
|
49
|
+
|
|
50
|
+
// Phase 4: SA ID parsing (Android only). Pure CPU work (regex + arithmetic on a
|
|
51
|
+
// short string) -- synchronous, no Promise needed, same as getPipelineVersion.
|
|
52
|
+
// `text` is expected to be OCR output (e.g. recognizeText's `fullText`), not tied
|
|
53
|
+
// to any specific OCR engine.
|
|
54
|
+
parseSouthAfricanID(text: string): {
|
|
55
|
+
idNumber: string | null;
|
|
56
|
+
isValid: boolean;
|
|
57
|
+
dateOfBirth: {
|
|
58
|
+
year: number;
|
|
59
|
+
month: number;
|
|
60
|
+
day: number;
|
|
61
|
+
} | null;
|
|
62
|
+
gender: 'male' | 'female' | null;
|
|
63
|
+
citizenship: 'citizen' | 'permanent_resident' | 'refugee' | 'unknown' | null;
|
|
64
|
+
validation: {
|
|
65
|
+
isValid: boolean;
|
|
66
|
+
hasValidChecksum: boolean;
|
|
67
|
+
hasValidDateOfBirth: boolean;
|
|
68
|
+
hasValidCitizenshipDigit: boolean;
|
|
69
|
+
} | null;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
// Phase 5: PDF417 barcode scanning + SA driver's licence parsing (Android only).
|
|
73
|
+
// See the README's confidence note on the reverse-engineered barcode format
|
|
74
|
+
// before relying on the decoded *licence fields* in production -- the barcode
|
|
75
|
+
// detection/decoding itself (ZXing) has no such caveat.
|
|
76
|
+
|
|
77
|
+
// Decodes a PDF417 barcode from a still image. `imagePath` may be a bare
|
|
78
|
+
// filesystem path or a URI. Rejects (does not resolve null) if no barcode is
|
|
79
|
+
// found in the image -- there's no meaningful "empty" PDF417 scan result.
|
|
80
|
+
scanPDF417(imagePath: string): Promise<{
|
|
81
|
+
/** Base64-encoded raw decoded barcode bytes (binary for SA licence barcodes). */
|
|
82
|
+
rawDataBase64: string;
|
|
83
|
+
/** Best-effort ISO-8859-1 text view of the same bytes; rarely meaningful for encrypted payloads. */
|
|
84
|
+
rawText: string;
|
|
85
|
+
}>;
|
|
86
|
+
|
|
87
|
+
// Parses already-scanned raw barcode bytes (e.g. scanPDF417's rawDataBase64) into
|
|
88
|
+
// SA driver's licence fields. Synchronous -- pure CPU work (RSA modPow over a few
|
|
89
|
+
// hundred bytes + nibble parsing), no I/O. Throws if rawDataBase64 doesn't decode
|
|
90
|
+
// to a recognized SA driver's licence barcode format -- unlike parseSouthAfricanID,
|
|
91
|
+
// there's no meaningful partial/not-found result to return here since this is
|
|
92
|
+
// expected to be called on data that already came from a barcode scan.
|
|
93
|
+
parseSouthAfricanDriversLicense(rawDataBase64: string): {
|
|
94
|
+
version: 'v1' | 'v2';
|
|
95
|
+
surname: string;
|
|
96
|
+
initials: string;
|
|
97
|
+
idNumber: string | null;
|
|
98
|
+
idNumberValidation: {
|
|
99
|
+
isValid: boolean;
|
|
100
|
+
hasValidChecksum: boolean;
|
|
101
|
+
hasValidDateOfBirth: boolean;
|
|
102
|
+
hasValidCitizenshipDigit: boolean;
|
|
103
|
+
} | null;
|
|
104
|
+
licenseNumber: string;
|
|
105
|
+
vehicleCodes: string[];
|
|
106
|
+
vehicleRestrictions: string[];
|
|
107
|
+
prdpCode: string | null;
|
|
108
|
+
idCountryOfIssue: string;
|
|
109
|
+
licenseCountryOfIssue: string;
|
|
110
|
+
gender: 'male' | 'female' | null;
|
|
111
|
+
dateOfBirth: { year: number; month: number; day: number } | null;
|
|
112
|
+
licenseIssueNumber: number | null;
|
|
113
|
+
licenseValidFrom: { year: number; month: number; day: number } | null;
|
|
114
|
+
licenseValidTo: { year: number; month: number; day: number } | null;
|
|
115
|
+
prdpExpiry: { year: number; month: number; day: number } | null;
|
|
116
|
+
driverRestrictionCodes: number[];
|
|
117
|
+
parseWarnings: string[];
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
// Convenience: opens a headless (no preview UI) camera session, scans for a
|
|
121
|
+
// PDF417 barcode, and parses it as an SA driver's licence in one call -- i.e.
|
|
122
|
+
// scanPDF417 + parseSouthAfricanDriversLicense composed natively. Rejects on
|
|
123
|
+
// timeout (~20s) or if no Activity is available, rather than resolving null.
|
|
124
|
+
scanSouthAfricanDriversLicense(): Promise<{
|
|
125
|
+
version: 'v1' | 'v2';
|
|
126
|
+
surname: string;
|
|
127
|
+
initials: string;
|
|
128
|
+
idNumber: string | null;
|
|
129
|
+
idNumberValidation: {
|
|
130
|
+
isValid: boolean;
|
|
131
|
+
hasValidChecksum: boolean;
|
|
132
|
+
hasValidDateOfBirth: boolean;
|
|
133
|
+
hasValidCitizenshipDigit: boolean;
|
|
134
|
+
} | null;
|
|
135
|
+
licenseNumber: string;
|
|
136
|
+
vehicleCodes: string[];
|
|
137
|
+
vehicleRestrictions: string[];
|
|
138
|
+
prdpCode: string | null;
|
|
139
|
+
idCountryOfIssue: string;
|
|
140
|
+
licenseCountryOfIssue: string;
|
|
141
|
+
gender: 'male' | 'female' | null;
|
|
142
|
+
dateOfBirth: { year: number; month: number; day: number } | null;
|
|
143
|
+
licenseIssueNumber: number | null;
|
|
144
|
+
licenseValidFrom: { year: number; month: number; day: number } | null;
|
|
145
|
+
licenseValidTo: { year: number; month: number; day: number } | null;
|
|
146
|
+
prdpExpiry: { year: number; month: number; day: number } | null;
|
|
147
|
+
driverRestrictionCodes: number[];
|
|
148
|
+
parseWarnings: string[];
|
|
149
|
+
}>;
|
|
150
|
+
|
|
151
|
+
// Phase 6: SA vehicle registration document parsing (Android only). Label
|
|
152
|
+
// wording is based on general knowledge of NATIS/eNaTIS documents, not verified
|
|
153
|
+
// against a physical sample -- see the README's confidence note before relying
|
|
154
|
+
// on extracted fields in production. Government API integration, face matching,
|
|
155
|
+
// driver's licence, and passport parsing are explicitly out of scope here.
|
|
156
|
+
|
|
157
|
+
// Parses OCR text (e.g. recognizeText's `fullText`) into vehicle registration
|
|
158
|
+
// fields. Synchronous -- pure CPU work (regex-based field extraction), same
|
|
159
|
+
// reasoning as parseSouthAfricanID. Never throws for unrecognized/partial text --
|
|
160
|
+
// returns as much as could be extracted, with `documentType: 'unknown'` and/or
|
|
161
|
+
// `parseWarnings` entries flagging what looked off.
|
|
162
|
+
parseVehicleRegistration(text: string): {
|
|
163
|
+
documentType:
|
|
164
|
+
| 'registration_certificate'
|
|
165
|
+
| 'license_disc'
|
|
166
|
+
| 'notification_of_change'
|
|
167
|
+
| 'roadworthy_certificate'
|
|
168
|
+
| 'unknown';
|
|
169
|
+
registrationNumber: string | null;
|
|
170
|
+
vin: string | null;
|
|
171
|
+
vinValidation: {
|
|
172
|
+
hasValidLength: boolean;
|
|
173
|
+
hasValidCharacterSet: boolean;
|
|
174
|
+
isStructurallyValid: boolean;
|
|
175
|
+
northAmericanCheckDigitValid: boolean | null;
|
|
176
|
+
} | null;
|
|
177
|
+
engineNumber: string | null;
|
|
178
|
+
make: string | null;
|
|
179
|
+
model: string | null;
|
|
180
|
+
yearModel: number | null;
|
|
181
|
+
ownerName: string | null;
|
|
182
|
+
ownerIdNumber: string | null;
|
|
183
|
+
ownerIdValidation: {
|
|
184
|
+
isValid: boolean;
|
|
185
|
+
hasValidChecksum: boolean;
|
|
186
|
+
hasValidDateOfBirth: boolean;
|
|
187
|
+
hasValidCitizenshipDigit: boolean;
|
|
188
|
+
} | null;
|
|
189
|
+
validation: {
|
|
190
|
+
isValid: boolean;
|
|
191
|
+
hasStructurallyValidVin: boolean;
|
|
192
|
+
hasPlausibleRegistrationNumber: boolean;
|
|
193
|
+
hasPlausibleYearModel: boolean;
|
|
194
|
+
};
|
|
195
|
+
parseWarnings: string[];
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
// Convenience: opens a headless (no preview UI) camera session, runs OCR on
|
|
199
|
+
// frames, and parses each result as a vehicle registration document in one call
|
|
200
|
+
// -- stops once a frame yields a registration number or VIN, or the scan window
|
|
201
|
+
// (~20s) elapses. Rejects on timeout or if no Activity is available, rather than
|
|
202
|
+
// resolving null (same convention as scanSouthAfricanDriversLicense).
|
|
203
|
+
scanVehicleRegistration(): Promise<{
|
|
204
|
+
documentType:
|
|
205
|
+
| 'registration_certificate'
|
|
206
|
+
| 'license_disc'
|
|
207
|
+
| 'notification_of_change'
|
|
208
|
+
| 'roadworthy_certificate'
|
|
209
|
+
| 'unknown';
|
|
210
|
+
registrationNumber: string | null;
|
|
211
|
+
vin: string | null;
|
|
212
|
+
vinValidation: {
|
|
213
|
+
hasValidLength: boolean;
|
|
214
|
+
hasValidCharacterSet: boolean;
|
|
215
|
+
isStructurallyValid: boolean;
|
|
216
|
+
northAmericanCheckDigitValid: boolean | null;
|
|
217
|
+
} | null;
|
|
218
|
+
engineNumber: string | null;
|
|
219
|
+
make: string | null;
|
|
220
|
+
model: string | null;
|
|
221
|
+
yearModel: number | null;
|
|
222
|
+
ownerName: string | null;
|
|
223
|
+
ownerIdNumber: string | null;
|
|
224
|
+
ownerIdValidation: {
|
|
225
|
+
isValid: boolean;
|
|
226
|
+
hasValidChecksum: boolean;
|
|
227
|
+
hasValidDateOfBirth: boolean;
|
|
228
|
+
hasValidCitizenshipDigit: boolean;
|
|
229
|
+
} | null;
|
|
230
|
+
validation: {
|
|
231
|
+
isValid: boolean;
|
|
232
|
+
hasStructurallyValidVin: boolean;
|
|
233
|
+
hasPlausibleRegistrationNumber: boolean;
|
|
234
|
+
hasPlausibleYearModel: boolean;
|
|
235
|
+
};
|
|
236
|
+
parseWarnings: string[];
|
|
237
|
+
}>;
|
|
238
|
+
|
|
239
|
+
// Phase 7: ICAO 9303 TD3 (passport) MRZ parsing (Android only). Everything
|
|
240
|
+
// runs on-device -- ML Kit text recognition is on-device, and MRZChecksum/
|
|
241
|
+
// MRZParser/PassportParser are pure Kotlin with no network calls. NFC chip
|
|
242
|
+
// reading, face matching, visa parsing (TD1/TD2), driver's licence, and
|
|
243
|
+
// vehicle documents are explicitly out of scope for this phase.
|
|
244
|
+
|
|
245
|
+
// Parses MRZ text (e.g. recognizeText's `fullText`, already cropped to the MRZ
|
|
246
|
+
// region, or pasted raw) into passport fields. Synchronous -- pure CPU work
|
|
247
|
+
// (regex + checksum arithmetic over two 44-character lines), same reasoning as
|
|
248
|
+
// parseSouthAfricanID/parseVehicleRegistration. Never throws/rejects -- when no
|
|
249
|
+
// MRZ-shaped line pair can be located in the text at all, every string field
|
|
250
|
+
// comes back empty and `parseWarnings` explains why, same "always return a
|
|
251
|
+
// result object" convention as parseSouthAfricanID's null idNumber.
|
|
252
|
+
parseMRZ(text: string): {
|
|
253
|
+
documentCode: string;
|
|
254
|
+
issuingCountry: string;
|
|
255
|
+
surname: string;
|
|
256
|
+
givenNames: string;
|
|
257
|
+
passportNumber: string;
|
|
258
|
+
nationality: string;
|
|
259
|
+
dateOfBirth: { year: number; month: number; day: number; twoDigitYear: number } | null;
|
|
260
|
+
sex: 'male' | 'female' | 'unspecified';
|
|
261
|
+
dateOfExpiry: { year: number; month: number; day: number; twoDigitYear: number } | null;
|
|
262
|
+
personalNumber: string | null;
|
|
263
|
+
validation: {
|
|
264
|
+
isValid: boolean;
|
|
265
|
+
passportNumberCheckValid: boolean;
|
|
266
|
+
dateOfBirthCheckValid: boolean;
|
|
267
|
+
expiryDateCheckValid: boolean;
|
|
268
|
+
personalNumberCheckValid: boolean;
|
|
269
|
+
compositeCheckValid: boolean;
|
|
270
|
+
};
|
|
271
|
+
parseWarnings: string[];
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
// Convenience: opens a headless (no preview UI) camera session; per frame,
|
|
275
|
+
// detects the document boundary, crops just the MRZ band (ICAO 9303's
|
|
276
|
+
// Effective Reading Zone geometry), runs OCR on *only that crop*, and parses
|
|
277
|
+
// it -- stops once a frame yields a non-empty passport number, or the scan
|
|
278
|
+
// window (~20s) elapses. Rejects on timeout or if no Activity is available
|
|
279
|
+
// (same convention as scanSouthAfricanDriversLicense/scanVehicleRegistration).
|
|
280
|
+
scanPassport(): Promise<{
|
|
281
|
+
documentCode: string;
|
|
282
|
+
issuingCountry: string;
|
|
283
|
+
surname: string;
|
|
284
|
+
givenNames: string;
|
|
285
|
+
passportNumber: string;
|
|
286
|
+
nationality: string;
|
|
287
|
+
dateOfBirth: { year: number; month: number; day: number; twoDigitYear: number } | null;
|
|
288
|
+
sex: 'male' | 'female' | 'unspecified';
|
|
289
|
+
dateOfExpiry: { year: number; month: number; day: number; twoDigitYear: number } | null;
|
|
290
|
+
personalNumber: string | null;
|
|
291
|
+
validation: {
|
|
292
|
+
isValid: boolean;
|
|
293
|
+
passportNumberCheckValid: boolean;
|
|
294
|
+
dateOfBirthCheckValid: boolean;
|
|
295
|
+
expiryDateCheckValid: boolean;
|
|
296
|
+
personalNumberCheckValid: boolean;
|
|
297
|
+
compositeCheckValid: boolean;
|
|
298
|
+
};
|
|
299
|
+
parseWarnings: string[];
|
|
300
|
+
}>;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
export default TurboModuleRegistry.getEnforcing<Spec>('PetrusReactNative');
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { NativeSyntheticEvent } from 'react-native';
|
|
2
|
+
import NativePetrusCameraView from './PetrusCameraViewNativeComponent';
|
|
3
|
+
import type {
|
|
4
|
+
PetrusFrameMetadata,
|
|
5
|
+
PetrusDocumentDetectedEvent,
|
|
6
|
+
PetrusCapturedEvent,
|
|
7
|
+
} from './types';
|
|
8
|
+
import type { PetrusCameraViewProps } from './PetrusCameraView';
|
|
9
|
+
|
|
10
|
+
export type { PetrusCameraViewProps } from './PetrusCameraView';
|
|
11
|
+
|
|
12
|
+
export function PetrusCameraView({
|
|
13
|
+
onFrameMetadata,
|
|
14
|
+
onDocumentDetected,
|
|
15
|
+
onCaptured,
|
|
16
|
+
...rest
|
|
17
|
+
}: PetrusCameraViewProps) {
|
|
18
|
+
return (
|
|
19
|
+
<NativePetrusCameraView
|
|
20
|
+
{...rest}
|
|
21
|
+
onFrameMetadata={
|
|
22
|
+
onFrameMetadata == null
|
|
23
|
+
? undefined
|
|
24
|
+
: (event: NativeSyntheticEvent<PetrusFrameMetadata>) => {
|
|
25
|
+
onFrameMetadata(event.nativeEvent);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
onDocumentDetected={
|
|
29
|
+
onDocumentDetected == null
|
|
30
|
+
? undefined
|
|
31
|
+
: (event: NativeSyntheticEvent<PetrusDocumentDetectedEvent>) => {
|
|
32
|
+
onDocumentDetected(event.nativeEvent);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
onCaptured={
|
|
36
|
+
onCaptured == null
|
|
37
|
+
? undefined
|
|
38
|
+
: (event: NativeSyntheticEvent<PetrusCapturedEvent>) => {
|
|
39
|
+
onCaptured(event.nativeEvent);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ViewProps } from 'react-native';
|
|
2
|
+
import type {
|
|
3
|
+
PetrusFrameMetadata,
|
|
4
|
+
PetrusDocumentDetectedEvent,
|
|
5
|
+
PetrusCapturedEvent,
|
|
6
|
+
} from './types';
|
|
7
|
+
|
|
8
|
+
export interface PetrusCameraViewProps extends ViewProps {
|
|
9
|
+
isActive?: boolean;
|
|
10
|
+
onFrameMetadata?: (metadata: PetrusFrameMetadata) => void;
|
|
11
|
+
onDocumentDetected?: (event: PetrusDocumentDetectedEvent) => void;
|
|
12
|
+
onCaptured?: (event: PetrusCapturedEvent) => void;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function PetrusCameraView(_props: PetrusCameraViewProps): never {
|
|
16
|
+
throw new Error(
|
|
17
|
+
"'petrus-react-native's PetrusCameraView is only supported on native platforms."
|
|
18
|
+
);
|
|
19
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { codegenNativeComponent, type ViewProps, type CodegenTypes } from 'react-native';
|
|
2
|
+
|
|
3
|
+
type NativePoint2D = Readonly<{
|
|
4
|
+
x: CodegenTypes.Double;
|
|
5
|
+
y: CodegenTypes.Double;
|
|
6
|
+
}>;
|
|
7
|
+
|
|
8
|
+
// Always sent as a well-formed object (zeroed when nothing's detected), never
|
|
9
|
+
// null/omitted -- avoids relying on nullable/optional object support in the event
|
|
10
|
+
// codegen path. `detected`/callers should treat these as meaningless when unset.
|
|
11
|
+
type NativeDocumentCorners = Readonly<{
|
|
12
|
+
topLeft: NativePoint2D;
|
|
13
|
+
topRight: NativePoint2D;
|
|
14
|
+
bottomRight: NativePoint2D;
|
|
15
|
+
bottomLeft: NativePoint2D;
|
|
16
|
+
frameWidth: CodegenTypes.Int32;
|
|
17
|
+
frameHeight: CodegenTypes.Int32;
|
|
18
|
+
rotationDegrees: CodegenTypes.Int32;
|
|
19
|
+
}>;
|
|
20
|
+
|
|
21
|
+
export interface NativeProps extends ViewProps {
|
|
22
|
+
isActive?: CodegenTypes.WithDefault<boolean, false>;
|
|
23
|
+
|
|
24
|
+
onFrameMetadata?: CodegenTypes.DirectEventHandler<{
|
|
25
|
+
width: CodegenTypes.Int32;
|
|
26
|
+
height: CodegenTypes.Int32;
|
|
27
|
+
rotation: CodegenTypes.Int32;
|
|
28
|
+
// Epoch milliseconds -- exceeds Int32 range, so this must be Double.
|
|
29
|
+
timestamp: CodegenTypes.Double;
|
|
30
|
+
}>;
|
|
31
|
+
|
|
32
|
+
onDocumentDetected?: CodegenTypes.DirectEventHandler<{
|
|
33
|
+
detected: boolean;
|
|
34
|
+
isStable: boolean;
|
|
35
|
+
stableDurationMs: CodegenTypes.Double;
|
|
36
|
+
confidence: CodegenTypes.Double;
|
|
37
|
+
corners: NativeDocumentCorners;
|
|
38
|
+
}>;
|
|
39
|
+
|
|
40
|
+
onCaptured?: CodegenTypes.DirectEventHandler<{
|
|
41
|
+
corners: NativeDocumentCorners;
|
|
42
|
+
confidence: CodegenTypes.Double;
|
|
43
|
+
timestamp: CodegenTypes.Double;
|
|
44
|
+
}>;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export default codegenNativeComponent<NativeProps>('PetrusCameraView');
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import PetrusReactNative from './NativePetrusReactNative';
|
|
2
|
+
import type { PetrusCameraPermissionStatus } from './types';
|
|
3
|
+
|
|
4
|
+
export function checkPermission(): Promise<PetrusCameraPermissionStatus> {
|
|
5
|
+
return PetrusReactNative.checkPermission();
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function requestPermission(): Promise<PetrusCameraPermissionStatus> {
|
|
9
|
+
return PetrusReactNative.requestPermission();
|
|
10
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { PetrusCameraPermissionStatus } from './types';
|
|
2
|
+
|
|
3
|
+
export function checkPermission(): Promise<PetrusCameraPermissionStatus> {
|
|
4
|
+
return Promise.reject(
|
|
5
|
+
new Error("'petrus-react-native' is only supported on native platforms.")
|
|
6
|
+
);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function requestPermission(): Promise<PetrusCameraPermissionStatus> {
|
|
10
|
+
return Promise.reject(
|
|
11
|
+
new Error("'petrus-react-native' is only supported on native platforms.")
|
|
12
|
+
);
|
|
13
|
+
}
|
package/src/index.tsx
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { getPipelineVersion } from './getPipelineVersion';
|
|
2
|
+
import { checkPermission, requestPermission } from './cameraPermissions';
|
|
3
|
+
import { recognizeText } from './recognizeText';
|
|
4
|
+
import { parseSouthAfricanID } from './parseSouthAfricanID';
|
|
5
|
+
import { scanPDF417 } from './scanPDF417';
|
|
6
|
+
import { parseSouthAfricanDriversLicense } from './parseSouthAfricanDriversLicense';
|
|
7
|
+
import { scanSouthAfricanDriversLicense } from './scanSouthAfricanDriversLicense';
|
|
8
|
+
import { parseVehicleRegistration } from './parseVehicleRegistration';
|
|
9
|
+
import { scanVehicleRegistration } from './scanVehicleRegistration';
|
|
10
|
+
import { parseMRZ } from './parseMRZ';
|
|
11
|
+
import { scanPassport } from './scanPassport';
|
|
12
|
+
|
|
13
|
+
export { getPipelineVersion } from './getPipelineVersion';
|
|
14
|
+
|
|
15
|
+
export { PetrusCameraView } from './PetrusCameraView';
|
|
16
|
+
export type { PetrusCameraViewProps } from './PetrusCameraView';
|
|
17
|
+
|
|
18
|
+
export { checkPermission, requestPermission } from './cameraPermissions';
|
|
19
|
+
|
|
20
|
+
export { recognizeText } from './recognizeText';
|
|
21
|
+
|
|
22
|
+
export { parseSouthAfricanID } from './parseSouthAfricanID';
|
|
23
|
+
|
|
24
|
+
export { scanPDF417 } from './scanPDF417';
|
|
25
|
+
export { parseSouthAfricanDriversLicense } from './parseSouthAfricanDriversLicense';
|
|
26
|
+
export { scanSouthAfricanDriversLicense } from './scanSouthAfricanDriversLicense';
|
|
27
|
+
|
|
28
|
+
export { parseVehicleRegistration } from './parseVehicleRegistration';
|
|
29
|
+
export { scanVehicleRegistration } from './scanVehicleRegistration';
|
|
30
|
+
|
|
31
|
+
export { parseMRZ } from './parseMRZ';
|
|
32
|
+
export { scanPassport } from './scanPassport';
|
|
33
|
+
|
|
34
|
+
export type {
|
|
35
|
+
PetrusCameraPermissionStatus,
|
|
36
|
+
PetrusFrameMetadata,
|
|
37
|
+
PetrusPoint2D,
|
|
38
|
+
PetrusDocumentCorners,
|
|
39
|
+
PetrusDocumentDetectedEvent,
|
|
40
|
+
PetrusCapturedEvent,
|
|
41
|
+
OCRBoundingBox,
|
|
42
|
+
OCRWord,
|
|
43
|
+
OCRLine,
|
|
44
|
+
OCRBlock,
|
|
45
|
+
OCRResult,
|
|
46
|
+
SouthAfricanGender,
|
|
47
|
+
SouthAfricanCitizenship,
|
|
48
|
+
SouthAfricanDateOfBirth,
|
|
49
|
+
SouthAfricanIDValidation,
|
|
50
|
+
SouthAfricanIDResult,
|
|
51
|
+
PDF417ScanResult,
|
|
52
|
+
SouthAfricanDriversLicenseVersion,
|
|
53
|
+
CalendarDate,
|
|
54
|
+
SouthAfricanDriversLicenseResult,
|
|
55
|
+
VehicleDocumentType,
|
|
56
|
+
VINValidation,
|
|
57
|
+
VehicleRegistrationValidation,
|
|
58
|
+
VehicleRegistrationResult,
|
|
59
|
+
MRZSex,
|
|
60
|
+
MRZDate,
|
|
61
|
+
MRZValidation,
|
|
62
|
+
PassportResult,
|
|
63
|
+
} from './types';
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Namespaced convenience alias over the same functions above (e.g.
|
|
67
|
+
* `Petrus.parseSouthAfricanID(text)`), for callers who prefer a single import over
|
|
68
|
+
* multiple named ones. Both forms call the same underlying implementation.
|
|
69
|
+
*/
|
|
70
|
+
export const Petrus = {
|
|
71
|
+
getPipelineVersion,
|
|
72
|
+
checkPermission,
|
|
73
|
+
requestPermission,
|
|
74
|
+
recognizeText,
|
|
75
|
+
parseSouthAfricanID,
|
|
76
|
+
scanPDF417,
|
|
77
|
+
parseSouthAfricanDriversLicense,
|
|
78
|
+
scanSouthAfricanDriversLicense,
|
|
79
|
+
parseVehicleRegistration,
|
|
80
|
+
scanVehicleRegistration,
|
|
81
|
+
parseMRZ,
|
|
82
|
+
scanPassport,
|
|
83
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import PetrusReactNative from './NativePetrusReactNative';
|
|
2
|
+
import type { PassportResult } from './types';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* `text` is expected to be MRZ OCR output (e.g. recognizeText's `fullText`,
|
|
6
|
+
* already cropped to the MRZ region, or pasted raw two-line MRZ text). Never
|
|
7
|
+
* throws -- see PassportResult's doc comment for the "no MRZ found" shape.
|
|
8
|
+
*/
|
|
9
|
+
export function parseMRZ(text: string): PassportResult {
|
|
10
|
+
return PetrusReactNative.parseMRZ(text);
|
|
11
|
+
}
|
package/src/parseMRZ.tsx
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import PetrusReactNative from './NativePetrusReactNative';
|
|
2
|
+
import type { SouthAfricanDriversLicenseResult } from './types';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* `rawDataBase64` is expected to be already-scanned raw barcode bytes (e.g.
|
|
6
|
+
* scanPDF417's `rawDataBase64`). Throws if it doesn't decode to a recognized SA
|
|
7
|
+
* driver's licence barcode format.
|
|
8
|
+
*/
|
|
9
|
+
export function parseSouthAfricanDriversLicense(
|
|
10
|
+
rawDataBase64: string
|
|
11
|
+
): SouthAfricanDriversLicenseResult {
|
|
12
|
+
return PetrusReactNative.parseSouthAfricanDriversLicense(rawDataBase64);
|
|
13
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { SouthAfricanDriversLicenseResult } from './types';
|
|
2
|
+
|
|
3
|
+
export function parseSouthAfricanDriversLicense(
|
|
4
|
+
_rawDataBase64: string
|
|
5
|
+
): SouthAfricanDriversLicenseResult {
|
|
6
|
+
throw new Error(
|
|
7
|
+
"'petrus-react-native' is only supported on native platforms."
|
|
8
|
+
);
|
|
9
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import PetrusReactNative from './NativePetrusReactNative';
|
|
2
|
+
import type { SouthAfricanIDResult } from './types';
|
|
3
|
+
|
|
4
|
+
/** `text` is expected to be OCR output (e.g. recognizeText's `fullText`). */
|
|
5
|
+
export function parseSouthAfricanID(text: string): SouthAfricanIDResult {
|
|
6
|
+
return PetrusReactNative.parseSouthAfricanID(text);
|
|
7
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import PetrusReactNative from './NativePetrusReactNative';
|
|
2
|
+
import type { VehicleRegistrationResult } from './types';
|
|
3
|
+
|
|
4
|
+
/** `text` is expected to be OCR output (e.g. recognizeText's `fullText`). */
|
|
5
|
+
export function parseVehicleRegistration(
|
|
6
|
+
text: string
|
|
7
|
+
): VehicleRegistrationResult {
|
|
8
|
+
return PetrusReactNative.parseVehicleRegistration(text);
|
|
9
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import PetrusReactNative from './NativePetrusReactNative';
|
|
2
|
+
import type { OCRResult } from './types';
|
|
3
|
+
|
|
4
|
+
/** `imagePath` may be a bare filesystem path or a URI (file://, content://, ...). */
|
|
5
|
+
export function recognizeText(imagePath: string): Promise<OCRResult> {
|
|
6
|
+
return PetrusReactNative.recognizeText(imagePath);
|
|
7
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import PetrusReactNative from './NativePetrusReactNative';
|
|
2
|
+
import type { PDF417ScanResult } from './types';
|
|
3
|
+
|
|
4
|
+
/** `imagePath` may be a bare filesystem path or a URI (file://, content://, ...). */
|
|
5
|
+
export function scanPDF417(imagePath: string): Promise<PDF417ScanResult> {
|
|
6
|
+
return PetrusReactNative.scanPDF417(imagePath);
|
|
7
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import PetrusReactNative from './NativePetrusReactNative';
|
|
2
|
+
import type { PassportResult } from './types';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Opens a headless (no preview UI) camera session; per frame, detects the
|
|
6
|
+
* document boundary, crops just the MRZ band, runs OCR on only that crop, and
|
|
7
|
+
* parses it -- stops once a frame yields a non-empty passport number. Rejects
|
|
8
|
+
* on timeout (~20s) or if no Activity is available.
|
|
9
|
+
*/
|
|
10
|
+
export function scanPassport(): Promise<PassportResult> {
|
|
11
|
+
return PetrusReactNative.scanPassport();
|
|
12
|
+
}
|