├── .all-contributorsrc ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── lock.yml └── stale.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── android ├── README.md ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── reactlibrary │ ├── TesseractOcrModule.java │ └── TesseractOcrPackage.java ├── commitlint.config.js ├── example ├── .buckconfig ├── .eslintrc.js ├── .flowconfig ├── .gitattributes ├── .gitignore ├── .prettierrc.js ├── .watchmanconfig ├── App.js ├── android │ ├── app │ │ ├── BUCK │ │ ├── build.gradle │ │ ├── build_defs.bzl │ │ ├── debug.keystore │ │ ├── proguard-rules.pro │ │ └── src │ │ │ ├── debug │ │ │ ├── AndroidManifest.xml │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── ReactNativeFlipper.java │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── assets │ │ │ └── tessdata │ │ │ │ └── .gitkeep │ │ │ ├── java │ │ │ └── com │ │ │ │ └── example │ │ │ │ ├── MainActivity.java │ │ │ │ └── MainApplication.java │ │ │ └── res │ │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ └── values │ │ │ ├── strings.xml │ │ │ └── styles.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ └── settings.gradle ├── app.json ├── babel.config.js ├── index.js ├── ios │ ├── Podfile │ ├── example-tvOS │ │ └── Info.plist │ ├── example-tvOSTests │ │ └── Info.plist │ ├── example.xcodeproj │ │ ├── project.pbxproj │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ ├── example-tvOS.xcscheme │ │ │ └── example.xcscheme │ ├── example │ │ ├── AppDelegate.h │ │ ├── AppDelegate.m │ │ ├── Base.lproj │ │ │ └── LaunchScreen.xib │ │ ├── Images.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ ├── Info.plist │ │ └── main.m │ └── exampleTests │ │ ├── Info.plist │ │ └── exampleTests.m ├── metro.config.js ├── package.json ├── showcase.android.camera.gif ├── showcase.android.picker.gif └── yarn.lock ├── index.d.ts ├── index.js ├── ios ├── TesseractOcr.h ├── TesseractOcr.m ├── TesseractOcr.xcodeproj │ └── project.pbxproj └── TesseractOcr.xcworkspace │ └── contents.xcworkspacedata ├── package.json ├── react-native-tesseract-ocr.podspec └── yarn.lock /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | "README.md" 4 | ], 5 | "imageSize": 100, 6 | "commit": false, 7 | "contributors": [ 8 | { 9 | "login": "jonathanpalma", 10 | "name": "Jonathan Palma", 11 | "avatar_url": "https://avatars3.githubusercontent.com/u/12414771?v=4", 12 | "profile": "http://jonathanpalma.me", 13 | "contributions": [ 14 | "code", 15 | "doc", 16 | "example" 17 | ] 18 | }, 19 | { 20 | "login": "jrunestone", 21 | "name": "Johan Runsten", 22 | "avatar_url": "https://avatars3.githubusercontent.com/u/2293001?v=4", 23 | "profile": "https://github.com/jrunestone", 24 | "contributions": [ 25 | "code" 26 | ] 27 | }, 28 | { 29 | "login": "DavitVosk", 30 | "name": "Davit Voskanyan", 31 | "avatar_url": "https://avatars2.githubusercontent.com/u/26307378?v=4", 32 | "profile": "https://github.com/DavitVosk", 33 | "contributions": [ 34 | "ideas" 35 | ] 36 | } 37 | ], 38 | "contributorsPerLine": 7, 39 | "projectName": "react-native-tesseract-ocr", 40 | "projectOwner": "jonathanpalma", 41 | "repoType": "github", 42 | "repoHost": "https://github.com", 43 | "skipCi": true 44 | } 45 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: jonathanpalma 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **Dependencies (please complete the following information):** 14 | - react: [e.g. 16.11.0] 15 | - react-native: [e.g. 0.62.2] 16 | - react-native-tesseract-ocr: [e.g. 2.0.0] 17 | 18 | **To Reproduce** 19 | Steps to reproduce the behavior: 20 | 1. Go to '...' 21 | 2. Click on '....' 22 | 3. Scroll down to '....' 23 | 4. See error 24 | 25 | **Expected behavior** 26 | A clear and concise description of what you expected to happen. 27 | 28 | **Screenshots** 29 | If applicable, add screenshots to help explain your problem. 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | 35 | **Additional context** 36 | Add any other context about the problem here. 37 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/lock.yml: -------------------------------------------------------------------------------- 1 | # Configuration for Lock Threads - https://github.com/dessant/lock-threads-app 2 | 3 | # Number of days of inactivity before a closed issue or pull request is locked 4 | daysUntilLock: 30 5 | 6 | # Skip issues and pull requests created before a given timestamp. Timestamp must 7 | # follow ISO 8601 (`YYYY-MM-DD`). Set to `false` to disable 8 | skipCreatedBefore: false 9 | 10 | # Issues and pull requests with these labels will be ignored. Set to `[]` to disable 11 | exemptLabels: [] 12 | 13 | # Label to add before locking, such as `outdated`. Set to `false` to disable 14 | lockLabel: false 15 | 16 | # Comment to post before locking. Set to `false` to disable 17 | lockComment: > 18 | This thread has been automatically locked since there has not been 19 | any recent activity after it was closed. Please open a new issue for 20 | related bugs. 21 | 22 | # Assign `resolved` as the reason for locking. Set to `false` to disable 23 | setLockReason: true 24 | 25 | # Limit to only `issues` or `pulls` 26 | # only: issues 27 | 28 | # Optionally, specify configuration settings just for `issues` or `pulls` 29 | # issues: 30 | # exemptLabels: 31 | # - help-wanted 32 | # lockLabel: outdated 33 | 34 | # pulls: 35 | # daysUntilLock: 30 36 | 37 | # Repository to extend settings from 38 | # _extends: repo -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 60 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 7 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - pinned 8 | - security 9 | # Label to use when marking an issue as stale 10 | staleLabel: wontfix 11 | # Comment to post when marking an issue as stale. Set to `false` to disable 12 | markComment: > 13 | This issue has been automatically marked as stale because it has not had 14 | recent activity. It will be closed if no further activity occurs. Thank you 15 | for your contributions. 16 | # Comment to post when closing a stale issue. Set to `false` to disable 17 | closeComment: false -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # node.js 6 | # 7 | node_modules/ 8 | npm-debug.log 9 | yarn-error.log 10 | 11 | # Xcode 12 | # 13 | build/ 14 | *.pbxuser 15 | !default.pbxuser 16 | *.mode1v3 17 | !default.mode1v3 18 | *.mode2v3 19 | !default.mode2v3 20 | *.perspectivev3 21 | !default.perspectivev3 22 | xcuserdata 23 | *.xccheckout 24 | *.moved-aside 25 | DerivedData 26 | *.hmap 27 | *.ipa 28 | *.xcuserstate 29 | project.xcworkspace 30 | 31 | # Android/IntelliJ 32 | # 33 | build/ 34 | .idea 35 | .gradle 36 | local.properties 37 | *.iml 38 | .classpath 39 | .project 40 | .settings/ 41 | 42 | # Module's android/gradle 43 | android/gradle* 44 | 45 | # BUCK 46 | buck-out/ 47 | \.buckd/ 48 | *.keystore 49 | 50 | # Tesseract 51 | *.traineddata -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | ### [2.0.3](https://github.com/jonathanpalma/react-native-tesseract-ocr/compare/v2.0.1...v2.0.3) (2021-06-25) 6 | 7 | 8 | ### Features 9 | 10 | * update event listener api ([cd23498](https://github.com/jonathanpalma/react-native-tesseract-ocr/commit/cd2349857e524cec4a81b64882010daf493a92f5)) 11 | 12 | ### [2.0.1](https://github.com/jonathanpalma/react-native-tesseract-ocr/compare/v2.0.0...v2.0.1) (2020-08-10) 13 | 14 | 15 | ### Bug Fixes 16 | 17 | * remove npm dependency link ([8e7a539](https://github.com/jonathanpalma/react-native-tesseract-ocr/commit/8e7a53990c52c0c983c3cce5d2d8b45e0395b5ab)), closes [#85](https://github.com/jonathanpalma/react-native-tesseract-ocr/issues/85) 18 | 19 | ## 2.0.0 (2020-07-15) 20 | 21 | 22 | ### Features 23 | 24 | * add ts declaration file ([6e3a2b6](https://github.com/jonathanpalma/react-native-tesseract-ocr/commit/6e3a2b679e861ec5b55a8e9a6f077e0a9edd3dff)) 25 | * **android:** add allow/deny list support ([7fb8d91](https://github.com/jonathanpalma/react-native-tesseract-ocr/commit/7fb8d919c3b80aaddb8fde24d4d42597c4cc06a8)) 26 | * **android:** add recognition progress listener ([4442aab](https://github.com/jonathanpalma/react-native-tesseract-ocr/commit/4442aabbd8570a638aa08f68d5e1faa157a7c06d)) 27 | * **android:** add tesseract support [UNSAFE] ([efa152c](https://github.com/jonathanpalma/react-native-tesseract-ocr/commit/efa152cc9811ac275beefc3dc3f3a4a48f2c95b1)) 28 | * **example:** add fully working example ([c66018e](https://github.com/jonathanpalma/react-native-tesseract-ocr/commit/c66018e72ef784852c55c98b70e5d03fd92c1513)) 29 | 30 | 31 | ### Bug Fixes 32 | 33 | * add missing example files ([00d43b4](https://github.com/jonathanpalma/react-native-tesseract-ocr/commit/00d43b411cd7ef33a6e524f05b334145b5b6d2f2)) 34 | * fix callback invocation ([3173338](https://github.com/jonathanpalma/react-native-tesseract-ocr/commit/3173338126f0d8e51cc2261b8510a8c2d4bff53e)) 35 | * fix type definition ([10e0b3c](https://github.com/jonathanpalma/react-native-tesseract-ocr/commit/10e0b3ca6632775e365b9b239ef50d0d6c414b27)) 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Jonathan Palma 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |

react-native-tesseract-ocr 👀

3 |

4 | react-native-tesseract-ocr is a react-native wrapper for Tesseract OCR 5 |

6 |
7 | 8 | [![Version][version-badge]][package] 9 | [![Install Size][size-badge]][package-size] 10 | [![Downloads][downloads-badge]][npmcharts] 11 | [![PRs Welcome][prs-badge]][prs] 12 | [![Commitizen friendly][cz-badge]][cz] 13 | [![MIT License][license-badge]][license] 14 | 15 | [![All Contributors](https://img.shields.io/badge/all_contributors-3-orange.svg?style=flat-square)](#contributors-) 16 | 17 | 18 | [![Watch on GitHub][github-watch-badge]][github-watch] 19 | [![Star on GitHub][github-star-badge]][github-star] 20 | [![Tweet][twitter-badge]][twitter] 21 | 22 | This project uses: 23 | 24 | - [tess-two][url-tess-and] for Android 25 | - [Tesseract-OCR-iOS][url-tess-ios] for iOS ⚠️ (This has NOT been implemented yet) ⚠️ 26 | 27 | NOTE: It is recommended to use react-native >= 0.60.0 28 | 29 | ## Getting started 30 | 31 | `$ npm i react-native-tesseract-ocr --save` 32 | 33 | ### Mostly automatic installation 34 | 35 | `$ react-native link react-native-tesseract-ocr` 36 | 37 | 38 | ## Example 39 | 40 | 41 |
42 | Showcase Android using Picker 43 | Showcase Android using Camera 44 |
45 | 46 | Check the example by yourself [here][url-example] 47 | 48 | ## Usage 49 | 50 | ### tessOptions 51 | 52 | | Property | Type | Description | 53 | | --------- | -------- | -------------------------------------------------------------------------- | 54 | | allowlist | `string` | List of characters you want to recognize | 55 | | denylist | `string` | List of characters you DON'T want to recognize | 56 | | level | `Level` | Level of the tokens of the page hierarchy (only used in `recognizeTokens`) | 57 | 58 | _`Level` can be one of the following values 'symbol' | 'block' | 'line' | 'paragraph' | 'word'_ 59 | 60 | ### recognize 61 | 62 | ```typescript 63 | import TesseractOcr, { LANG_ENGLISH } from 'react-native-tesseract-ocr'; 64 | 65 | const tessOptions = {}; 66 | TesseractOcr.recognize(imageSource, LANG_ENGLISH, tessOptions); 67 | ``` 68 | 69 | ### recognizeTokens 70 | 71 | ```typescript 72 | import TesseractOcr, { LANG_ENGLISH, LEVEL_WORD } from 'react-native-tesseract-ocr'; 73 | 74 | const tessOptions = { level: LEVEL_WORD }; 75 | TesseractOcr.recognizeTokens(imageSource, LANG_ENGLISH, tessOptions); 76 | ``` 77 | 78 | 79 | ### useEventListener 80 | 81 | ```typescript 82 | import React, { useState } from 'react'; 83 | import { useEventListener } from 'react-native-tesseract-ocr'; 84 | 85 | function App() { 86 | const [progress, setProgress] = useState(0); 87 | useEventListener('onProgressChange', (p) => { 88 | setProgress(p.percent / 100); 89 | }); 90 | 91 | // return ... 92 | } 93 | ``` 94 | 95 | 96 | 97 | ## Contributing 98 | 99 | ### How to contribute? 100 | 101 | This is a `commitizen friendly` repository, so instead of creating commits using `git commit`, please use our custom CLI by running: 102 | 103 | `$ npm run cz` 104 | 105 | ## Contributors ✨ 106 | 107 | Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 |

Jonathan Palma

💻 📖 💡

Johan Runsten

💻
118 | 119 | This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! 120 | 121 | ## License 122 | 123 | MIT © [jonathanpalma](https://github.com/jonathanpalma) 124 | 125 | This library wouldn't be possible without these amazing projects: 126 | 127 | - [Tesseract OCR][url-tesseract] - [Apache 2.0 license][url-tesseract-lsc] 128 | - [tess-two][url-tess-and] - [Apache 2.0 license][url-tess-and-lsc] 129 | 130 | 131 | [downloads-badge]: https://img.shields.io/npm/dm/react-native-tesseract-ocr.svg?style=flat-square 132 | [license-badge]: https://img.shields.io/npm/l/react-native-tesseract-ocr.svg?style=flat-square 133 | [license]: https://github.com/jonathanpalma/react-native-tesseract-ocr/blob/master/LICENSE 134 | [npmcharts]: http://npmcharts.com/compare/react-native-tesseract-ocr 135 | [package-size]: https://packagephobia.now.sh/result?p=react-native-tesseract-ocr 136 | [package]: https://www.npmjs.com/package/react-native-tesseract-ocr 137 | [prs-badge]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square 138 | [prs]: http://makeapullrequest.com 139 | [cz-badge]: https://img.shields.io/badge/commitizen-friendly-brightgreen.svg?style=flat-square 140 | [cz]: http://commitizen.github.io/cz-cli/ 141 | [size-badge]: https://flat.badgen.net/packagephobia/install/react-native-tesseract-ocr 142 | [version-badge]: https://img.shields.io/npm/v/react-native-tesseract-ocr.svg?style=flat-square 143 | [github-watch-badge]: https://img.shields.io/github/watchers/jonathanpalma/react-native-tesseract-ocr.svg?style=social 144 | [github-watch]: https://github.com/jonathanpalma/react-native-tesseract-ocr/watchers 145 | [github-star-badge]: https://img.shields.io/github/stars/jonathanpalma/react-native-tesseract-ocr.svg?style=social 146 | [github-star]: https://github.com/jonathanpalma/react-native-tesseract-ocr/stargazers 147 | [url-example]: https://github.com/jonathanpalma/react-native-tesseract-ocr/tree/master/example 148 | [url-eslint]: https://eslint.org/ 149 | [url-prettier]: https://prettier.io/ 150 | [url-tesseract]: https://github.com/tesseract-ocr/tesseract 151 | [url-tesseract-lsc]: https://github.com/tesseract-ocr/tesseract/blob/master/LICENSE 152 | [url-tess-and]: https://github.com/rmtheis/tess-two 153 | [url-tess-and-lsc]: https://github.com/rmtheis/tess-two/blob/master/COPYING 154 | [url-tess-ios]: https://github.com/gali8/Tesseract-OCR-iOS 155 | [url-tess-ios-lsc]: https://github.com/gali8/Tesseract-OCR-iOS/blob/master/LICENSE.md 156 | [twitter]: https://twitter.com/intent/tweet?text=Check%20out%20react-native-tesseract-ocr!%20https://github.com/jonathanpalma/react-native-tesseract-ocr 157 | [twitter-badge]: https://img.shields.io/twitter/url/https/github.com/jonathanpalma/react-native-tesseract-ocr.svg?style=social 158 | -------------------------------------------------------------------------------- /android/README.md: -------------------------------------------------------------------------------- 1 | README 2 | ====== 3 | 4 | If you want to publish the lib as a maven dependency, follow these steps before publishing a new version to npm: 5 | 6 | 1. Be sure to have the Android [SDK](https://developer.android.com/studio/index.html) and [NDK](https://developer.android.com/ndk/guides/index.html) installed 7 | 2. Be sure to have a `local.properties` file in this folder that points to the Android SDK and NDK 8 | ``` 9 | ndk.dir=/Users/{username}/Library/Android/sdk/ndk-bundle 10 | sdk.dir=/Users/{username}/Library/Android/sdk 11 | ``` 12 | 3. Delete the `maven` folder 13 | 4. Run `./gradlew installArchives` 14 | 5. Verify that latest set of generated files is in the maven folder with the correct version number 15 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | // android/build.gradle 2 | 3 | // based on: 4 | // 5 | // * https://github.com/facebook/react-native/blob/0.60-stable/template/android/build.gradle 6 | // original location: 7 | // - https://github.com/facebook/react-native/blob/0.58-stable/local-cli/templates/HelloWorld/android/build.gradle 8 | // 9 | // * https://github.com/facebook/react-native/blob/0.60-stable/template/android/app/build.gradle 10 | // original location: 11 | // - https://github.com/facebook/react-native/blob/0.58-stable/local-cli/templates/HelloWorld/android/app/build.gradle 12 | 13 | def DEFAULT_COMPILE_SDK_VERSION = 28 14 | def DEFAULT_BUILD_TOOLS_VERSION = '28.0.3' 15 | def DEFAULT_MIN_SDK_VERSION = 16 16 | def DEFAULT_TARGET_SDK_VERSION = 28 17 | 18 | def safeExtGet(prop, fallback) { 19 | rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback 20 | } 21 | 22 | apply plugin: 'com.android.library' 23 | apply plugin: 'maven' 24 | 25 | buildscript { 26 | // The Android Gradle plugin is only required when opening the android folder stand-alone. 27 | // This avoids unnecessary downloads and potential conflicts when the library is included as a 28 | // module dependency in an application project. 29 | // ref: https://docs.gradle.org/current/userguide/tutorial_using_tasks.html#sec:build_script_external_dependencies 30 | if (project == rootProject) { 31 | repositories { 32 | google() 33 | jcenter() 34 | } 35 | dependencies { 36 | classpath 'com.android.tools.build:gradle:3.4.1' 37 | } 38 | } 39 | } 40 | 41 | apply plugin: 'com.android.library' 42 | apply plugin: 'maven' 43 | 44 | android { 45 | compileSdkVersion safeExtGet('compileSdkVersion', DEFAULT_COMPILE_SDK_VERSION) 46 | buildToolsVersion safeExtGet('buildToolsVersion', DEFAULT_BUILD_TOOLS_VERSION) 47 | defaultConfig { 48 | minSdkVersion safeExtGet('minSdkVersion', DEFAULT_MIN_SDK_VERSION) 49 | targetSdkVersion safeExtGet('targetSdkVersion', DEFAULT_TARGET_SDK_VERSION) 50 | versionCode 1 51 | versionName "1.0" 52 | } 53 | lintOptions { 54 | abortOnError false 55 | } 56 | } 57 | 58 | repositories { 59 | // ref: https://www.baeldung.com/maven-local-repository 60 | mavenLocal() 61 | maven { 62 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm 63 | url "$rootDir/../node_modules/react-native/android" 64 | } 65 | maven { 66 | // Android JSC is installed from npm 67 | url "$rootDir/../node_modules/jsc-android/dist" 68 | } 69 | google() 70 | jcenter() 71 | } 72 | 73 | dependencies { 74 | //noinspection GradleDynamicVersion 75 | implementation 'com.facebook.react:react-native:+' // From node_modules 76 | implementation 'com.rmtheis:tess-two:9.1.0' 77 | } 78 | 79 | def configureReactNativePom(def pom) { 80 | def packageJson = new groovy.json.JsonSlurper().parseText(file('../package.json').text) 81 | 82 | pom.project { 83 | name packageJson.title 84 | artifactId packageJson.name 85 | version = packageJson.version 86 | group = "com.reactlibrary" 87 | description packageJson.description 88 | url packageJson.repository.baseUrl 89 | 90 | licenses { 91 | license { 92 | name packageJson.license 93 | url packageJson.repository.baseUrl + '/blob/master/' + packageJson.licenseFilename 94 | distribution 'repo' 95 | } 96 | } 97 | 98 | developers { 99 | developer { 100 | id packageJson.author.username 101 | name packageJson.author.name 102 | } 103 | } 104 | } 105 | } 106 | 107 | afterEvaluate { project -> 108 | // some Gradle build hooks ref: 109 | // https://www.oreilly.com/library/view/gradle-beyond-the/9781449373801/ch03.html 110 | task androidJavadoc(type: Javadoc) { 111 | source = android.sourceSets.main.java.srcDirs 112 | classpath += files(android.bootClasspath) 113 | classpath += files(project.getConfigurations().getByName('compile').asList()) 114 | include '**/*.java' 115 | } 116 | 117 | task androidJavadocJar(type: Jar, dependsOn: androidJavadoc) { 118 | classifier = 'javadoc' 119 | from androidJavadoc.destinationDir 120 | } 121 | 122 | task androidSourcesJar(type: Jar) { 123 | classifier = 'sources' 124 | from android.sourceSets.main.java.srcDirs 125 | include '**/*.java' 126 | } 127 | 128 | android.libraryVariants.all { variant -> 129 | def name = variant.name.capitalize() 130 | def javaCompileTask = variant.javaCompileProvider.get() 131 | 132 | task "jar${name}"(type: Jar, dependsOn: javaCompileTask) { 133 | from javaCompileTask.destinationDir 134 | } 135 | } 136 | 137 | artifacts { 138 | archives androidSourcesJar 139 | archives androidJavadocJar 140 | } 141 | 142 | task installArchives(type: Upload) { 143 | configuration = configurations.archives 144 | repositories.mavenDeployer { 145 | // Deploy to react-native-event-bridge/maven, ready to publish to npm 146 | repository url: "file://${projectDir}/../android/maven" 147 | configureReactNativePom pom 148 | } 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /android/src/main/java/com/reactlibrary/TesseractOcrModule.java: -------------------------------------------------------------------------------- 1 | package com.reactlibrary; 2 | 3 | import android.graphics.Bitmap; 4 | import android.graphics.BitmapFactory; 5 | import android.graphics.Rect; 6 | import android.os.Environment; 7 | import android.util.Log; 8 | 9 | import androidx.annotation.Nullable; 10 | 11 | import com.facebook.react.bridge.Arguments; 12 | import com.facebook.react.bridge.Promise; 13 | import com.facebook.react.bridge.ReactApplicationContext; 14 | import com.facebook.react.bridge.ReactContextBaseJavaModule; 15 | import com.facebook.react.bridge.ReactMethod; 16 | import com.facebook.react.bridge.ReadableMap; 17 | import com.facebook.react.bridge.WritableArray; 18 | import com.facebook.react.bridge.WritableMap; 19 | import com.facebook.react.modules.core.DeviceEventManagerModule; 20 | import com.googlecode.tesseract.android.ResultIterator; 21 | import com.googlecode.tesseract.android.TessBaseAPI; 22 | 23 | import java.io.File; 24 | import java.io.FileOutputStream; 25 | import java.io.IOException; 26 | import java.io.InputStream; 27 | import java.io.OutputStream; 28 | 29 | public class TesseractOcrModule extends ReactContextBaseJavaModule { 30 | 31 | private final ReactApplicationContext reactContext; 32 | private static final String KEY_ALLOW_LIST = "allowlist"; 33 | private static final String KEY_DENY_LIST = "denylist"; 34 | private static final String KEY_TOKEN_LEVEL = "level"; 35 | private static final String TESS_FILES_DIRECTORY = "tessdata"; 36 | private static final String TESS_FILES_EXTENSION = ".traineddata"; 37 | private static String DATA_PATH = Environment.getExternalStorageDirectory().toString(); 38 | private static String TESS_FILES_PATH; 39 | private TessBaseAPI tesseract; 40 | 41 | public TesseractOcrModule(ReactApplicationContext reactContext) { 42 | super(reactContext); 43 | this.reactContext = reactContext; 44 | if (!this.DATA_PATH.contains(reactContext.getPackageName())) { 45 | this.DATA_PATH += File.separator + reactContext.getPackageName(); 46 | } 47 | this.TESS_FILES_PATH = this.DATA_PATH + File.separator + this.TESS_FILES_DIRECTORY; 48 | } 49 | 50 | @Override 51 | public String getName() { 52 | return "TesseractOcr"; 53 | } 54 | 55 | @ReactMethod 56 | public void stop(Promise promise) { 57 | Log.d(getName(), "stop"); 58 | 59 | try { 60 | tesseract.stop(); 61 | tesseract.end(); 62 | promise.resolve("Recognition cancelled successfully"); 63 | } catch (Exception e) { 64 | Log.e(getName(), "Could not stop recognition. " + e.toString(), e); 65 | promise.reject("Could not stop recognition", e.toString()); 66 | } 67 | } 68 | 69 | @ReactMethod 70 | public void recognize(String imageSource, final String lang, @Nullable final ReadableMap tessOptions, final Promise promise) { 71 | Log.d(getName(), "recognize"); 72 | 73 | try { 74 | if (shouldCopyTrainedFile(lang)) { 75 | prepareTrainedFilesDirectory(); 76 | copyTrainedFile(lang); 77 | } 78 | 79 | final Bitmap bitmap = getBitmap(imageSource); 80 | 81 | if (bitmap != null) { 82 | new Thread() { 83 | @Override 84 | public void run() { 85 | tesseract = createTesseractAPI(lang, tessOptions); 86 | tesseract.setImage(bitmap); 87 | tesseract.getHOCRText(0); 88 | 89 | String recognizedText = tesseract.getUTF8Text(); 90 | 91 | tesseract.end(); 92 | promise.resolve(recognizedText); 93 | } 94 | 95 | }.start(); 96 | } else { 97 | throw new IOException("Could not decode a file path into a bitmap."); 98 | } 99 | } catch (IOException e) { 100 | Log.e(getName(), "Could not access trained files. " + e.toString(), e); 101 | promise.reject("Could not access trained files", e.toString()); 102 | } catch (Exception e) { 103 | Log.e(getName(), "Could not recognize text. " + e.toString(), e); 104 | promise.reject("Could not recognize text", e.toString()); 105 | } 106 | } 107 | 108 | @ReactMethod 109 | public void recognizeTokens(String imageSource, final String lang, @Nullable final ReadableMap tessOptions, final Promise promise) { 110 | Log.d(getName(), "recognizeTokens"); 111 | 112 | try { 113 | if (shouldCopyTrainedFile(lang)) { 114 | prepareTrainedFilesDirectory(); 115 | copyTrainedFile(lang); 116 | } 117 | 118 | final int iteratorLevel = getIteratorLevel(tessOptions != null ? tessOptions.getString(KEY_TOKEN_LEVEL) : "word"); 119 | final Bitmap bitmap = getBitmap(imageSource); 120 | 121 | if (bitmap != null) { 122 | new Thread() { 123 | @Override 124 | public void run() { 125 | tesseract = createTesseractAPI(lang, tessOptions); 126 | tesseract.setImage(bitmap); 127 | tesseract.getHOCRText(0); 128 | 129 | WritableArray tokens = Arguments.createArray(); 130 | WritableMap tempMap; 131 | WritableMap bounding; 132 | 133 | ResultIterator iterator = tesseract.getResultIterator(); 134 | iterator.begin(); 135 | 136 | do { 137 | bounding = Arguments.createMap(); 138 | tempMap = Arguments.createMap(); 139 | Rect rect = iterator.getBoundingRect(iteratorLevel); 140 | 141 | bounding.putInt("bottom", rect.bottom); 142 | bounding.putInt("left", rect.left); 143 | bounding.putInt("right", rect.right); 144 | bounding.putInt("top", rect.top); 145 | 146 | tempMap.putString("token", iterator.getUTF8Text(iteratorLevel)); 147 | tempMap.putDouble("confidence", iterator.confidence(iteratorLevel)); 148 | tempMap.putMap("bounding", bounding); 149 | tokens.pushMap(tempMap); 150 | } while (iterator.next(iteratorLevel)); 151 | 152 | iterator.delete(); 153 | tesseract.end(); 154 | promise.resolve(tokens); 155 | } 156 | 157 | }.start(); 158 | } else { 159 | throw new IOException("Could not decode a file path into a bitmap."); 160 | } 161 | } catch (IOException e) { 162 | Log.e(getName(), "Could not access trained files. " + e.toString(), e); 163 | promise.reject("Could not access trained files", e.toString()); 164 | } catch (Exception e) { 165 | Log.e(getName(), "Could not recognize text. " + e.toString(), e); 166 | promise.reject("Could not recognize text", e.toString()); 167 | } 168 | 169 | } 170 | 171 | private TessBaseAPI createTesseractAPI(String lang, @Nullable final ReadableMap tessOptions) { 172 | TessBaseAPI tessBaseAPI = new TessBaseAPI(createProgressNotifier()); 173 | tessBaseAPI.init(DATA_PATH + File.separator, lang); 174 | 175 | if (tessOptions != null) { 176 | 177 | // Allow List - List of characters you want to detect 178 | if (tessOptions.hasKey(KEY_ALLOW_LIST) && tessOptions.getString(KEY_ALLOW_LIST) != null 179 | && !tessOptions.getString(KEY_ALLOW_LIST).isEmpty()) { 180 | Log.d(getName(), KEY_ALLOW_LIST + " " + tessOptions.getString(KEY_ALLOW_LIST)); 181 | tessBaseAPI.setVariable(TessBaseAPI.VAR_CHAR_WHITELIST, tessOptions.getString(KEY_ALLOW_LIST)); 182 | } 183 | 184 | // Deny List - List of characters you DON'T want to detect 185 | if (tessOptions.hasKey(KEY_DENY_LIST) && tessOptions.getString(KEY_DENY_LIST) != null 186 | && !tessOptions.getString(KEY_DENY_LIST).isEmpty()) { 187 | Log.d(getName(), KEY_DENY_LIST + " " + tessOptions.getString(KEY_DENY_LIST)); 188 | tessBaseAPI.setVariable(TessBaseAPI.VAR_CHAR_BLACKLIST, tessOptions.getString(KEY_DENY_LIST)); 189 | } 190 | } 191 | 192 | return tessBaseAPI; 193 | } 194 | 195 | private TessBaseAPI.ProgressNotifier createProgressNotifier() { 196 | return new TessBaseAPI.ProgressNotifier() { 197 | @Override 198 | public void onProgressValues(TessBaseAPI.ProgressValues progressValues) { 199 | Log.d(getName(), "progress " + String.valueOf(progressValues.getPercent())); 200 | onProgress(progressValues.getPercent()); 201 | } 202 | }; 203 | } 204 | 205 | private void onProgress(int percent) { 206 | Log.d(getName(), "onProgressChange " + Integer.toString(percent)); 207 | WritableMap payload = Arguments.createMap(); 208 | payload.putInt("percent", percent); 209 | this.reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit("onProgressChange", payload); 210 | } 211 | 212 | private int getIteratorLevel(String level) { 213 | switch (level) { 214 | case "block": 215 | return TessBaseAPI.PageIteratorLevel.RIL_BLOCK; 216 | case "paragraph": 217 | return TessBaseAPI.PageIteratorLevel.RIL_PARA; 218 | case "symbol": 219 | return TessBaseAPI.PageIteratorLevel.RIL_SYMBOL; 220 | case "line": 221 | return TessBaseAPI.PageIteratorLevel.RIL_TEXTLINE; 222 | default: // word 223 | return TessBaseAPI.PageIteratorLevel.RIL_WORD; 224 | } 225 | } 226 | 227 | private Bitmap getBitmap(String imageSource) throws Exception { 228 | String path = imageSource.startsWith("file://") ? imageSource.replace("file://", "") : imageSource; 229 | 230 | if (path.startsWith("http://") || path.startsWith("https://")) { 231 | // TODO: support remote files 232 | throw new Exception("Cannot select remote files"); 233 | } 234 | 235 | return BitmapFactory.decodeFile(path, new BitmapFactory.Options()); 236 | } 237 | 238 | private boolean shouldCopyTrainedFile(String lang) { 239 | Log.d(getName(), "should copy " + lang + " trained files?"); 240 | String filePath = TESS_FILES_PATH + File.separator + lang + TESS_FILES_EXTENSION; 241 | File file = new File(filePath); 242 | return !file.exists(); 243 | } 244 | 245 | private void prepareTrainedFilesDirectory() throws IOException { 246 | Log.d(getName(), "prepare trained files directory"); 247 | File dir = new File(TESS_FILES_PATH); 248 | if (!dir.exists()) { 249 | if (!dir.mkdirs()) { 250 | Log.e(getName(), "Could not create directory, please make sure the app has write permission"); 251 | throw new IOException("Could not create directory"); 252 | } 253 | } 254 | } 255 | 256 | private void copyTrainedFile(String lang) throws IOException { 257 | Log.d(getName(), "copy tesseract data file (" + lang + ")"); 258 | String assetPath = TESS_FILES_DIRECTORY + File.separator + lang + TESS_FILES_EXTENSION; 259 | String newAssetPath = DATA_PATH + File.separator + assetPath; 260 | copyAsset(assetPath, newAssetPath); 261 | } 262 | 263 | private void copyAsset(String from, String to) throws IOException { 264 | Log.d(getName(), "copy asset " + from + " to " + to); 265 | 266 | InputStream in = reactContext.getAssets().open(from); 267 | OutputStream out = new FileOutputStream(to); 268 | byte[] buf = new byte[1024]; 269 | int len; 270 | 271 | while ((len = in.read(buf)) > 0) { 272 | out.write(buf, 0, len); 273 | } 274 | in.close(); 275 | out.close(); 276 | } 277 | } 278 | -------------------------------------------------------------------------------- /android/src/main/java/com/reactlibrary/TesseractOcrPackage.java: -------------------------------------------------------------------------------- 1 | package com.reactlibrary; 2 | 3 | import java.util.Arrays; 4 | import java.util.Collections; 5 | import java.util.List; 6 | 7 | import com.facebook.react.ReactPackage; 8 | import com.facebook.react.bridge.NativeModule; 9 | import com.facebook.react.bridge.ReactApplicationContext; 10 | import com.facebook.react.uimanager.ViewManager; 11 | import com.facebook.react.bridge.JavaScriptModule; 12 | 13 | public class TesseractOcrPackage implements ReactPackage { 14 | @Override 15 | public List createNativeModules(ReactApplicationContext reactContext) { 16 | return Arrays.asList(new TesseractOcrModule(reactContext)); 17 | } 18 | 19 | // Deprecated from RN 0.47 20 | public List> createJSModules() { 21 | return Collections.emptyList(); 22 | } 23 | 24 | @Override 25 | public List createViewManagers(ReactApplicationContext reactContext) { 26 | return Collections.emptyList(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ["@commitlint/config-conventional"], 3 | }; 4 | -------------------------------------------------------------------------------- /example/.buckconfig: -------------------------------------------------------------------------------- 1 | 2 | [android] 3 | target = Google Inc.:Google APIs:23 4 | 5 | [maven_repositories] 6 | central = https://repo1.maven.org/maven2 7 | -------------------------------------------------------------------------------- /example/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: '@react-native-community', 4 | }; 5 | -------------------------------------------------------------------------------- /example/.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | ; We fork some components by platform 3 | .*/*[.]android.js 4 | 5 | ; Ignore "BUCK" generated dirs 6 | /\.buckd/ 7 | 8 | ; Ignore polyfills 9 | node_modules/react-native/Libraries/polyfills/.* 10 | 11 | ; These should not be required directly 12 | ; require from fbjs/lib instead: require('fbjs/lib/warning') 13 | node_modules/warning/.* 14 | 15 | ; Flow doesn't support platforms 16 | .*/Libraries/Utilities/LoadingView.js 17 | 18 | [untyped] 19 | .*/node_modules/@react-native-community/cli/.*/.* 20 | 21 | [include] 22 | 23 | [libs] 24 | node_modules/react-native/interface.js 25 | node_modules/react-native/flow/ 26 | 27 | [options] 28 | emoji=true 29 | 30 | esproposal.optional_chaining=enable 31 | esproposal.nullish_coalescing=enable 32 | 33 | module.file_ext=.js 34 | module.file_ext=.json 35 | module.file_ext=.ios.js 36 | 37 | munge_underscores=true 38 | 39 | module.name_mapper='^react-native/\(.*\)$' -> '/node_modules/react-native/\1' 40 | module.name_mapper='^@?[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> '/node_modules/react-native/Libraries/Image/RelativeImageStub' 41 | 42 | suppress_type=$FlowIssue 43 | suppress_type=$FlowFixMe 44 | suppress_type=$FlowFixMeProps 45 | suppress_type=$FlowFixMeState 46 | 47 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(\\)? *\\(site=[a-z,_]*react_native\\(_ios\\)?_\\(oss\\|fb\\)[a-z,_]*\\)?)\\) 48 | suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(\\)? *\\(site=[a-z,_]*react_native\\(_ios\\)?_\\(oss\\|fb\\)[a-z,_]*\\)?)\\)?:? #[0-9]+ 49 | suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError 50 | 51 | [lints] 52 | sketchy-null-number=warn 53 | sketchy-null-mixed=warn 54 | sketchy-number=warn 55 | untyped-type-import=warn 56 | nonstrict-import=warn 57 | deprecated-type=warn 58 | unsafe-getters-setters=warn 59 | inexact-spread=warn 60 | unnecessary-invariant=warn 61 | signature-verification-failure=warn 62 | deprecated-utility=error 63 | 64 | [strict] 65 | deprecated-type 66 | nonstrict-import 67 | sketchy-null 68 | unclear-type 69 | unsafe-getters-setters 70 | untyped-import 71 | untyped-type-import 72 | 73 | [version] 74 | ^0.113.0 75 | -------------------------------------------------------------------------------- /example/.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj -text 2 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | 24 | # Android/IntelliJ 25 | # 26 | build/ 27 | .idea 28 | .gradle 29 | local.properties 30 | *.iml 31 | 32 | # node.js 33 | # 34 | node_modules/ 35 | npm-debug.log 36 | yarn-error.log 37 | 38 | # BUCK 39 | buck-out/ 40 | \.buckd/ 41 | *.keystore 42 | !debug.keystore 43 | 44 | # fastlane 45 | # 46 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 47 | # screenshots whenever they are needed. 48 | # For more information about the recommended setup visit: 49 | # https://docs.fastlane.tools/best-practices/source-control/ 50 | 51 | */fastlane/report.xml 52 | */fastlane/Preview.html 53 | */fastlane/screenshots 54 | 55 | # Bundle artifact 56 | *.jsbundle 57 | 58 | # CocoaPods 59 | /ios/Pods/ 60 | -------------------------------------------------------------------------------- /example/.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | bracketSpacing: false, 3 | jsxBracketSameLine: true, 4 | singleQuote: true, 5 | trailingComma: 'all', 6 | }; 7 | -------------------------------------------------------------------------------- /example/.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /example/App.js: -------------------------------------------------------------------------------- 1 | import React, {useState} from 'react'; 2 | import {Button, StyleSheet, Text, View, Image} from 'react-native'; 3 | import ImagePicker from 'react-native-image-crop-picker'; 4 | import ProgressCircle from 'react-native-progress/Circle'; 5 | import TesseractOcr, { 6 | LANG_ENGLISH, 7 | useEventListener, 8 | } from 'react-native-tesseract-ocr'; 9 | 10 | const DEFAULT_HEIGHT = 500; 11 | const DEFAULT_WITH = 600; 12 | const defaultPickerOptions = { 13 | cropping: true, 14 | height: DEFAULT_HEIGHT, 15 | width: DEFAULT_WITH, 16 | }; 17 | 18 | function App() { 19 | const [isLoading, setIsLoading] = useState(false); 20 | const [progress, setProgress] = useState(0); 21 | const [imgSrc, setImgSrc] = useState(null); 22 | const [text, setText] = useState(''); 23 | useEventListener('onProgressChange', (p) => { 24 | setProgress(p.percent / 100); 25 | }); 26 | 27 | const recognizeTextFromImage = async (path) => { 28 | setIsLoading(true); 29 | 30 | try { 31 | const tesseractOptions = {}; 32 | const recognizedText = await TesseractOcr.recognize( 33 | path, 34 | LANG_ENGLISH, 35 | tesseractOptions, 36 | ); 37 | setText(recognizedText); 38 | } catch (err) { 39 | console.error(err); 40 | setText(''); 41 | } 42 | 43 | setIsLoading(false); 44 | setProgress(0); 45 | }; 46 | 47 | const recognizeFromPicker = async (options = defaultPickerOptions) => { 48 | try { 49 | const image = await ImagePicker.openPicker(options); 50 | setImgSrc({uri: image.path}); 51 | await recognizeTextFromImage(image.path); 52 | } catch (err) { 53 | if (err.message !== 'User cancelled image selection') { 54 | console.error(err); 55 | } 56 | } 57 | }; 58 | 59 | const recognizeFromCamera = async (options = defaultPickerOptions) => { 60 | try { 61 | const image = await ImagePicker.openCamera(options); 62 | setImgSrc({uri: image.path}); 63 | await recognizeTextFromImage(image.path); 64 | } catch (err) { 65 | if (err.message !== 'User cancelled image selection') { 66 | console.error(err); 67 | } 68 | } 69 | }; 70 | 71 | return ( 72 | 73 | Tesseract OCR example 74 | Select an image source: 75 | 76 | 77 |