Coverage Report

Created: 2026-07-25 06:27

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/tesseract/src/ccutil/tessdatamanager.cpp
Line
Count
Source
1
///////////////////////////////////////////////////////////////////////
2
// File:        tessdatamanager.cpp
3
// Description: Functions to handle loading/combining tesseract data files.
4
// Author:      Daria Antonova
5
//
6
// (C) Copyright 2009, Google Inc.
7
// Licensed under the Apache License, Version 2.0 (the "License");
8
// you may not use this file except in compliance with the License.
9
// You may obtain a copy of the License at
10
// http://www.apache.org/licenses/LICENSE-2.0
11
// Unless required by applicable law or agreed to in writing, software
12
// distributed under the License is distributed on an "AS IS" BASIS,
13
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
// See the License for the specific language governing permissions and
15
// limitations under the License.
16
//
17
///////////////////////////////////////////////////////////////////////
18
19
#ifdef HAVE_CONFIG_H
20
#  include "config_auto.h"
21
#endif
22
23
#include "tessdatamanager.h"
24
25
#include <cstdio>
26
#include <string>
27
28
#if defined(HAVE_LIBARCHIVE)
29
#  include <archive.h>
30
#  include <archive_entry.h>
31
#endif
32
33
#include <tesseract/version.h>
34
#include "errcode.h"
35
#include "helpers.h"
36
#include "params.h"
37
#include "serialis.h"
38
#include "tprintf.h"
39
40
namespace tesseract {
41
42
0
TessdataManager::TessdataManager() : reader_(nullptr), is_loaded_(false), swap_(false) {
43
0
  SetVersionString(TESSERACT_VERSION_STR);
44
0
}
45
46
TessdataManager::TessdataManager(FileReader reader)
47
4
    : reader_(reader), is_loaded_(false), swap_(false) {
48
4
  SetVersionString(TESSERACT_VERSION_STR);
49
4
}
50
51
// Lazily loads from the given filename. Won't actually read the file
52
// until it needs it.
53
0
void TessdataManager::LoadFileLater(const char *data_file_name) {
54
0
  Clear();
55
0
  data_file_name_ = data_file_name;
56
0
}
57
58
#if defined(HAVE_LIBARCHIVE)
59
bool TessdataManager::LoadArchiveFile(const char *filename) {
60
  bool result = false;
61
  archive *a = archive_read_new();
62
  if (a != nullptr) {
63
    archive_read_support_filter_all(a);
64
    archive_read_support_format_all(a);
65
    if (archive_read_open_filename(a, filename, 8192) == ARCHIVE_OK) {
66
      archive_entry *ae;
67
      while (archive_read_next_header(a, &ae) == ARCHIVE_OK) {
68
        const char *component = archive_entry_pathname(ae);
69
        if (component != nullptr) {
70
          TessdataType type;
71
          if (TessdataTypeFromFileName(component, &type)) {
72
            int64_t size = archive_entry_size(ae);
73
            if (size > 0) {
74
              entries_[type].resize(size);
75
              if (archive_read_data(a, &entries_[type][0], size) == size) {
76
                is_loaded_ = true;
77
              }
78
            }
79
          }
80
        }
81
      }
82
      result = is_loaded_;
83
    }
84
    archive_read_free(a);
85
  }
86
  return result;
87
}
88
#endif
89
90
4
bool TessdataManager::Init(const char *data_file_name) {
91
4
  std::vector<char> data;
92
4
  if (reader_ == nullptr) {
93
#if defined(HAVE_LIBARCHIVE)
94
    if (LoadArchiveFile(data_file_name)) {
95
      return true;
96
    }
97
#endif
98
4
    if (!LoadDataFromFile(data_file_name, &data)) {
99
0
      return false;
100
0
    }
101
4
  } else {
102
0
    if (!(*reader_)(data_file_name, &data)) {
103
0
      return false;
104
0
    }
105
0
  }
106
4
  return LoadMemBuffer(data_file_name, &data[0], data.size());
107
4
}
108
109
// Loads from the given memory buffer as if a file.
110
4
bool TessdataManager::LoadMemBuffer(const char *name, const char *data, int size) {
111
  // TODO: This method supports only the proprietary file format.
112
4
  if (size < 0) {
113
0
    return false;
114
0
  }
115
4
  Clear();
116
4
  data_file_name_ = name;
117
4
  TFile fp;
118
4
  fp.Open(data, size);
119
4
  uint32_t num_entries;
120
4
  if (!fp.DeSerialize(&num_entries)) {
121
0
    return false;
122
0
  }
123
4
  swap_ = num_entries > kMaxNumTessdataEntries;
124
4
  fp.set_swap(swap_);
125
4
  if (swap_) {
126
0
    ReverseN(&num_entries, sizeof(num_entries));
127
0
  }
128
4
  if (num_entries > kMaxNumTessdataEntries) {
129
0
    return false;
130
0
  }
131
  // TODO: optimize (no init required).
132
4
  std::vector<int64_t> offset_table(num_entries);
133
4
  if (!fp.DeSerialize(&offset_table[0], num_entries)) {
134
0
    return false;
135
0
  }
136
100
  for (unsigned i = 0; i < num_entries && i < TESSDATA_NUM_ENTRIES; ++i) {
137
96
    if (offset_table[i] >= 0) {
138
72
      if (offset_table[i] > size) {
139
0
        return false;
140
0
      }
141
72
      int64_t entry_size = size - offset_table[i];
142
72
      unsigned j = i + 1;
143
92
      while (j < num_entries && offset_table[j] == -1) {
144
20
        ++j;
145
20
      }
146
72
      if (j < num_entries) {
147
68
        if (offset_table[j] < 0 || offset_table[j] > size) {
148
0
          return false;
149
0
        }
150
68
        entry_size = offset_table[j] - offset_table[i];
151
68
      }
152
72
      if (entry_size < 0) {
153
0
        return false;
154
0
      }
155
72
      entries_[i].resize(entry_size);
156
72
      if (entry_size > 0 && !fp.DeSerialize(&entries_[i][0], entry_size)) {
157
0
        return false;
158
0
      }
159
72
    }
160
96
  }
161
4
  if (entries_[TESSDATA_VERSION].empty()) {
162
0
    SetVersionString("Pre-4.0.0");
163
0
  }
164
4
  is_loaded_ = true;
165
4
  return true;
166
4
}
167
168
// Overwrites a single entry of the given type.
169
0
void TessdataManager::OverwriteEntry(TessdataType type, const char *data, int size) {
170
0
  is_loaded_ = true;
171
0
  entries_[type].resize(size);
172
0
  memcpy(&entries_[type][0], data, size);
173
0
}
174
175
// Saves to the given filename.
176
0
bool TessdataManager::SaveFile(const char *filename, FileWriter writer) const {
177
  // TODO: This method supports only the proprietary file format.
178
0
  ASSERT_HOST(is_loaded_);
179
0
  std::vector<char> data;
180
0
  Serialize(&data);
181
0
  if (writer == nullptr) {
182
0
    return SaveDataToFile(data, filename);
183
0
  } else {
184
0
    return (*writer)(data, filename);
185
0
  }
186
0
}
187
188
// Serializes to the given vector.
189
0
void TessdataManager::Serialize(std::vector<char> *data) const {
190
  // TODO: This method supports only the proprietary file format.
191
0
  ASSERT_HOST(is_loaded_);
192
  // Compute the offset_table and total size.
193
0
  int64_t offset_table[TESSDATA_NUM_ENTRIES];
194
0
  int64_t offset = sizeof(int32_t) + sizeof(offset_table);
195
0
  for (unsigned i = 0; i < TESSDATA_NUM_ENTRIES; ++i) {
196
0
    if (entries_[i].empty()) {
197
0
      offset_table[i] = -1;
198
0
    } else {
199
0
      offset_table[i] = offset;
200
0
      offset += entries_[i].size();
201
0
    }
202
0
  }
203
0
  data->resize(offset, 0);
204
0
  int32_t num_entries = TESSDATA_NUM_ENTRIES;
205
0
  TFile fp;
206
0
  fp.OpenWrite(data);
207
0
  fp.Serialize(&num_entries);
208
0
  fp.Serialize(&offset_table[0], countof(offset_table));
209
0
  for (const auto &entry : entries_) {
210
0
    if (!entry.empty()) {
211
0
      fp.Serialize(&entry[0], entry.size());
212
0
    }
213
0
  }
214
0
}
215
216
// Resets to the initial state, keeping the reader.
217
8
void TessdataManager::Clear() {
218
192
  for (auto &entry : entries_) {
219
192
    entry.clear();
220
192
  }
221
8
  is_loaded_ = false;
222
8
}
223
224
// Prints a directory of contents.
225
0
void TessdataManager::Directory() const {
226
0
  printf("Version:%s\n", VersionString().c_str());
227
0
  auto offset = TESSDATA_NUM_ENTRIES * sizeof(int64_t);
228
0
  for (unsigned i = 0; i < TESSDATA_NUM_ENTRIES; ++i) {
229
0
    if (!entries_[i].empty()) {
230
0
      printf("%u:%s:size=%zu, offset=%zu\n", i, kTessdataFileSuffixes[i], entries_[i].size(),
231
0
              offset);
232
0
      offset += entries_[i].size();
233
0
    }
234
0
  }
235
0
}
236
237
// Opens the given TFile pointer to the given component type.
238
// Returns false in case of failure.
239
76
bool TessdataManager::GetComponent(TessdataType type, TFile *fp) {
240
76
  if (!is_loaded_ && !Init(data_file_name_.c_str())) {
241
0
    return false;
242
0
  }
243
76
  const TessdataManager *const_this = this;
244
76
  return const_this->GetComponent(type, fp);
245
76
}
246
247
// As non-const version except it can't load the component if not already
248
// loaded.
249
84
bool TessdataManager::GetComponent(TessdataType type, TFile *fp) const {
250
84
  ASSERT_HOST(is_loaded_);
251
84
  if (entries_[type].empty()) {
252
16
    return false;
253
16
  }
254
68
  fp->Open(&entries_[type][0], entries_[type].size());
255
68
  fp->set_swap(swap_);
256
68
  return true;
257
84
}
258
259
// Returns the current version string.
260
0
std::string TessdataManager::VersionString() const {
261
0
  return std::string(&entries_[TESSDATA_VERSION][0], entries_[TESSDATA_VERSION].size());
262
0
}
263
264
// Sets the version string to the given v_str.
265
4
void TessdataManager::SetVersionString(const std::string &v_str) {
266
4
  entries_[TESSDATA_VERSION].resize(v_str.size());
267
4
  memcpy(&entries_[TESSDATA_VERSION][0], v_str.data(), v_str.size());
268
4
}
269
270
bool TessdataManager::CombineDataFiles(const char *language_data_path_prefix,
271
0
                                       const char *output_filename) {
272
  // Load individual tessdata components from files.
273
0
  for (auto filesuffix : kTessdataFileSuffixes) {
274
0
    TessdataType type;
275
0
    ASSERT_HOST(TessdataTypeFromFileSuffix(filesuffix, &type));
276
0
    std::string filename = language_data_path_prefix;
277
0
    filename += filesuffix;
278
0
    FILE *fp = fopen(filename.c_str(), "rb");
279
0
    if (fp != nullptr) {
280
0
      fclose(fp);
281
0
      if (!LoadDataFromFile(filename.c_str(), &entries_[type])) {
282
0
        tprintf("Load of file %s failed!\n", filename.c_str());
283
0
        return false;
284
0
      }
285
0
    }
286
0
  }
287
0
  is_loaded_ = true;
288
289
  // Make sure that the required components are present.
290
0
  if (!IsBaseAvailable() && !IsLSTMAvailable()) {
291
0
    tprintf(
292
0
        "Error: traineddata file must contain at least (a unicharset file"
293
0
        " and inttemp) OR an lstm file.\n");
294
0
    return false;
295
0
  }
296
  // Write updated data to the output traineddata file.
297
0
  return SaveFile(output_filename, nullptr);
298
0
}
299
300
bool TessdataManager::OverwriteComponents(const char *new_traineddata_filename,
301
0
                                          char **component_filenames, int num_new_components) {
302
  // Open the files with the new components.
303
  // TODO: This method supports only the proprietary file format.
304
0
  for (int i = 0; i < num_new_components; ++i) {
305
0
    TessdataType type;
306
0
    if (TessdataTypeFromFileName(component_filenames[i], &type)) {
307
0
      if (!LoadDataFromFile(component_filenames[i], &entries_[type])) {
308
0
        tprintf("Failed to read component file:%s\n", component_filenames[i]);
309
0
        return false;
310
0
      }
311
0
    }
312
0
  }
313
314
  // Write updated data to the output traineddata file.
315
0
  return SaveFile(new_traineddata_filename, nullptr);
316
0
}
317
318
0
bool TessdataManager::ExtractToFile(const char *filename) {
319
0
  TessdataType type = TESSDATA_NUM_ENTRIES;
320
0
  ASSERT_HOST(tesseract::TessdataManager::TessdataTypeFromFileName(filename, &type));
321
0
  if (entries_[type].empty()) {
322
0
    return false;
323
0
  }
324
0
  return SaveDataToFile(entries_[type], filename);
325
0
}
326
327
0
bool TessdataManager::TessdataTypeFromFileSuffix(const char *suffix, TessdataType *type) {
328
0
  for (unsigned i = 0; i < TESSDATA_NUM_ENTRIES; ++i) {
329
0
    if (strcmp(kTessdataFileSuffixes[i], suffix) == 0) {
330
0
      *type = static_cast<TessdataType>(i);
331
0
      return true;
332
0
    }
333
0
  }
334
#if !defined(NDEBUG)
335
  tprintf(
336
      "TessdataManager can't determine which tessdata"
337
      " component is represented by %s\n",
338
      suffix);
339
#endif
340
0
  return false;
341
0
}
342
343
0
bool TessdataManager::TessdataTypeFromFileName(const char *filename, TessdataType *type) {
344
  // Get the file suffix (extension)
345
0
  const char *suffix = strrchr(filename, '.');
346
0
  if (suffix == nullptr || *(++suffix) == '\0') {
347
0
    return false;
348
0
  }
349
0
  return TessdataTypeFromFileSuffix(suffix, type);
350
0
}
351
352
} // namespace tesseract