Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 52 additions & 5 deletions src/ccutil/indexmapbidi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ IndexMap::~IndexMap() = default;
// Uses a binary search to find the result. For faster speed use
// IndexMapBiDi, but that takes more memory.
int IndexMap::SparseToCompact(int sparse_index) const {
if (compact_map_.empty()) {
return -1;
}
auto pos = std::upper_bound(compact_map_.begin(), compact_map_.end(), sparse_index);
if (pos > compact_map_.begin()) {
--pos;
Expand Down Expand Up @@ -142,6 +145,16 @@ void IndexMapBiDi::CopyFrom(const IndexMapBiDi &src) {
// the merges must be concluded by a call to CompleteMerges.
// Returns true if a merge was actually performed.
bool IndexMapBiDi::Merge(int compact_index1, int compact_index2) {
// A compact index may be -1 (meaning "merge away") or in [0, compact size).
const bool index1_ok =
compact_index1 == -1 ||
(compact_index1 >= 0 && static_cast<size_t>(compact_index1) < compact_map_.size());
const bool index2_ok =
compact_index2 == -1 ||
(compact_index2 >= 0 && static_cast<size_t>(compact_index2) < compact_map_.size());
if (!index1_ok || !index2_ok) {
return false;
}
// Find the current master index for index1 and index2.
compact_index1 = MasterCompactIndex(compact_index1);
compact_index2 = MasterCompactIndex(compact_index2);
Expand Down Expand Up @@ -241,14 +254,42 @@ bool IndexMapBiDi::DeSerialize(bool swap, FILE *fp) {
if (!tesseract::DeSerialize(swap, fp, remaining_pairs)) {
return false;
}
// The indices in the file are untrusted. Validate them before using them
// as subscripts, so that corrupt or crafted data is rejected instead of
// writing outside sparse_map_ (or leaving invalid compact indices behind).
// Each sparse slot may be claimed by at most one compact representative
// or one remaining pair, as in any map produced by Setup/CompleteMerges;
// duplicate claims would let a crafted file encode a master cycle that
// makes MasterCompactIndex loop forever.
const size_t sparse_size = static_cast<size_t>(sparse_size_);
std::vector<uint8_t> claimed(sparse_size, 0);
for (int32_t sparse_index : compact_map_) {
if (sparse_index < 0 || static_cast<size_t>(sparse_index) >= sparse_size ||
claimed[sparse_index]) {
return false;
}
claimed[sparse_index] = 1;
}
if (remaining_pairs.size() % 2 != 0) {
return false;
}
for (size_t i = 0; i < remaining_pairs.size(); i += 2) {
const int32_t sparse_index = remaining_pairs[i];
const int32_t compact_index = remaining_pairs[i + 1];
if (sparse_index < 0 || static_cast<size_t>(sparse_index) >= sparse_size ||
compact_index < 0 || static_cast<size_t>(compact_index) >= compact_map_.size() ||
claimed[sparse_index]) {
return false;
}
claimed[sparse_index] = 1;
}
sparse_map_.clear();
sparse_map_.resize(sparse_size_, -1);
sparse_map_.resize(sparse_size, -1);
for (unsigned i = 0; i < compact_map_.size(); ++i) {
sparse_map_[compact_map_[i]] = i;
}
for (size_t i = 0; i < remaining_pairs.size(); ++i) {
int sparse_index = remaining_pairs[i++];
sparse_map_[sparse_index] = remaining_pairs[i];
for (size_t i = 0; i < remaining_pairs.size(); i += 2) {
sparse_map_[remaining_pairs[i]] = remaining_pairs[i + 1];
Comment thread
stweil marked this conversation as resolved.
}
return true;
}
Expand All @@ -264,7 +305,13 @@ int IndexMapBiDi::MapFeatures(const std::vector<int> &sparse, std::vector<int> *
int missed_features = 0;
int prev_good_feature = -1;
for (int f = 0; f < num_features; ++f) {
int feature = sparse_map_[sparse[f]];
const int sparse_index = sparse[f];
if (sparse_index < 0 || static_cast<size_t>(sparse_index) >= sparse_map_.size()) {
// A feature outside the sparse space cannot map to the compact space.
++missed_features;
continue;
}
int feature = sparse_map_[sparse_index];
if (feature >= 0) {
if (feature != prev_good_feature) {
compact->push_back(feature);
Expand Down
10 changes: 10 additions & 0 deletions src/ccutil/indexmapbidi.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ class TESS_API IndexMap {
// CompactToSparse takes a compact index to the corresponding index in the
// sparse space.
int CompactToSparse(int compact_index) const {
if (compact_index < 0 || static_cast<size_t>(compact_index) >= compact_map_.size()) {
return -1;
}
return compact_map_[compact_index];
}
// The size of the sparse space.
Expand Down Expand Up @@ -130,6 +133,10 @@ class TESS_API IndexMapBiDi : public IndexMap {
bool Merge(int compact_index1, int compact_index2);
// Returns true if the given compact index has been deleted.
bool IsCompactDeleted(int index) const {
// An index outside the compact space is not a live compact index.
if (index < 0 || static_cast<size_t>(index) >= compact_map_.size()) {
return true;
}
return MasterCompactIndex(index) < 0;
}
// Completes one or more Merge operations by further compacting the
Expand All @@ -138,6 +145,9 @@ class TESS_API IndexMapBiDi : public IndexMap {

// SparseToCompact takes a sparse index to an index in the compact space.
int SparseToCompact(int sparse_index) const override {
if (sparse_index < 0 || static_cast<size_t>(sparse_index) >= sparse_map_.size()) {
return -1;
}
return sparse_map_[sparse_index];
}
// The size of the sparse space.
Expand Down
95 changes: 62 additions & 33 deletions src/training/pango/pango_font_info.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -222,23 +222,31 @@ bool PangoFontInfo::CoversUTF8Text(const char *utf8_text, int byte_length) const
return false;
}
PangoCoverage *coverage = pango_font_get_coverage(font, nullptr);
for (UNICHAR::const_iterator it = UNICHAR::begin(utf8_text, byte_length);
it != UNICHAR::end(utf8_text, byte_length); ++it) {
if (IsWhitespace(*it) || pango_is_zero_width(*it)) {
continue;
}
if (pango_coverage_get(coverage, *it) != PANGO_COVERAGE_EXACT) {
char tmp[5];
int len = it.get_utf8(tmp);
tmp[len] = '\0';
tlog(2, "'%s' (U+%x) not covered by font\n", tmp, *it);
const char *const text_end = utf8_text + byte_length;
for (const char *p = utf8_text; p < text_end;) {
const int step = UNICHAR::utf8_step(p);
if (step > 0 && step <= text_end - p) {
const int unicode = UNICHAR(p, step).first_uni();
if (!IsWhitespace(unicode) && !pango_is_zero_width(unicode) &&
pango_coverage_get(coverage, unicode) != PANGO_COVERAGE_EXACT) {
char tmp[5];
memcpy(tmp, p, step);
tmp[step] = '\0';
tlog(2, "'%s' (U+%x) not covered by font\n", tmp, unicode);
#if PANGO_VERSION_CHECK(1, 50, 4)
g_object_unref(coverage);
g_object_unref(coverage);
#else
pango_coverage_unref(coverage);
pango_coverage_unref(coverage);
#endif
g_object_unref(font);
return false;
g_object_unref(font);
return false;
}
p += step;
} else {
// An illegal byte or a multibyte sequence truncated at the end of the
// string is not a character to check coverage for. Skipping it one
// byte at a time also avoids reading past the end of the string.
++p;
}
}
#if PANGO_VERSION_CHECK(1, 50, 4)
Expand Down Expand Up @@ -286,19 +294,22 @@ int PangoFontInfo::DropUncoveredChars(std::string *utf8_text) const {
// will repeatedly copy one covered UTF8 character from one to the other, and
// at the end resize the string to the right length.
char *out = const_cast<char *>(utf8_text->c_str());
const UNICHAR::const_iterator it_begin = UNICHAR::begin(utf8_text->c_str(), utf8_text->length());
const UNICHAR::const_iterator it_end = UNICHAR::end(utf8_text->c_str(), utf8_text->length());
for (UNICHAR::const_iterator it = it_begin; it != it_end;) {
// Skip bad utf-8.
if (!it.is_legal()) {
++it; // One suitable error message will still be issued.
const char *const in_end = utf8_text->c_str() + utf8_text->length();
for (const char *p = utf8_text->c_str(); p < in_end;) {
const int step = UNICHAR::utf8_step(p);
if (step <= 0) {
// Skip bad utf-8.
++p;
continue;
}
int unicode = *it;
int utf8_len = it.utf8_len();
const char *utf8_char = it.utf8_data();
// Move it forward before the data gets modified.
++it;
if (step > in_end - p) {
// A multibyte sequence truncated at the end of the string: drop the
// stray bytes instead of reading past the NUL terminator.
++num_dropped_chars;
++p;
continue;
}
const int unicode = UNICHAR(p, step).first_uni();
if (!IsWhitespace(unicode) && !pango_is_zero_width(unicode) &&
pango_coverage_get(coverage, unicode) != PANGO_COVERAGE_EXACT) {
if (TLOG_IS_ON(2)) {
Expand All @@ -308,10 +319,11 @@ int PangoFontInfo::DropUncoveredChars(std::string *utf8_text) const {
delete[] str;
}
++num_dropped_chars;
continue;
} else {
my_strnmove(out, p, step);
out += step;
}
my_strnmove(out, utf8_char, utf8_len);
out += utf8_len;
p += step;
}
#if PANGO_VERSION_CHECK(1, 50, 4)
g_object_unref(coverage);
Expand All @@ -336,10 +348,26 @@ bool PangoFontInfo::GetSpacingProperties(const std::string &utf8_char, int *x_be
// Handle multi-unicode strings by reporting the left-most position of the
// x-bearing, and right-most position of the x-advance if the string were to
// be rendered.
const UNICHAR::const_iterator it_begin = UNICHAR::begin(utf8_char.c_str(), utf8_char.length());
const UNICHAR::const_iterator it_end = UNICHAR::end(utf8_char.c_str(), utf8_char.length());
for (UNICHAR::const_iterator it = it_begin; it != it_end; ++it) {
PangoGlyph glyph_index = get_glyph(font, *it);
bool first_char = true;
const char *p = utf8_char.c_str();
const char *const p_end = p + utf8_char.length();
for (; p < p_end;) {
const int step = UNICHAR::utf8_step(p);
int unicode;
if (step <= 0) {
// The iterator maps an illegal leading byte to a space.
unicode = ' ';
++p;
} else if (step > p_end - p) {
// A multibyte sequence truncated at the end of the string: skip the
// stray bytes instead of reading past the NUL terminator.
++p;
continue;
} else {
unicode = UNICHAR(p, step).first_uni();
p += step;
}
PangoGlyph glyph_index = get_glyph(font, unicode);
if (!glyph_index) {
// Glyph for given unicode character doesn't exist in font.
g_object_unref(font);
Expand All @@ -352,9 +380,10 @@ bool PangoFontInfo::GetSpacingProperties(const std::string &utf8_char, int *x_be
pango_extents_to_pixels(&logical_rect, nullptr);

int bearing = total_advance + PANGO_LBEARING(ink_rect);
if (it == it_begin || bearing < min_bearing) {
if (first_char || bearing < min_bearing) {
min_bearing = bearing;
}
first_char = false;
total_advance += PANGO_RBEARING(logical_rect);
}
*x_bearing = min_bearing;
Expand Down
46 changes: 38 additions & 8 deletions src/training/unicharset/normstrngs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -243,24 +243,54 @@ bool IsUTF8Whitespace(const char *text) {

unsigned int SpanUTF8Whitespace(const char *text) {
int n_white = 0;
for (UNICHAR::const_iterator it = UNICHAR::begin(text, strlen(text));
it != UNICHAR::end(text, strlen(text)); ++it) {
if (!IsWhitespace(*it)) {
const char *p = text;
const char *const end = p + strlen(text);
while (p < end) {
const int step = UNICHAR::utf8_step(p);
if (step <= 0) {
// The iterator maps an illegal leading byte to a space (one byte).
++n_white;
++p;
continue;
}
if (step > end - p) {
// A multibyte sequence truncated at the end of the string is not a
// whitespace character. Stopping here also avoids reading past the NUL
// terminator.
break;
}
if (!IsWhitespace(UNICHAR(p, step).first_uni())) {
break;
}
n_white += it.utf8_len();
n_white += step;
p += step;
}
return n_white;
}

unsigned int SpanUTF8NotWhitespace(const char *text) {
int n_notwhite = 0;
for (UNICHAR::const_iterator it = UNICHAR::begin(text, strlen(text));
it != UNICHAR::end(text, strlen(text)); ++it) {
if (IsWhitespace(*it)) {
const char *p = text;
const char *const end = p + strlen(text);
while (p < end) {
const int step = UNICHAR::utf8_step(p);
if (step <= 0) {
// The iterator maps an illegal leading byte to a space, which ends the
// span of non-whitespace.
break;
}
if (step > end - p) {
// A multibyte sequence truncated at the end of the string is not
// whitespace. Count the remaining bytes without reading past the NUL
// terminator.
n_notwhite += static_cast<int>(end - p);
break;
}
if (IsWhitespace(UNICHAR(p, step).first_uni())) {
break;
}
n_notwhite += it.utf8_len();
n_notwhite += step;
p += step;
}
return n_notwhite;
}
Expand Down
Loading
Loading