blob: a5f8625c3da42d7a0a2c007abd2a9f63468a5cb7 [file] [log] [blame]
/*
* Copyright (C) 2018 The Android Open Source Project
*
* 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 <memory>
#include <gtest/gtest.h>
#include "minikin/Hyphenator.h"
#include "FileUtils.h"
#include "FontTestUtils.h"
#include "HyphenatorMap.h"
#include "LineBreakerTestHelper.h"
#include "LocaleListCache.h"
#include "MinikinInternal.h"
#include "OptimalLineBreaker.h"
#include "UnicodeUtils.h"
#include "WordBreaker.h"
namespace minikin {
namespace {
using line_breaker_test_helper::ConstantRun;
using line_breaker_test_helper::LineBreakExpectation;
using line_breaker_test_helper::RectangleLineWidth;
using line_breaker_test_helper::sameLineBreak;
using line_breaker_test_helper::toString;
// The ascent/descent of Ascii.ttf with text size = 10.
constexpr float ASCENT = -80.0f;
constexpr float DESCENT = 20.0f;
// The ascent/descent of CustomExtent.ttf with text size = 10.
constexpr float CUSTOM_ASCENT = -160.0f;
constexpr float CUSTOM_DESCENT = 40.0f;
class OptimalLineBreakerTest : public testing::Test {
public:
OptimalLineBreakerTest() {}
virtual ~OptimalLineBreakerTest() {}
virtual void SetUp() override {
mHyphenationPattern = readWholeFile("/system/usr/hyphen-data/hyph-en-us.hyb");
Hyphenator* hyphenator = Hyphenator::loadBinary(
mHyphenationPattern.data(), 2 /* min prefix */, 2 /* min suffix */, "en-US");
HyphenatorMap::add("en-US", hyphenator);
HyphenatorMap::add("pl", Hyphenator::loadBinary(nullptr, 0, 0, "pl"));
}
virtual void TearDown() override { HyphenatorMap::clear(); }
protected:
LineBreakResult doLineBreak(const U16StringPiece& textBuffer, BreakStrategy strategy,
HyphenationFrequency frequency, const std::string& lang,
float lineWidth, bool ignoreKerning) {
MeasuredTextBuilder builder;
auto family1 = buildFontFamily("Ascii.ttf");
auto family2 = buildFontFamily("CustomExtent.ttf");
std::vector<std::shared_ptr<FontFamily>> families = {family1, family2};
auto fc = std::make_shared<FontCollection>(families);
MinikinPaint paint(fc);
paint.size = 10.0f; // Make 1em=10px
paint.localeListId = LocaleListCache::getId(lang);
builder.addStyleRun(0, textBuffer.size(), std::move(paint), 0, 0, false);
bool computeHyphen = frequency != HyphenationFrequency::None;
std::unique_ptr<MeasuredText> measuredText =
builder.build(textBuffer, computeHyphen, false /* compute full layout */,
ignoreKerning, nullptr /* no hint */);
return doLineBreak(textBuffer, *measuredText, strategy, frequency, lineWidth);
}
LineBreakResult doLineBreak(const U16StringPiece& textBuffer, const MeasuredText& measuredText,
BreakStrategy strategy, HyphenationFrequency frequency,
float lineWidth) {
RectangleLineWidth rectangleLineWidth(lineWidth);
return breakLineOptimal(textBuffer, measuredText, rectangleLineWidth, strategy, frequency,
false /* justified */);
}
void expectBreak(const std::vector<LineBreakExpectation>& expect,
const U16StringPiece& textBuffer, BreakStrategy strategy,
HyphenationFrequency frequency, const std::string& lang, float lineWidth) {
{
char msg[256] = {};
snprintf(msg, 256, "width = %f, lang = %s, strategy = %u, frequency = %u, fullyHyphen",
lineWidth, lang.c_str(), (uint32_t)strategy, (uint32_t)frequency);
SCOPED_TRACE(msg);
auto actual = doLineBreak(textBuffer, strategy, frequency, lang, lineWidth,
false /* ignoreKerning */);
EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
<< " vs " << std::endl
<< toString(textBuffer, actual);
}
{
char msg[256] = {};
snprintf(msg, 256, "width = %f, lang = %s, strategy = %u, frequency = %u, fullyHyphen",
lineWidth, lang.c_str(), (uint32_t)strategy, (uint32_t)frequency);
SCOPED_TRACE(msg);
auto actual = doLineBreak(textBuffer, strategy, frequency, lang, lineWidth,
true /* ignoreKerning */);
EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
<< " vs " << std::endl
<< toString(textBuffer, actual);
}
}
private:
std::vector<uint8_t> mHyphenationPattern;
};
TEST_F(OptimalLineBreakerTest, testBreakWithoutHyphenation) {
constexpr BreakStrategy HIGH_QUALITY = BreakStrategy::HighQuality;
constexpr BreakStrategy BALANCED = BreakStrategy::Balanced;
constexpr HyphenationFrequency NO_HYPHENATION = HyphenationFrequency::None;
constexpr HyphenationFrequency NORMAL_HYPHENATION = HyphenationFrequency::Normal;
const std::vector<uint16_t> textBuf = utf8ToUtf16("This is an example text.");
constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
constexpr EndHyphenEdit END_HYPHEN = EndHyphenEdit::INSERT_HYPHEN;
// Note that disable clang-format everywhere since aligned expectation is more readable.
{
constexpr float LINE_WIDTH = 1000;
std::vector<LineBreakExpectation> expect = {
{"This is an example text.", 240, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHENATION, "en-US", LINE_WIDTH);
expectBreak(expect, textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
expectBreak(expect, textBuf, BALANCED, NO_HYPHENATION, "en-US", LINE_WIDTH);
expectBreak(expect, textBuf, BALANCED, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
}
{
constexpr float LINE_WIDTH = 240;
std::vector<LineBreakExpectation> expect = {
{"This is an example text.", 240, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHENATION, "en-US", LINE_WIDTH);
expectBreak(expect, textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
expectBreak(expect, textBuf, BALANCED, NO_HYPHENATION, "en-US", LINE_WIDTH);
expectBreak(expect, textBuf, BALANCED, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
}
{
constexpr float LINE_WIDTH = 230;
// clang-format off
std::vector<LineBreakExpectation> expect = {
{ "This is an example " , 180, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "text." , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHENATION, "en-US", LINE_WIDTH);
expectBreak(expect, textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
// clang-format off
expect = {
{ "This is an " , 100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "example text." , 130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
expectBreak(expect, textBuf, BALANCED, NO_HYPHENATION, "en-US", LINE_WIDTH);
// clang-format off
expect = {
{ "This is an ex-" , 140, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
{ "ample text." , 110, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
expectBreak(expect, textBuf, BALANCED, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
}
return;
{
constexpr float LINE_WIDTH = 170;
// clang-format off
std::vector<LineBreakExpectation> expect = {
{ "This is an " , 100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "example text." , 130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHENATION, "en-US", LINE_WIDTH);
// clang-format off
expect = {
{ "This is an exam-" , 160, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
{ "ple text." , 90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
expectBreak(expect, textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
// clang-format off
expect = {
{ "This is an " , 100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "example text." , 130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
expectBreak(expect, textBuf, BALANCED, NO_HYPHENATION, "en-US", LINE_WIDTH);
// clang-format off
expect = {
{ "This is an ex-", 140, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
{ "ample text." , 110, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
expectBreak(expect, textBuf, BALANCED, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
}
{
constexpr float LINE_WIDTH = 160;
// clang-format off
std::vector<LineBreakExpectation> expect = {
{ "This is an " , 100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "example text." , 130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHENATION, "en-US", LINE_WIDTH);
// clang-format off
expect = {
{ "This is an exam-" , 160, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
{ "ple text." , 90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
expectBreak(expect, textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
// clang-format off
expect = {
{ "This is an " , 100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "example text." , 130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
expectBreak(expect, textBuf, BALANCED, NO_HYPHENATION, "en-US", LINE_WIDTH);
// clang-format off
expect = {
{ "This is an ex-", 140, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
{ "ample text." , 110, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
expectBreak(expect, textBuf, BALANCED, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
}
{
constexpr float LINE_WIDTH = 150;
// clang-format off
std::vector<LineBreakExpectation> expect = {
{ "This is an " , 100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "example text." , 130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHENATION, "en-US", LINE_WIDTH);
// clang-format off
expect = {
{ "This is an ex-", 140, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
{ "ample text." , 110, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
expectBreak(expect, textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
// clang-format off
expect = {
{ "This is an " , 100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "example text." , 130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
expectBreak(expect, textBuf, BALANCED, NO_HYPHENATION, "en-US", LINE_WIDTH);
// clang-format off
expect = {
{ "This is an ex-", 140, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
{ "ample text." , 110, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
expectBreak(expect, textBuf, BALANCED, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
}
{
constexpr float LINE_WIDTH = 130;
// clang-format off
std::vector<LineBreakExpectation> expect = {
{ "This is an " , 100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "example text." , 130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHENATION, "en-US", LINE_WIDTH);
expectBreak(expect, textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
expectBreak(expect, textBuf, BALANCED, NO_HYPHENATION, "en-US", LINE_WIDTH);
expectBreak(expect, textBuf, BALANCED, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
}
{
constexpr float LINE_WIDTH = 120;
// clang-format off
std::vector<LineBreakExpectation> expect = {
{ "This is an ", 100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "example " , 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "text." , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHENATION, "en-US", LINE_WIDTH);
expectBreak(expect, textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
expectBreak(expect, textBuf, BALANCED, NO_HYPHENATION, "en-US", LINE_WIDTH);
// clang-format off
expect = {
{ "This is " , 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "an exam-" , 80, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
{ "ple text.", 90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
expectBreak(expect, textBuf, BALANCED, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
}
{
constexpr float LINE_WIDTH = 90;
// clang-format off
std::vector<LineBreakExpectation> expect = {
{ "This " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "is an " , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "example ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "text." , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHENATION, "en-US", LINE_WIDTH);
// clang-format off
expect = {
{ "This is " , 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "an exam-" , 80, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
{ "ple text.", 90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
expectBreak(expect, textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
// clang-format off
expect = {
{ "This " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "is an " , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "example ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "text." , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
expectBreak(expect, textBuf, BALANCED, NO_HYPHENATION, "en-US", LINE_WIDTH);
// clang-format off
expect = {
{ "This is " , 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "an exam-" , 80, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
{ "ple text.", 90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
expectBreak(expect, textBuf, BALANCED, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
}
{
constexpr float LINE_WIDTH = 80;
// clang-format off
std::vector<LineBreakExpectation> expect = {
{ "This " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "is an " , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "example ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "text." , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHENATION, "en-US", LINE_WIDTH);
expectBreak(expect, textBuf, BALANCED, NO_HYPHENATION, "en-US", LINE_WIDTH);
// clang-format off
expect = {
{ "This is ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "an ex-" , 60, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
{ "ample " , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "text." , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
expectBreak(expect, textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
expectBreak(expect, textBuf, BALANCED, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
}
{
constexpr float LINE_WIDTH = 70;
// clang-format off
std::vector<LineBreakExpectation> expect = {
{ "This " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "is an " , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "example ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "text." , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHENATION, "en-US", LINE_WIDTH);
expectBreak(expect, textBuf, BALANCED, NO_HYPHENATION, "en-US", LINE_WIDTH);
// clang-format off
expect = {
{ "This is ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "an ex-" , 60, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
{ "ample " , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "text." , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
expectBreak(expect, textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
expectBreak(expect, textBuf, BALANCED, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
}
{
constexpr float LINE_WIDTH = 60;
// clang-format off
std::vector<LineBreakExpectation> expect = {
{ "This " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "is an ", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
// TODO: Is this desperate break working correctly?
{ "exa" , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "mple " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "text." , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHENATION, "en-US", LINE_WIDTH);
expectBreak(expect, textBuf, BALANCED, NO_HYPHENATION, "en-US", LINE_WIDTH);
// clang-format off
expect = {
{ "This " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "is an ", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "exam-" , 50, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
{ "ple " , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "text." , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
expectBreak(expect, textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
expectBreak(expect, textBuf, BALANCED, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
}
{
constexpr float LINE_WIDTH = 50;
// clang-format off
std::vector<LineBreakExpectation> expect = {
{ "This " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "is an ", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
// TODO: Is this desperate break working correctly?
{ "exa" , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "mple " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "text." , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHENATION, "en-US", LINE_WIDTH);
expectBreak(expect, textBuf, BALANCED, NO_HYPHENATION, "en-US", LINE_WIDTH);
// clang-format off
expect = {
{ "This " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "is an ", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "exam-" , 50, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
{ "ple " , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "text." , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
expectBreak(expect, textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
expectBreak(expect, textBuf, BALANCED, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
}
{
constexpr float LINE_WIDTH = 40;
// clang-format off
std::vector<LineBreakExpectation> expect = {
{ "This " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "is " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "an " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
// TODO: Is this desperate break working correctly?
{ "exa" , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "mple " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "text" , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "." , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHENATION, "en-US", LINE_WIDTH);
// clang-format off
expect = {
{ "This " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "is " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "an " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
// TODO: Is this desperate break working correctly?
{ "exa" , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "mple " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
// TODO: Is this desperate break working correctly?
{ "t" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "ext." , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
expectBreak(expect, textBuf, BALANCED, NO_HYPHENATION, "en-US", LINE_WIDTH);
// clang-format off
expect = {
{ "This ", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "is " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "an " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "ex-" , 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
{ "am-" , 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
{ "ple " , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "text" , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "." , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
expectBreak(expect, textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
// clang-format off
expect = {
{ "This ", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "is " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "an " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "ex-" , 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
{ "am-" , 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
{ "ple " , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
// TODO: Is this desperate break working correctly?
{ "te" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "xt." , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
expectBreak(expect, textBuf, BALANCED, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
}
{
constexpr float LINE_WIDTH = 30;
// clang-format off
std::vector<LineBreakExpectation> expect = {
// TODO: Is this desperate break working correctly?
{ "T" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "his ", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "is " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "an " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
// TODO: Is this desperate break working correctly?
{ "e" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "xam" , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "ple ", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "tex" , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "t." , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHENATION, "en-US", LINE_WIDTH);
// clang-format off
expect = {
// TODO: Is this desperate break working correctly?
{ "T" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "his ", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "is " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "an " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
// TODO: Is this desperate break working correctly?
{ "e" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "xam" , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "ple ", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
// TODO: Is this desperate break working correctly?
{ "te" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "xt." , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
expectBreak(expect, textBuf, BALANCED, NO_HYPHENATION, "en-US", LINE_WIDTH);
// clang-format off
expect = {
// TODO: Is this desperate break working correctly?
{ "T" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "his ", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "is " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "an " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "ex-" , 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
{ "am-" , 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
{ "ple ", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "tex" , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "t." , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
expectBreak(expect, textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
// clang-format off
expect = {
// TODO: Is this desperate break working correctly?
{"T" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"his ", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"is " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"an " , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"ex-" , 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT},
{"am-" , 30, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT},
{"ple ", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
// TODO: Is this desperate break working correctly?
{"te" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"xt." , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
// clang-format on
expectBreak(expect, textBuf, BALANCED, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
}
{
constexpr float LINE_WIDTH = 20;
// clang-format off
std::vector<LineBreakExpectation> expect = {
{ "Th" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "is ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "is ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "an ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
// TODO: Is this desperate break working correctly?
{ "e" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "xa" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "mp" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "le ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "te" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "xt" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "." , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHENATION, "en-US", LINE_WIDTH);
expectBreak(expect, textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
// clang-format off
expect = {
{ "Th" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "is ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "is ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "an ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
// TODO: Is this desperate break working correctly?
{ "e" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "xa" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "mp" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "le ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
// TODO: Is this desperate break working correctly?
{ "t" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "ex" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "t." , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
expectBreak(expect, textBuf, BALANCED, NO_HYPHENATION, "en-US", LINE_WIDTH);
expectBreak(expect, textBuf, BALANCED, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
}
{
constexpr float LINE_WIDTH = 10;
// clang-format off
std::vector<LineBreakExpectation> expect = {
{ "T" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "h" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "i" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "s ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "i" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "s ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "a" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "n ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "e" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "x" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "a" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "m" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "p" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "l" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "e ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "t" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "e" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "x" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "t" , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
{ "." , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHENATION, "en-US", LINE_WIDTH);
expectBreak(expect, textBuf, BALANCED, NO_HYPHENATION, "en-US", LINE_WIDTH);
expectBreak(expect, textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
expectBreak(expect, textBuf, BALANCED, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
}
}
TEST_F(OptimalLineBreakerTest, testHyphenationStartLineChange) {
constexpr BreakStrategy HIGH_QUALITY = BreakStrategy::HighQuality;
constexpr HyphenationFrequency NORMAL_HYPHENATION = HyphenationFrequency::Normal;
// "hyphenation" is hyphnated to "hy-phen-a-tion".
const std::vector<uint16_t> textBuf = utf8ToUtf16("czerwono-niebieska");
constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
constexpr StartHyphenEdit START_HYPHEN = StartHyphenEdit::INSERT_HYPHEN;
// Note that disable clang-format everywhere since aligned expectation is more readable.
{
constexpr float LINE_WIDTH = 1000;
std::vector<LineBreakExpectation> expect = {
{"czerwono-niebieska", 180, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
expectBreak(expect, textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "pl", LINE_WIDTH);
}
{
constexpr float LINE_WIDTH = 180;
std::vector<LineBreakExpectation> expect = {
{"czerwono-niebieska", 180, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
expectBreak(expect, textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "pl", LINE_WIDTH);
}
{
constexpr float LINE_WIDTH = 130;
// clang-format off
std::vector<LineBreakExpectation> expect = {
{"czerwono-" , 90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"-niebieska", 100, START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
// clang-format on
expectBreak(expect, textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "pl", LINE_WIDTH);
}
}
TEST_F(OptimalLineBreakerTest, testZeroWidthLine) {
constexpr BreakStrategy HIGH_QUALITY = BreakStrategy::HighQuality;
constexpr HyphenationFrequency NORMAL_HYPHENATION = HyphenationFrequency::Normal;
constexpr float LINE_WIDTH = 0;
constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
{
const auto textBuf = utf8ToUtf16("");
std::vector<LineBreakExpectation> expect = {};
expectBreak(expect, textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
}
{
const auto textBuf = utf8ToUtf16("A");
std::vector<LineBreakExpectation> expect = {
{"A", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
expectBreak(expect, textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
}
{
const auto textBuf = utf8ToUtf16("AB");
std::vector<LineBreakExpectation> expect = {
{"A", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"B", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
expectBreak(expect, textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
}
}
TEST_F(OptimalLineBreakerTest, testZeroWidthCharacter) {
constexpr float CHAR_WIDTH = 0.0;
constexpr BreakStrategy HIGH_QUALITY = BreakStrategy::HighQuality;
constexpr HyphenationFrequency NORMAL_HYPHENATION = HyphenationFrequency::Normal;
constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
{
constexpr float LINE_WIDTH = 1.0;
const auto textBuf = utf8ToUtf16("This is an example text.");
std::vector<LineBreakExpectation> expect = {
{"This is an example text.", 0, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
MeasuredTextBuilder builder;
builder.addCustomRun<ConstantRun>(Range(0, textBuf.size()), "en-US", CHAR_WIDTH, ASCENT,
DESCENT);
std::unique_ptr<MeasuredText> measuredText = builder.build(
textBuf, true /* compute hyphenation */, false /* compute full layout */,
false /* ignore kerning */, nullptr /* no hint */);
const auto actual =
doLineBreak(textBuf, *measuredText, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
<< " vs " << std::endl
<< toString(textBuf, actual);
}
{
constexpr float LINE_WIDTH = 0.0;
const auto textBuf = utf8ToUtf16("This is an example text.");
std::vector<LineBreakExpectation> expect = {
{"This is an example text.", 0, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
MeasuredTextBuilder builder;
builder.addCustomRun<ConstantRun>(Range(0, textBuf.size()), "en-US", CHAR_WIDTH, ASCENT,
DESCENT);
std::unique_ptr<MeasuredText> measuredText = builder.build(
textBuf, true /* compute hyphenation */, false /* compute full layout */,
false /* ignore kerning */, nullptr /* no hint */);
const auto actual =
doLineBreak(textBuf, *measuredText, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
<< " vs " << std::endl
<< toString(textBuf, actual);
}
}
TEST_F(OptimalLineBreakerTest, testLocaleSwitchTest) {
constexpr float CHAR_WIDTH = 10.0;
constexpr BreakStrategy HIGH_QUALITY = BreakStrategy::HighQuality;
constexpr HyphenationFrequency NORMAL_HYPHENATION = HyphenationFrequency::Normal;
constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
constexpr float LINE_WIDTH = 240;
const auto textBuf = utf8ToUtf16("This is an example text.");
{
std::vector<LineBreakExpectation> expect = {
{"This is an example text.", 240, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
MeasuredTextBuilder builder;
builder.addCustomRun<ConstantRun>(Range(0, 18), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
builder.addCustomRun<ConstantRun>(Range(18, textBuf.size()), "en-US", CHAR_WIDTH, ASCENT,
DESCENT);
std::unique_ptr<MeasuredText> measuredText = builder.build(
textBuf, true /* compute hyphenation */, false /* compute full layout */,
false /* ignore kerning */, nullptr /* no hint */);
const auto actual =
doLineBreak(textBuf, *measuredText, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
<< " vs " << std::endl
<< toString(textBuf, actual);
}
{
std::vector<LineBreakExpectation> expect = {
{"This is an example text.", 240, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
MeasuredTextBuilder builder;
builder.addCustomRun<ConstantRun>(Range(0, 18), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
builder.addCustomRun<ConstantRun>(Range(18, textBuf.size()), "fr-FR", CHAR_WIDTH, ASCENT,
DESCENT);
std::unique_ptr<MeasuredText> measuredText = builder.build(
textBuf, true /* compute hyphenation */, false /* compute full layout */,
false /* ignore kerning */, nullptr /* no hint */);
const auto actual =
doLineBreak(textBuf, *measuredText, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
<< " vs " << std::endl
<< toString(textBuf, actual);
}
}
TEST_F(OptimalLineBreakerTest, testEmailOrUrl) {
constexpr BreakStrategy HIGH_QUALITY = BreakStrategy::HighQuality;
constexpr BreakStrategy BALANCED = BreakStrategy::Balanced;
constexpr HyphenationFrequency NO_HYPHENATION = HyphenationFrequency::None;
constexpr HyphenationFrequency NORMAL_HYPHENATION = HyphenationFrequency::Normal;
constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
{
constexpr float LINE_WIDTH = 240;
const auto textBuf = utf8ToUtf16("This is an url: http://a.b");
// clang-format off
std::vector<LineBreakExpectation> expect = {
// TODO: Fix this. Prefer not to break inside URL.
{"This is an url: http://a", 240, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{".b", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
// clang-format on
expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHENATION, "en-US", LINE_WIDTH);
expectBreak(expect, textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
// clang-format off
expect = {
{"This is an url: ", 150, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"http://a.b", 100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
// clang-format on
expectBreak(expect, textBuf, BALANCED, NO_HYPHENATION, "en-US", LINE_WIDTH);
expectBreak(expect, textBuf, BALANCED, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
}
{
constexpr float LINE_WIDTH = 240;
const auto textBuf = utf8ToUtf16("This is an email: a@example.com");
// clang-format off
std::vector<LineBreakExpectation> expect = {
{"This is an email: ", 170, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"a@example.com" , 130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
// clang-format on
expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHENATION, "en-US", LINE_WIDTH);
expectBreak(expect, textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
expectBreak(expect, textBuf, BALANCED, NO_HYPHENATION, "en-US", LINE_WIDTH);
expectBreak(expect, textBuf, BALANCED, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
}
}
TEST_F(OptimalLineBreakerTest, testLocaleSwitch_InEmailOrUrl) {
constexpr float CHAR_WIDTH = 10.0;
constexpr BreakStrategy HIGH_QUALITY = BreakStrategy::HighQuality;
constexpr BreakStrategy BALANCED = BreakStrategy::Balanced;
constexpr HyphenationFrequency NO_HYPHENATION = HyphenationFrequency::None;
constexpr HyphenationFrequency NORMAL_HYPHENATION = HyphenationFrequency::Normal;
constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
constexpr float LINE_WIDTH = 240;
{
const auto textBuf = utf8ToUtf16("This is an url: http://a.b");
MeasuredTextBuilder builder;
builder.addCustomRun<ConstantRun>(Range(0, 18), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
builder.addCustomRun<ConstantRun>(Range(18, textBuf.size()), "fr-FR", CHAR_WIDTH, ASCENT,
DESCENT);
std::unique_ptr<MeasuredText> measured = builder.build(
textBuf, true /* compute hyphenation */, false /* compute full layout */,
false /* ignore kerning */, nullptr /* no hint */);
// clang-format off
std::vector<LineBreakExpectation> expect = {
// TODO: Fix this. Prefer not to break inside URL.
{"This is an url: http://a", 240, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{".b", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
// clang-format on
auto actual = doLineBreak(textBuf, *measured, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
<< " vs " << std::endl
<< toString(textBuf, actual);
actual = doLineBreak(textBuf, *measured, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
<< " vs " << std::endl
<< toString(textBuf, actual);
// clang-format off
expect = {
{"This is an url: ", 150, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"http://a.b", 100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
// clang-format on
actual = doLineBreak(textBuf, *measured, BALANCED, NO_HYPHENATION, LINE_WIDTH);
EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
<< " vs " << std::endl
<< toString(textBuf, actual);
actual = doLineBreak(textBuf, *measured, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
<< " vs " << std::endl
<< toString(textBuf, actual);
}
{
const auto textBuf = utf8ToUtf16("This is an email: a@example.com");
MeasuredTextBuilder builder;
builder.addCustomRun<ConstantRun>(Range(0, 18), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
builder.addCustomRun<ConstantRun>(Range(18, textBuf.size()), "fr-FR", CHAR_WIDTH, ASCENT,
DESCENT);
std::unique_ptr<MeasuredText> measured = builder.build(
textBuf, true /* compute hyphenation */, false /* compute full layout */,
false /* ignore kerning */, nullptr /* no hint */);
// clang-format off
std::vector<LineBreakExpectation> expect = {
{"This is an email: ", 170, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"a@example.com", 130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
// clang-format on
auto actual = doLineBreak(textBuf, *measured, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
<< " vs " << std::endl
<< toString(textBuf, actual);
actual = doLineBreak(textBuf, *measured, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
<< " vs " << std::endl
<< toString(textBuf, actual);
actual = doLineBreak(textBuf, *measured, BALANCED, NO_HYPHENATION, LINE_WIDTH);
EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
<< " vs " << std::endl
<< toString(textBuf, actual);
actual = doLineBreak(textBuf, *measured, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
<< " vs " << std::endl
<< toString(textBuf, actual);
}
}
TEST_F(OptimalLineBreakerTest, ExtentTest) {
constexpr HyphenationFrequency NO_HYPHEN = HyphenationFrequency::None;
const std::vector<uint16_t> textBuf = utf8ToUtf16("The \u3042\u3044\u3046 is Japanese.");
constexpr BreakStrategy HIGH_QUALITY = BreakStrategy::HighQuality;
constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
{
constexpr float LINE_WIDTH = 1000;
std::vector<LineBreakExpectation> expect = {
{"The \u3042\u3044\u3046 is Japanese.", 200, NO_START_HYPHEN, NO_END_HYPHEN,
CUSTOM_ASCENT, CUSTOM_DESCENT},
};
expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHEN, "en-US", LINE_WIDTH);
}
{
constexpr float LINE_WIDTH = 200;
std::vector<LineBreakExpectation> expect = {
{"The \u3042\u3044\u3046 is Japanese.", 200, NO_START_HYPHEN, NO_END_HYPHEN,
CUSTOM_ASCENT, CUSTOM_DESCENT},
};
expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHEN, "en-US", LINE_WIDTH);
}
{
constexpr float LINE_WIDTH = 190;
std::vector<LineBreakExpectation> expect = {
{"The \u3042\u3044\u3046 is ", 100, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT,
CUSTOM_DESCENT},
{"Japanese.", 90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHEN, "en-US", LINE_WIDTH);
}
{
constexpr float LINE_WIDTH = 90;
std::vector<LineBreakExpectation> expect = {
{"The \u3042", 50, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT, CUSTOM_DESCENT},
{"\u3044\u3046 is ", 50, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT,
CUSTOM_DESCENT},
{"Japanese.", 90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHEN, "en-US", LINE_WIDTH);
}
{
constexpr float LINE_WIDTH = 50;
std::vector<LineBreakExpectation> expect = {
{"The \u3042", 50, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT, CUSTOM_DESCENT},
{"\u3044\u3046 is ", 50, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT,
CUSTOM_DESCENT},
{"Japan", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"ese.", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHEN, "en-US", LINE_WIDTH);
}
{
constexpr float LINE_WIDTH = 40;
std::vector<LineBreakExpectation> expect = {
{"The ", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"\u3042\u3044", 20, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT, CUSTOM_DESCENT},
{"\u3046 is ", 40, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT, CUSTOM_DESCENT},
{"Japa", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"nese", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{".", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHEN, "en-US", LINE_WIDTH);
}
{
constexpr float LINE_WIDTH = 20;
std::vector<LineBreakExpectation> expect = {
{"T", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"he ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"\u3042", 10, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT, CUSTOM_DESCENT},
{"\u3044\u3046 ", 20, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT,
CUSTOM_DESCENT},
{"is ", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"Ja", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"pa", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"ne", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"se", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{".", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHEN, "en-US", LINE_WIDTH);
}
{
constexpr float LINE_WIDTH = 10;
std::vector<LineBreakExpectation> expect = {
{"T", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"h", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"e ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"\u3042", 10, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT, CUSTOM_DESCENT},
{"\u3044", 10, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT, CUSTOM_DESCENT},
{"\u3046 ", 10, NO_START_HYPHEN, NO_END_HYPHEN, CUSTOM_ASCENT, CUSTOM_DESCENT},
{"i", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"s ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"J", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"a", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"p", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"a", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"n", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"e", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"s", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"e", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{".", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHEN, "en-US", LINE_WIDTH);
}
}
TEST_F(OptimalLineBreakerTest, testReplacementSpanNotBreakTest_SingleChar) {
constexpr float CHAR_WIDTH = 10.0;
constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
const auto textBuf = utf8ToUtf16("This is an example \u2639 text.");
// In this test case, assign a replacement run for "U+2639" with 5 times of CHAR_WIDTH.
auto doLineBreak = [=](float width) {
MeasuredTextBuilder builder;
builder.addCustomRun<ConstantRun>(Range(0, 19), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
builder.addReplacementRun(19, 21, 5 * CHAR_WIDTH, LocaleListCache::getId("en-US"));
builder.addCustomRun<ConstantRun>(Range(21, textBuf.size()), "en-US", CHAR_WIDTH, ASCENT,
DESCENT);
std::unique_ptr<MeasuredText> measuredText = builder.build(
textBuf, false /* compute hyphenation */, false /* compute full layout */,
false /* ignore kerning */, nullptr /* no hint */);
RectangleLineWidth rectangleLineWidth(width);
TabStops tabStops(nullptr, 0, 0);
return breakLineOptimal(textBuf, *measuredText, rectangleLineWidth,
BreakStrategy::HighQuality, HyphenationFrequency::None,
false /* justified */);
};
{
constexpr float LINE_WIDTH = 100;
// "is an" is a single replacement span. Do not break.
// clang-format off
std::vector<LineBreakExpectation> expect = {
{"This is an ", 100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"example ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"\u2639 text.", 100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
// clang-format on
const auto actual = doLineBreak(LINE_WIDTH);
EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
<< " vs " << std::endl
<< toString(textBuf, actual);
}
{
constexpr float LINE_WIDTH = 90;
// "is an" is a single replacement span. Do not break.
// clang-format off
std::vector<LineBreakExpectation> expect = {
{"This ", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"is an ", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"example ",70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"\u2639 ", 50, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
{"text.", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
// clang-format on
const auto actual = doLineBreak(LINE_WIDTH);
EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
<< " vs " << std::endl
<< toString(textBuf, actual);
}
{
constexpr float LINE_WIDTH = 10;
// "is an" is a single replacement span. Do not break.
// clang-format off
std::vector<LineBreakExpectation> expect = {
{"T", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"h", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"i", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"s ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"i", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"s ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"a", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"n ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"e", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"x", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"a", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"m", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"p", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"l", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"e ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"\u2639 ",50, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
{"t", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"e", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"x", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"t", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{".", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
// clang-format on
const auto actual = doLineBreak(LINE_WIDTH);
EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
<< " vs " << std::endl
<< toString(textBuf, actual);
}
}
TEST_F(OptimalLineBreakerTest, testReplacementSpanNotBreakTest_MultipleChars) {
constexpr float CHAR_WIDTH = 10.0;
constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
const auto textBuf = utf8ToUtf16("This is an example text.");
// In this test case, assign a replacement run for "is an " with 5 times of CHAR_WIDTH.
auto doLineBreak = [=](float width) {
MeasuredTextBuilder builder;
builder.addCustomRun<ConstantRun>(Range(0, 5), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
builder.addReplacementRun(5, 11, 5 * CHAR_WIDTH, LocaleListCache::getId("en-US"));
builder.addCustomRun<ConstantRun>(Range(11, textBuf.size()), "en-US", CHAR_WIDTH, ASCENT,
DESCENT);
std::unique_ptr<MeasuredText> measuredText = builder.build(
textBuf, false /* compute hyphenation */, false /* compute full layout */,
false /* ignore kerning */, nullptr /* no hint */);
RectangleLineWidth rectangleLineWidth(width);
TabStops tabStops(nullptr, 0, 0);
return breakLineOptimal(textBuf, *measuredText, rectangleLineWidth,
BreakStrategy::HighQuality, HyphenationFrequency::None,
false /* justified */);
};
{
constexpr float LINE_WIDTH = 100;
// "is an" is a single replacement span. Do not break.
// clang-format off
std::vector<LineBreakExpectation> expect = {
{"This is an ", 100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"example ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"text.", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
// clang-format on
const auto actual = doLineBreak(LINE_WIDTH);
EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
<< " vs " << std::endl
<< toString(textBuf, actual);
}
{
constexpr float LINE_WIDTH = 90;
// "is an" is a single replacement span. Do not break.
// clang-format off
std::vector<LineBreakExpectation> expect = {
{"This ", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"is an ", 50, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
{"example ",70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"text.", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
// clang-format on
const auto actual = doLineBreak(LINE_WIDTH);
EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
<< " vs " << std::endl
<< toString(textBuf, actual);
}
{
constexpr float LINE_WIDTH = 10;
// "is an" is a single replacement span. Do not break.
// clang-format off
std::vector<LineBreakExpectation> expect = {
{"T", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"h", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"i", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"s ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"is an ", 50, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
{"e", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"x", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"a", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"m", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"p", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"l", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"e ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"t", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"e", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"x", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"t", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{".", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
// clang-format on
const auto actual = doLineBreak(LINE_WIDTH);
EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
<< " vs " << std::endl
<< toString(textBuf, actual);
}
}
TEST_F(OptimalLineBreakerTest, testReplacementSpanNotBreakTest_continuedReplacementSpan) {
constexpr float CHAR_WIDTH = 10.0;
constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
const auto textBuf = utf8ToUtf16("This is an example text.");
// In this test case, assign a replacement run for "is an " with 5 times of CHAR_WIDTH.
auto doLineBreak = [=](float width) {
MeasuredTextBuilder builder;
builder.addReplacementRun(0, 5, 5 * CHAR_WIDTH, LocaleListCache::getId("en-US"));
builder.addReplacementRun(5, 8, 3 * CHAR_WIDTH, LocaleListCache::getId("en-US"));
builder.addReplacementRun(8, 11, 3 * CHAR_WIDTH, LocaleListCache::getId("en-US"));
builder.addReplacementRun(11, 19, 8 * CHAR_WIDTH, LocaleListCache::getId("en-US"));
builder.addReplacementRun(19, 24, 5 * CHAR_WIDTH, LocaleListCache::getId("en-US"));
std::unique_ptr<MeasuredText> measuredText = builder.build(
textBuf, false /* compute hyphenation */, false /* compute full layout */,
false /* ignore kerning */, nullptr /* no hint */);
RectangleLineWidth rectangleLineWidth(width);
TabStops tabStops(nullptr, 0, 0);
return breakLineOptimal(textBuf, *measuredText, rectangleLineWidth,
BreakStrategy::HighQuality, HyphenationFrequency::None,
false /* justified */);
};
{
constexpr float LINE_WIDTH = 100;
// clang-format off
std::vector<LineBreakExpectation> expect = {
{"This ", 50, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
{"is an ", 60, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
{"example ", 80, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
{"text.", 50, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
};
// clang-format on
const auto actual = doLineBreak(LINE_WIDTH);
EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
<< " vs " << std::endl
<< toString(textBuf, actual);
}
{
constexpr float LINE_WIDTH = 40;
// clang-format off
std::vector<LineBreakExpectation> expect = {
{"This ", 50, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
{"is ", 30, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
{"an ", 30, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
{"example ", 80, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
{"text.", 50, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
};
// clang-format on
const auto actual = doLineBreak(LINE_WIDTH);
EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
<< " vs " << std::endl
<< toString(textBuf, actual);
}
{
constexpr float LINE_WIDTH = 10;
// clang-format off
std::vector<LineBreakExpectation> expect = {
{"This ", 50, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
{"is ", 30, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
{"an ", 30, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
{"example ", 80, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
{"text.", 50, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
};
// clang-format on
const auto actual = doLineBreak(LINE_WIDTH);
EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
<< " vs " << std::endl
<< toString(textBuf, actual);
}
}
TEST_F(OptimalLineBreakerTest, testReplacementSpanNotBreakTest_CJK) {
constexpr float CHAR_WIDTH = 10.0;
constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
// Example string: "Today is a sunny day." in Japanese.
const auto textBuf = utf8ToUtf16("\u672C\u65E5\u306F\u6674\u5929\u306A\u308A");
// In this test case, assign a replacement run for "\u6674\u5929" with 5 times of CHAR_WIDTH.
auto doLineBreak = [=](float width) {
MeasuredTextBuilder builder;
builder.addCustomRun<ConstantRun>(Range(0, 3), "ja-JP", CHAR_WIDTH, ASCENT, DESCENT);
builder.addReplacementRun(3, 5, 5 * CHAR_WIDTH, LocaleListCache::getId("ja-JP"));
builder.addCustomRun<ConstantRun>(Range(5, textBuf.size()), "ja-JP", CHAR_WIDTH, ASCENT,
DESCENT);
std::unique_ptr<MeasuredText> measuredText = builder.build(
textBuf, false /* compute hyphenation */, false /* compute full layout */,
false /* ignore krening */, nullptr /* no hint */);
RectangleLineWidth rectangleLineWidth(width);
TabStops tabStops(nullptr, 0, 0);
return breakLineOptimal(textBuf, *measuredText, rectangleLineWidth,
BreakStrategy::HighQuality, HyphenationFrequency::None,
false /* justified */);
};
{
constexpr float LINE_WIDTH = 100;
// "\u6674\u5929" is a single replacement span. Do not break.
// clang-format off
std::vector<LineBreakExpectation> expect = {
{"\u672C\u65E5\u306F\u6674\u5929\u306A\u308A",
100, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
// clang-format on
const auto actual = doLineBreak(LINE_WIDTH);
EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
<< " vs " << std::endl
<< toString(textBuf, actual);
}
{
constexpr float LINE_WIDTH = 90;
// "\u6674\u5929" is a single replacement span. Do not break.
// clang-format off
std::vector<LineBreakExpectation> expect = {
{"\u672C\u65E5\u306F\u6674\u5929\u306A",
90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"\u308A",
10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
// clang-format on
const auto actual = doLineBreak(LINE_WIDTH);
EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
<< " vs " << std::endl
<< toString(textBuf, actual);
}
{
constexpr float LINE_WIDTH = 80;
// "\u6674\u5929" is a single replacement span. Do not break.
// clang-format off
std::vector<LineBreakExpectation> expect = {
{"\u672C\u65E5\u306F\u6674\u5929",
80, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"\u306A\u308A",
20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
// clang-format on
const auto actual = doLineBreak(LINE_WIDTH);
EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
<< " vs " << std::endl
<< toString(textBuf, actual);
}
{
constexpr float LINE_WIDTH = 70;
// "\u6674\u5929" is a single replacement span. Do not break.
// clang-format off
std::vector<LineBreakExpectation> expect = {
{"\u672C\u65E5\u306F", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"\u6674\u5929\u306A\u308A", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
// clang-format on
const auto actual = doLineBreak(LINE_WIDTH);
EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
<< " vs " << std::endl
<< toString(textBuf, actual);
}
{
constexpr float LINE_WIDTH = 60;
// "\u6674\u5929" is a single replacement span. Do not break.
// clang-format off
std::vector<LineBreakExpectation> expect = {
{"\u672C\u65E5\u306F", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"\u6674\u5929\u306A", 60, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"\u308A", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
// clang-format on
const auto actual = doLineBreak(LINE_WIDTH);
EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
<< " vs " << std::endl
<< toString(textBuf, actual);
}
{
constexpr float LINE_WIDTH = 50;
// "\u6674\u5929" is a single replacement span. Do not break.
// clang-format off
std::vector<LineBreakExpectation> expect = {
{"\u672C\u65E5\u306F", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"\u6674\u5929", 50, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
{"\u306A\u308A", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
// clang-format on
const auto actual = doLineBreak(LINE_WIDTH);
EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
<< " vs " << std::endl
<< toString(textBuf, actual);
}
{
constexpr float LINE_WIDTH = 40;
// "\u6674\u5929" is a single replacement span. Do not break.
// clang-format off
std::vector<LineBreakExpectation> expect = {
{"\u672C\u65E5\u306F", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"\u6674\u5929", 50, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
{"\u306A\u308A", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
// clang-format on
const auto actual = doLineBreak(LINE_WIDTH);
EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
<< " vs " << std::endl
<< toString(textBuf, actual);
}
{
constexpr float LINE_WIDTH = 10;
// "\u6674\u5929" is a single replacement span. Do not break.
// clang-format off
std::vector<LineBreakExpectation> expect = {
{"\u672C", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"\u65E5", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"\u306F", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"\u6674\u5929", 50, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
{"\u306A", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"\u308A", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
// clang-format on
const auto actual = doLineBreak(LINE_WIDTH);
EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
<< " vs " << std::endl
<< toString(textBuf, actual);
}
}
// http://b/119657685
// Following test case is for verifying that the ReplacementSpan should not be broken into multiple
// pieces. The actual break point is not a part of expectation. For example, it would be good to
// break the starting offset of the ReplacementSpan for some case.
TEST_F(OptimalLineBreakerTest, testReplacementSpan_GraphemeLineBreakWithMultipleRepalcementSpans) {
constexpr float CHAR_WIDTH = 10.0;
constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
const auto textBuf = utf8ToUtf16("ab de\u00A0\u00A0fg ij\u00A0\u00A0kl no\u00A0\u00A0pq st");
auto doLineBreak = [=](float width) {
MeasuredTextBuilder builder;
builder.addReplacementRun(0, 5, 5 * CHAR_WIDTH, LocaleListCache::getId("en-US"));
builder.addCustomRun<ConstantRun>(Range(5, 7), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
builder.addReplacementRun(7, 12, 5 * CHAR_WIDTH, LocaleListCache::getId("en-US"));
builder.addCustomRun<ConstantRun>(Range(12, 14), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
builder.addReplacementRun(14, 19, 5 * CHAR_WIDTH, LocaleListCache::getId("en-US"));
builder.addCustomRun<ConstantRun>(Range(19, 21), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
builder.addReplacementRun(21, 26, 5 * CHAR_WIDTH, LocaleListCache::getId("en-US"));
std::unique_ptr<MeasuredText> measuredText = builder.build(
textBuf, false /* compute hyphenation */, false /* compute full layout */,
false /* ignore kerning */, nullptr /* no hint */);
RectangleLineWidth rectangleLineWidth(width);
TabStops tabStops(nullptr, 0, 0);
return breakLineOptimal(textBuf, *measuredText, rectangleLineWidth,
BreakStrategy::HighQuality, HyphenationFrequency::None,
false /* justified */);
};
{
constexpr float LINE_WIDTH = 1000;
// clang-format off
std::vector<LineBreakExpectation> expect = {
{"ab de\u00A0\u00A0fg ij\u00A0\u00A0kl no\u00A0\u00A0pq st",
260, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
// clang-format on
const auto actual = doLineBreak(LINE_WIDTH);
EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
<< " vs " << std::endl
<< toString(textBuf, actual);
}
{
constexpr float LINE_WIDTH = 250;
// clang-format off
std::vector<LineBreakExpectation> expect = {
{"ab de\u00A0\u00A0fg ij\u00A0\u00A0kl no\u00A0\u00A0",
210, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"pq st",
50, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
};
// clang-format on
const auto actual = doLineBreak(LINE_WIDTH);
EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
<< " vs " << std::endl
<< toString(textBuf, actual);
}
{
constexpr float LINE_WIDTH = 180;
// clang-format off
std::vector<LineBreakExpectation> expect = {
{"ab de\u00A0\u00A0fg ij\u00A0\u00A0",
140, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"kl no\u00A0\u00A0pq st",
120, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
// clang-format on
const auto actual = doLineBreak(LINE_WIDTH);
EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
<< " vs " << std::endl
<< toString(textBuf, actual);
}
{
constexpr float LINE_WIDTH = 130;
// clang-format off
std::vector<LineBreakExpectation> expect = {
{"ab de\u00A0\u00A0fg ij\u00A0",
130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"\u00A0kl no\u00A0\u00A0pq st",
130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
// clang-format on
const auto actual = doLineBreak(LINE_WIDTH);
EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
<< " vs " << std::endl
<< toString(textBuf, actual);
}
{
constexpr float LINE_WIDTH = 110;
// clang-format off
std::vector<LineBreakExpectation> expect = {
{"ab de\u00A0", 60, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"\u00A0fg ij\u00A0\u00A0", 80, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"kl no\u00A0\u00A0", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"pq st", 50, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
};
// clang-format on
const auto actual = doLineBreak(LINE_WIDTH);
EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
<< " vs " << std::endl
<< toString(textBuf, actual);
}
{
constexpr float LINE_WIDTH = 60;
// clang-format off
std::vector<LineBreakExpectation> expect = {
{"ab de\u00A0", 60, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"\u00A0fg ij", 60, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"\u00A0\u00A0", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"kl no\u00A0", 60, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"\u00A0pq st", 60, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
// clang-format on
const auto actual = doLineBreak(LINE_WIDTH);
EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
<< " vs " << std::endl
<< toString(textBuf, actual);
}
{
constexpr float LINE_WIDTH = 50;
// clang-format off
std::vector<LineBreakExpectation> expect = {
{"ab de", 50, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
{"\u00A0\u00A0", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"fg ij", 50, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
{"\u00A0\u00A0", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"kl no", 50, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
{"\u00A0\u00A0", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"pq st", 50, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
};
// clang-format on
const auto actual = doLineBreak(LINE_WIDTH);
EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
<< " vs " << std::endl
<< toString(textBuf, actual);
}
}
TEST_F(OptimalLineBreakerTest, testReplacementSpanNotBreakTest_with_punctuation) {
constexpr float CHAR_WIDTH = 10.0;
constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
const auto textBuf = utf8ToUtf16("This (is an) example text.");
// In this test case, assign a replacement run for "U+2639" with 5 times of CHAR_WIDTH.
auto doLineBreak = [=](float width) {
MeasuredTextBuilder builder;
builder.addCustomRun<ConstantRun>(Range(0, 6), "en-US", CHAR_WIDTH, ASCENT, DESCENT);
builder.addReplacementRun(6, 11, 5 * CHAR_WIDTH, LocaleListCache::getId("en-US"));
builder.addCustomRun<ConstantRun>(Range(11, textBuf.size()), "en-US", CHAR_WIDTH, ASCENT,
DESCENT);
std::unique_ptr<MeasuredText> measuredText = builder.build(
textBuf, false /* compute hyphenation */, false /* compute full layout */,
false /* ignore kerning */, nullptr /* no hint */);
RectangleLineWidth rectangleLineWidth(width);
TabStops tabStops(nullptr, 0, 0);
return breakLineOptimal(textBuf, *measuredText, rectangleLineWidth,
BreakStrategy::HighQuality, HyphenationFrequency::Normal,
false /* justified */);
};
{
constexpr float LINE_WIDTH = 1000;
// "is an" is a single replacement span. Do not break.
// clang-format off
std::vector<LineBreakExpectation> expect = {
{"This (is an) example text.",
260, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
// clang-format on
const auto actual = doLineBreak(LINE_WIDTH);
EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
<< " vs " << std::endl
<< toString(textBuf, actual);
}
{
constexpr float LINE_WIDTH = 250;
// "is an" is a single replacement span. Do not break.
// clang-format off
std::vector<LineBreakExpectation> expect = {
{"This (is an) example ", 200, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"text.", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
// clang-format on
const auto actual = doLineBreak(LINE_WIDTH);
EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
<< " vs " << std::endl
<< toString(textBuf, actual);
}
{
constexpr float LINE_WIDTH = 190;
// "is an" is a single replacement span. Do not break.
// clang-format off
std::vector<LineBreakExpectation> expect = {
{"This (is an) ", 120, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"example text.", 130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
// clang-format on
const auto actual = doLineBreak(LINE_WIDTH);
EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
<< " vs " << std::endl
<< toString(textBuf, actual);
}
{
constexpr float LINE_WIDTH = 120;
// "is an" is a single replacement span. Do not break.
// clang-format off
std::vector<LineBreakExpectation> expect = {
{"This (is an) ", 120, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"example ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"text.", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
// clang-format on
const auto actual = doLineBreak(LINE_WIDTH);
EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
<< " vs " << std::endl
<< toString(textBuf, actual);
}
{
constexpr float LINE_WIDTH = 110;
// "is an" is a single replacement span. Do not break.
// clang-format off
std::vector<LineBreakExpectation> expect = {
{"This ", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"(is an) ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"example ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"text.", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
// clang-format on
const auto actual = doLineBreak(LINE_WIDTH);
EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
<< " vs " << std::endl
<< toString(textBuf, actual);
}
{
constexpr float LINE_WIDTH = 60;
// "is an" is a single replacement span. Do not break.
// clang-format off
std::vector<LineBreakExpectation> expect = {
{"This ", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"(is an", 60, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{") ex", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"ample ", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"text.", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
// clang-format on
const auto actual = doLineBreak(LINE_WIDTH);
EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
<< " vs " << std::endl
<< toString(textBuf, actual);
}
{
constexpr float LINE_WIDTH = 50;
// "is an" is a single replacement span. Do not break.
// clang-format off
std::vector<LineBreakExpectation> expect = {
{"This ", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"(", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"is an", 50, NO_START_HYPHEN, NO_END_HYPHEN, 0, 0},
{") ex", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"ample ", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"text.", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
// clang-format on
const auto actual = doLineBreak(LINE_WIDTH);
EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
<< " vs " << std::endl
<< toString(textBuf, actual);
}
{
constexpr float LINE_WIDTH = 40;
// "is an" is a single replacement span. Do not break.
// clang-format off
std::vector<LineBreakExpectation> expect = {
{"This ", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
// TODO(nona): This might be wrongly broken. "(is an" should be broken into "(" and
// "is an" as the desperate break.
{"(is an", 60, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{") ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"exa", 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"mple ", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"text", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{".", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
// clang-format on
const auto actual = doLineBreak(LINE_WIDTH);
EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
<< " vs " << std::endl
<< toString(textBuf, actual);
}
{
constexpr float LINE_WIDTH = 10;
// "is an" is a single replacement span. Do not break.
// clang-format off
std::vector<LineBreakExpectation> expect = {
{"T", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"h", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"i", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"s ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
// TODO(nona): This might be wrongly broken. "(is an" should be broken into "(" and
// "is an" as the desperate break.
{"(is an", 60, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{") ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"e", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"x", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"a", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"m", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"p", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"l", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"e ", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"t", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"e", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"x", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"t", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{".", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
// clang-format on
const auto actual = doLineBreak(LINE_WIDTH);
EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
<< " vs " << std::endl
<< toString(textBuf, actual);
}
}
TEST_F(OptimalLineBreakerTest, testControllCharAfterSpace) {
constexpr BreakStrategy HIGH_QUALITY = BreakStrategy::HighQuality;
constexpr BreakStrategy BALANCED = BreakStrategy::Balanced;
constexpr HyphenationFrequency NO_HYPHENATION = HyphenationFrequency::None;
const std::vector<uint16_t> textBuf = utf8ToUtf16("example \u2066example");
constexpr StartHyphenEdit NO_START_HYPHEN = StartHyphenEdit::NO_EDIT;
constexpr EndHyphenEdit NO_END_HYPHEN = EndHyphenEdit::NO_EDIT;
{
constexpr float LINE_WIDTH = 90;
// Note that HarfBuzz assigns 0px for control characters regardless of glyph existence in
// the font.
std::vector<LineBreakExpectation> expect = {
{"example ", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"\u2066example", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHENATION, "en-US", LINE_WIDTH);
expectBreak(expect, textBuf, BALANCED, NO_HYPHENATION, "en-US", LINE_WIDTH);
}
}
TEST_F(OptimalLineBreakerTest, roundingError) {
MeasuredTextBuilder builder;
auto family1 = buildFontFamily("Ascii.ttf");
std::vector<std::shared_ptr<FontFamily>> families = {family1};
auto fc = std::make_shared<FontCollection>(families);
MinikinPaint paint(fc);
paint.size = 56.0f; // Make 1em=56px
paint.scaleX = 1;
paint.letterSpacing = -0.093f;
paint.localeListId = LocaleListCache::getId("en-US");
const std::vector<uint16_t> textBuffer = utf8ToUtf16("8888888888888888888");
float measured = Layout::measureText(textBuffer, Range(0, textBuffer.size()), Bidi::LTR, paint,
StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, nullptr);
builder.addStyleRun(0, textBuffer.size(), std::move(paint), 0, 0, false);
std::unique_ptr<MeasuredText> measuredText = builder.build(
textBuffer, false /* compute hyphenation */, false /* compute full layout */,
false /* ignore kerning */, nullptr /* no hint */);
RectangleLineWidth rectangleLineWidth(measured);
TabStops tabStops(nullptr, 0, 10);
LineBreakResult r = doLineBreak(textBuffer, *measuredText, BreakStrategy::Balanced,
HyphenationFrequency::None, measured);
EXPECT_EQ(1u, r.breakPoints.size());
}
} // namespace
} // namespace minikin