@hxa-rn/react-native-document-scanner
v0.0.1-beta
Published
Scan documents, automatic border detection, automatic crop
Readme
@hxa-rn/react-native-document-scanner
本项目基于 react-native-document-scanner 开发。如果在使用过程中有任何问题,欢迎在 GitCode 提交 Issue,会及时跟进。
项目介绍
@hxa-rn/react-native-document-scanner 是 react-native-document-scanner(上游版本 1.4.2)的鸿蒙(OpenHarmony / HarmonyOS,RNOH)适配包,当前版本 0.0.1-beta。
上游是一个文档扫描库:拍摄纸质文档、自动识别边框并做透视裁剪,把裁剪后的图片以文件路径或 Base64 字符串的形式回传给业务方,并在取景过程中回报每次边框检测的结果。
鸿蒙适配版提供两种扫描引擎,二者都在全屏扫描会话中运行:
- Vision Kit 引擎(缺省):基于系统文档扫描控件(
@hms.ai.DocumentScanner),控件内完成取景、自动拍摄、裁剪、滤镜、编辑与保存; - 自建取景引擎:基于 Camera Kit 相机会话与 ArkUI
XComponent预览,实现上游的实时边框检测(onRectangleDetect)、检测框遮罩(overlayColor)、手电筒(enableTorch)、前后摄切换(useFrontCam)、自动拍摄阈值与检测间隔,并回传真实的裁剪前原图与四角坐标。
两种引擎都支持:单张与连续拍摄、文件路径或 Base64 回传、逐张触发 onPictureTaken、quality / brightness / saturation / contrast 对结果图生效、结构化错误码。
⚠️ 交互模型与上游不同(重要):上游是内嵌在 RN 视图树里的实时相机取景组件;鸿蒙侧两种引擎的取景都在独立的全屏会话中进行,不内嵌在 RN 页面内。<DocumentScanner> 在页面上渲染一个触发面,点击它或调用 ref.startScan() 拉起全屏扫描会话,扫描结束后回到原页面。
| 鸿蒙适配包版本 | 上游版本 | React Native / RNOH | 编译 SDK | 接入方式 |
| --- | --- | --- | --- | --- |
| 0.0.1-beta | 1.4.2 | RN >=0.72(基于 0.72.5 + @react-native-oh/react-native-harmony 0.72.143) | compatibleSdkVersion 5.1.0(18),targetSdkVersion 6.1.1(24) | Autolink(RNOH hvigor 插件)或 Manual Link,两条路线均在示例宿主副本上仅经 tgz 安装本包编译验证 |
包内鸿蒙模块的 ohpm 名为
@oh-rn/react-native-document-scanner(源码 HARharmony/document_scanner.har,同源源码目录harmony/document_scanner),与 npm 包名不同;下文接入步骤里两个名字请照抄,不要互换。
集成指南
1. 安装
npm install @hxa-rn/[email protected]宿主为 RN 0.72 工程时如遇 peer 冲突,可加 --legacy-peer-deps。peerDependencies:react-native >= 0.72,react *。
2. JS 侧导入
包内已声明 harmony.alias 为 react-native-document-scanner。宿主 Metro 配置使用 RNOH 的 createHarmonyMetroConfig(RNOH 工程模板默认如此)时,打包会把上游包名重定向到本包,业务代码仍从上游包名导入:
import DocumentScanner, { RNPdfScannerManager } from 'react-native-document-scanner';不要写成 from '@hxa-rn/react-native-document-scanner'。
3. 鸿蒙原生接入
包内 harmony/document_scanner.har 是源码 HAR(含 ETS 源码、C++ 源码与 codegen 产物,ohpm 名 @oh-rn/react-native-document-scanner),与同队其他 @hxa-rn/* 适配包形态一致。接入分两条路线,二选一,不要同时做(否则 CMake 会报同一 binary dir 被用两次)。以下片段与本仓 example_auto 示例宿主的接线代码逐字相同(示例以仓内源码路径依赖本模块,这里改为 npm 安装位置)。
① 工程根 oh-package.json5:为 RNOH 框架配置 overrides(按宿主既有的 RNOH 接入方式填写;示例工程使用 npm 包内的源码版 HAR):
{
"overrides": {
"@rnoh/react-native-openharmony": "file:../node_modules/@react-native-oh/react-native-harmony/react_native_openharmony.har"
}
}本模块自身对 @rnoh/react-native-openharmony 的依赖写的是相对路径 file:../react_native_openharmony_release.har,必须依靠上面这条 overrides 指到宿主的 RNOH,否则 ohpm install 会报找不到该 HAR。
② entry/oh-package.json5:依赖包内的 HAR:
"dependencies": {
"@oh-rn/react-native-document-scanner": "file:../../node_modules/@hxa-rn/react-native-document-scanner/harmony/document_scanner.har"
}在 harmony 工程目录执行 ohpm install --all(或在 DevEco Studio 中 Sync)。ohpm 会把 HAR 解包成工程内 oh_modules/@oh-rn/react-native-document-scanner/ 目录(含 src/main/cpp),后续 CMake 与 ETS 导入都从这里解析。
路线 A:Autolink(宿主 hvigorfile.ts 使用 RNOH 的 createRNOHModulePlugin 且开启 autolinking)
package.json 已声明 harmony.autolinking(cmakeLibraryTargetName document_scanner、ohPackageName @oh-rn/react-native-document-scanner、etsPackageClassName / cppPackageClassName DocumentScannerPackage)。构建时插件会自动:把 ② 的依赖写入工程根 oh-package.json5、生成 entry/src/main/cpp/autolinking.cmake 与 RNOHPackagesFactory.h / RNOHPackagesFactory.ets 并注册 DocumentScannerPackage。宿主只需保证 CMakeLists.txt 里有 include("${CMAKE_CURRENT_SOURCE_DIR}/autolinking.cmake") 与 autolink_libraries(rnoh_app)、PackageProvider.cpp / RNPackagesFactory.ets 里合并了 createRNOHPackages(ctx)(RNOH 工程模板默认如此),不要再手动注册。
路线 B:Manual Link(宿主未启用 autolinking,或在 hvigorfile.ts 的 autolinking.excludeNpmPackages 中写入了 '@hxa-rn/react-native-document-scanner')
排除项按 npm 包名匹配,写成 ohpm 名
@oh-rn/…不会生效。
③ entry/src/main/cpp/CMakeLists.txt:
# RNOH 模板工程已定义 OH_MODULE_DIR;未定义时先加:
# set(OH_MODULE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules")
add_subdirectory("${OH_MODULE_DIR}/@oh-rn/react-native-document-scanner/src/main/cpp" ./document_scanner)
target_link_libraries(rnoh_app PUBLIC document_scanner)④ entry/src/main/cpp/PackageProvider.cpp:
#include "RNOH/PackageProvider.h"
#include "DocumentScannerPackage.h"
using namespace rnoh;
std::vector<std::shared_ptr<Package>> PackageProvider::getPackages(Package::Context ctx) {
return {
std::make_shared<DocumentScannerPackage>(ctx),
};
}⑤ entry/src/main/ets/RNPackagesFactory.ets:
import { RNPackageContext, RNPackage } from '@rnoh/react-native-openharmony/ts';
import DocumentScannerPackage from '@oh-rn/react-native-document-scanner';
export function createRNPackages(ctx: RNPackageContext): RNPackage[] {
return [
new DocumentScannerPackage(ctx),
];
}包内同时附带源码目录
harmony/document_scanner/(与 HAR 同源),供查阅或自行assembleHar;不要把它以file:源码目录方式直接作为 ohpm 依赖——ohpm 会以 junction 链接到工程外,hvigor 的CompileArkTS会报EINVAL: invalid argument, mkdir '…\C:'。
4. 宿主工程还需接线三处
鸿蒙的 HAR 模块不能自行声明 Ability、页面与权限。两种引擎各需要一个承载 Ability 与一个承载页,以下三处必须由宿主应用完成;缺任一项,对应引擎的 startScan() 会以错误码 reject。所需文件已随包附在 host-template/(与示例宿主逐字一致),复制即可。
1. 声明相机权限(entry/src/main/module.json5),usedScene.abilities 必须同时列出两个承载 Ability:
"requestPermissions": [
{
"name": "ohos.permission.CAMERA",
"reason": "$string:camera_reason",
"usedScene": { "abilities": ["DocumentScannerAbility", "SelfBuiltScannerAbility"], "when": "inuse" }
}
]$string:camera_reason 需在宿主 resources/base/element/string.json 中定义。
2. 声明两个承载 Ability(entry/src/main/module.json5 的 abilities):
{
"name": "DocumentScannerAbility",
"srcEntry": "./ets/documentscannerability/DocumentScannerAbility.ets",
"description": "$string:EntryAbility_desc",
"icon": "$media:layered_image",
"label": "$string:EntryAbility_label",
"startWindowIcon": "$media:startIcon",
"startWindowBackground": "$color:start_window_background",
"launchType": "singleton",
"exported": false
},
{
"name": "SelfBuiltScannerAbility",
"srcEntry": "./ets/documentscannerability/SelfBuiltScannerAbility.ets",
"description": "$string:EntryAbility_desc",
"icon": "$media:layered_image",
"label": "$string:EntryAbility_label",
"startWindowIcon": "$media:startIcon",
"startWindowBackground": "$color:start_window_background",
"launchType": "singleton",
"exported": false
}description / icon / label / startWindowIcon / startWindowBackground 五项照抄宿主自己 EntryAbility 的取值即可。
两个 srcEntry 都是继承本库基类的空子类,直接复制包内 host-template/DocumentScannerAbility.ets 与 host-template/SelfBuiltScannerAbility.ets 到 entry/src/main/ets/documentscannerability/:
import { DocumentScannerAbility as BaseDocumentScannerAbility } from '@oh-rn/react-native-document-scanner';
export default class DocumentScannerAbility extends BaseDocumentScannerAbility {}import { SelfBuiltScannerAbility as BaseSelfBuiltScannerAbility } from '@oh-rn/react-native-document-scanner';
export default class SelfBuiltScannerAbility extends BaseSelfBuiltScannerAbility {}3. 提供两个承载页并注册进 main_pages.json:pages/DocumentScannerPage(挂载 Vision Kit 控件)与 pages/SelfBuiltScannerPage(相机预览、检测遮罩与控制条),两者都用本库导出的函数完成配置折算、结果回传与错误回传。复制包内 host-template/DocumentScannerPage.ets 与 host-template/SelfBuiltScannerPage.ets 到 entry/src/main/ets/pages/,并在 entry/src/main/resources/base/profile/main_pages.json 中加入:
{
"src": [
"pages/Index",
"pages/DocumentScannerPage",
"pages/SelfBuiltScannerPage"
]
}只打算使用 Vision Kit 引擎时也请完成自建取景引擎的接线:调用方一旦传入
onRectangleDetect、enableTorch等属性就会自动切换到自建取景引擎。
使用说明
基础用法
import React from 'react';
import { View, Image } from 'react-native';
import DocumentScanner from 'react-native-document-scanner';
export default function Demo() {
const scannerRef = React.useRef(null);
const [image, setImage] = React.useState(null);
return (
<View>
<DocumentScanner
ref={scannerRef}
captureMultiple={false}
useBase64={false}
quality={0.8}
onPictureTaken={(data) => setImage(data.croppedImage)}
/>
{image ? <Image source={{ uri: image }} style={{ width: 200, height: 200 }} /> : null}
</View>
);
}以上写法不涉及取景微调属性,使用 Vision Kit 引擎。需要实时检测回调或取景微调时,直接写出对应属性即可切换到自建取景引擎:
<DocumentScanner
ref={scannerRef}
overlayColor="rgba(255,130,0,0.7)"
enableTorch={false}
detectionCountBeforeCapture={5}
detectionRefreshRateInMS={50}
onRectangleDetect={({ stableCounter, lastDetectionType }) => console.log(stableCounter, lastDetectionType)}
onPictureTaken={(data) => console.log(data.croppedImage, data.rectangleCoordinates)}
/>scanEngine未传(或为'auto')时:写出overlayColor、enableTorch、useFrontCam、detectionCountBeforeCapture、detectionRefreshRateInMS、onRectangleDetect中任意一项(即使值为false或undefined)即使用自建取景引擎,一项都不写则使用 Vision Kit 引擎;- 也可显式指定
scanEngine="visionkit"或scanEngine="selfbuilt"; onRectangleDetect的参数直接就是{ stableCounter, lastDetectionType },写法与上游一致。
命令式拉起扫描
// 拉起全屏扫描会话;会话结束(保存、取消或失败)时返回
const result = await scannerRef.current.startScan();
// result = { cancelled: boolean, code: number, pictures: Picture[] }capture() 按调用时的会话状态执行:
| 调用时机 | ref.capture() / RNPdfScannerManager.capture() 的行为 |
|---|---|
| 没有进行中的扫描会话 | ref.capture() 等同于 startScan()(使用组件当前属性);RNPdfScannerManager.capture() 以默认配置(单张、文件路径、Vision Kit 引擎)拉起会话,结果只在返回值中,不触发组件的 onPictureTaken |
| 自建取景会话进行中 | 立即拍一张,返回 { cancelled: false, code: 200, pictures: [该张] };正在拍摄或会话已关闭时返回 { cancelled: true, code: -1, pictures: [] }。该张同时计入本次会话结果 |
| Vision Kit 会话进行中 | 以 document_scanner_busy reject(系统控件不支持从外部触发拍摄) |
扫描会话期间 RN 页面处于后台,JS 定时器暂停,事件回调照常到达。因此会话中的 capture() 请在 onRectangleDetect 等事件回调中调用,或直接点击自建取景界面上的「拍摄」按钮:
<DocumentScanner
ref={scannerRef}
scanEngine="selfbuilt"
onRectangleDetect={({ stableCounter }) => {
if (stableCounter === 2) {
scannerRef.current.capture().then((shot) => console.log(shot.pictures.length));
}
}}
/>上游的 NativeModules.RNPdfScannerManager.capture() 在鸿蒙侧改为具名导出:
import { RNPdfScannerManager } from 'react-native-document-scanner';
const result = await RNPdfScannerManager.capture();连续拍摄与 Base64
<DocumentScanner
captureMultiple // 一次会话可连续拍摄多张
maxShotCount={5} // 单次会话最多 5 张(缺省 9)
useBase64 // 结果以 Base64 字符串返回,而非文件路径
onPictureTaken={(data) => console.log(data.croppedImage.length)}
/>onPictureTaken 在扫描会话结束时按顺序逐张触发。
错误处理
try {
await scannerRef.current.startScan();
} catch (e) {
// e.code 为结构化错误码,e.message 为可读描述
console.warn(e.code, e.message);
}接口文档
组件属性
| 属性 | 类型 | 默认值 | 说明 | 鸿蒙支持 |
|---|---|---|---|---|
| captureMultiple | boolean | false | 是否连续拍摄 | ✅ 两种引擎均生效。严格布尔判定:仅 true 视为开启,字符串 'true'、数字 1 等按 false 处理 |
| maxShotCount | number | 9 | captureMultiple 为 true 时单次会话最多拍摄的张数(鸿蒙新增) | ✅ 两种引擎均生效。正整数向下取整,非法值按 9 处理 |
| useBase64 | boolean | false | 结果以 Base64 字符串返回,而非文件路径 | ✅ 两种引擎均生效 |
| scanButtonText | string | '点击扫描文档' | 触发面按钮文案(鸿蒙新增) | ✅ 生效 |
| scanEngine | 'visionkit' \| 'selfbuilt' \| 'auto' | 'auto' | 选择扫描引擎(鸿蒙新增),'auto' 规则见「使用说明」 | ✅ 生效。非法值按 'auto' 处理 |
| manualOnly | boolean | false | 关闭自动拍摄,只允许手动拍(按上游 1.4.3 语义提供) | ✅ 两种引擎均生效:Vision Kit 切手动拍摄模式;自建取景不再按 detectionCountBeforeCapture 自动拍 |
| onPictureTaken | (data) => void | 空函数 | 每张图片触发一次,载荷见下表 | ✅ 两种引擎均生效,在会话结束时逐张触发 |
| onRectangleDetect | ({ stableCounter, lastDetectionType }) => void | — | 每次检测到文档边框时触发 | ✅ 自建取景引擎生效;Vision Kit 引擎下不触发 |
| overlayColor | string \| number | 不绘制 | 检测框遮罩颜色,支持 #RRGGBBAA、rgba() 等;数值按 0xAARRGGBB 解析 | ✅ 自建取景引擎生效;Vision Kit 引擎下不生效 |
| enableTorch | boolean | false | 手电筒 | ✅ 自建取景引擎生效(设备不支持时不动作);Vision Kit 引擎下不生效 |
| useFrontCam | boolean | false | 使用前置摄像头 | ✅ 自建取景引擎生效(启动时设备没有所需摄像头会以 document_scanner_no_camera reject;在取景界面切换时没有对应摄像头则保持当前摄像头);Vision Kit 引擎下不生效 |
| detectionCountBeforeCapture | number | 5 | 连续检出的合格边框数超过该值时自动拍摄(缺省第 6 次) | ✅ 自建取景引擎生效;Vision Kit 引擎下不生效 |
| detectionRefreshRateInMS | number | 50 | 两次边框检测的间隔,实际间隔为该值 × 10 毫秒(缺省约 0.5 秒,与上游一致) | ✅ 自建取景引擎生效;Vision Kit 引擎下不生效 |
| brightness | number | 0 | 结果图亮度 | ✅ 两种引擎均对结果图调色;自建取景引擎只作用于裁剪成功的 croppedImage |
| saturation | number | 1 | 结果图饱和度(传 0 与上游一样按 1 处理) | ✅ 同 brightness |
| contrast | number | 1 | 结果图对比度 | ✅ 同 brightness |
| quality | number | 0.8 | JPEG 压缩质量,取值钳制到 0.1~1 | ✅ 两种引擎均生效:显式传入数值时按该质量重新编码结果图;未传时保留引擎原始图片 |
实例方法
| 方法 | 签名 | 说明 |
|---|---|---|
| startScan() | () => Promise<ScanResult> | 拉起全屏扫描会话(鸿蒙新增)。会话结束时 resolve;拉起失败时 reject |
| capture() | () => Promise<ScanResult> | 对齐上游 capture():无会话时等同于 startScan();自建取景会话中立即拍一张;Vision Kit 会话中 reject document_scanner_busy |
| RNPdfScannerManager.capture() | () => Promise<ScanResult> | 具名导出,对应上游 NativeModules.RNPdfScannerManager.capture()。会话中的行为同 capture();无会话时以默认配置拉起会话,不触发组件回调 |
返回值与回调载荷
ScanResult:
| 字段 | 类型 | 说明 |
|---|---|---|
| cancelled | boolean | 用户取消、未保存任何图片或会话失败时为 true |
| code | number | 200 表示成功;取消时为 -1 |
| pictures | Picture[] | 逐张结果;cancelled 为 true 时为空数组 |
onPictureTaken 的载荷(即 Picture):
| 字段 | 类型 | 说明 |
|---|---|---|
| croppedImage | string | 裁剪后的图片:file:// URI 或 Base64 字符串(不含换行),取决于 useBase64 |
| initialImage | string | 裁剪前原图。Vision Kit 引擎下与 croppedImage 取值相同;自建取景引擎下为未裁剪的原始拍照图(未能裁剪时与 croppedImage 相同) |
| rectangleCoordinates | object \| null | 四角坐标 { topLeft, topRight, bottomLeft, bottomRight },每个角为 { x, y }。Vision Kit 引擎下恒为 null;自建取景引擎裁剪成功时为拍照图像素坐标,未能裁剪时为 null |
onRectangleDetect 的参数:
| 字段 | 类型 | 说明 |
|---|---|---|
| stableCounter | number | 连续检出合格边框的次数;检出非合格边框时清零,未检出边框时保持不变 |
| lastDetectionType | number | 本次检出的边框类型:0 合格、1 角度不佳、2 距离过远 |
错误码
startScan() / capture() / RNPdfScannerManager.capture() reject 时,error.code 为下列之一,error.message 为可读描述:
| 错误码 | 含义 | 常见原因 |
|---|---|---|
| document_scanner_busy | 已有扫描会话在进行中 | 上一次会话尚未结束时再次 startScan();或在 Vision Kit 会话中调用 capture() |
| document_scanner_permission_denied | 相机权限被拒绝 | 用户在系统权限弹窗上选择了「不允许」,或宿主未声明相机权限(见「集成指南」第 4 节第 1 步) |
| document_scanner_no_context | 无法拉起扫描会话 | 宿主未声明对应的承载 Ability(见「集成指南」第 4 节第 2 步) |
| document_scanner_permission_not_declared | 相机权限申请异常 | 权限申请所用上下文不合法 |
| document_scanner_load_content_failed | 扫描页加载失败 | 宿主未提供承载页或未注册进 main_pages.json(见「集成指南」第 4 节第 3 步) |
| document_scanner_no_camera | 没有可用的摄像头 | 自建取景引擎下设备无摄像头、要求前置摄像头但设备没有,或摄像头无可用预览规格 |
| document_scanner_camera_session_failed | 相机会话启动失败 | 自建取景引擎下系统相机会话装配或启动出错 |
其他底层异常会原样抛出(无 code 字段)。
快速验证(运行 Example)
以下为源码仓的验证流程;npm 包不含示例工程与 scripts/,需先克隆仓库。
前置条件
| 依赖 | 版本要求 |
|------|----------|
| Node.js | >= 20(示例工程 engines 要求;库本身 >= 18) |
| DevEco Studio | 6.1.1(示例验证用 6.1.1.290) |
| HarmonyOS SDK | compatibleSdkVersion 5.1.0(18),targetSdkVersion 6.1.1(24) |
运行步骤
1. 克隆仓库(鸿蒙适配代码在 ohos/ 子目录,根目录保留上游 iOS 实现)
git clone -b br_dev https://gitcode.com/hxa-rn/react-native-document-scanner.git
cd react-native-document-scanner/ohosWindows/DevEco 环境建议将仓库克隆到短路径(如
D:\rn\ds)。RNOH 源码模式编译会生成较深的 native 中间路径,目录过长可能在 ninja 阶段因.rsp路径过长失败。
2. 安装库依赖并构建 JS 产物
npm install --legacy-peer-deps # 触发 prepare:tsc --noEmit && bob build → dist/3. 进入 example_auto 目录,安装依赖(example 同构)
cd example_auto
npm install --legacy-peer-deps示例工程以 file:.. 依赖本库源码,Metro 与 tsconfig 已把 react-native-document-scanner 映射到本库。
4. 生成 JS Bundle
npm run dev产物:harmony/entry/src/main/resources/rawfile/bundle.harmony.js
5. 用 DevEco Studio 打开鸿蒙工程
- 打开 DevEco Studio,选择
example_auto/harmony目录,等待 Sync 完成; - Sync 完成后,在
example_auto/harmony目录执行 libevent 2.1.13 补丁脚本(示例工程使用 RNOH 源码依赖模式):
bash ../../scripts/patch-libevent-2113.sh .6. 编译并运行 HAP
在 DevEco Studio 中点击运行按钮,将 HAP 安装到设备。命令行等价(hvigorw 位于 DevEco Studio 安装目录 tools/hvigor/bin,需加入 PATH):
hvigorw --mode module -p module=entry@default -p product=default -p buildMode=release assembleHap --no-daemon注意:Example 中已预置插件依赖、Package 注册(Manual Link)、「宿主工程还需接线三处」要求的两个 Ability 子类、两个扫描页面(均已登记进
main_pages.json)以及相机权限声明(usedScene.abilities已含两者),无需手动配置。
约束与限制
兼容性
| 项 | 值 |
|---|---|
| 适配的鸿蒙 SDK | compatibleSdkVersion 5.1.0(18)、targetSdkVersion 6.1.1(24),runtimeOS HarmonyOS |
| 依赖的系统能力 | Vision Kit 文档扫描控件 @hms.ai.DocumentScanner(@kit.VisionKit,起始版本 5.0.0(12));Camera Kit(@ohos.multimedia.camera);Image Kit、Effect Kit、Core File Kit(结果图处理) |
| 上游库版本 | react-native-document-scanner 1.4.2 |
| React Native | >=0.72(适配基于 0.72.5 + @react-native-oh/react-native-harmony 0.72.143) |
| 支持设备类型 | default(手机)、tablet(平板)、2in1;Vision Kit 文档扫描控件官方仅支持手机、平板 |
验证状态
- 真机黑盒:适配源码此前的构建包已在 API 22 / 23 / 24 / 26 四档真机按 20 条用例完成黑盒验证,四档累计 80/80 通过,Vision Kit 与自建取景两条引擎路径均覆盖(数据出处:库仓
交付件/整体测试报告.md,报告日期 2026-09-16)。 - API 24 定向复测(2026-09-25)对应基线
f479aa0的历史构建包:修复手电筒状态回读与输出路径等问题;纸张检测与透视裁剪质量一项因机位缺受控场景记为部分通过。 - 当前源码(
f007b4e,含上述修复)只做过 release 重建与离线核验;该重建包与本 npm 包均未在真机安装运行。 - 本 npm 包(
0.0.1-beta):候选 tgz 在 Windows 构建机(DevEco Studio 6.1.1.290、RNOH 0.72.143 源码依赖模式)上以example_auto的副本为宿主、仅通过 tgz 安装本包(无任何指向库仓源码的路径),按本文「集成指南」路线 A(Autolink)与路线 B(Manual Link)各完成一次 releaseassembleHap编译验证;宿主目录在多轮尝试间复用(依赖与产物目录每轮重置)。 - 只在上述环境验证过;不据此声明所有 RNOH 版本或所有 API 版本均兼容。
权限
本库依赖 1 项系统权限:
| 权限 | 类型 | 用途 |
|---|---|---|
| ohos.permission.CAMERA | user_grant | 两种引擎扫描时的相机取景与拍摄 |
⚠️ 该权限为 user_grant,且 HAR 模块不能自行声明权限:
- 宿主应用必须在自己的
entry/src/main/module.json5中声明它,并在usedScene.abilities中同时列出DocumentScannerAbility与SelfBuiltScannerAbility(见「集成指南」第 4 节第 1 步); - 运行时的动态申请由本库在承载 Ability 内自动完成,宿主无需重复申请;
- 用户拒绝授权时,
startScan()会以document_scanner_permission_deniedreject。
使用约束
- 交互模型:两种引擎的扫描界面都是全屏会话,不内嵌在 RN 页面内,需由点击触发面或调用
ref.startScan()/ref.capture()显式拉起。 - 会话期间的 JS:会话进行中 RN 页面处于后台,JS 定时器暂停、事件回调照常到达;会话中的
capture()请在事件回调中调用。 - 回调时机:
onPictureTaken在会话结束时逐张触发,而非每拍一张立即触发;需要在会话中立即拿到某一张时,使用会话中capture()的返回值。 - Vision Kit 引擎:拍摄模式默认自动、滤镜默认原图(彩色),扫描界面无相册导入与分享入口;不回传裁剪前原图与四角坐标,不触发
onRectangleDetect,取景微调属性不生效,会话中不支持capture()。 - 自建取景引擎:边框检测为独立实现,同一场景下检出的边框可能与 iOS 不同;
rectangleCoordinates为拍照图像素坐标,数值不与 iOS 逐位可比;切换前后摄像头后会按enableTorch重新设置手电筒;拍照时不会额外触发一次onRectangleDetect。 - 连续拍摄:
captureMultiple为true时单次会话最多maxShotCount张(缺省 9),上游不限张数。 - 结果图处理:
brightness/saturation/contrast/quality在拍摄完成后作用于结果图,不改变取景预览画面;quality未传时不重新编码。 - 文件形态:文件结果为
file://URI;Base64 结果不含换行。 - 1.4.3 才引入的上游属性(
noGrayScale/documentAnimation/saveInAppDocument/onProcessing/onPermissionsDenied)不在本包 API 面内;manualOnly按 1.4.3 语义作为本端新增属性提供,缺省false时行为与 1.4.2 一致。
开源license
本项目基于 MIT 协议,详见 LICENSE 文件,与上游 react-native-document-scanner 保持一致。
问题反馈渠道
如果在使用过程中遇到任何问题,欢迎通过 GitCode 仓库与 Issue 反馈:
https://gitcode.com/hxa-rn/react-native-document-scanner
https://gitcode.com/hxa-rn/react-native-document-scanner/issues
