/src/tesseract/src/ccmain/tessedit.cpp
Line | Count | Source |
1 | | /********************************************************************** |
2 | | * File: tessedit.cpp (Formerly tessedit.c) |
3 | | * Description: (Previously) Main program for merge of tess and editor. |
4 | | * Now just code to load the language model and various |
5 | | * engine-specific data files. |
6 | | * Author: Ray Smith |
7 | | * |
8 | | * (C) Copyright 1992, Hewlett-Packard Ltd. |
9 | | ** Licensed under the Apache License, Version 2.0 (the "License"); |
10 | | ** you may not use this file except in compliance with the License. |
11 | | ** You may obtain a copy of the License at |
12 | | ** http://www.apache.org/licenses/LICENSE-2.0 |
13 | | ** Unless required by applicable law or agreed to in writing, software |
14 | | ** distributed under the License is distributed on an "AS IS" BASIS, |
15 | | ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
16 | | ** See the License for the specific language governing permissions and |
17 | | ** limitations under the License. |
18 | | * |
19 | | **********************************************************************/ |
20 | | |
21 | | // Include automatically generated configuration file if running autoconf. |
22 | | #ifdef HAVE_CONFIG_H |
23 | | # include "config_auto.h" |
24 | | #endif |
25 | | |
26 | | #include "control.h" |
27 | | #include "matchdefs.h" |
28 | | #include "pageres.h" |
29 | | #include "params.h" |
30 | | #include "stopper.h" |
31 | | #include "tesseractclass.h" |
32 | | #include "tessvars.h" |
33 | | #include "tprintf.h" |
34 | | #ifndef DISABLED_LEGACY_ENGINE |
35 | | # include "chop.h" |
36 | | # include "intmatcher.h" |
37 | | # include "reject.h" |
38 | | #endif |
39 | | #include "lstmrecognizer.h" |
40 | | |
41 | | namespace tesseract { |
42 | | |
43 | | // Read a "config" file containing a set of variable, value pairs. |
44 | | // Searches the standard places: tessdata/configs, tessdata/tessconfigs |
45 | | // and also accepts a relative or absolute path name. |
46 | 0 | void Tesseract::read_config_file(const char *filename, SetParamConstraint constraint) { |
47 | 0 | std::string path = datadir; |
48 | 0 | path += "configs/"; |
49 | 0 | path += filename; |
50 | 0 | FILE *fp; |
51 | 0 | if ((fp = fopen(path.c_str(), "rb")) != nullptr) { |
52 | 0 | fclose(fp); |
53 | 0 | } else { |
54 | 0 | path = datadir; |
55 | 0 | path += "tessconfigs/"; |
56 | 0 | path += filename; |
57 | 0 | if ((fp = fopen(path.c_str(), "rb")) != nullptr) { |
58 | 0 | fclose(fp); |
59 | 0 | } else { |
60 | 0 | path = filename; |
61 | 0 | } |
62 | 0 | } |
63 | 0 | ParamUtils::ReadParamsFile(path.c_str(), constraint, this->params()); |
64 | 0 | } |
65 | | |
66 | | // Returns false if a unicharset file for the specified language was not found |
67 | | // or was invalid. |
68 | | // This function initializes TessdataManager. After TessdataManager is |
69 | | // no longer needed, TessdataManager::End() should be called. |
70 | | // |
71 | | // This function sets tessedit_oem_mode to the given OcrEngineMode oem, unless |
72 | | // it is OEM_DEFAULT, in which case the value of the variable will be obtained |
73 | | // from the language-specific config file (stored in [lang].traineddata), from |
74 | | // the config files specified on the command line or left as the default |
75 | | // OEM_TESSERACT_ONLY if none of the configs specify this variable. |
76 | | bool Tesseract::init_tesseract_lang_data(const std::string &language, OcrEngineMode oem, |
77 | | char **configs, int configs_size, |
78 | | const std::vector<std::string> *vars_vec, |
79 | | const std::vector<std::string> *vars_values, |
80 | 4 | bool set_only_non_debug_params, TessdataManager *mgr) { |
81 | | // Set the language data path prefix |
82 | 4 | lang = !language.empty() ? language : "eng"; |
83 | 4 | language_data_path_prefix = datadir; |
84 | 4 | language_data_path_prefix += lang; |
85 | 4 | language_data_path_prefix += "."; |
86 | | |
87 | | // Initialize TessdataManager. |
88 | 4 | std::string tessdata_path = language_data_path_prefix + kTrainedDataSuffix; |
89 | 4 | if (!mgr->is_loaded() && !mgr->Init(tessdata_path.c_str())) { |
90 | 0 | tprintf("Error opening data file %s\n", tessdata_path.c_str()); |
91 | 0 | tprintf( |
92 | 0 | "Please make sure the TESSDATA_PREFIX environment variable is set" |
93 | 0 | " to your \"tessdata\" directory.\n"); |
94 | 0 | return false; |
95 | 0 | } |
96 | | #ifdef DISABLED_LEGACY_ENGINE |
97 | | tessedit_ocr_engine_mode.set_value(OEM_LSTM_ONLY); |
98 | | #else |
99 | 4 | if (oem == OEM_DEFAULT) { |
100 | | // Set the engine mode from availability, which can then be overridden by |
101 | | // the config file when we read it below. |
102 | 4 | if (!mgr->IsLSTMAvailable()) { |
103 | 0 | tessedit_ocr_engine_mode.set_value(OEM_TESSERACT_ONLY); |
104 | 4 | } else if (!mgr->IsBaseAvailable()) { |
105 | 0 | tessedit_ocr_engine_mode.set_value(OEM_LSTM_ONLY); |
106 | 4 | } else { |
107 | 4 | tessedit_ocr_engine_mode.set_value(OEM_TESSERACT_LSTM_COMBINED); |
108 | 4 | } |
109 | 4 | } |
110 | 4 | #endif // ndef DISABLED_LEGACY_ENGINE |
111 | | |
112 | | // If a language specific config file (lang.config) exists, load it in. |
113 | 4 | TFile fp; |
114 | 4 | if (mgr->GetComponent(TESSDATA_LANG_CONFIG, &fp)) { |
115 | 0 | ParamUtils::ReadParamsFromFp(SET_PARAM_CONSTRAINT_NONE, &fp, this->params()); |
116 | 0 | } |
117 | | |
118 | 4 | SetParamConstraint set_params_constraint = |
119 | 4 | set_only_non_debug_params ? SET_PARAM_CONSTRAINT_NON_DEBUG_ONLY : SET_PARAM_CONSTRAINT_NONE; |
120 | | // Load tesseract variables from config files. This is done after loading |
121 | | // language-specific variables from [lang].traineddata file, so that custom |
122 | | // config files can override values in [lang].traineddata file. |
123 | 4 | for (int i = 0; i < configs_size; ++i) { |
124 | 0 | read_config_file(configs[i], set_params_constraint); |
125 | 0 | } |
126 | | |
127 | | // Set params specified in vars_vec (done after setting params from config |
128 | | // files, so that params in vars_vec can override those from files). |
129 | 4 | if (vars_vec != nullptr && vars_values != nullptr) { |
130 | 0 | for (unsigned i = 0; i < vars_vec->size(); ++i) { |
131 | 0 | if (!ParamUtils::SetParam((*vars_vec)[i].c_str(), (*vars_values)[i].c_str(), |
132 | 0 | set_params_constraint, this->params())) { |
133 | 0 | tprintf("Warning: The parameter '%s' was not found.\n", (*vars_vec)[i].c_str()); |
134 | 0 | } |
135 | 0 | } |
136 | 0 | } |
137 | | |
138 | 4 | if (!tessedit_write_params_to_file.empty()) { |
139 | 0 | FILE *params_file = fopen(tessedit_write_params_to_file.c_str(), "wb"); |
140 | 0 | if (params_file != nullptr) { |
141 | 0 | ParamUtils::PrintParams(params_file, this->params()); |
142 | 0 | fclose(params_file); |
143 | 0 | } else { |
144 | 0 | tprintf("Failed to open %s for writing params.\n", tessedit_write_params_to_file.c_str()); |
145 | 0 | } |
146 | 0 | } |
147 | | |
148 | 4 | #ifndef DISABLED_LEGACY_ENGINE |
149 | | // Determine which ocr engine(s) should be loaded and used for recognition. |
150 | 4 | if (oem != OEM_DEFAULT) { |
151 | 0 | tessedit_ocr_engine_mode.set_value(oem); |
152 | 0 | } |
153 | 4 | #endif |
154 | | |
155 | | // If we are only loading the config file (and so not planning on doing any |
156 | | // recognition) then there's nothing else do here. |
157 | 4 | if (tessedit_init_config_only) { |
158 | 0 | return true; |
159 | 0 | } |
160 | | |
161 | | // The various OcrEngineMode settings (see tesseract/publictypes.h) determine |
162 | | // which engine-specific data files need to be loaded. If LSTM_ONLY is |
163 | | // requested, the base Tesseract files are *Not* required. |
164 | | #ifdef DISABLED_LEGACY_ENGINE |
165 | | if (tessedit_ocr_engine_mode == OEM_LSTM_ONLY) { |
166 | | #else |
167 | 4 | if (tessedit_ocr_engine_mode == OEM_LSTM_ONLY || |
168 | 4 | tessedit_ocr_engine_mode == OEM_TESSERACT_LSTM_COMBINED) { |
169 | 4 | #endif // ndef DISABLED_LEGACY_ENGINE |
170 | 4 | if (mgr->IsComponentAvailable(TESSDATA_LSTM)) { |
171 | 4 | lstm_recognizer_ = new LSTMRecognizer(language_data_path_prefix.c_str()); |
172 | 4 | if (!lstm_recognizer_->Load(this->params(), lstm_use_matrix ? language : "", mgr)) { |
173 | 0 | delete lstm_recognizer_; |
174 | 0 | lstm_recognizer_ = nullptr; |
175 | 0 | tprintf("Error: Failed to load the LSTM model from %s\n", tessdata_path.c_str()); |
176 | 0 | return false; |
177 | 0 | } |
178 | 4 | } else { |
179 | | #ifdef DISABLED_LEGACY_ENGINE |
180 | | // The legacy engine is compiled out, so we cannot fall back to it. |
181 | | // Returning false here propagates a load failure to init_tesseract, |
182 | | // which logs "Failed loading language '%s'" and skips this language. |
183 | | tprintf( |
184 | | "Error: LSTM requested, but not present in %s; the legacy " |
185 | | "engine is disabled in this build, so there is no fallback.\n", |
186 | | tessdata_path.c_str()); |
187 | | return false; |
188 | | #else |
189 | 0 | tprintf("Error: LSTM requested, but not present!! Loading tesseract.\n"); |
190 | 0 | tessedit_ocr_engine_mode.set_value(OEM_TESSERACT_ONLY); |
191 | 0 | #endif // DISABLED_LEGACY_ENGINE |
192 | 0 | } |
193 | 4 | } |
194 | | |
195 | | // Load the unicharset |
196 | 4 | if (tessedit_ocr_engine_mode == OEM_LSTM_ONLY) { |
197 | | // Avoid requiring a unicharset when we aren't running base tesseract. |
198 | 0 | unicharset.CopyFrom(lstm_recognizer_->GetUnicharset()); |
199 | 0 | } |
200 | 4 | #ifndef DISABLED_LEGACY_ENGINE |
201 | 4 | else if (!mgr->GetComponent(TESSDATA_UNICHARSET, &fp) || !unicharset.load_from_file(&fp, false)) { |
202 | 0 | tprintf( |
203 | 0 | "Error: Tesseract (legacy) engine requested, but components are " |
204 | 0 | "not present in %s!!\n", |
205 | 0 | tessdata_path.c_str()); |
206 | 0 | return false; |
207 | 0 | } |
208 | 4 | #endif // ndef DISABLED_LEGACY_ENGINE |
209 | 4 | if (unicharset.size() > MAX_NUM_CLASSES) { |
210 | 0 | tprintf("Error: Size of unicharset is greater than MAX_NUM_CLASSES\n"); |
211 | 0 | return false; |
212 | 0 | } |
213 | 4 | right_to_left_ = unicharset.major_right_to_left(); |
214 | | |
215 | 4 | #ifndef DISABLED_LEGACY_ENGINE |
216 | | |
217 | | // Setup initial unichar ambigs table and read universal ambigs. |
218 | 4 | UNICHARSET encoder_unicharset; |
219 | 4 | encoder_unicharset.CopyFrom(unicharset); |
220 | 4 | unichar_ambigs.InitUnicharAmbigs(unicharset, use_ambigs_for_adaption); |
221 | 4 | unichar_ambigs.LoadUniversal(encoder_unicharset, &unicharset); |
222 | | |
223 | 4 | if (!tessedit_ambigs_training && mgr->GetComponent(TESSDATA_AMBIGS, &fp)) { |
224 | 4 | unichar_ambigs.LoadUnicharAmbigs(encoder_unicharset, &fp, ambigs_debug_level, |
225 | 4 | use_ambigs_for_adaption, &unicharset); |
226 | 4 | } |
227 | | |
228 | | // Loading the ambiguities inserts the correct n-grams and their fragments |
229 | | // into the unicharset, so it may have grown beyond the limit checked above. |
230 | | // A unichar id indexes fixed-size arrays of MAX_NUM_CLASSES entries in the |
231 | | // legacy classifier, so reject it here rather than failing later. |
232 | 4 | if (unicharset.size() > MAX_NUM_CLASSES) { |
233 | 0 | tprintf("Error: Size of unicharset is greater than MAX_NUM_CLASSES\n"); |
234 | 0 | return false; |
235 | 0 | } |
236 | | |
237 | | // Init ParamsModel. |
238 | | // Load pass1 and pass2 weights (for now these two sets are the same, but in |
239 | | // the future separate sets of weights can be generated). |
240 | 12 | for (int p = ParamsModel::PTRAIN_PASS1; p < ParamsModel::PTRAIN_NUM_PASSES; ++p) { |
241 | 8 | language_model_->getParamsModel().SetPass(static_cast<ParamsModel::PassEnum>(p)); |
242 | 8 | if (mgr->GetComponent(TESSDATA_PARAMS_MODEL, &fp)) { |
243 | 0 | if (!language_model_->getParamsModel().LoadFromFp(lang.c_str(), &fp)) { |
244 | 0 | return false; |
245 | 0 | } |
246 | 0 | } |
247 | 8 | } |
248 | 4 | #endif // ndef DISABLED_LEGACY_ENGINE |
249 | | |
250 | 4 | return true; |
251 | 4 | } |
252 | | |
253 | | // Helper returns true if the given string is in the vector of strings. |
254 | 8 | static bool IsStrInList(const std::string &str, const std::vector<std::string> &str_list) { |
255 | 8 | for (const auto &i : str_list) { |
256 | 0 | if (i == str) { |
257 | 0 | return true; |
258 | 0 | } |
259 | 0 | } |
260 | 8 | return false; |
261 | 8 | } |
262 | | |
263 | | // Parse a string of the form [~]<lang>[+[~]<lang>]*. |
264 | | // Langs with no prefix get appended to to_load, provided they |
265 | | // are not in there already. |
266 | | // Langs with ~ prefix get appended to not_to_load, provided they are not in |
267 | | // there already. |
268 | | void Tesseract::ParseLanguageString(const std::string &lang_str, std::vector<std::string> *to_load, |
269 | 8 | std::vector<std::string> *not_to_load) { |
270 | 8 | std::string remains(lang_str); |
271 | | // Look whether the model file uses a prefix which must be applied to |
272 | | // included model files as well. |
273 | 8 | std::string prefix; |
274 | 8 | size_t found = lang.find_last_of('/'); |
275 | 8 | if (found != std::string::npos) { |
276 | | // A prefix was found. |
277 | 0 | prefix = lang.substr(0, found + 1); |
278 | 0 | } |
279 | 12 | while (!remains.empty()) { |
280 | | // Find the start of the lang code and which vector to add to. |
281 | 4 | const char *start = remains.c_str(); |
282 | 4 | while (*start == '+') { |
283 | 0 | ++start; |
284 | 0 | } |
285 | 4 | std::vector<std::string> *target = to_load; |
286 | 4 | if (*start == '~') { |
287 | 0 | target = not_to_load; |
288 | 0 | ++start; |
289 | 0 | } |
290 | | // Find the index of the end of the lang code in string start. |
291 | 4 | int end = strlen(start); |
292 | 4 | const char *plus = strchr(start, '+'); |
293 | 4 | if (plus != nullptr && plus - start < end) { |
294 | 0 | end = plus - start; |
295 | 0 | } |
296 | 4 | std::string lang_code(start); |
297 | 4 | lang_code.resize(end); |
298 | 4 | std::string next(start + end); |
299 | 4 | remains = std::move(next); |
300 | 4 | lang_code = prefix + lang_code; |
301 | | // Check whether lang_code is already in the target vector and add. |
302 | 4 | if (!IsStrInList(lang_code, *target)) { |
303 | 4 | target->push_back(std::move(lang_code)); |
304 | 4 | } |
305 | 4 | } |
306 | 8 | } |
307 | | |
308 | | // Initialize for potentially a set of languages defined by the language |
309 | | // string and recursively any additional languages required by any language |
310 | | // traineddata file (via tessedit_load_sublangs in its config) that is loaded. |
311 | | // arg0 is the datapath for the tessdata directory, which could be the |
312 | | // path of the tessdata directory with no trailing /, or (if tessdata |
313 | | // lives in the same directory as the executable, the path of the executable, |
314 | | // hence the name arg0. |
315 | | // See init_tesseract_internal for remaining args. |
316 | | int Tesseract::init_tesseract(const std::string &arg0, const std::string &textbase, |
317 | | const std::string &language, OcrEngineMode oem, char **configs, |
318 | | int configs_size, const std::vector<std::string> *vars_vec, |
319 | | const std::vector<std::string> *vars_values, |
320 | 4 | bool set_only_non_debug_params, TessdataManager *mgr) { |
321 | 4 | std::vector<std::string> langs_to_load; |
322 | 4 | std::vector<std::string> langs_not_to_load; |
323 | 4 | ParseLanguageString(language, &langs_to_load, &langs_not_to_load); |
324 | | |
325 | 4 | for (auto *lang : sub_langs_) { |
326 | 0 | delete lang; |
327 | 0 | } |
328 | | |
329 | | // Set the basename, compute the data directory. |
330 | 4 | main_setup(arg0, textbase); |
331 | | |
332 | 4 | sub_langs_.clear(); |
333 | | // Find the first loadable lang and load into this. |
334 | | // Add any languages that this language requires |
335 | 4 | bool loaded_primary = false; |
336 | | // Load the rest into sub_langs_. |
337 | | // WARNING: A range based for loop does not work here because langs_to_load |
338 | | // might be changed in the loop when a new submodel is found. |
339 | 8 | for (size_t lang_index = 0; lang_index < langs_to_load.size(); ++lang_index) { |
340 | 4 | auto &lang_to_load = langs_to_load[lang_index]; |
341 | 4 | if (!IsStrInList(lang_to_load, langs_not_to_load)) { |
342 | 4 | const char *lang_str = lang_to_load.c_str(); |
343 | 4 | Tesseract *tess_to_init; |
344 | 4 | if (!loaded_primary) { |
345 | 4 | tess_to_init = this; |
346 | 4 | } else { |
347 | 0 | tess_to_init = new Tesseract; |
348 | 0 | tess_to_init->main_setup(arg0, textbase); |
349 | 0 | } |
350 | | |
351 | 4 | int result = tess_to_init->init_tesseract_internal(textbase, lang_str, oem, configs, |
352 | 4 | configs_size, vars_vec, vars_values, |
353 | 4 | set_only_non_debug_params, mgr); |
354 | | // Forget that language, but keep any reader we were given. |
355 | 4 | mgr->Clear(); |
356 | | |
357 | 4 | if (!loaded_primary) { |
358 | 4 | if (result < 0) { |
359 | 0 | tprintf("Failed loading language '%s'\n", lang_str); |
360 | 4 | } else { |
361 | 4 | ParseLanguageString(tess_to_init->tessedit_load_sublangs, &langs_to_load, |
362 | 4 | &langs_not_to_load); |
363 | 4 | loaded_primary = true; |
364 | 4 | } |
365 | 4 | } else { |
366 | 0 | if (result < 0) { |
367 | 0 | tprintf("Failed loading language '%s'\n", lang_str); |
368 | 0 | delete tess_to_init; |
369 | 0 | } else { |
370 | 0 | sub_langs_.push_back(tess_to_init); |
371 | | // Add any languages that this language requires |
372 | 0 | ParseLanguageString(tess_to_init->tessedit_load_sublangs, &langs_to_load, |
373 | 0 | &langs_not_to_load); |
374 | 0 | } |
375 | 0 | } |
376 | 4 | } |
377 | 4 | } |
378 | 4 | if (!loaded_primary && !langs_to_load.empty()) { |
379 | 0 | tprintf("Tesseract couldn't load any languages!\n"); |
380 | 0 | return -1; // Couldn't load any language! |
381 | 0 | } |
382 | 4 | #ifndef DISABLED_LEGACY_ENGINE |
383 | 4 | if (!sub_langs_.empty()) { |
384 | | // In multilingual mode word ratings have to be directly comparable, |
385 | | // so use the same language model weights for all languages: |
386 | | // use the primary language's params model if |
387 | | // tessedit_use_primary_params_model is set, |
388 | | // otherwise use default language model weights. |
389 | 0 | if (tessedit_use_primary_params_model) { |
390 | 0 | for (auto &sub_lang : sub_langs_) { |
391 | 0 | sub_lang->language_model_->getParamsModel().Copy(this->language_model_->getParamsModel()); |
392 | 0 | } |
393 | 0 | tprintf("Using params model of the primary language\n"); |
394 | 0 | } else { |
395 | 0 | this->language_model_->getParamsModel().Clear(); |
396 | 0 | for (auto &sub_lang : sub_langs_) { |
397 | 0 | sub_lang->language_model_->getParamsModel().Clear(); |
398 | 0 | } |
399 | 0 | } |
400 | 0 | } |
401 | | |
402 | 4 | SetupUniversalFontIds(); |
403 | 4 | #endif // ndef DISABLED_LEGACY_ENGINE |
404 | 4 | return 0; |
405 | 4 | } |
406 | | |
407 | | // Common initialization for a single language. |
408 | | // textbase is an optional output file basename (used only for training) |
409 | | // language is the language code to load. |
410 | | // oem controls which engine(s) will operate on the image |
411 | | // configs (argv) is an array of config filenames to load variables from. |
412 | | // May be nullptr. |
413 | | // configs_size (argc) is the number of elements in configs. |
414 | | // vars_vec is an optional vector of variables to set. |
415 | | // vars_values is an optional corresponding vector of values for the variables |
416 | | // in vars_vec. |
417 | | // If set_only_non_debug_params is true, only params that do not contain |
418 | | // "debug" in the name will be set. |
419 | | int Tesseract::init_tesseract_internal(const std::string &textbase, |
420 | | const std::string &language, OcrEngineMode oem, |
421 | | char **configs, int configs_size, |
422 | | const std::vector<std::string> *vars_vec, |
423 | | const std::vector<std::string> *vars_values, |
424 | 4 | bool set_only_non_debug_params, TessdataManager *mgr) { |
425 | 4 | if (!init_tesseract_lang_data(language, oem, configs, configs_size, vars_vec, |
426 | 4 | vars_values, set_only_non_debug_params, mgr)) { |
427 | 0 | return -1; |
428 | 0 | } |
429 | 4 | if (tessedit_init_config_only) { |
430 | 0 | return 0; |
431 | 0 | } |
432 | | // If only LSTM will be used, skip loading Tesseract classifier's |
433 | | // pre-trained templates and dictionary. |
434 | 4 | bool init_tesseract = tessedit_ocr_engine_mode != OEM_LSTM_ONLY; |
435 | 4 | if (!program_editup(textbase, init_tesseract ? mgr : nullptr, init_tesseract ? mgr : nullptr)) { |
436 | 0 | return -1; |
437 | 0 | } |
438 | 4 | return 0; // Normal exit |
439 | 4 | } |
440 | | |
441 | | #ifndef DISABLED_LEGACY_ENGINE |
442 | | |
443 | | // Helper builds the all_fonts table by adding new fonts from new_fonts. |
444 | | static void CollectFonts(const UnicityTable<FontInfo> &new_fonts, |
445 | 4 | UnicityTable<FontInfo> *all_fonts) { |
446 | 1.71k | for (int i = 0; i < new_fonts.size(); ++i) { |
447 | | // UnicityTable uniques as we go. |
448 | 1.70k | all_fonts->push_back(new_fonts.at(i)); |
449 | 1.70k | } |
450 | 4 | } |
451 | | |
452 | | // Helper assigns an id to lang_fonts using the index in all_fonts table. |
453 | 4 | static void AssignIds(const UnicityTable<FontInfo> &all_fonts, UnicityTable<FontInfo> *lang_fonts) { |
454 | 1.71k | for (int i = 0; i < lang_fonts->size(); ++i) { |
455 | 1.70k | auto index = all_fonts.get_index(lang_fonts->at(i)); |
456 | 1.70k | lang_fonts->at(i).universal_id = index; |
457 | 1.70k | } |
458 | 4 | } |
459 | | |
460 | | // Set the universal_id member of each font to be unique among all |
461 | | // instances of the same font loaded. |
462 | 4 | void Tesseract::SetupUniversalFontIds() { |
463 | | // Note that we can get away with bitwise copying FontInfo in |
464 | | // all_fonts, as it is a temporary structure and we avoid setting the |
465 | | // delete callback. |
466 | 4 | UnicityTable<FontInfo> all_fonts; |
467 | | |
468 | | // Create the universal ID table. |
469 | 4 | CollectFonts(get_fontinfo_table(), &all_fonts); |
470 | 4 | for (auto &sub_lang : sub_langs_) { |
471 | 0 | CollectFonts(sub_lang->get_fontinfo_table(), &all_fonts); |
472 | 0 | } |
473 | | // Assign ids from the table to each font table. |
474 | 4 | AssignIds(all_fonts, &get_fontinfo_table()); |
475 | 4 | for (auto &sub_lang : sub_langs_) { |
476 | 0 | AssignIds(all_fonts, &sub_lang->get_fontinfo_table()); |
477 | 0 | } |
478 | 4 | font_table_size_ = all_fonts.size(); |
479 | 4 | } |
480 | | |
481 | | #endif // ndef DISABLED_LEGACY_ENGINE |
482 | | |
483 | 0 | void Tesseract::end_tesseract() { |
484 | 0 | end_recog(); |
485 | 0 | } |
486 | | |
487 | | /* Define command type identifiers */ |
488 | | |
489 | | enum CMD_EVENTS { ACTION_1_CMD_EVENT, RECOG_WERDS, RECOG_PSEUDO, ACTION_2_CMD_EVENT }; |
490 | | } // namespace tesseract |