blob: 5b8739912cf14dd0af7db2f562b4198c7ccc76e8 [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/platform/fonts/shaping/run_segmenter.h"
#include <string>
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/renderer/platform/fonts/orientation_iterator.h"
#include "third_party/blink/renderer/platform/wtf/assertions.h"
#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
#include "third_party/blink/renderer/platform/wtf/vector.h"
namespace blink {
struct SegmenterTestRun {
std::string text;
UScriptCode script;
OrientationIterator::RenderOrientation render_orientation;
FontFallbackPriority font_fallback_priority;
};
struct SegmenterExpectedRun {
unsigned start;
unsigned limit;
UScriptCode script;
OrientationIterator::RenderOrientation render_orientation;
FontFallbackPriority font_fallback_priority;
SegmenterExpectedRun(
unsigned the_start,
unsigned the_limit,
UScriptCode the_script,
OrientationIterator::RenderOrientation the_render_orientation,
FontFallbackPriority the_font_fallback_priority)
: start(the_start),
limit(the_limit),
script(the_script),
render_orientation(the_render_orientation),
font_fallback_priority(the_font_fallback_priority) {}
};
class RunSegmenterTest : public testing::Test {
protected:
void CheckRuns(const Vector<SegmenterTestRun>& runs,
FontOrientation orientation) {
StringBuilder text;
text.Ensure16Bit();
Vector<SegmenterExpectedRun> expect;
for (auto& run : runs) {
unsigned length_before = text.length();
text.Append(String::FromUTF8(run.text.c_str()));
expect.push_back(SegmenterExpectedRun(length_before, text.length(),
run.script, run.render_orientation,
run.font_fallback_priority));
}
RunSegmenter run_segmenter(text.Characters16(), text.length(), orientation);
VerifyRuns(&run_segmenter, expect);
}
void CheckRunsMixed(const Vector<SegmenterTestRun>& runs) {
CheckRuns(runs, FontOrientation::kVerticalMixed);
}
void CheckRunsHorizontal(const Vector<SegmenterTestRun>& runs) {
CheckRuns(runs, FontOrientation::kHorizontal);
}
void VerifyRuns(RunSegmenter* run_segmenter,
const Vector<SegmenterExpectedRun>& expect) {
RunSegmenter::RunSegmenterRange segmenter_range;
size_t run_count = 0;
while (run_segmenter->Consume(&segmenter_range)) {
ASSERT_LT(run_count, expect.size());
ASSERT_EQ(expect[run_count].start, segmenter_range.start);
ASSERT_EQ(expect[run_count].limit, segmenter_range.end);
ASSERT_EQ(expect[run_count].script, segmenter_range.script);
ASSERT_EQ(expect[run_count].render_orientation,
segmenter_range.render_orientation);
ASSERT_EQ(expect[run_count].font_fallback_priority,
segmenter_range.font_fallback_priority);
++run_count;
}
ASSERT_EQ(expect.size(), run_count);
}
};
TEST_F(RunSegmenterTest, Empty) {
String empty(g_empty_string16_bit);
RunSegmenter::RunSegmenterRange segmenter_range = {
0, 0, USCRIPT_INVALID_CODE, OrientationIterator::kOrientationKeep};
RunSegmenter run_segmenter(empty.Characters16(), empty.length(),
FontOrientation::kVerticalMixed);
DCHECK(!run_segmenter.Consume(&segmenter_range));
ASSERT_EQ(segmenter_range.start, 0u);
ASSERT_EQ(segmenter_range.end, 0u);
ASSERT_EQ(segmenter_range.script, USCRIPT_INVALID_CODE);
ASSERT_EQ(segmenter_range.render_orientation,
OrientationIterator::kOrientationKeep);
ASSERT_EQ(segmenter_range.font_fallback_priority,
FontFallbackPriority::kText);
}
TEST_F(RunSegmenterTest, LatinPunctuationSideways) {
CheckRunsMixed({{"Abc.;?Xyz", USCRIPT_LATIN,
OrientationIterator::kOrientationRotateSideways,
FontFallbackPriority::kText}});
}
TEST_F(RunSegmenterTest, OneSpace) {
CheckRunsMixed(
{{" ", USCRIPT_COMMON, OrientationIterator::kOrientationRotateSideways,
FontFallbackPriority::kText}});
}
TEST_F(RunSegmenterTest, ArabicHangul) {
CheckRunsMixed(
{{"نص", USCRIPT_ARABIC, OrientationIterator::kOrientationRotateSideways,
FontFallbackPriority::kText},
{"키스의", USCRIPT_HANGUL, OrientationIterator::kOrientationKeep,
FontFallbackPriority::kText}});
}
TEST_F(RunSegmenterTest, JapaneseHindiEmojiMix) {
CheckRunsMixed(
{{"百家姓", USCRIPT_HAN, OrientationIterator::kOrientationKeep,
FontFallbackPriority::kText},
{"ऋषियों", USCRIPT_DEVANAGARI,
OrientationIterator::kOrientationRotateSideways,
FontFallbackPriority::kText},
{"🌱🌲🌳🌴", USCRIPT_DEVANAGARI, OrientationIterator::kOrientationKeep,
FontFallbackPriority::kEmojiEmoji},
{"百家姓", USCRIPT_HAN, OrientationIterator::kOrientationKeep,
FontFallbackPriority::kText},
{"🌱🌲", USCRIPT_HAN, OrientationIterator::kOrientationKeep,
FontFallbackPriority::kEmojiEmoji}});
}
TEST_F(RunSegmenterTest, CombiningCirlce) {
CheckRunsHorizontal(
{{"◌́◌̀◌̈◌̂◌̄◌̊", USCRIPT_COMMON, OrientationIterator::kOrientationKeep,
FontFallbackPriority::kText}});
}
TEST_F(RunSegmenterTest, HangulSpace) {
CheckRunsMixed(
{{"키스의", USCRIPT_HANGUL, OrientationIterator::kOrientationKeep,
FontFallbackPriority::kText},
{" ", USCRIPT_HANGUL, OrientationIterator::kOrientationRotateSideways,
FontFallbackPriority::kText},
{"고유조건은", USCRIPT_HANGUL, OrientationIterator::kOrientationKeep,
FontFallbackPriority::kText}});
}
TEST_F(RunSegmenterTest, TechnicalCommonUpright) {
CheckRunsMixed({{"⌀⌁⌂", USCRIPT_COMMON, OrientationIterator::kOrientationKeep,
FontFallbackPriority::kText}});
}
TEST_F(RunSegmenterTest, PunctuationCommonSideways) {
CheckRunsMixed(
{{".…¡", USCRIPT_COMMON, OrientationIterator::kOrientationRotateSideways,
FontFallbackPriority::kText}});
}
TEST_F(RunSegmenterTest, JapanesePunctuationMixedInside) {
CheckRunsMixed(
{{"いろはに", USCRIPT_HIRAGANA, OrientationIterator::kOrientationKeep,
FontFallbackPriority::kText},
{".…¡", USCRIPT_HIRAGANA,
OrientationIterator::kOrientationRotateSideways,
FontFallbackPriority::kText},
{"ほへと", USCRIPT_HIRAGANA, OrientationIterator::kOrientationKeep,
FontFallbackPriority::kText}});
}
TEST_F(RunSegmenterTest, JapanesePunctuationMixedInsideHorizontal) {
CheckRunsHorizontal(
{{"いろはに.…¡ほへと", USCRIPT_HIRAGANA,
OrientationIterator::kOrientationKeep, FontFallbackPriority::kText}});
}
TEST_F(RunSegmenterTest, PunctuationDevanagariCombining) {
CheckRunsHorizontal(
{{"क+े", USCRIPT_DEVANAGARI, OrientationIterator::kOrientationKeep,
FontFallbackPriority::kText}});
}
TEST_F(RunSegmenterTest, EmojiZWJSequences) {
CheckRunsHorizontal(
{{"👩‍👩‍👧‍👦👩‍❤️‍💋‍👨", USCRIPT_LATIN,
OrientationIterator::kOrientationKeep,
FontFallbackPriority::kEmojiEmoji},
{"abcd", USCRIPT_LATIN, OrientationIterator::kOrientationKeep,
FontFallbackPriority::kText},
{"👩‍👩", USCRIPT_LATIN, OrientationIterator::kOrientationKeep,
FontFallbackPriority::kEmojiEmoji},
{u8"\U0000200D‍efg", USCRIPT_LATIN,
OrientationIterator::kOrientationKeep, FontFallbackPriority::kText}});
}
TEST_F(RunSegmenterTest, JapaneseLetterlikeEnd) {
CheckRunsMixed(
{{"いろは", USCRIPT_HIRAGANA, OrientationIterator::kOrientationKeep,
FontFallbackPriority::kText},
{"ℐℒℐℒℐℒℐℒℐℒℐℒℐℒ", USCRIPT_HIRAGANA,
OrientationIterator::kOrientationRotateSideways,
FontFallbackPriority::kText}});
}
TEST_F(RunSegmenterTest, JapaneseCase) {
CheckRunsMixed(
{{"いろは", USCRIPT_HIRAGANA, OrientationIterator::kOrientationKeep,
FontFallbackPriority::kText},
{"aaAA", USCRIPT_LATIN, OrientationIterator::kOrientationRotateSideways,
FontFallbackPriority::kText},
{"いろは", USCRIPT_HIRAGANA, OrientationIterator::kOrientationKeep,
FontFallbackPriority::kText}});
}
TEST_F(RunSegmenterTest, DingbatsMiscSymbolsModifier) {
CheckRunsHorizontal({{"⛹🏻✍🏻✊🏼", USCRIPT_COMMON,
OrientationIterator::kOrientationKeep,
FontFallbackPriority::kEmojiEmoji}});
}
TEST_F(RunSegmenterTest, ArmenianCyrillicCase) {
CheckRunsHorizontal(
{{"աբգ", USCRIPT_ARMENIAN, OrientationIterator::kOrientationKeep,
FontFallbackPriority::kText},
{"αβγ", USCRIPT_GREEK, OrientationIterator::kOrientationKeep,
FontFallbackPriority::kText},
{"ԱԲԳ", USCRIPT_ARMENIAN, OrientationIterator::kOrientationKeep,
FontFallbackPriority::kText}});
}
TEST_F(RunSegmenterTest, EmojiSubdivisionFlags) {
CheckRunsHorizontal(
{{"🏴󠁧󠁢󠁷󠁬󠁳󠁿🏴󠁧󠁢󠁳󠁣󠁴󠁿🏴󠁧󠁢"
"󠁥󠁮󠁧󠁿",
USCRIPT_COMMON, OrientationIterator::kOrientationKeep,
FontFallbackPriority::kEmojiEmoji}});
}
TEST_F(RunSegmenterTest, NonEmojiPresentationSymbols) {
CheckRunsHorizontal(
{{u8"\U00002626\U0000262a\U00002638\U0000271d\U00002721\U00002627"
u8"\U00002628\U00002629\U0000262b\U0000262c\U00002670"
"\U00002671\U0000271f\U00002720",
USCRIPT_COMMON, OrientationIterator::kOrientationKeep,
FontFallbackPriority::kText}});
}
TEST_F(RunSegmenterTest, StartOffset) {
String text = String::FromUTF8("abcשלום");
text.Ensure16Bit();
// skip first 3 characters
unsigned start_offset = 3;
Vector<SegmenterExpectedRun> expect;
expect.push_back(SegmenterExpectedRun(
start_offset, text.length(), USCRIPT_HEBREW,
OrientationIterator::kOrientationKeep, FontFallbackPriority::kText));
RunSegmenter run_segmenter(text.Characters16(), text.length() - start_offset,
FontOrientation::kHorizontal, start_offset);
VerifyRuns(&run_segmenter, expect);
}
TEST_F(RunSegmenterTest, CJKBracketsAfterLatinLetter) {
CheckRunsHorizontal(
{{"A", USCRIPT_LATIN, OrientationIterator::kOrientationKeep,
FontFallbackPriority::kText},
{"\u300C" // CJK LEFT CORNER BRACKET
"\u56FD" // CJK UNIFIED IDEOGRAPH
"\u300D", // CJK RIGHT CORNER BRACKET
USCRIPT_HAN, OrientationIterator::kOrientationKeep,
FontFallbackPriority::kText}});
}
TEST_F(RunSegmenterTest, CJKBracketsAfterLatinParenthesis) {
CheckRunsHorizontal(
{{"A(", USCRIPT_LATIN, OrientationIterator::kOrientationKeep,
FontFallbackPriority::kText},
{"\u300C" // CJK LEFT CORNER BRACKET
"\u56FD" // CJK UNIFIED IDEOGRAPH
"\u300D", // CJK RIGHT CORNER BRACKET
USCRIPT_HAN, OrientationIterator::kOrientationKeep,
FontFallbackPriority::kText},
{")", USCRIPT_LATIN, OrientationIterator::kOrientationKeep,
FontFallbackPriority::kText}});
}
TEST_F(RunSegmenterTest, CJKBracketsWithLatinParenthesisInside) {
CheckRunsHorizontal(
{{"A", USCRIPT_LATIN, OrientationIterator::kOrientationKeep,
FontFallbackPriority::kText},
{"\u300C" // CJK LEFT CORNER BRACKET
"\u56FD" // CJK UNIFIED IDEOGRAPH
"(",
USCRIPT_HAN, OrientationIterator::kOrientationKeep,
FontFallbackPriority::kText},
{"A", USCRIPT_LATIN, OrientationIterator::kOrientationKeep,
FontFallbackPriority::kText},
{")"
"\u300D", // CJK RIGHT CORNER BRACKET
USCRIPT_HAN, OrientationIterator::kOrientationKeep,
FontFallbackPriority::kText}});
}
TEST_F(RunSegmenterTest, CJKBracketsAfterUnmatchingLatinParenthesis) {
CheckRunsHorizontal(
{{"A((", USCRIPT_LATIN, OrientationIterator::kOrientationKeep,
FontFallbackPriority::kText},
{"\u300C" // CJK LEFT CORNER BRACKET
"\u56FD" // CJK UNIFIED IDEOGRAPH
"\u300D", // CJK RIGHT CORNER BRACKET
USCRIPT_HAN, OrientationIterator::kOrientationKeep,
FontFallbackPriority::kText},
{")", USCRIPT_LATIN, OrientationIterator::kOrientationKeep,
FontFallbackPriority::kText}});
}
} // namespace blink