vision-camera-mrz-scanner
Version:
VisionCamera Frame Processor Plugin to detect and read MRZ data from passports using MLKit Text Recognition.
264 lines (249 loc) • 12.6 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _react = _interopRequireWildcard(require("react"));
var _reactNative = require("react-native");
var _reactNativeReanimated = require("react-native-reanimated");
var _reactNativeVisionCamera = require("react-native-vision-camera");
var _visionCameraMrzScanner = require("vision-camera-mrz-scanner");
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
const MRZCamera = _ref => {
let {
enableBoundingBox,
boundingBoxStyle,
boundingBoxHorizontalPadding,
boundingBoxVerticalPadding,
style,
skipButtonEnabled: photoSkipButtonEnabled,
skipButton: photoSkipButton,
onSkipPressed: photoSkipOnPress,
skipButtonStyle: photoSkipButtonStyle,
cameraProps,
onData,
scanSuccess,
skipButtonText,
cameraDirection,
isActiveCamera
} = _ref;
//*****************************************************************************************
// setting up the state
//*****************************************************************************************
// Permissions
const [hasPermission, setHasPermission] = _react.default.useState(false);
// camera states
const devices = (0, _reactNativeVisionCamera.useCameraDevices)();
const direction = cameraDirection ?? 'back';
const device = devices[direction];
const camera = (0, _react.useRef)(null);
const {
height: screenHeight,
width: screenWidth
} = (0, _reactNative.useWindowDimensions)();
const [isActive, setIsActive] = (0, _react.useState)(true);
const [feedbackText, setFeedbackText] = (0, _react.useState)('');
const [ocrElements, setOcrElements] = (0, _react.useState)([]);
const [frameDimensions, setFrameDimensions] = (0, _react.useState)();
const landscapeMode = screenWidth > screenHeight;
const [pixelRatio, setPixelRatio] = _react.default.useState(1);
//*****************************************************************************************
// Comp Logic
//*****************************************************************************************
// const xRatio = frame.width / WINDOW_WIDTH;
// const yRatio = frame.height / WINDOW_HEIGHT;
/* A cleanup function that is called when the component is unmounted. */
(0, _react.useEffect)(() => {
return () => {
setIsActive(false);
};
}, []);
// which format should we use
const formats = (0, _react.useMemo)(() => device === null || device === void 0 ? void 0 : device.formats.sort(_visionCameraMrzScanner.sortFormatsByResolution), [device === null || device === void 0 ? void 0 : device.formats]);
//figure our what happens if it is undefined?
const [format, setFormat] = (0, _react.useState)(formats && formats.length > 0 ? formats[0] : undefined);
/**
* Prevents sending copious amounts of scans
*/
const handleScan = (0, _react.useCallback)((data, frame) => {
const isRotated = !landscapeMode;
setFrameDimensions(isRotated ? {
width: frame.height,
height: frame.width
} : {
width: frame.width,
height: frame.height
});
if (data && data.result && data.result.blocks && data.result.blocks.length === 0) {
setFeedbackText('');
}
/* Scanning the text from the image and then setting the state of the component. */
if (data && data.result && data.result.blocks && data.result.blocks.length > 0) {
let updatedOCRElements = [];
data.result.blocks.forEach(block => {
if (block.frame.width / screenWidth < 0.8) {
setFeedbackText('Hold Still');
} else {
setFeedbackText('Scanning...');
}
updatedOCRElements.push({
...block.frame
});
});
let lines = [];
data.result.blocks.forEach(block => {
lines.push(block.text);
});
if (lines.length > 0 && isActive && onData) {
setOcrElements(updatedOCRElements);
onData(lines);
} else {
setOcrElements([]);
}
}
}, [isActive, landscapeMode, onData, screenWidth]);
/* Setting the format to the first format in the formats array. */
(0, _react.useEffect)(() => {
setFormat(formats && formats.length > 0 ? formats[0] : undefined);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [device]);
/* Using the useFrameProcessor hook to process the video frames. */
const frameProcessor = (0, _reactNativeVisionCamera.useFrameProcessor)(frame => {
'worklet';
if (!scanSuccess) {
const ocrData = (0, _visionCameraMrzScanner.scanMRZ)(frame);
(0, _reactNativeReanimated.runOnJS)(handleScan)(ocrData, frame);
}
}, [handleScan]);
(0, _react.useEffect)(() => {
(async () => {
const status = await _reactNativeVisionCamera.Camera.requestCameraPermission();
setHasPermission(status === 'authorized');
})();
}, []);
/* Using the useMemo hook to create a style object. */
const boundingStyle = (0, _react.useMemo)(() => ({
position: 'absolute',
top: 0,
left: 0,
width: screenWidth,
height: screenHeight
}), [screenWidth, screenHeight]);
const bounds = ocrElements[ocrElements.length - 1];
//*****************************************************************************************
// stylesheet
//*****************************************************************************************
const styles = _reactNative.StyleSheet.create({
fixToText: {
flexDirection: 'row',
justifyContent: 'space-between'
},
skipButtonContainer: {
position: 'absolute',
bottom: screenHeight * 0.05,
width: screenWidth,
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'row'
},
feedbackContainer: {
position: 'absolute',
top: screenHeight * 0.3,
width: screenWidth,
alignItems: 'center'
},
feedbackText: {
backgroundColor: 'white',
color: 'black',
fontSize: 18,
paddingRight: 8,
paddingLeft: 8,
textAlign: 'center'
},
boundingBox: {
borderRadius: 5,
borderWidth: 3,
borderColor: 'yellow',
position: 'absolute',
left: bounds ? bounds.x * pixelRatio : 0,
top: bounds ? bounds.y * pixelRatio : 0,
height: bounds ? bounds.height : 35,
width: bounds ? bounds.width : 35
}
});
//*****************************************************************************************
// Components
//*****************************************************************************************
return /*#__PURE__*/_react.default.createElement(_reactNative.View, {
style: style
}, device && hasPermission ? /*#__PURE__*/_react.default.createElement(_reactNativeVisionCamera.Camera, {
style: (cameraProps === null || cameraProps === void 0 ? void 0 : cameraProps.style) ?? _reactNative.StyleSheet.absoluteFill,
device: (cameraProps === null || cameraProps === void 0 ? void 0 : cameraProps.device) ?? device,
torch: cameraProps === null || cameraProps === void 0 ? void 0 : cameraProps.torch,
isActive: isActiveCamera ? isActiveCamera : cameraProps !== null && cameraProps !== void 0 && cameraProps.isActive ? cameraProps === null || cameraProps === void 0 ? void 0 : cameraProps.isActive : isActive,
ref: camera,
photo: cameraProps === null || cameraProps === void 0 ? void 0 : cameraProps.photo,
video: cameraProps === null || cameraProps === void 0 ? void 0 : cameraProps.video,
audio: cameraProps === null || cameraProps === void 0 ? void 0 : cameraProps.audio,
zoom: cameraProps === null || cameraProps === void 0 ? void 0 : cameraProps.zoom,
enableZoomGesture: cameraProps === null || cameraProps === void 0 ? void 0 : cameraProps.enableZoomGesture,
preset: cameraProps === null || cameraProps === void 0 ? void 0 : cameraProps.preset,
format: (cameraProps === null || cameraProps === void 0 ? void 0 : cameraProps.format) ?? format,
fps: (cameraProps === null || cameraProps === void 0 ? void 0 : cameraProps.fps) ?? 10,
hdr: cameraProps === null || cameraProps === void 0 ? void 0 : cameraProps.hdr,
lowLightBoost: cameraProps === null || cameraProps === void 0 ? void 0 : cameraProps.lowLightBoost,
colorSpace: cameraProps === null || cameraProps === void 0 ? void 0 : cameraProps.colorSpace,
videoStabilizationMode: cameraProps === null || cameraProps === void 0 ? void 0 : cameraProps.videoStabilizationMode,
enableDepthData: cameraProps === null || cameraProps === void 0 ? void 0 : cameraProps.enableDepthData,
enablePortraitEffectsMatteDelivery: cameraProps === null || cameraProps === void 0 ? void 0 : cameraProps.enablePortraitEffectsMatteDelivery,
enableHighQualityPhotos: cameraProps === null || cameraProps === void 0 ? void 0 : cameraProps.enableHighQualityPhotos,
onError: cameraProps === null || cameraProps === void 0 ? void 0 : cameraProps.onError,
onInitialized: cameraProps === null || cameraProps === void 0 ? void 0 : cameraProps.onInitialized,
onFrameProcessorPerformanceSuggestionAvailable: cameraProps === null || cameraProps === void 0 ? void 0 : cameraProps.onFrameProcessorPerformanceSuggestionAvailable,
frameProcessor: (cameraProps === null || cameraProps === void 0 ? void 0 : cameraProps.frameProcessor) ?? frameProcessor,
frameProcessorFps: (cameraProps === null || cameraProps === void 0 ? void 0 : cameraProps.frameProcessorFps) ?? 30,
onLayout: event => {
setPixelRatio(event.nativeEvent.layout.width / _reactNative.PixelRatio.getPixelSizeForLayoutSize(event.nativeEvent.layout.width));
}
}) : undefined, enableBoundingBox && ocrElements.length > 0 ? /*#__PURE__*/_react.default.createElement(_reactNative.View, {
style: boundingStyle,
testID: "faceDetectionBoxView"
}, frameDimensions && (() => {
const {
adjustRect
} = (0, _visionCameraMrzScanner.boundingBoxAdjustToView)(frameDimensions, {
width: landscapeMode ? screenHeight : screenWidth,
height: landscapeMode ? screenWidth : screenHeight
}, landscapeMode, boundingBoxVerticalPadding, boundingBoxHorizontalPadding);
return ocrElements ? ocrElements.map((i, index) => {
const {
left,
...others
} = adjustRect(i);
return /*#__PURE__*/_react.default.createElement(_reactNative.View, {
key: index,
style: [styles.boundingBox, {
...others,
left: left
}, boundingBoxStyle]
});
}) : undefined;
})()) : null, photoSkipButton ? /*#__PURE__*/_react.default.createElement(_reactNative.View, {
style: [styles.fixToText]
}, photoSkipButtonEnabled ? photoSkipButton ? /*#__PURE__*/_react.default.createElement(_reactNative.TouchableOpacity, {
onPress: photoSkipOnPress
}, photoSkipButton) : /*#__PURE__*/_react.default.createElement(_reactNative.View, {
style: [styles.skipButtonContainer, photoSkipButtonStyle]
}, /*#__PURE__*/_react.default.createElement(_reactNative.Button, {
title: skipButtonText ? skipButtonText : 'Skip',
onPress: photoSkipOnPress
})) : undefined) : undefined, feedbackText ? /*#__PURE__*/_react.default.createElement(_reactNative.View, {
style: styles.feedbackContainer
}, /*#__PURE__*/_react.default.createElement(_reactNative.Text, {
style: styles.feedbackText
}, feedbackText)) : null);
};
var _default = MRZCamera;
exports.default = _default;
//# sourceMappingURL=MRZCamera.js.map