diff --git a/README.md b/README.md
index 06cc87d..cb6c766 100644
--- a/README.md
+++ b/README.md
@@ -20,4 +20,4 @@ Expense manager
along with this program. If not, see .
```
-The base app includes the [tessdata_best](https://github.com/tesseract-ocr/tessdata_best) English trained data, ©️ [tessdata_best / Tesseract contributors](https://github.com/tesseract-ocr/tessdata_best/graphs/contributors), used under the [Apache 2.0 license](https://github.com/tesseract-ocr/tessdata_best/blob/main/LICENSE)
+The base app includes the [tessdata_fast](https://github.com/tesseract-ocr/tessdata_fast) English trained data, ©️ [tessdata_fast / Tesseract contributors](https://github.com/tesseract-ocr/tessdata_fast/graphs/contributors), used under the [Apache 2.0 license](https://github.com/tesseract-ocr/tessdata_fast/blob/main/LICENSE)
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 80a5e2f..23e2c46 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -1,4 +1,6 @@
+
+
> getAvailableData() async {
var res = await _client.get(
- "https://git.mnau.xyz/api/v1/repos/hernik/tessdata_best/contents",
+ "https://git.mnau.xyz/api/v1/repos/hernik/tessdata_fast/contents",
options: Options(headers: {"Accept": "application/json"}));
if ((res.statusCode ?? 500) > 399) {
return Future.error("The server returned status code ${res.statusCode}");
@@ -28,25 +28,36 @@ class TessdataApi {
static Future deleteData(String name) async {
var dataDir = Directory(await FlutterTesseractOcr.getTessdataPath());
+ if (!dataDir.existsSync()) {
+ dataDir.createSync();
+ }
var dataFile = File("${dataDir.path}/$name.traineddata");
if (!dataFile.existsSync()) return;
dataFile.deleteSync();
}
- static Future> getDownloadedData() async =>
- Directory(await FlutterTesseractOcr.getTessdataPath())
- .listSync()
- .where((element) => element.path.endsWith(".traineddata"))
- .map((e) => e.path.split("/").last)
- .toList();
+ static Future> getDownloadedData() async {
+ var tessDir = Directory(await FlutterTesseractOcr.getTessdataPath());
+ if (!tessDir.existsSync()) {
+ tessDir.createSync();
+ }
+ return tessDir
+ .listSync()
+ .where((element) => element.path.endsWith(".traineddata"))
+ .map((e) => e.path.split("/").last)
+ .toList();
+ }
static Future downloadData(String isoCode,
{void Function(int, int)? callback}) async {
- var file = File(
- "${(await FlutterTesseractOcr.getTessdataPath())}/$isoCode.traineddata");
+ var tessDir = Directory(await FlutterTesseractOcr.getTessdataPath());
+ if (!tessDir.existsSync()) {
+ tessDir.createSync();
+ }
+ var file = File("${tessDir.path}/$isoCode.traineddata");
if (file.existsSync()) return; // TODO: maybe ask to redownload?
var res = await _client.get(
- "https://git.mnau.xyz/hernik/tessdata_best/raw/branch/main/$isoCode.traineddata",
+ "https://git.mnau.xyz/hernik/tessdata_fast/raw/branch/main/$isoCode.traineddata",
options: Options(responseType: ResponseType.bytes),
onReceiveProgress: callback);
if ((res.statusCode ?? 500) > 399) {
diff --git a/lib/views/home.dart b/lib/views/home.dart
index dd1529c..3bda87f 100644
--- a/lib/views/home.dart
+++ b/lib/views/home.dart
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_slidable/flutter_slidable.dart';
import 'package:flutter_speed_dial/flutter_speed_dial.dart';
+import 'package:flutter_tesseract_ocr/flutter_tesseract_ocr.dart';
import 'package:grouped_list/grouped_list.dart';
import 'package:image_picker/image_picker.dart';
import 'package:intl/date_symbol_data_local.dart';
@@ -14,6 +15,7 @@ import 'package:prasule/pw/platformbutton.dart';
import 'package:prasule/pw/platformdialog.dart';
import 'package:prasule/views/create_entry.dart';
import 'package:prasule/views/settings/settings.dart';
+import 'package:prasule/views/settings/tessdata_list.dart';
import 'package:prasule/views/setup.dart';
class HomeView extends StatefulWidget {
@@ -85,48 +87,8 @@ class _HomeViewState extends State {
SpeedDialChild(
child: const Icon(Icons.image),
label: "Add through saved image",
- onTap: () async {
- var availableLanguages = await TessdataApi.getDownloadedData();
- if (mounted) {
- var selectedLanguages =
- List.filled(availableLanguages.length, false);
- selectedLanguages[
- availableLanguages.indexOf("eng.traineddata")] = true;
- showDialog(
- context: context,
- builder: (c) => PlatformDialog(
- title: "Select languages for OCR",
- content: Column(
- children: [
- ...List.generate(
- availableLanguages.length,
- (index) => Row(
- children: [
- Checkbox(
- value: selectedLanguages[index],
- onChanged: (value) {
- if (value == null ||
- (selectedLanguages
- .where((element) => element)
- .length <=
- 1 &&
- !value)) return;
- selectedLanguages[index] = value;
- setState(() {}); // todo: builder
- },
- ),
- const SizedBox(
- width: 10,
- ),
- Text(availableLanguages[index].split(".").first)
- ],
- ),
- )
- ],
- ),
- ),
- );
- }
+ onTap: () {
+ startOcr(ImageSource.gallery);
},
),
],
@@ -286,6 +248,102 @@ class _HomeViewState extends State {
);
}
+ Future startOcr(ImageSource imgSrc) async {
+ var availableLanguages = await TessdataApi.getDownloadedData();
+ if (availableLanguages.isEmpty) {
+ if (!mounted) return;
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(
+ content:
+ const Text("You do not have any OCR language data downloaded"),
+ action: SnackBarAction(
+ label: "Download",
+ onPressed: () {
+ Navigator.of(context).push(
+ MaterialPageRoute(
+ builder: (c) => const TessdataListView(),
+ ),
+ );
+ },
+ ),
+ ),
+ );
+ return;
+ }
+ if (!mounted) return;
+ var selectedLanguages = List.filled(availableLanguages.length, false);
+ if (selectedLanguages.length == 1) {
+ selectedLanguages[0] = true;
+ }
+ showDialog(
+ context: context,
+ builder: (c) => PlatformDialog(
+ actions: [
+ TextButton(
+ onPressed: () async {
+ final ImagePicker picker = ImagePicker();
+ final XFile? media = await picker.pickImage(source: imgSrc);
+ if (media == null) {
+ if (mounted) Navigator.of(context).pop();
+ return;
+ }
+ // get selected languages
+ var selected = availableLanguages
+ .where((element) =>
+ selectedLanguages[availableLanguages.indexOf(element)])
+ .join("+")
+ .replaceAll(".traineddata", "");
+ logger.i(selected);
+ var string = await FlutterTesseractOcr.extractText(media.path,
+ language: selected,
+ args: {
+ //"psm": "4",
+ "preserve_interword_spaces": "1",
+ });
+ logger.i(string);
+ if (mounted) Navigator.of(context).pop();
+ return;
+ },
+ child: const Text("Ok")),
+ TextButton(
+ onPressed: () {
+ Navigator.of(c).pop();
+ },
+ child: const Text("Cancel")),
+ ],
+ title: "Select languages for OCR",
+ content: Column(
+ children: [
+ ...List.generate(
+ availableLanguages.length,
+ (index) => Row(
+ children: [
+ Checkbox(
+ value: selectedLanguages[index],
+ onChanged: (value) {
+ if (value == null ||
+ (selectedLanguages
+ .where((element) => element)
+ .length <=
+ 1 &&
+ !value)) return;
+ selectedLanguages[index] = value;
+ setState(() {}); // todo: builder
+ },
+ ),
+ const SizedBox(
+ width: 10,
+ ),
+ Text(availableLanguages[index].split(".").first)
+ ],
+ ),
+ )
+ ],
+ ),
+ ),
+ );
+ }
+
Future getLostData() async {
final ImagePicker picker = ImagePicker();
final LostDataResponse response = await picker.retrieveLostData();
diff --git a/lib/views/settings/tessdata_list.dart b/lib/views/settings/tessdata_list.dart
index 7039731..db96744 100644
--- a/lib/views/settings/tessdata_list.dart
+++ b/lib/views/settings/tessdata_list.dart
@@ -133,6 +133,7 @@ class _TessdataListViewState extends State {
/// Used to find which `.traineddata` is already downloaded and which not
/// so we can show it to the user
void loadAllTessdata() async {
+ var tessDir = Directory(await FlutterTesseractOcr.getTessdataPath());
var d = await TessdataApi.getAvailableData();
var dataStatus =