2019-01-22 22:17:54 +08:00
|
|
|
// (C) Copyright 2017, 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 "include_gunit.h"
|
|
|
|
#include "normstrngs.h"
|
|
|
|
#include "normstrngs_test.h"
|
2018-08-24 21:07:48 +08:00
|
|
|
|
|
|
|
namespace tesseract {
|
|
|
|
|
|
|
|
// Though the unicode example for Telugu in section 12.7:
|
|
|
|
// http://www.unicode.org/versions/Unicode9.0.0/ch12.pdf
|
|
|
|
// shows using ZWNJ to force an explicit virama, in practice a ZWNJ is used to
|
|
|
|
// suppress a conjugate that would otherwise occur. If a consonant is followed
|
|
|
|
// by a virama and then by a non-Indic character, OpenType will presume that
|
|
|
|
// the user simply meant to suppress the inherent vowel of the consonant
|
|
|
|
// and render it as the consonant with an explicit virama, the same as if
|
|
|
|
// a ZWNJ had followed. Since this is confusing to an OCR engine, the
|
|
|
|
// normalizer always puts a termninating ZWNJ on the end if not present,
|
|
|
|
// and accepts the string as valid.
|
|
|
|
TEST(ValidateIndicTest, AddsJoinerToTerminalVirama) {
|
2021-03-13 05:06:34 +08:00
|
|
|
std::string str = "\u0c15\u0c4d"; // KA - virama
|
|
|
|
std::string target_str = "\u0c15\u0c4d\u200c"; // KA - virama - ZWNJ
|
2018-08-24 21:07:48 +08:00
|
|
|
ExpectGraphemeModeResults(str, UnicodeNormMode::kNFC, 3, 2, 1, target_str);
|
|
|
|
// Same result if we started with the normalized string.
|
2021-03-13 05:06:34 +08:00
|
|
|
ExpectGraphemeModeResults(target_str, UnicodeNormMode::kNFC, 3, 2, 1, target_str);
|
2018-08-24 21:07:48 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Only one dependent vowel is allowed.
|
|
|
|
TEST(ValidateIndicTest, OnlyOneDependentVowel) {
|
2021-03-13 05:06:34 +08:00
|
|
|
std::string str = "\u0d15\u0d3e\u0d42"; // KA AA UU
|
2019-01-22 22:17:54 +08:00
|
|
|
std::string dest;
|
2021-03-13 05:06:34 +08:00
|
|
|
EXPECT_FALSE(NormalizeUTF8String(UnicodeNormMode::kNFC, OCRNorm::kNone, GraphemeNorm::kNormalize,
|
|
|
|
str.c_str(), &dest))
|
2018-08-24 21:07:48 +08:00
|
|
|
<< PrintString32WithUnicodes(str);
|
|
|
|
}
|
|
|
|
|
|
|
|
// [c26][c4d][c01]
|
|
|
|
// A consonant (DA) followed by the virama followed by a bindu
|
|
|
|
// Syllable modifiers [c01][c02][c03] all modify the pronunciation of
|
|
|
|
// the vowel in a syllable, as does the virama [c04]. You can only
|
|
|
|
// have one of these on a syllable.
|
|
|
|
//
|
|
|
|
// References:
|
|
|
|
// http://www.omniglot.com/writing/telugu.htm
|
|
|
|
TEST(ValidateIndicTest, OnlyOneVowelModifier) {
|
2021-03-13 05:06:34 +08:00
|
|
|
std::string str = "\u0c26\u0c4d\u0c01"; // DA virama candrabindu
|
2019-01-22 22:17:54 +08:00
|
|
|
std::string result;
|
2021-03-13 05:06:34 +08:00
|
|
|
EXPECT_TRUE(NormalizeUTF8String(UnicodeNormMode::kNFC, OCRNorm::kNone, GraphemeNorm::kNormalize,
|
|
|
|
str.c_str(), &result));
|
2018-08-24 21:07:48 +08:00
|
|
|
// It made 1 grapheme of 4 chars, by terminating the explicit virama.
|
2019-01-22 22:17:54 +08:00
|
|
|
EXPECT_EQ(std::string("\u0c26\u0c4d\u200c\u0c01"), result);
|
2018-08-24 21:07:48 +08:00
|
|
|
|
2021-03-13 05:06:34 +08:00
|
|
|
str = "\u0995\u0983\u0981"; // KA visarga candrabindu
|
|
|
|
EXPECT_FALSE(NormalizeUTF8String(UnicodeNormMode::kNFC, OCRNorm::kNone, GraphemeNorm::kNormalize,
|
|
|
|
str.c_str(), &result));
|
2018-08-24 21:07:48 +08:00
|
|
|
|
|
|
|
// Exception: Malayalam allows multiple anusvara.
|
2021-03-13 05:06:34 +08:00
|
|
|
str = "\u0d15\u0d02\u0d02"; // KA Anusvara Anusvara
|
|
|
|
EXPECT_TRUE(NormalizeUTF8String(UnicodeNormMode::kNFC, OCRNorm::kNone, GraphemeNorm::kNormalize,
|
|
|
|
str.c_str(), &result));
|
2018-08-24 21:07:48 +08:00
|
|
|
EXPECT_EQ(str, result);
|
|
|
|
}
|
|
|
|
|
|
|
|
// [c28][c02][c3f]
|
|
|
|
// A consonant (NA) followed by the Anusvara/sunna and another matra (I).
|
|
|
|
// The anusvara [c02] is a pronunciation directive
|
|
|
|
// for a whole syllable and only appears at the end of the syllable
|
|
|
|
// References:
|
|
|
|
// + Unicode v9, 12.1 "Modifier Mark Rules R10,"
|
|
|
|
// and the Microsoft page
|
|
|
|
// http://www.microsoft.com/typography/otfntdev/teluguot/shaping.aspx
|
|
|
|
TEST(ValidateIndicTest, VowelModifierMustBeLast) {
|
2021-03-13 05:06:34 +08:00
|
|
|
std::string str = "\u0c28\u0c02\u0c3f"; // NA Sunna I
|
2019-01-22 22:17:54 +08:00
|
|
|
std::string dest;
|
2021-03-13 05:06:34 +08:00
|
|
|
EXPECT_FALSE(NormalizeUTF8String(UnicodeNormMode::kNFC, OCRNorm::kNone, GraphemeNorm::kNormalize,
|
|
|
|
str.c_str(), &dest))
|
2018-08-24 21:07:48 +08:00
|
|
|
<< PrintString32WithUnicodes(str);
|
|
|
|
// Swap c02/c3f and all is ok.
|
2021-03-13 05:06:34 +08:00
|
|
|
str = "\u0c28\u0c3f\u0c02"; // NA I Sunna
|
|
|
|
EXPECT_TRUE(NormalizeUTF8String(UnicodeNormMode::kNFC, OCRNorm::kNone, GraphemeNorm::kNormalize,
|
|
|
|
str.c_str(), &dest))
|
2018-08-24 21:07:48 +08:00
|
|
|
<< PrintString32WithUnicodes(str);
|
|
|
|
EXPECT_EQ(dest, str);
|
|
|
|
}
|
|
|
|
|
|
|
|
// [c05][c47]
|
|
|
|
// A Vowel (A) followed by a combining vowel/matra (EE).
|
|
|
|
// In Telugu, matras are only put on consonants, not independent
|
|
|
|
// vowels.
|
|
|
|
// References:
|
|
|
|
// + Unicode v9, 12.1:
|
|
|
|
// Principles of the Devanagari Script: Dependent Vowel Signs (Matras).
|
|
|
|
// + http://varamozhi.sourceforge.net/iscii91.pdf
|
|
|
|
TEST(ValidateIndicTest, MatrasFollowConsonantsNotVowels) {
|
2021-03-13 05:06:34 +08:00
|
|
|
std::string str = "\u0c05\u0c47"; // A EE
|
2019-01-22 22:17:54 +08:00
|
|
|
std::string dest;
|
2021-03-13 05:06:34 +08:00
|
|
|
EXPECT_FALSE(NormalizeUTF8String(UnicodeNormMode::kNFC, OCRNorm::kNone, GraphemeNorm::kNormalize,
|
|
|
|
str.c_str(), &dest))
|
2018-08-24 21:07:48 +08:00
|
|
|
<< PrintString32WithUnicodes(str);
|
2021-03-13 05:06:34 +08:00
|
|
|
str = "\u0c1e\u0c3e"; // NYA AA
|
|
|
|
EXPECT_TRUE(NormalizeUTF8String(UnicodeNormMode::kNFC, OCRNorm::kNone, GraphemeNorm::kNormalize,
|
|
|
|
str.c_str(), &dest))
|
2018-08-24 21:07:48 +08:00
|
|
|
<< PrintString32WithUnicodes(str);
|
|
|
|
EXPECT_EQ(dest, str);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sub-graphemes are allowed if GraphemeNorm is turned off.
|
|
|
|
TEST(ValidateIndicTest, SubGraphemes) {
|
2021-03-13 05:06:34 +08:00
|
|
|
std::string str = "\u0d3e"; // AA
|
2019-01-22 22:17:54 +08:00
|
|
|
std::string dest;
|
2021-03-13 05:06:34 +08:00
|
|
|
EXPECT_FALSE(NormalizeUTF8String(UnicodeNormMode::kNFC, OCRNorm::kNone, GraphemeNorm::kNormalize,
|
|
|
|
str.c_str(), &dest))
|
2018-08-24 21:07:48 +08:00
|
|
|
<< PrintString32WithUnicodes(str);
|
2021-03-13 05:06:34 +08:00
|
|
|
EXPECT_TRUE(NormalizeUTF8String(UnicodeNormMode::kNFC, OCRNorm::kNone, GraphemeNorm::kNone,
|
|
|
|
str.c_str(), &dest))
|
2018-08-24 21:07:48 +08:00
|
|
|
<< PrintString32WithUnicodes(str);
|
|
|
|
EXPECT_EQ(dest, str);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ValidateIndicTest, Nukta) {
|
2021-03-13 05:06:34 +08:00
|
|
|
std::string str = "\u0c95\u0cbc\u0ccd\u0cb9"; // KA Nukta Virama HA
|
2019-01-22 22:17:54 +08:00
|
|
|
std::vector<std::string> glyphs;
|
2021-03-13 05:06:34 +08:00
|
|
|
EXPECT_TRUE(NormalizeCleanAndSegmentUTF8(UnicodeNormMode::kNFC, OCRNorm::kNone,
|
|
|
|
GraphemeNormMode::kGlyphSplit, true, str.c_str(),
|
|
|
|
&glyphs));
|
2018-08-24 21:07:48 +08:00
|
|
|
EXPECT_EQ(glyphs.size(), 3);
|
2019-01-22 22:17:54 +08:00
|
|
|
EXPECT_EQ(glyphs[2], std::string("\u0ccd\u0cb9"));
|
2018-08-24 21:07:48 +08:00
|
|
|
// Swapped Nukta and Virama are not allowed, but NFC normalization fixes it.
|
2021-03-13 05:06:34 +08:00
|
|
|
std::string str2 = "\u0c95\u0ccd\u0cbc\u0cb9"; // KA Virama Nukta HA
|
2018-08-24 21:07:48 +08:00
|
|
|
ExpectGraphemeModeResults(str2, UnicodeNormMode::kNFC, 4, 3, 1, str);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sinhala has some of its own specific rules. See www.macciato.com/sinhala
|
|
|
|
TEST(ValidateIndicTest, SinhalaRakaransaya) {
|
2021-03-13 05:06:34 +08:00
|
|
|
std::string str = "\u0d9a\u0dca\u200d\u0dbb"; // KA Virama ZWJ Rayanna
|
2019-01-22 22:17:54 +08:00
|
|
|
std::string dest;
|
2021-03-13 05:06:34 +08:00
|
|
|
EXPECT_TRUE(NormalizeUTF8String(UnicodeNormMode::kNFC, OCRNorm::kNone, GraphemeNorm::kNormalize,
|
|
|
|
str.c_str(), &dest))
|
2018-08-24 21:07:48 +08:00
|
|
|
<< PrintString32WithUnicodes(str);
|
|
|
|
EXPECT_EQ(dest, str);
|
2019-01-22 22:17:54 +08:00
|
|
|
std::vector<std::string> glyphs;
|
2021-03-13 05:06:34 +08:00
|
|
|
EXPECT_TRUE(NormalizeCleanAndSegmentUTF8(UnicodeNormMode::kNFC, OCRNorm::kNone,
|
|
|
|
GraphemeNormMode::kGlyphSplit, true, str.c_str(),
|
|
|
|
&glyphs));
|
2018-08-24 21:07:48 +08:00
|
|
|
EXPECT_EQ(glyphs.size(), 2);
|
2019-01-22 22:17:54 +08:00
|
|
|
EXPECT_EQ(glyphs[1], std::string("\u0dca\u200d\u0dbb"));
|
2018-08-24 21:07:48 +08:00
|
|
|
// Can be followed by a dependent vowel.
|
2021-03-13 05:06:34 +08:00
|
|
|
str += "\u0dd9"; // E
|
|
|
|
EXPECT_TRUE(NormalizeUTF8String(UnicodeNormMode::kNFC, OCRNorm::kNone, GraphemeNorm::kNormalize,
|
|
|
|
str.c_str(), &dest))
|
2018-08-24 21:07:48 +08:00
|
|
|
<< PrintString32WithUnicodes(str);
|
|
|
|
EXPECT_EQ(dest, str);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ValidateIndicTest, SinhalaYansaya) {
|
2021-03-13 05:06:34 +08:00
|
|
|
std::string str = "\u0d9a\u0dca\u200d\u0dba"; // KA Virama ZWJ Yayanna
|
2019-01-22 22:17:54 +08:00
|
|
|
std::string dest;
|
2021-03-13 05:06:34 +08:00
|
|
|
EXPECT_TRUE(NormalizeUTF8String(UnicodeNormMode::kNFC, OCRNorm::kNone, GraphemeNorm::kNormalize,
|
|
|
|
str.c_str(), &dest))
|
2018-08-24 21:07:48 +08:00
|
|
|
<< PrintString32WithUnicodes(str);
|
|
|
|
EXPECT_EQ(dest, str);
|
|
|
|
// Can be followed by a dependent vowel.
|
2021-03-13 05:06:34 +08:00
|
|
|
str += "\u0ddd"; // OO
|
|
|
|
EXPECT_TRUE(NormalizeUTF8String(UnicodeNormMode::kNFC, OCRNorm::kNone, GraphemeNorm::kNormalize,
|
|
|
|
str.c_str(), &dest))
|
2018-08-24 21:07:48 +08:00
|
|
|
<< PrintString32WithUnicodes(str);
|
|
|
|
EXPECT_EQ(dest, str);
|
2019-01-22 22:17:54 +08:00
|
|
|
std::vector<std::string> glyphs;
|
2021-03-13 05:06:34 +08:00
|
|
|
EXPECT_TRUE(NormalizeCleanAndSegmentUTF8(UnicodeNormMode::kNFC, OCRNorm::kNone,
|
|
|
|
GraphemeNormMode::kGlyphSplit, true, str.c_str(),
|
|
|
|
&glyphs));
|
2018-08-24 21:07:48 +08:00
|
|
|
EXPECT_EQ(glyphs.size(), 3);
|
2019-01-22 22:17:54 +08:00
|
|
|
EXPECT_EQ(glyphs[1], std::string("\u0dca\u200d\u0dba"));
|
2018-08-24 21:07:48 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ValidateIndicTest, SinhalaRepaya) {
|
2021-03-13 05:06:34 +08:00
|
|
|
std::string str = "\u0d9a\u0dbb\u0dca\u200d\u0db8"; // KA Rayanna Virama ZWJ MA
|
2019-01-22 22:17:54 +08:00
|
|
|
std::vector<std::string> glyphs;
|
2021-03-13 05:06:34 +08:00
|
|
|
EXPECT_TRUE(NormalizeCleanAndSegmentUTF8(UnicodeNormMode::kNFC, OCRNorm::kNone,
|
|
|
|
GraphemeNormMode::kCombined, true, str.c_str(),
|
|
|
|
&glyphs));
|
2018-08-24 21:07:48 +08:00
|
|
|
EXPECT_EQ(glyphs.size(), 2);
|
2019-01-22 22:17:54 +08:00
|
|
|
EXPECT_EQ(glyphs[1], std::string("\u0dbb\u0dca\u200d\u0db8"));
|
2021-03-13 05:06:34 +08:00
|
|
|
EXPECT_TRUE(NormalizeCleanAndSegmentUTF8(UnicodeNormMode::kNFC, OCRNorm::kNone,
|
|
|
|
GraphemeNormMode::kGlyphSplit, true, str.c_str(),
|
|
|
|
&glyphs));
|
2018-08-24 21:07:48 +08:00
|
|
|
EXPECT_EQ(glyphs.size(), 3);
|
2019-01-22 22:17:54 +08:00
|
|
|
EXPECT_EQ(glyphs[1], std::string("\u0dbb\u0dca\u200d"));
|
2018-08-24 21:07:48 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ValidateIndicTest, SinhalaSpecials) {
|
|
|
|
// Sinhala has some exceptions from the usual rules.
|
2019-01-22 22:17:54 +08:00
|
|
|
std::string str = "\u0dc0\u0d9c\u0dca\u200d\u0dbb\u0dca\u200d\u0dbb\u0dca\u200d";
|
|
|
|
std::vector<std::string> glyphs;
|
2021-03-13 05:06:34 +08:00
|
|
|
EXPECT_TRUE(NormalizeCleanAndSegmentUTF8(UnicodeNormMode::kNFC, OCRNorm::kNone,
|
|
|
|
GraphemeNormMode::kGlyphSplit, true, str.c_str(),
|
|
|
|
&glyphs));
|
2018-08-24 21:07:48 +08:00
|
|
|
EXPECT_EQ(glyphs.size(), 5) << PrintStringVectorWithUnicodes(glyphs);
|
2019-01-22 22:17:54 +08:00
|
|
|
EXPECT_EQ(glyphs[0], std::string("\u0dc0"));
|
|
|
|
EXPECT_EQ(glyphs[1], std::string("\u0d9c"));
|
|
|
|
EXPECT_EQ(glyphs[2], std::string("\u0dca\u200d\u0dbb"));
|
|
|
|
EXPECT_EQ(glyphs[3], std::string("\u0dca\u200d"));
|
|
|
|
EXPECT_EQ(glyphs[4], std::string("\u0dbb\u0dca\u200d"));
|
2018-08-24 21:07:48 +08:00
|
|
|
str = "\u0dc3\u0dbb\u0dca\u200d\u0dbb\u0dca\u200d\u0dcf";
|
2021-03-13 05:06:34 +08:00
|
|
|
EXPECT_TRUE(NormalizeCleanAndSegmentUTF8(UnicodeNormMode::kNFC, OCRNorm::kNone,
|
|
|
|
GraphemeNormMode::kGlyphSplit, true, str.c_str(),
|
|
|
|
&glyphs));
|
2018-08-24 21:07:48 +08:00
|
|
|
EXPECT_EQ(glyphs.size(), 4) << PrintStringVectorWithUnicodes(glyphs);
|
2019-01-22 22:17:54 +08:00
|
|
|
EXPECT_EQ(glyphs[0], std::string("\u0dc3"));
|
|
|
|
EXPECT_EQ(glyphs[1], std::string("\u0dbb\u0dca\u200d"));
|
|
|
|
EXPECT_EQ(glyphs[2], std::string("\u0dbb\u0dca\u200d"));
|
|
|
|
EXPECT_EQ(glyphs[3], std::string("\u0dcf"));
|
2018-08-24 21:07:48 +08:00
|
|
|
}
|
|
|
|
|
2021-03-13 05:06:34 +08:00
|
|
|
} // namespace tesseract
|