Skip to content

Commit ed99fdf

Browse files
committed
Init
0 parents  commit ed99fdf

File tree

8 files changed

+284
-0
lines changed

8 files changed

+284
-0
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
notes.md

LICENSE

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
2+
Version 2, December 2004
3+
4+
Copyright (C) 2004 Sam Hocevar <[email protected]>
5+
6+
Everyone is permitted to copy and distribute verbatim or modified
7+
copies of this license document, and changing it is allowed as long
8+
as the name is changed.
9+
10+
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
11+
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
12+
13+
0. You just DO WHAT THE FUCK YOU WANT TO.

README.MD

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# tesseract-ocr-server-web-ui
2+
3+
![Screenshot](screenshot.png)
4+
5+
## Pre-Installation
6+
7+
1. You should have [Docker](https://docs.docker.com/engine/install/) installed.
8+
2. You should have [tesseract-server](https://github.com/hertzg/tesseract-server) up and running.
9+
10+
## Installation
11+
12+
1. Clone this repository:
13+
14+
```bash
15+
git clone https://github.com/m-primo/tesseract-server-ui.git
16+
```
17+
18+
2. Build the Docker image:
19+
20+
```bash
21+
docker build -t tesseract-ocr-server-web-ui .
22+
```
23+
24+
3. Run the Docker container (edit whatever you want):
25+
26+
```bash
27+
docker run --name tesseract-ocr-server-web-ui -d --restart unless-stopped -v /path/to/config:/app/config -p 8080:8080 tesseract-ocr-server-web-ui
28+
```
29+
30+
4. Create a Docker network to connect both containers to each other:
31+
32+
```bash
33+
docker network create tesseract-network
34+
```
35+
36+
5. Attach the network to the `tesseract-server` container (replace `<tesseract-server-container-id>` with the `tesseract-server` container id):
37+
38+
```bash
39+
docker network connect tesseract-network <tesseract-server-container-id>
40+
```
41+
42+
6. Attach the network to the `tesseract-ocr-server-web-ui` container:
43+
44+
```bash
45+
docker network connect tesseract-network tesseract-ocr-server-web-ui
46+
```
47+
48+
7. Get the IP of the `tesseract-server` container (replace `<tesseract-server-container-id>` with the `tesseract-server` container id):
49+
50+
```bash
51+
docker inspect <tesseract-server-container-id>
52+
```
53+
54+
Then scroll to the very down of the response until you see an object called `Networks` and inside it another object called `tesseract-network`.
55+
56+
Copy the value of a key called `"IPAddress"`.
57+
It should be like this `172.18.0.2`. Copy it.
58+
59+
8. Edit `config.php` to match the container IP you just copied. The final `config.php` should like this (notice that `8884` is the internal port of the `tesseract-server` port, not the port you exposed it):
60+
61+
```php
62+
<?php
63+
$config = [
64+
'url' => 'http://172.18.0.2:8884',
65+
'timeout' => 0, // 0 to disable
66+
];
67+
68+
define('CFG', $config);
69+
?>
70+
```

app/config/config.php

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
$config = [
3+
'url' => 'http://172.18.0.2:8884',
4+
'timeout' => 0, // 0 to disable
5+
];
6+
7+
define('CFG', $config);
8+
?>

app/index.html

+104
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<title>tesseract-server-ui</title>
7+
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-KK94CHFLLe+nY2dmCWGMq91rCGa5gtU4mk92HdvYe+M/SXH301p5ILy+dN9+nJOZ" crossorigin="anonymous">
8+
</head>
9+
<body>
10+
<div class="container my-5" id="root">
11+
<h1>Tesseract Server User Interface (V1.1)</h1>
12+
13+
<form enctype="multipart/form-data" @submit.prevent="submitForm">
14+
<div class="mb-3">
15+
<label for="fileInput" class="form-label">Upload the image</label>
16+
<input class="form-control" type="file" id="fileInput" ref="file" />
17+
</div>
18+
<div class="mb-3">
19+
<label for="languageSelect" class="form-label">Select the language(s) in the image</label>
20+
<select multiple class="form-select" id="languageSelect" v-model="languages">
21+
<option value="ara">Arabic</option>
22+
<option value="eng">English</option>
23+
<option value="spa">Spanish</option>
24+
</select>
25+
</div>
26+
<div class="mb-3">
27+
<input type="checkbox" class="btn-check" id="btncheck1" autocomplete="off" v-model="debug">
28+
<label class="btn btn-outline-dark" for="btncheck1">Debug Mode</label>
29+
</div>
30+
<button type="submit" class="btn btn-primary" :disabled="processing">
31+
<template v-if="processing">
32+
<div class="spinner-grow spinner-grow-sm me-2" role="status"></div>
33+
Processing...
34+
</template>
35+
<template v-else>Process</template>
36+
</button>
37+
</form>
38+
39+
<div class="my-4" v-if="error">
40+
<div class="alert alert-danger mt-3 text-break" style="white-space: break-spaces" v-html="error"></div>
41+
</div>
42+
<div class="mt-4" v-if="result">
43+
<div class="alert alert-success text-break" style="white-space: break-spaces" v-if="result.stdout" v-html="result.stdout"></div>
44+
<div class="alert mt-3 text-break" style="white-space: break-spaces" v-if="result.stderr" :class="[result.stderr.toLowerCase().includes('warning: ') ? 'alert-warning' : 'alert-danger']" v-html="result.stderr"></div>
45+
</div>
46+
</div>
47+
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-ENjdO4Dr2bkBIFxQpeoTz1HIcje39Wm4jDKdf19U8gI4ddQ3GYNS7NTKfAdVQSZe" crossorigin="anonymous"></script>
48+
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.min.js"></script>
49+
<script>
50+
new Vue({
51+
el: '#root',
52+
data: {
53+
file: null,
54+
languages: [],
55+
debug: false,
56+
processing: false,
57+
result: null,
58+
error: null,
59+
},
60+
methods: {
61+
submitForm() {
62+
this.error = null;
63+
this.file = this.$refs.file.files[0];
64+
if(!this.file) {
65+
this.error = 'Please select the image.';
66+
return;
67+
}
68+
if(this.languages.length <= 0) {
69+
this.error = 'Please select at least one language.';
70+
return;
71+
}
72+
let formData = new FormData();
73+
formData.append('file', this.file);
74+
formData.append('languages', JSON.stringify(this.languages));
75+
formData.append('debug', this.debug ? '1' : '0');
76+
this.apiRequest(formData);
77+
},
78+
async apiRequest(formData) {
79+
this.result = null;
80+
this.processing = true;
81+
try {
82+
const response = await fetch('process.php', { method: 'POST', body: formData });
83+
const data = await response.json();
84+
console.info('data', data);
85+
if(!response.ok) {
86+
throw new Error(data?.error);
87+
}
88+
this.result = data.data;
89+
} catch(error) {
90+
if(typeof error === 'object' && error.hasOwnProperty('error')) {
91+
this.error = error.error;
92+
} else {
93+
this.error = error;
94+
}
95+
console.warn('error', error);
96+
} finally {
97+
this.processing = false;
98+
}
99+
},
100+
}
101+
});
102+
</script>
103+
</body>
104+
</html>

app/process.php

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<?php
2+
if(!$_POST) exit;
3+
4+
require_once __DIR__.'/config/config.php';
5+
6+
set_time_limit(0);
7+
8+
function process($data) {
9+
$ch = curl_init();
10+
$url = CFG['url'].'/tesseract';
11+
12+
if(isset($data['debug']) && $data['debug']) {
13+
var_dump($data);
14+
}
15+
16+
$file_extension = explode('.', $data['file']['name']);
17+
$file_extension = end($file_extension);
18+
19+
$postdata = [
20+
'file' => new CURLFile($data['file']['tmp_name'], $data['file']['type'], 'file-'.uniqid().'.'.$file_extension),
21+
'options' => json_encode(['languages' => $data['languages']]),
22+
];
23+
24+
if(isset($data['debug']) && $data['debug']) {
25+
var_dump($postdata);
26+
}
27+
28+
// $boundary = uniqid();
29+
30+
$options = array(
31+
CURLOPT_URL => $url,
32+
CURLOPT_POST => true,
33+
CURLOPT_POSTFIELDS => $postdata,
34+
CURLOPT_HTTPHEADER => array(
35+
'Content-Type: multipart/form-data'
36+
),
37+
CURLOPT_RETURNTRANSFER => true,
38+
CURLOPT_TIMEOUT => CFG['timeout'],
39+
CURLOPT_CONNECTTIMEOUT => CFG['timeout']
40+
);
41+
42+
curl_setopt_array($ch, $options);
43+
$response = curl_exec($ch);
44+
45+
if(file_exists($data['file']['tmp_name'])) {
46+
unlink($data['file']['tmp_name']);
47+
}
48+
49+
if (curl_errno($ch)) {
50+
throw new Exception("Error: " . curl_error($ch));
51+
}
52+
53+
curl_close($ch);
54+
55+
return $response;
56+
}
57+
58+
header('Content-Type: application/json; charset=UTF-8');
59+
60+
try {
61+
http_response_code(200);
62+
echo process([
63+
'file' => $_FILES['file'],
64+
'languages' => json_decode($_POST['languages'], true),
65+
'debug' => isset($_POST['debug']) ? $_POST['debug'] : false,
66+
]);
67+
} catch (\Throwable $th) {
68+
http_response_code(500);
69+
echo json_encode(['error' => $th->getMessage()]);
70+
}
71+
?>

dockerfile

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
FROM php:8.1-cli-alpine
2+
3+
# Set the environment variable for the port to be used
4+
ARG PORT=8080
5+
ENV PORT=$PORT
6+
7+
# Set the working directory to /app
8+
WORKDIR /app
9+
10+
# Copy the files to the working directory
11+
COPY ./app .
12+
13+
# Expose the port to the outside world
14+
EXPOSE $PORT
15+
16+
# Run the script using PHP's built-in web server
17+
ENTRYPOINT ["sh", "-c", "php -S 0.0.0.0:${PORT}"]

screenshot.png

72.7 KB
Loading

0 commit comments

Comments
 (0)