source: main/trunk/model-interfaces-dev/atea/ocr/src/components/MainPage.vue@ 35748

Last change on this file since 35748 was 35748, checked in by cstephen, 2 years ago

Annotated some UI components

File size: 6.7 KB
Line 
1<template>
2<div class="main-page-root">
3 <edit-page v-if="showEditor" class="image-editor" :src="imageUrl" :imageBuffer="imageBuffer"
4 @closeAndDiscard="onEditorCloseRequested" @closeAndSave="onEditorSave" />
5
6 <div class="paper root-container" :class="{ 'root-container-image-state': imageUrl !== null }">
7 <div v-if="imageUrl === null" class="upload-area" @click="uploadFile">
8 <span class="heading1">{{ translations.get("Title") }}</span>
9 <span class="material-icons mdi-xl">upload_file</span>
10 <span>{{ translations.get("MainPage_Upload") }}</span>
11 </div>
12
13 <div v-if="imageUrl !== null" class="image-area">
14 <div class="controls">
15 <button class="btn-primary" @click="doOcr" :disabled="ocrInProgress">
16 <span class="material-icons">play_arrow</span>
17 <span>{{ translations.get("MainPage_PerformOCR") }}</span>
18 </button>
19
20 <button class="btn-primary" @click="showEditor = true" :disabled="ocrInProgress">
21 <span class="material-icons">edit</span>
22 <span>{{ translations.get("MainPage_EditImage") }}</span>
23 </button>
24
25 <button class="btn-primary" @click="reset" :disabled="ocrInProgress">
26 <span class="material-icons">restart_alt</span>
27 <span>{{ translations.get("MainPage_NewImage") }}</span>
28 </button>
29 </div>
30
31 <div class="progress-bar-container ocr-progress">
32 <div v-if="ocrInProgress" class="progress-bar-value progress-bar-indeterminate" />
33 </div>
34
35 <img class="image-display" :src="imageUrl" />
36
37 <div class="text-container">
38 <span v-if="ocrResult === null && !ocrInProgress" class="text-placeholder">{{ translations.get("MainPage_OCRHint") }}</span>
39 <pre>{{ ocrResult }}</pre>
40 </div>
41 </div>
42 </div>
43
44 <input ref="fileInput" type="file" @input="onFilesChanged" class="hidden"
45 accept="image/png,image/jpeg,image/gif,image/bmp,image/tiff,image/webp,application/pdf" />
46</div>
47
48</template>
49
50<style lang="scss" scoped>
51.main-page-root {
52 display: flex;
53 align-items: center;
54 justify-content: center;
55}
56
57.root-container {
58 padding: 1em;
59 transition-duration: var(--transition-duration);
60}
61
62.root-container-image-state {
63 width: calc(100% - 3em);
64 height: calc(100vh - 3em);
65 max-height: calc(100vh - 3em);
66}
67
68.upload-area {
69 display: grid;
70 grid-template-columns: auto auto;
71 grid-template-rows: auto 1fr;
72 align-items: center;
73 justify-items: center;
74
75 padding: 2em;
76 gap: 1em;
77
78 font-size: 2rem;
79 cursor: pointer;
80 border: 3px dashed var(--bg-color);
81 border-radius: var(--border-radius);
82
83 .heading1 {
84 grid-column-start: span 2;
85 color: #666;
86 }
87}
88
89.hidden {
90 display: none;
91}
92
93.image-editor {
94 position: absolute;
95 height: 100%;
96 width: 100%;
97 top: 0;
98 left: 0;
99 z-index: 9;
100}
101
102.image-area {
103 display: grid;
104 grid-template-columns: 1fr 1fr;
105 grid-template-rows: auto auto 1fr;
106 height: 100%;
107
108 gap: 1rem;
109 align-items: center;
110 justify-items: center;
111
112 position: relative;
113 overflow: hidden;
114
115 .text-container {
116 height: 100%;
117 width: 100%;
118 }
119}
120
121.image-display {
122 border: 1px solid #444;
123 max-width: 100%;
124 max-height: 80vh;
125 object-fit: contain;
126}
127
128.controls {
129 display: flex;
130 gap: 1em;
131 grid-column-start: span 2;
132}
133
134.ocr-progress {
135 grid-column-start: span 2;
136}
137
138.text-container {
139 overflow: scroll;
140}
141</style>
142
143<script>
144import { mapState } from "vuex";
145import EditPage from "./EditPage.vue";
146import OcrService, { OcrOptions } from "../js/OcrService"
147import { log } from "../js/Util";
148
149const ocrService = new OcrService();
150
151export default {
152 name: "MainPage",
153 components: {
154 EditPage
155 },
156 data() {
157 return {
158 imageType: null,
159 /** @type {ArrayBuffer} */
160 imageBuffer: null,
161 imageUrl: null,
162 ocrInProgress: false,
163 ocrResult: null,
164 showEditor: false
165 }
166 },
167 computed: {
168 ...mapState({
169 translations: state => state.translations
170 })
171 },
172 watch: {
173 imageBuffer(newValue) {
174 if (this.imageUrl !== null) {
175 URL.revokeObjectURL(this.imageUrl);
176 }
177
178 if (newValue === null) {
179 return;
180 }
181
182 const arrayView = new Uint8Array(newValue);
183 const blob = new Blob([ arrayView ]);
184 this.imageUrl = URL.createObjectURL(blob);
185 }
186 },
187 methods: {
188 uploadFile() {
189 this.$refs.fileInput.click();
190 },
191 async onFilesChanged() {
192 /** @type {File[]} */
193 const files = this.$refs.fileInput.files;
194 if (files === null || files === undefined || files.length !== 1) {
195 return;
196 }
197
198 this.imageBuffer = await files[0].arrayBuffer();
199 this.imageType = files[0].type;
200 },
201
202 async doOcr() {
203 try {
204 this.ocrInProgress = true;
205
206 const arrayView = new Uint8Array(this.imageBuffer);
207 const imageBlob = new Blob([ arrayView ], { type: this.imageType });
208
209 const result = await ocrService.run([
210 {
211 image: imageBlob,
212 fileName: "file.png",
213 options: new OcrOptions(false)
214 }
215 ]);
216
217 this.ocrResult = result[0].text;
218 }
219 catch (ex) {
220 // TODO: Display error
221 log("Failed to perform OCR", "error");
222 log(ex, "error");
223 }
224 finally {
225 this.ocrInProgress = false;
226 }
227 },
228
229 onEditorCloseRequested() {
230 this.showEditor = false;
231 },
232 /**
233 * Called when the editor is saved in order to update the stored image buffer.
234 * @param {Buffer} newBuffer The updated image buffer.
235 * @param {String} newType The updated MIME type of the image buffer.
236 */
237 onEditorSave(newBuffer, newType) {
238 this.imageBuffer = newBuffer.buffer;
239 this.imageType = newType;
240
241 this.onEditorCloseRequested();
242 },
243
244 reset() {
245 URL.revokeObjectURL(this.imageUrl);
246 this.imageUrl = null;
247 this.ocrInProgress = false;
248 this.ocrResult = null;
249 this.showEditor = false;
250 this.$refs.fileInput.value = "";
251 }
252 }
253}
254</script>
Note: See TracBrowser for help on using the repository browser.