TextIn OCR Frontend前端开源组件库发布!

发布于:2025-05-29 ⋅ 阅读:(20) ⋅ 点赞:(0)

为什么开源 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 开源组件库当前版本介绍。根据规划,组件库将持续迭代,实现:

  • 组件支持更多自定义配置、样式覆盖等特性

  • 支持可编辑、复制、导出结果

  • 支持更多复杂类型识别结果展示

在线体验https://cc.co/16YSTY


网站公告

今日签到

点亮在社区的每一天
去签到