Mercurial > hgrepos > Python2 > PyMuPDF
diff mupdf-source/thirdparty/tesseract/src/ccutil/ambigs.cpp @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mupdf-source/thirdparty/tesseract/src/ccutil/ambigs.cpp Mon Sep 15 11:43:07 2025 +0200 @@ -0,0 +1,411 @@ +/////////////////////////////////////////////////////////////////////// +// File: ambigs.cpp +// Description: Functions for dealing with ambiguities +// (training and recognition). +// Author: Daria Antonova +// +// (C) Copyright 2008, Google Inc. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +/////////////////////////////////////////////////////////////////////// + +#include "ambigs.h" + +#include "helpers.h" +#include "universalambigs.h" + +#include <cstdio> + +#if defined(_WIN32) && !defined(__GNUC__) +# define strtok_r(str, delim, saveptr) strtok_s(str, delim, saveptr) +#endif /* _WIN32 && !__GNUC__ */ + +namespace tesseract { + +static const char kAmbigDelimiters[] = "\t "; +static const char kIllegalMsg[] = "Illegal ambiguity specification on line %d\n"; +static const char kIllegalUnicharMsg[] = "Illegal unichar %s in ambiguity specification\n"; + +// Maximum line size: +// 10 for sizes of ambigs, tabs, abmig type and newline +// UNICHAR_LEN * (MAX_AMBIG_SIZE + 1) for each part of the ambig +const int kMaxAmbigStringSize = UNICHAR_LEN * (MAX_AMBIG_SIZE + 1); + +AmbigSpec::AmbigSpec() : correct_ngram_id(INVALID_UNICHAR_ID), type(NOT_AMBIG), wrong_ngram_size(0) { + wrong_ngram[0] = INVALID_UNICHAR_ID; + correct_fragments[0] = INVALID_UNICHAR_ID; +} + +// Initializes the ambigs by adding a nullptr pointer to each table. +void UnicharAmbigs::InitUnicharAmbigs(const UNICHARSET &unicharset, bool use_ambigs_for_adaption) { + for (unsigned i = 0; i < unicharset.size(); ++i) { + replace_ambigs_.push_back(nullptr); + dang_ambigs_.push_back(nullptr); + one_to_one_definite_ambigs_.push_back(nullptr); + if (use_ambigs_for_adaption) { + ambigs_for_adaption_.push_back(nullptr); + reverse_ambigs_for_adaption_.push_back(nullptr); + } + } +} + +// Loads the universal ambigs that are useful for any language. +void UnicharAmbigs::LoadUniversal(const UNICHARSET &encoder_set, UNICHARSET *unicharset) { + TFile file; + if (!file.Open(kUniversalAmbigsFile, ksizeofUniversalAmbigsFile)) { + return; + } + LoadUnicharAmbigs(encoder_set, &file, 0, false, unicharset); +} + +void UnicharAmbigs::LoadUnicharAmbigs(const UNICHARSET &encoder_set, TFile *ambig_file, + int debug_level, bool use_ambigs_for_adaption, + UNICHARSET *unicharset) { + UnicharIdVector *adaption_ambigs_entry; + if (debug_level) { + tprintf("Reading ambiguities\n"); + } + + int test_ambig_part_size; + int replacement_ambig_part_size; + // The space for buffer is allocated on the heap to avoid + // GCC frame size warning. + const int kBufferSize = 10 + 2 * kMaxAmbigStringSize; + char *buffer = new char[kBufferSize]; + char replacement_string[kMaxAmbigStringSize]; + UNICHAR_ID test_unichar_ids[MAX_AMBIG_SIZE + 1]; + int line_num = 0; + int type = NOT_AMBIG; + + // Determine the version of the ambigs file. + int version = 0; + ASSERT_HOST(ambig_file->FGets(buffer, kBufferSize) != nullptr && buffer[0] != '\0'); + if (*buffer == 'v') { + version = static_cast<int>(strtol(buffer + 1, nullptr, 10)); + ++line_num; + } else { + ambig_file->Rewind(); + } + while (ambig_file->FGets(buffer, kBufferSize) != nullptr) { + chomp_string(buffer); + if (debug_level > 2) { + tprintf("read line %s\n", buffer); + } + ++line_num; + if (!ParseAmbiguityLine(line_num, version, debug_level, encoder_set, buffer, + &test_ambig_part_size, test_unichar_ids, &replacement_ambig_part_size, + replacement_string, &type)) { + continue; + } + // Construct AmbigSpec and add it to the appropriate AmbigSpec_LIST. + auto *ambig_spec = new AmbigSpec(); + if (!InsertIntoTable((type == REPLACE_AMBIG) ? replace_ambigs_ : dang_ambigs_, + test_ambig_part_size, test_unichar_ids, replacement_ambig_part_size, + replacement_string, type, ambig_spec, unicharset)) { + continue; + } + + // Update one_to_one_definite_ambigs_. + if (test_ambig_part_size == 1 && replacement_ambig_part_size == 1 && type == DEFINITE_AMBIG) { + if (one_to_one_definite_ambigs_[test_unichar_ids[0]] == nullptr) { + one_to_one_definite_ambigs_[test_unichar_ids[0]] = new UnicharIdVector(); + } + one_to_one_definite_ambigs_[test_unichar_ids[0]]->push_back(ambig_spec->correct_ngram_id); + } + // Update ambigs_for_adaption_. + if (use_ambigs_for_adaption) { + std::vector<UNICHAR_ID> encoding; + // Silently ignore invalid strings, as before, so it is safe to use a + // universal ambigs file. + if (unicharset->encode_string(replacement_string, true, &encoding, nullptr, nullptr)) { + for (int i = 0; i < test_ambig_part_size; ++i) { + if (ambigs_for_adaption_[test_unichar_ids[i]] == nullptr) { + ambigs_for_adaption_[test_unichar_ids[i]] = new UnicharIdVector(); + } + adaption_ambigs_entry = ambigs_for_adaption_[test_unichar_ids[i]]; + for (int id_to_insert : encoding) { + ASSERT_HOST(id_to_insert != INVALID_UNICHAR_ID); + // Add the new unichar id to adaption_ambigs_entry (only if the + // vector does not already contain it) keeping it in sorted order. + size_t j; + for (j = 0; + j < adaption_ambigs_entry->size() && (*adaption_ambigs_entry)[j] > id_to_insert; + ++j) { + } + if (j < adaption_ambigs_entry->size()) { + if ((*adaption_ambigs_entry)[j] != id_to_insert) { + adaption_ambigs_entry->insert(adaption_ambigs_entry->begin() + j, id_to_insert); + } + } else { + adaption_ambigs_entry->push_back(id_to_insert); + } + } + } + } + } + } + delete[] buffer; + + // Fill in reverse_ambigs_for_adaption from ambigs_for_adaption vector. + if (use_ambigs_for_adaption) { + for (size_t i = 0; i < ambigs_for_adaption_.size(); ++i) { + adaption_ambigs_entry = ambigs_for_adaption_[i]; + if (adaption_ambigs_entry == nullptr) { + continue; + } + for (size_t j = 0; j < adaption_ambigs_entry->size(); ++j) { + UNICHAR_ID ambig_id = (*adaption_ambigs_entry)[j]; + if (reverse_ambigs_for_adaption_[ambig_id] == nullptr) { + reverse_ambigs_for_adaption_[ambig_id] = new UnicharIdVector(); + } + reverse_ambigs_for_adaption_[ambig_id]->push_back(i); + } + } + } + + // Print what was read from the input file. + if (debug_level > 1) { + for (int tbl = 0; tbl < 2; ++tbl) { + const UnicharAmbigsVector &print_table = (tbl == 0) ? replace_ambigs_ : dang_ambigs_; + for (size_t i = 0; i < print_table.size(); ++i) { + AmbigSpec_LIST *lst = print_table[i]; + if (lst == nullptr) { + continue; + } + if (!lst->empty()) { + tprintf("%s Ambiguities for %s:\n", (tbl == 0) ? "Replaceable" : "Dangerous", + unicharset->debug_str(i).c_str()); + } + AmbigSpec_IT lst_it(lst); + for (lst_it.mark_cycle_pt(); !lst_it.cycled_list(); lst_it.forward()) { + AmbigSpec *ambig_spec = lst_it.data(); + tprintf("wrong_ngram:"); + UnicharIdArrayUtils::print(ambig_spec->wrong_ngram, *unicharset); + tprintf("correct_fragments:"); + UnicharIdArrayUtils::print(ambig_spec->correct_fragments, *unicharset); + } + } + } + if (use_ambigs_for_adaption) { + for (int vec_id = 0; vec_id < 2; ++vec_id) { + const std::vector<UnicharIdVector *> &vec = + (vec_id == 0) ? ambigs_for_adaption_ : reverse_ambigs_for_adaption_; + for (size_t i = 0; i < vec.size(); ++i) { + adaption_ambigs_entry = vec[i]; + if (adaption_ambigs_entry != nullptr) { + tprintf("%sAmbigs for adaption for %s:\n", (vec_id == 0) ? "" : "Reverse ", + unicharset->debug_str(i).c_str()); + for (size_t j = 0; j < adaption_ambigs_entry->size(); ++j) { + tprintf("%s ", unicharset->debug_str((*adaption_ambigs_entry)[j]).c_str()); + } + tprintf("\n"); + } + } + } + } + } +} + +bool UnicharAmbigs::ParseAmbiguityLine(int line_num, int version, int debug_level, + const UNICHARSET &unicharset, char *buffer, + int *test_ambig_part_size, UNICHAR_ID *test_unichar_ids, + int *replacement_ambig_part_size, char *replacement_string, + int *type) { + if (version > 1) { + // Simpler format is just wrong-string correct-string type\n. + std::string input(buffer); + std::vector<std::string> fields = split(input, ' '); + if (fields.size() != 3) { + if (debug_level) { + tprintf(kIllegalMsg, line_num); + } + return false; + } + // Encode wrong-string. + std::vector<UNICHAR_ID> unichars; + if (!unicharset.encode_string(fields[0].c_str(), true, &unichars, nullptr, nullptr)) { + return false; + } + *test_ambig_part_size = unichars.size(); + if (*test_ambig_part_size > MAX_AMBIG_SIZE) { + if (debug_level) { + tprintf("Too many unichars in ambiguity on line %d\n", line_num); + } + return false; + } + // Copy encoded string to output. + for (size_t i = 0; i < unichars.size(); ++i) { + test_unichar_ids[i] = unichars[i]; + } + test_unichar_ids[unichars.size()] = INVALID_UNICHAR_ID; + // Encode replacement-string to check validity. + if (!unicharset.encode_string(fields[1].c_str(), true, &unichars, nullptr, nullptr)) { + return false; + } + *replacement_ambig_part_size = unichars.size(); + if (*replacement_ambig_part_size > MAX_AMBIG_SIZE) { + if (debug_level) { + tprintf("Too many unichars in ambiguity on line %d\n", line_num); + } + return false; + } + if (sscanf(fields[2].c_str(), "%d", type) != 1) { + if (debug_level) { + tprintf(kIllegalMsg, line_num); + } + return false; + } + snprintf(replacement_string, kMaxAmbigStringSize, "%s", fields[1].c_str()); + return true; + } + int i; + char *next_token; + char *token = strtok_r(buffer, kAmbigDelimiters, &next_token); + if (!token || sscanf(token, "%d", test_ambig_part_size) != 1 || + *test_ambig_part_size <= 0) { + if (debug_level) { + tprintf(kIllegalMsg, line_num); + } + return false; + } + if (*test_ambig_part_size > MAX_AMBIG_SIZE) { + if (debug_level) { + tprintf("Too many unichars in ambiguity on line %d\n", line_num); + } + return false; + } + for (i = 0; i < *test_ambig_part_size; ++i) { + if (!(token = strtok_r(nullptr, kAmbigDelimiters, &next_token))) { + break; + } + if (!unicharset.contains_unichar(token)) { + if (debug_level) { + tprintf(kIllegalUnicharMsg, token); + } + break; + } + test_unichar_ids[i] = unicharset.unichar_to_id(token); + } + test_unichar_ids[i] = INVALID_UNICHAR_ID; + + if (i != *test_ambig_part_size || !(token = strtok_r(nullptr, kAmbigDelimiters, &next_token)) || + sscanf(token, "%d", replacement_ambig_part_size) != 1 || + *replacement_ambig_part_size <= 0) { + if (debug_level) { + tprintf(kIllegalMsg, line_num); + } + return false; + } + if (*replacement_ambig_part_size > MAX_AMBIG_SIZE) { + if (debug_level) { + tprintf("Too many unichars in ambiguity on line %d\n", line_num); + } + return false; + } + replacement_string[0] = '\0'; + for (i = 0; i < *replacement_ambig_part_size; ++i) { + if (!(token = strtok_r(nullptr, kAmbigDelimiters, &next_token))) { + break; + } + strcat(replacement_string, token); + if (!unicharset.contains_unichar(token)) { + if (debug_level) { + tprintf(kIllegalUnicharMsg, token); + } + break; + } + } + if (i != *replacement_ambig_part_size) { + if (debug_level) { + tprintf(kIllegalMsg, line_num); + } + return false; + } + if (version > 0) { + // The next field being true indicates that the ambiguity should + // always be substituted (e.g. '' should always be changed to "). + // For such "certain" n -> m ambigs tesseract will insert character + // fragments for the n pieces in the unicharset. AmbigsFound() + // will then replace the incorrect ngram with the character + // fragments of the correct character (or ngram if m > 1). + // Note that if m > 1, an ngram will be inserted into the + // modified word, not the individual unigrams. Tesseract + // has limited support for ngram unichar (e.g. dawg permuter). + token = strtok_r(nullptr, kAmbigDelimiters, &next_token); + if (!token || sscanf(token, "%d", type) != 1) { + if (debug_level) { + tprintf(kIllegalMsg, line_num); + } + return false; + } + } + return true; +} + +bool UnicharAmbigs::InsertIntoTable(UnicharAmbigsVector &table, int test_ambig_part_size, + UNICHAR_ID *test_unichar_ids, int replacement_ambig_part_size, + const char *replacement_string, int type, AmbigSpec *ambig_spec, + UNICHARSET *unicharset) { + ambig_spec->type = static_cast<AmbigType>(type); + if (test_ambig_part_size == 1 && replacement_ambig_part_size == 1 && + unicharset->to_lower(test_unichar_ids[0]) == + unicharset->to_lower(unicharset->unichar_to_id(replacement_string))) { + ambig_spec->type = CASE_AMBIG; + } + + ambig_spec->wrong_ngram_size = + UnicharIdArrayUtils::copy(test_unichar_ids, ambig_spec->wrong_ngram); + + // Since we need to maintain a constant number of unichar positions in + // order to construct ambig_blob_choices vector in NoDangerousAmbig(), for + // each n->m ambiguity we will have to place n character fragments of the + // correct ngram into the corresponding positions in the vector (e.g. given + // "vvvvw" and vvvv->ww we will place v and |ww|0|4 into position 0, v and + // |ww|1|4 into position 1 and so on. The correct ngram is reconstructed + // from fragments by dawg_permute_and_select(). + + // Insert the corresponding correct ngram into the unicharset. + // Unicharset code assumes that the "base" ngram is inserted into + // the unicharset before fragments of this ngram are inserted. + unicharset->unichar_insert(replacement_string, OldUncleanUnichars::kTrue); + ambig_spec->correct_ngram_id = unicharset->unichar_to_id(replacement_string); + if (replacement_ambig_part_size > 1) { + unicharset->set_isngram(ambig_spec->correct_ngram_id, true); + } + // Add the corresponding fragments of the wrong ngram to unicharset. + int i; + for (i = 0; i < test_ambig_part_size; ++i) { + UNICHAR_ID unichar_id; + if (test_ambig_part_size == 1) { + unichar_id = ambig_spec->correct_ngram_id; + } else { + std::string frag_str = + CHAR_FRAGMENT::to_string(replacement_string, i, test_ambig_part_size, false); + unicharset->unichar_insert(frag_str.c_str(), OldUncleanUnichars::kTrue); + unichar_id = unicharset->unichar_to_id(frag_str.c_str()); + } + ambig_spec->correct_fragments[i] = unichar_id; + } + ambig_spec->correct_fragments[i] = INVALID_UNICHAR_ID; + + // Add AmbigSpec for this ambiguity to the corresponding AmbigSpec_LIST. + // Keep AmbigSpec_LISTs sorted by AmbigSpec.wrong_ngram. + if (table[test_unichar_ids[0]] == nullptr) { + table[test_unichar_ids[0]] = new AmbigSpec_LIST(); + } + if (table[test_unichar_ids[0]]->add_sorted(AmbigSpec::compare_ambig_specs, true, ambig_spec)) { + return true; + } + delete ambig_spec; + return false; +} + +} // namespace tesseract
