为什么开源 TextIn OCR Frontend 前端组件库?
在 TextIn 社群中,我们时常接到用户反馈,调取 API 进行票据等文件批量识别后,需要另行完成前端工程,实现比对环节。为助力用户节省工程成本,TextIn 团队正式开源 OCR Frontend 前端组件库,便于用户搭建前端界面,完成识别结果审核,提升使用体验。
此外,对于有翻译、校对等需求的开发者,也可灵活应用开源组件库,进行二次开发。
TextIn OCR Frontend 是一个用于展示 Textin 识别结果的 React 组件库,支持文件预览、坐标回显和结果展示。
组件库适配票据类解析结果(key-value)的展示,前端界面案例见下图。

特性
-
📄 支持图片和 PDF 文件预览
-
🎯 支持文本区域坐标回显和高亮
-
🔄 预览区域和识别结果双向联动
-
📊 支持 JSON 格式结果展示
-
🎨 TODO:可自定义样式和主题
安装与使用
拉取项目
git clone https://github.com/intsig-textin/textin-ocr-frontend.git
npm install textin-ocr-frontend
# 或
yarn add textin-ocr-frontend
快速开始
import { FilePreview, ResultView, JsonView } from "textin-ocr-frontend";
functionApp() {
return (
<div style={{ display: "flex" }}>
<div style={{ flex: 1 }}>
<FilePreview src="path/to/image.jpg" rects={rects} pages={pages} />
</div>
<div style={{ flex: 1 }}>
<ResultView resultList={resultList} />
</div>
</div>
);
}
组件说明
1. FilePreview 文件预览组件
文件预览组件,支持 PDF 和图片预览,支持缩放、旋转、分页等功能。
Props

2. ResultView 结果展示组件
结果展示组件,支持表格和列表两种展示方式。
Props

3. MarkLayer 标注层组件
标注层组件,用于在图片显示标注框。
Props

4. JsonView JSON 展示组件
JSON 数据展示组件,用于格式化展示 JSON 数据。 本项目 JSON 数据采用react-json-view
库渲染,API 保持一致,详细属性可参考其官方文档。
Props

API Interface 定义
PDFSrc
PDF 文件源配置
interface DocumentInitParameters {
[key: string]: any;
url?: string | URL;
data?: TypedArray | ArrayBuffer | Array<number> | string;
httpHeaders?: Object;
withCredentials?: boolean;
password?: string;
length?: boolean;
}
type PDFSrc = DocumentInitParameters;
IRectItem
标注框数据
interface IRectItem {
[key: string]: any;
key?: string;
type?: string;
rect_type?: string;
uid: string;
parent_uid?: string;
content_id: string;
parent_id?: string;
position: number[];
angle?: number;
render_text?: string;
}
IPageItem
页面数据
interface IPageItem {
page_number: number;
duration: number;
ppi: number;
width: number;
height: number;
angle?: number;
}
IResultListItem
结果列表项
interface IResultListItem extends IRectItem {
type: string;
description: string;
no: number;
list: IFieldItem[];
flightList: IFieldItem[][];
page_id?: number;
}
IFieldItem
字段项
interface IFieldItem extends IOriginFieldItem {
uid: string;
parent_uid?: string;
}
interface IOriginFieldItem {
key: string;
type?: string;
value: string;
description: string;
position: number[];
}
ToolbarOptions
工具栏配置
interface ToolbarOptions {
tools: PreviewToolItem[];
}
interface PreviewToolItem {
Icon: React.ComponentType<any>;
onClick: () => void;
type: string;
disabled?: boolean;
}
PreviewToolItem
工具栏配置项
interface PreviewToolItem {
Icon: React.ComponentType<any>; // 工具栏图标组件
onClick: () => void; // 点击事件处理函数
type: string; // 工具类型
disabled?: boolean; // 是否禁用
}
Hooks
useContentLinkage
用于实现预览区域和识别结果的双向联动。
const { activeContentId, activeParentContentId, registerLinkage } =
useContentLinkage({
viewContainerRef,
resultContainerRef,
});
参数

返回值

usePDFMarkLayer
用于在 PDF 文档上实现标注层功能。
const { run } = usePDFMarkLayer({
containerRef,
pdfViewerRef,
rects,
pages,
dpi,
activeContentId,
showMark,
});
参数

返回值

usePreviewTool
用于实现预览工具栏功能,包括缩放、旋转和 1:1 还原。
const { tools, scale, rotate, position, onMouseDown, onWheel, resizeScale } =
usePreviewTool({
viewContainerRef,
viewRef,
toolbarOptions,
});
参数

返回值

示例
图片示例
import { useLayoutEffect, useRef, useState } from "react";
import FilePreview from "../components/FilePreview";
import { RadioGroup } from "../components/RadioGroup";
import ResultView from "../components/ResultView";
import { imageExample } from "./data";
import JsonView from "../components/JsonView";
import { useContentLinkage } from "../hooks/useContentLinkage";
export default functionImageExample() {
const [resultTab, setResultTab] = useState("text");
const viewContainerRef = useRef<HTMLElement | null>(null);
const resultContainerRef = useRef<HTMLElement | null>(null);
const { activeParentContentId, activeContentId, registerLinkage } =
useContentLinkage({
viewContainerRef,
resultContainerRef,
});
useLayoutEffect(() => {
registerLinkage();
}, []);
return (
<div
style={{
display: "flex",
width: "100%",
height: "calc(100% - 80px)",
padding: 16,
textAlign: "center",
columnGap: 32,
}}
>
<div style={{ flex: 4, minWidth: "40%", maxWidth: "60%" }}>
<div style={{ margin: 16 }}>预览</div>
<div
style={{
position: "relative",
overflow: "hidden",
maxWidth: "100%",
maxHeight: "calc(100% - 80px)",
height: "calc(100% - 80px)",
}}
>
<FilePreview
src={imageExample.src}
rects={imageExample.rects}
pages={imageExample.pages}
getContainerRef={viewContainerRef}
activeContentId={activeContentId}
/>
</div>
</div>
<div style={{ flex: 6, minWidth: "40%", maxWidth: "60%" }}>
<RadioGroup
style={{ margin: 16 }}
optionStyle={{ flex: 1 }}
type="line"
options={[
{ label: "识别结果", value: "text" },
{ label: "JSON结果", value: "json" },
]}
value={resultTab}
onChange={setResultTab}
/>
{resultTab === "text" && (
<ResultView
style={{
position: "relative",
overflow: "auto",
maxWidth: "100%",
maxHeight: "calc(100% - 80px)",
height: "calc(100% - 80px)",
}}
// resultList={example2.result}
resultList={imageExample.result}
getContainerRef={resultContainerRef}
activeContentId={activeContentId}
activeParentContentId={activeParentContentId}
/>
)}
{resultTab === "json" && (
<JsonView
style={{
padding: "0 16px",
height: "calc(100% - 80px)",
overflow: "auto",
}}
src={imageExample.json}
/>
)}
</div>
</div>
);
}
PDF 示例
import { useLayoutEffect, useRef, useState } from "react";
import FilePreview from "../components/FilePreview";
import { RadioGroup } from "../components/RadioGroup";
import ResultView from "../components/ResultView";
import { pdfExample } from "./data";
import JsonView from "../components/JsonView";
import { useContentLinkage } from "../hooks/useContentLinkage";
export default functionPDFExample() {
const [resultTab, setResultTab] = useState("text");
const viewContainerRef = useRef<HTMLElement | null>(null);
const resultContainerRef = useRef<HTMLElement | null>(null);
const { activeParentContentId, activeContentId, registerLinkage } =
useContentLinkage({
viewContainerRef,
resultContainerRef,
});
useLayoutEffect(() => {
registerLinkage();
}, []);
return (
<div
style={{
display: "flex",
width: "100%",
height: "calc(100vh - 100px)",
padding: 16,
textAlign: "center",
columnGap: 32,
}}
>
<div style={{ flex: 4, minWidth: "40%", maxWidth: "60%" }}>
<div style={{ margin: 16 }}>预览</div>
<div
style={{
position: "relative",
overflow: "hidden",
maxWidth: "100%",
maxHeight: "calc(100% - 80px)",
height: "calc(100% - 80px)",
}}
>
<FilePreview
src={{
url: pdfExample.src,
}}
rects={pdfExample.rects}
pages={pdfExample.pages}
getContainerRef={viewContainerRef}
activeContentId={activeContentId}
/>
</div>
</div>
<div style={{ flex: 6, minWidth: "40%", maxWidth: "60%" }}>
<RadioGroup
style={{ margin: 16 }}
optionStyle={{ flex: 1 }}
type="line"
options={[
{ label: "识别结果", value: "text" },
{ label: "JSON结果", value: "json" },
]}
value={resultTab}
onChange={setResultTab}
/>
{resultTab === "text" && (
<ResultView
style={{
position: "relative",
overflow: "auto",
maxWidth: "100%",
maxHeight: "calc(100% - 80px)",
height: "calc(100% - 80px)",
}}
resultList={pdfExample.result}
getContainerRef={resultContainerRef}
activeContentId={activeContentId}
activeParentContentId={activeParentContentId}
/>
)}
{resultTab === "json" && (
<JsonView
style={{
padding: "0 16px",
height: "calc(100% - 80px)",
overflow: "auto",
}}
src={pdfExample.json}
/>
)}
</div>
</div>
);
}
二次开发
项目基于 vite 和 react 构建,您可将该项目 fork 到本地自主扩展:
拉取项目
git clone https://github.com/intsig-textin/textin-ocr-frontend.git
安装依赖
npm install
启动项目
npm run dev
浏览器访问 http://localhost:5173/
在线预览前端界面:https://cc.co/16YSTY
以上为 TextIn OCR Frontend 开源组件库当前版本介绍。根据规划,组件库将持续迭代,实现:
-
组件支持更多自定义配置、样式覆盖等特性
-
支持可编辑、复制、导出结果
-
支持更多复杂类型识别结果展示