summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-05-10 07:11:14 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-05-10 07:11:14 +0000
commit51aee8dc46b2b4fc4d06d44a1950a9ca012da5b8 (patch)
treea18e89087afaf754171443658f43a0c46d77f843
parent33c5c2df64f144f97e659817c3c6598c5df4495c (diff)
parent63438e24011d2201e85e17089f51af37dbc50a2b (diff)
downloadminikin-android13-mainline-adbd-release.tar.gz
Snap for 8564071 from 63438e24011d2201e85e17089f51af37dbc50a2b to mainline-adbd-releaseaml_adb_331113120aml_adb_331011050aml_adb_331011040android13-mainline-adbd-release
Change-Id: Ibfe79a62eb9104f96e280b2ad935e4fddf84109c
-rw-r--r--include/minikin/Emoji.h12
-rw-r--r--include/minikin/Hyphenator.h18
-rw-r--r--include/minikin/LineBreakStyle.h40
-rw-r--r--include/minikin/MeasuredText.h52
-rw-r--r--include/minikin/Measurement.h8
-rw-r--r--libs/minikin/Android.bp3
-rw-r--r--libs/minikin/FontCollection.cpp92
-rw-r--r--libs/minikin/FontFeatureUtils.cpp74
-rw-r--r--libs/minikin/FontFeatureUtils.h40
-rw-r--r--libs/minikin/GreedyLineBreaker.cpp3
-rw-r--r--libs/minikin/LayoutCore.cpp56
-rw-r--r--libs/minikin/LayoutUtils.cpp2
-rw-r--r--libs/minikin/LineBreakerUtil.h72
-rw-r--r--libs/minikin/Locale.cpp138
-rw-r--r--libs/minikin/Locale.h39
-rw-r--r--libs/minikin/MeasuredText.cpp36
-rw-r--r--libs/minikin/Measurement.cpp23
-rw-r--r--libs/minikin/WordBreaker.cpp21
-rw-r--r--libs/minikin/WordBreaker.h27
-rw-r--r--tests/data/Ascii.ttfbin1944 -> 1956 bytes
-rw-r--r--tests/data/Ascii.ttx1
-rw-r--r--tests/data/EmojiBase.ttfbin1428 -> 1584 bytes
-rw-r--r--tests/data/EmojiBase.ttx118
-rw-r--r--tests/data/OverrideEmoji.ttfbin1200 -> 1392 bytes
-rw-r--r--tests/data/OverrideEmoji.ttx148
-rw-r--r--tests/perftests/WordBreaker.cpp2
-rw-r--r--tests/unittest/Android.bp1
-rw-r--r--tests/unittest/FontCollectionItemizeTest.cpp116
-rw-r--r--tests/unittest/FontFamilyTest.cpp49
-rw-r--r--tests/unittest/FontFeatureTest.cpp150
-rw-r--r--tests/unittest/GreedyLineBreakerTest.cpp84
-rw-r--r--tests/unittest/LineBreakerTestHelper.h8
-rw-r--r--tests/unittest/MeasuredTextTest.cpp139
-rw-r--r--tests/unittest/OptimalLineBreakerTest.cpp604
-rw-r--r--tests/unittest/WordBreakerTests.cpp164
-rwxr-xr-xtools/mk_hyb_file.py22
36 files changed, 1537 insertions, 825 deletions
diff --git a/include/minikin/Emoji.h b/include/minikin/Emoji.h
index 046a9d6..7c1d81c 100644
--- a/include/minikin/Emoji.h
+++ b/include/minikin/Emoji.h
@@ -27,6 +27,18 @@ bool isEmojiBase(uint32_t c);
// Returns true if c is emoji modifier.
bool isEmojiModifier(uint32_t c);
+inline bool isRegionalIndicator(uint32_t c) {
+ return 0x1F1E6 <= c && c <= 0x1F1FF;
+}
+
+inline bool isKeyCap(uint32_t c) {
+ return c == 0x20E3;
+}
+
+inline bool isTagChar(uint32_t c) {
+ return 0xE0000 <= c && c <= 0xE007F;
+}
+
// Bidi override for ICU that knows about new emoji.
UCharDirection emojiBidiOverride(const void* context, UChar32 c);
diff --git a/include/minikin/Hyphenator.h b/include/minikin/Hyphenator.h
index 26c2a19..da265f0 100644
--- a/include/minikin/Hyphenator.h
+++ b/include/minikin/Hyphenator.h
@@ -125,16 +125,16 @@ template <typename T, size_t size>
constexpr size_t ARRAYSIZE(T const (&)[size]) {
return size;
}
-constexpr uint32_t HYPHEN_STR_ZWJ[] = {CHAR_ZWJ};
-constexpr uint32_t HYPHEN_STR_HYPHEN[] = {CHAR_HYPHEN};
-constexpr uint32_t HYPHEN_STR_ARMENIAN_HYPHEN[] = {CHAR_ARMENIAN_HYPHEN};
-constexpr uint32_t HYPHEN_STR_MAQAF[] = {CHAR_MAQAF};
-constexpr uint32_t HYPHEN_STR_UCAS_HYPHEN[] = {CHAR_UCAS_HYPHEN};
-constexpr uint32_t HYPHEN_STR_ZWJ_AND_HYPHEN[] = {CHAR_ZWJ, CHAR_HYPHEN};
-constexpr std::pair<const uint32_t*, size_t> EMPTY_HYPHEN_STR(nullptr, 0);
+constexpr uint16_t HYPHEN_STR_ZWJ[] = {CHAR_ZWJ};
+constexpr uint16_t HYPHEN_STR_HYPHEN[] = {CHAR_HYPHEN};
+constexpr uint16_t HYPHEN_STR_ARMENIAN_HYPHEN[] = {CHAR_ARMENIAN_HYPHEN};
+constexpr uint16_t HYPHEN_STR_MAQAF[] = {CHAR_MAQAF};
+constexpr uint16_t HYPHEN_STR_UCAS_HYPHEN[] = {CHAR_UCAS_HYPHEN};
+constexpr uint16_t HYPHEN_STR_ZWJ_AND_HYPHEN[] = {CHAR_ZWJ, CHAR_HYPHEN};
+constexpr std::pair<const uint16_t*, size_t> EMPTY_HYPHEN_STR(nullptr, 0);
#define MAKE_HYPHEN_STR(chars) std::make_pair((chars), ARRAYSIZE(chars))
-inline std::pair<const uint32_t*, size_t> getHyphenString(StartHyphenEdit hyph) {
+inline std::pair<const uint16_t*, size_t> getHyphenString(StartHyphenEdit hyph) {
if (hyph == StartHyphenEdit::INSERT_ZWJ) {
return MAKE_HYPHEN_STR(HYPHEN_STR_ZWJ);
} else if (hyph == StartHyphenEdit::INSERT_HYPHEN) {
@@ -144,7 +144,7 @@ inline std::pair<const uint32_t*, size_t> getHyphenString(StartHyphenEdit hyph)
}
}
-inline std::pair<const uint32_t*, size_t> getHyphenString(EndHyphenEdit hyph) {
+inline std::pair<const uint16_t*, size_t> getHyphenString(EndHyphenEdit hyph) {
switch (hyph) {
case EndHyphenEdit::REPLACE_WITH_HYPHEN: // fall through
case EndHyphenEdit::INSERT_HYPHEN:
diff --git a/include/minikin/LineBreakStyle.h b/include/minikin/LineBreakStyle.h
new file mode 100644
index 0000000..f474cbd
--- /dev/null
+++ b/include/minikin/LineBreakStyle.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#ifndef MINIKIN_LINE_BREAK_STYLE_H
+#define MINIKIN_LINE_BREAK_STYLE_H
+
+namespace minikin {
+
+// The line break style(lb) of the strings.
+// See https://www.unicode.org/reports/tr35/#Key_Type_Definitions
+enum class LineBreakStyle : uint8_t {
+ None = 0,
+ Loose = 1,
+ Normal = 2,
+ Strict = 3,
+};
+
+// The line break word style(lw) of the strings.
+// See the key "lw" in https://www.unicode.org/reports/tr35/#Key_Type_Definitions
+enum class LineBreakWordStyle : uint8_t {
+ None = 0,
+ Phrase = 1,
+};
+
+} // namespace minikin
+
+#endif // MINIKIN_LINE_BREAK_STYLE_H
diff --git a/include/minikin/MeasuredText.h b/include/minikin/MeasuredText.h
index 2c4b3f8..fa37eb6 100644
--- a/include/minikin/MeasuredText.h
+++ b/include/minikin/MeasuredText.h
@@ -23,6 +23,7 @@
#include "minikin/FontCollection.h"
#include "minikin/Layout.h"
#include "minikin/LayoutPieces.h"
+#include "minikin/LineBreakStyle.h"
#include "minikin/Macros.h"
#include "minikin/MinikinFont.h"
#include "minikin/Range.h"
@@ -41,6 +42,12 @@ public:
// Returns true if this run can be broken into multiple pieces for line breaking.
virtual bool canBreak() const = 0;
+ // Return the line break style(lb) for this run.
+ virtual LineBreakStyle lineBreakStyle() const = 0;
+
+ // Return the line break word style(lw) for this run.
+ virtual LineBreakWordStyle lineBreakWordStyle() const = 0;
+
// Returns the locale list ID for this run.
virtual uint32_t getLocaleListId() const = 0;
@@ -59,6 +66,8 @@ public:
StartHyphenEdit startHyphen, EndHyphenEdit endHyphen,
Layout* outLayout) const = 0;
+ virtual float measureText(const U16StringPiece& text) const = 0;
+
// Following two methods are only called when the implementation returns true for
// canBreak method.
@@ -83,10 +92,21 @@ protected:
class StyleRun : public Run {
public:
- StyleRun(const Range& range, MinikinPaint&& paint, bool isRtl)
- : Run(range), mPaint(std::move(paint)), mIsRtl(isRtl) {}
+ StyleRun(const Range& range, MinikinPaint&& paint, int lineBreakStyle, int lineBreakWordStyle,
+ bool isRtl)
+ : Run(range),
+ mPaint(std::move(paint)),
+ mLineBreakStyle(lineBreakStyle),
+ mLineBreakWordStyle(lineBreakWordStyle),
+ mIsRtl(isRtl) {}
bool canBreak() const override { return true; }
+ LineBreakStyle lineBreakStyle() const override {
+ return static_cast<LineBreakStyle>(mLineBreakStyle);
+ }
+ LineBreakWordStyle lineBreakWordStyle() const override {
+ return static_cast<LineBreakWordStyle>(mLineBreakWordStyle);
+ }
uint32_t getLocaleListId() const override { return mPaint.localeListId; }
bool isRtl() const override { return mIsRtl; }
@@ -109,9 +129,12 @@ public:
float measureHyphenPiece(const U16StringPiece& text, const Range& range,
StartHyphenEdit startHyphen, EndHyphenEdit endHyphen,
LayoutPieces* pieces) const override;
+ float measureText(const U16StringPiece& text) const;
private:
MinikinPaint mPaint;
+ int mLineBreakStyle;
+ int mLineBreakWordStyle;
const bool mIsRtl;
};
@@ -122,6 +145,8 @@ public:
bool isRtl() const { return false; }
bool canBreak() const { return false; }
+ LineBreakStyle lineBreakStyle() const override { return LineBreakStyle::None; }
+ LineBreakWordStyle lineBreakWordStyle() const override { return LineBreakWordStyle::None; }
uint32_t getLocaleListId() const { return mLocaleListId; }
void getMetrics(const U16StringPiece& /* text */, std::vector<float>* advances,
@@ -148,6 +173,8 @@ public:
StartHyphenEdit /* startHyphen */, EndHyphenEdit /* endHyphen */,
Layout* /* outLayout*/) const override {}
+ float measureText(const U16StringPiece&) const { return 0; }
+
private:
const float mWidth;
const uint32_t mLocaleListId;
@@ -206,13 +233,14 @@ private:
friend class MeasuredTextBuilder;
void measure(const U16StringPiece& textBuf, bool computeHyphenation, bool computeLayout,
- MeasuredText* hint);
+ bool ignoreHyphenKerning, MeasuredText* hint);
// Use MeasuredTextBuilder instead.
MeasuredText(const U16StringPiece& textBuf, std::vector<std::unique_ptr<Run>>&& runs,
- bool computeHyphenation, bool computeLayout, MeasuredText* hint)
+ bool computeHyphenation, bool computeLayout, bool ignoreHyphenKerning,
+ MeasuredText* hint)
: widths(textBuf.size()), runs(std::move(runs)) {
- measure(textBuf, computeHyphenation, computeLayout, hint);
+ measure(textBuf, computeHyphenation, computeLayout, ignoreHyphenKerning, hint);
}
};
@@ -220,8 +248,10 @@ class MeasuredTextBuilder {
public:
MeasuredTextBuilder() {}
- void addStyleRun(int32_t start, int32_t end, MinikinPaint&& paint, bool isRtl) {
- mRuns.emplace_back(std::make_unique<StyleRun>(Range(start, end), std::move(paint), isRtl));
+ void addStyleRun(int32_t start, int32_t end, MinikinPaint&& paint, int lineBreakStyle,
+ int lineBreakWordStyle, bool isRtl) {
+ mRuns.emplace_back(std::make_unique<StyleRun>(Range(start, end), std::move(paint),
+ lineBreakStyle, lineBreakWordStyle, isRtl));
}
void addReplacementRun(int32_t start, int32_t end, float width, uint32_t localeListId) {
@@ -235,10 +265,12 @@ public:
}
std::unique_ptr<MeasuredText> build(const U16StringPiece& textBuf, bool computeHyphenation,
- bool computeLayout, MeasuredText* hint) {
+ bool computeLayout, bool ignoreHyphenKerning,
+ MeasuredText* hint) {
// Unable to use make_unique here since make_unique is not a friend of MeasuredText.
- return std::unique_ptr<MeasuredText>(new MeasuredText(
- textBuf, std::move(mRuns), computeHyphenation, computeLayout, hint));
+ return std::unique_ptr<MeasuredText>(new MeasuredText(textBuf, std::move(mRuns),
+ computeHyphenation, computeLayout,
+ ignoreHyphenKerning, hint));
}
MINIKIN_PREVENT_COPY_ASSIGN_AND_MOVE(MeasuredTextBuilder);
diff --git a/include/minikin/Measurement.h b/include/minikin/Measurement.h
index d0aaa51..76f3701 100644
--- a/include/minikin/Measurement.h
+++ b/include/minikin/Measurement.h
@@ -17,11 +17,12 @@
#ifndef MINIKIN_MEASUREMENT_H
#define MINIKIN_MEASUREMENT_H
+#include <minikin/Layout.h>
+#include <minikin/MinikinExtent.h>
+
#include <cstddef>
#include <cstdint>
-#include <minikin/Layout.h>
-
namespace minikin {
float getRunAdvance(const float* advances, const uint16_t* buf, size_t start, size_t count,
@@ -34,6 +35,9 @@ void getBounds(const U16StringPiece& str, const Range& range, Bidi bidiFlags,
const MinikinPaint& paint, StartHyphenEdit startHyphen, EndHyphenEdit endHyphen,
MinikinRect* out);
+MinikinExtent getFontExtent(const U16StringPiece& str, const Range& range, Bidi bidiFlags,
+ const MinikinPaint& paint);
+
} // namespace minikin
#endif // MINIKIN_MEASUREMENT_H
diff --git a/libs/minikin/Android.bp b/libs/minikin/Android.bp
index 8356ea4..c0c4035 100644
--- a/libs/minikin/Android.bp
+++ b/libs/minikin/Android.bp
@@ -34,6 +34,7 @@ cc_library {
"Font.cpp",
"FontCollection.cpp",
"FontFamily.cpp",
+ "FontFeatureUtils.cpp",
"FontFileParser.cpp",
"FontUtils.cpp",
"GraphemeBreak.cpp",
@@ -89,8 +90,6 @@ cc_library {
],
export_header_lib_headers: ["libminikin_headers"],
- clang: true,
-
target: {
android: {
shared_libs: [
diff --git a/libs/minikin/FontCollection.cpp b/libs/minikin/FontCollection.cpp
index 9e4dd3b..38fc022 100644
--- a/libs/minikin/FontCollection.cpp
+++ b/libs/minikin/FontCollection.cpp
@@ -18,17 +18,17 @@
#include "minikin/FontCollection.h"
-#include <algorithm>
-
#include <log/log.h>
#include <unicode/unorm2.h>
-#include "minikin/Emoji.h"
-#include "minikin/FontFileParser.h"
+#include <algorithm>
#include "Locale.h"
#include "LocaleListCache.h"
#include "MinikinInternal.h"
+#include "minikin/Characters.h"
+#include "minikin/Emoji.h"
+#include "minikin/FontFileParser.h"
using std::vector;
@@ -46,7 +46,13 @@ static std::atomic<uint32_t> gNextCollectionId = {0};
namespace {
-uint32_t getGlyphCount(U16StringPiece text, uint32_t start, uint32_t end,
+inline bool isEmojiBreak(uint32_t prevCh, uint32_t ch) {
+ return !(isEmojiModifier(ch) || (isRegionalIndicator(prevCh) && isRegionalIndicator(ch)) ||
+ isKeyCap(ch) || isTagChar(ch) || ch == CHAR_ZWJ || prevCh == CHAR_ZWJ);
+}
+
+// Lower is better
+uint32_t getGlyphScore(U16StringPiece text, uint32_t start, uint32_t end,
const HbFontUniquePtr& font) {
HbBufferUniquePtr buffer(hb_buffer_create());
hb_buffer_set_direction(buffer.get(), HB_DIRECTION_LTR);
@@ -55,7 +61,61 @@ uint32_t getGlyphCount(U16StringPiece text, uint32_t start, uint32_t end,
unsigned int numGlyphs = -1;
hb_shape(font.get(), buffer.get(), nullptr, 0);
- hb_buffer_get_glyph_infos(buffer.get(), &numGlyphs);
+ hb_glyph_info_t* info = hb_buffer_get_glyph_infos(buffer.get(), &numGlyphs);
+
+ // HarfBuzz squashed unsupported tag sequence into first emoji glyph. So, we cannot use glyph
+ // count for the font selection score. Give extra score if the base score is different from the
+ // first glyph.
+ if (numGlyphs == 1) {
+ constexpr uint32_t TAG_SEQUENCE_FALLBACK_PENALTY = 0x10000;
+
+ uint32_t ch = 0;
+ const uint16_t* string = text.data();
+ const uint32_t string_size = text.size();
+ uint32_t readLength = 0;
+
+ U16_NEXT(string, readLength, string_size, ch);
+ if (U_IS_SURROGATE(ch)) {
+ return numGlyphs; // Broken surrogate pair.
+ }
+
+ if (readLength >= string_size) {
+ return numGlyphs; // No more characters remaining.
+ }
+
+ uint32_t nextCh = 0;
+ U16_NEXT(string, readLength, string_size, nextCh);
+
+ if (!isTagChar(nextCh)) {
+ return numGlyphs; // Not a tag sequence.
+ }
+
+ uint32_t composedGlyphId = info[0].codepoint;
+
+ // Shape only the first base emoji.
+ hb_buffer_reset(buffer.get());
+ hb_buffer_set_direction(buffer.get(), HB_DIRECTION_LTR);
+ hb_buffer_add_codepoints(buffer.get(), &ch, 1, 0, 1);
+ hb_buffer_guess_segment_properties(buffer.get());
+
+ unsigned int numGlyphs = -1;
+ hb_shape(font.get(), buffer.get(), nullptr, 0);
+ info = hb_buffer_get_glyph_infos(buffer.get(), &numGlyphs);
+
+ if (numGlyphs != 1) {
+ // If the single code point of the first base emoji is decomposed to multiple glyphs,
+ // we don't support it.
+ return numGlyphs;
+ }
+
+ uint32_t baseGlyphId = info[0].codepoint;
+ if (composedGlyphId == baseGlyphId) {
+ return numGlyphs + TAG_SEQUENCE_FALLBACK_PENALTY;
+ } else {
+ return numGlyphs;
+ }
+ }
+
return numGlyphs;
}
@@ -552,8 +612,15 @@ std::vector<FontCollection::Run> FontCollection::itemize(U16StringPiece text, Fo
if (intersection.empty()) {
breakRun = true; // None of last family can draw the given char.
} else {
- lastFamilyIndices = intersection;
- breakRun = false;
+ breakRun = isEmojiBreak(prevCh, ch);
+ if (!breakRun) {
+ // To select sequence supported families, update family indices with the
+ // intersection between the supported families between prev char and
+ // current char.
+ familyIndices = intersection;
+ lastFamilyIndices = intersection;
+ run->familyMatch = intersection;
+ }
}
} else {
breakRun = familyIndices[0] != lastFamilyIndices[0];
@@ -627,17 +694,18 @@ std::vector<FontCollection::Run> FontCollection::itemize(U16StringPiece text, Fo
FakedFont FontCollection::getBestFont(U16StringPiece text, const Run& run, FontStyle style) {
uint8_t bestIndex = 0;
- uint32_t bestGlyphCount = 0xFFFFFFFF;
+ uint32_t bestScore = 0xFFFFFFFF;
const std::shared_ptr<FontFamily>& family = mFamilies[run.familyMatch[0]];
if (family->isColorEmojiFamily() && run.familyMatch.size() > 1) {
for (size_t i = 0; i < run.familyMatch.size(); ++i) {
const std::shared_ptr<FontFamily>& family = mFamilies[run.familyMatch[i]];
const HbFontUniquePtr& font = family->getFont(0)->baseFont();
- uint32_t glyphCount = getGlyphCount(text, run.start, run.end, font);
- if (glyphCount < bestGlyphCount) {
+ uint32_t score = getGlyphScore(text, run.start, run.end, font);
+
+ if (score < bestScore) {
bestIndex = run.familyMatch[i];
- bestGlyphCount = glyphCount;
+ bestScore = score;
}
}
} else {
diff --git a/libs/minikin/FontFeatureUtils.cpp b/libs/minikin/FontFeatureUtils.cpp
new file mode 100644
index 0000000..e1ec065
--- /dev/null
+++ b/libs/minikin/FontFeatureUtils.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2021 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 "FontFeatureUtils.h"
+
+#include "StringPiece.h"
+
+namespace minikin {
+
+std::vector<hb_feature_t> cleanAndAddDefaultFontFeatures(const MinikinPaint& paint) {
+ std::vector<hb_feature_t> features;
+ // Disable default-on non-required ligature features if letter-spacing
+ // See http://dev.w3.org/csswg/css-text-3/#letter-spacing-property
+ // "When the effective spacing between two characters is not zero (due to
+ // either justification or a non-zero value of letter-spacing), user agents
+ // should not apply optional ligatures."
+ if (fabs(paint.letterSpacing) > 0.03) {
+ static constexpr hb_feature_t no_liga = {HB_TAG('l', 'i', 'g', 'a'), 0, 0, ~0u};
+ static constexpr hb_feature_t no_clig = {HB_TAG('c', 'l', 'i', 'g'), 0, 0, ~0u};
+ features.push_back(no_liga);
+ features.push_back(no_clig);
+ }
+
+ bool default_enable_chws = true;
+
+ static constexpr hb_tag_t chws_tag = HB_TAG('c', 'h', 'w', 's');
+ static constexpr hb_tag_t halt_tag = HB_TAG('h', 'a', 'l', 't');
+ static constexpr hb_tag_t palt_tag = HB_TAG('p', 'a', 'l', 't');
+
+ SplitIterator it(paint.fontFeatureSettings, ',');
+ while (it.hasNext()) {
+ StringPiece featureStr = it.next();
+ static hb_feature_t feature;
+ // We do not allow setting features on ranges. As such, reject any setting that has
+ // non-universal range.
+ if (hb_feature_from_string(featureStr.data(), featureStr.size(), &feature) &&
+ feature.start == 0 && feature.end == (unsigned int)-1) {
+ // OpenType requires disabling default `chws` feature if glyph-width features.
+ // https://docs.microsoft.com/en-us/typography/opentype/spec/features_ae#tag-chws
+ // Here, we follow Chrome's impl: not enabling default `chws` feature if `palt` or
+ // `halt` is enabled.
+ // https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/platform/fonts/shaping/font_features.cc;drc=77a9a09de0688ca449f5333a305ceaf3f36b6daf;l=215
+ if (default_enable_chws &&
+ (feature.tag == chws_tag ||
+ (feature.value && (feature.tag == halt_tag || feature.tag == palt_tag)))) {
+ default_enable_chws = false;
+ }
+
+ features.push_back(feature);
+ }
+ }
+
+ if (default_enable_chws) {
+ static constexpr hb_feature_t chws = {chws_tag, 1, 0, ~0u};
+ features.push_back(chws);
+ }
+
+ return features;
+}
+
+} // namespace minikin
diff --git a/libs/minikin/FontFeatureUtils.h b/libs/minikin/FontFeatureUtils.h
new file mode 100644
index 0000000..1a02173
--- /dev/null
+++ b/libs/minikin/FontFeatureUtils.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#ifndef MINIKIN_FONT_FEATURE_UTILS_H
+#define MINIKIN_FONT_FEATURE_UTILS_H
+
+#include <hb.h>
+
+#include "minikin/MinikinPaint.h"
+
+namespace minikin {
+
+/**
+ * Returns the final set of font features based on the features requested by this paint object and
+ * extra defaults or implied font features.
+ *
+ * Features are included from the paint object if they are:
+ * 1) in a supported range
+ *
+ * Default features are added based if they are:
+ * 1) implied due to Paint settings such as letterSpacing
+ * 2) default features that do not conflict with requested features
+ */
+std::vector<hb_feature_t> cleanAndAddDefaultFontFeatures(const MinikinPaint& paint);
+
+} // namespace minikin
+#endif // MINIKIN_LAYOUT_UTILS_H
diff --git a/libs/minikin/GreedyLineBreaker.cpp b/libs/minikin/GreedyLineBreaker.cpp
index 9bcb22d..bb2f91c 100644
--- a/libs/minikin/GreedyLineBreaker.cpp
+++ b/libs/minikin/GreedyLineBreaker.cpp
@@ -333,7 +333,8 @@ void GreedyLineBreaker::process() {
uint32_t newLocaleListId = run->getLocaleListId();
if (localeListId != newLocaleListId) {
Locale locale = getEffectiveLocale(newLocaleListId);
- nextWordBoundaryOffset = wordBreaker.followingWithLocale(locale, range.getStart());
+ nextWordBoundaryOffset = wordBreaker.followingWithLocale(
+ locale, run->lineBreakStyle(), run->lineBreakWordStyle(), range.getStart());
mHyphenator = HyphenatorMap::lookup(locale);
localeListId = newLocaleListId;
}
diff --git a/libs/minikin/LayoutCore.cpp b/libs/minikin/LayoutCore.cpp
index f1d0de8..6f33ad7 100644
--- a/libs/minikin/LayoutCore.cpp
+++ b/libs/minikin/LayoutCore.cpp
@@ -18,12 +18,6 @@
#include "minikin/LayoutCore.h"
-#include <cmath>
-#include <iostream>
-#include <mutex>
-#include <string>
-#include <vector>
-
#include <hb-icu.h>
#include <hb-ot.h>
#include <log/log.h>
@@ -31,16 +25,22 @@
#include <unicode/utf16.h>
#include <utils/LruCache.h>
-#include "minikin/Emoji.h"
-#include "minikin/HbUtils.h"
-#include "minikin/LayoutCache.h"
-#include "minikin/LayoutPieces.h"
-#include "minikin/Macros.h"
+#include <cmath>
+#include <iostream>
+#include <mutex>
+#include <string>
+#include <vector>
#include "BidiUtils.h"
+#include "FontFeatureUtils.h"
#include "LayoutUtils.h"
#include "LocaleListCache.h"
#include "MinikinInternal.h"
+#include "minikin/Emoji.h"
+#include "minikin/HbUtils.h"
+#include "minikin/LayoutCache.h"
+#include "minikin/LayoutPieces.h"
+#include "minikin/Macros.h"
namespace minikin {
@@ -189,20 +189,6 @@ static bool isScriptOkForLetterspacing(hb_script_t script) {
script == HB_SCRIPT_TIRHUTA || script == HB_SCRIPT_OGHAM);
}
-static void addFeatures(const std::string& str, std::vector<hb_feature_t>* features) {
- SplitIterator it(str, ',');
- while (it.hasNext()) {
- StringPiece featureStr = it.next();
- static hb_feature_t feature;
- /* We do not allow setting features on ranges. As such, reject any
- * setting that has non-universal range. */
- if (hb_feature_from_string(featureStr.data(), featureStr.size(), &feature) &&
- feature.start == 0 && feature.end == (unsigned int)-1) {
- features->push_back(feature);
- }
- }
-}
-
static inline hb_codepoint_t determineHyphenChar(hb_codepoint_t preferredHyphen, hb_font_t* font) {
hb_codepoint_t glyph;
if (preferredHyphen == 0x058A /* ARMENIAN_HYPHEN */
@@ -230,9 +216,7 @@ static inline hb_codepoint_t determineHyphenChar(hb_codepoint_t preferredHyphen,
template <typename HyphenEdit>
static inline void addHyphenToHbBuffer(const HbBufferUniquePtr& buffer, const HbFontUniquePtr& font,
HyphenEdit hyphen, uint32_t cluster) {
- const uint32_t* chars;
- size_t size;
- std::tie(chars, size) = getHyphenString(hyphen);
+ auto [chars, size] = getHyphenString(hyphen);
for (size_t i = 0; i < size; i++) {
hb_buffer_add(buffer.get(), determineHyphenChar(chars[i], font.get()), cluster);
}
@@ -359,19 +343,7 @@ LayoutPiece::LayoutPiece(const U16StringPiece& textBuf, const Range& range, bool
std::vector<FontCollection::Run> items =
paint.font->itemize(substr, paint.fontStyle, paint.localeListId, paint.familyVariant);
- std::vector<hb_feature_t> features;
- // Disable default-on non-required ligature features if letter-spacing
- // See http://dev.w3.org/csswg/css-text-3/#letter-spacing-property
- // "When the effective spacing between two characters is not zero (due to
- // either justification or a non-zero value of letter-spacing), user agents
- // should not apply optional ligatures."
- if (fabs(paint.letterSpacing) > 0.03) {
- static const hb_feature_t no_liga = {HB_TAG('l', 'i', 'g', 'a'), 0, 0, ~0u};
- static const hb_feature_t no_clig = {HB_TAG('c', 'l', 'i', 'g'), 0, 0, ~0u};
- features.push_back(no_liga);
- features.push_back(no_clig);
- }
- addFeatures(paint.fontFeatureSettings, &features);
+ std::vector<hb_feature_t> features = cleanAndAddDefaultFontFeatures(paint);
std::vector<HbFontUniquePtr> hbFonts;
double size = paint.size;
@@ -455,7 +427,7 @@ LayoutPiece::LayoutPiece(const U16StringPiece& textBuf, const Range& range, bool
if (localeList.size() != 0) {
hb_language_t hbLanguage = localeList.getHbLanguage(0);
for (size_t i = 0; i < localeList.size(); ++i) {
- if (localeList[i].supportsHbScript(script)) {
+ if (localeList[i].supportsScript(hb_script_to_iso15924_tag(script))) {
hbLanguage = localeList.getHbLanguage(i);
break;
}
diff --git a/libs/minikin/LayoutUtils.cpp b/libs/minikin/LayoutUtils.cpp
index acf07e2..c69dbf3 100644
--- a/libs/minikin/LayoutUtils.cpp
+++ b/libs/minikin/LayoutUtils.cpp
@@ -16,6 +16,8 @@
#include "LayoutUtils.h"
+#include "StringPiece.h"
+
namespace minikin {
/*
diff --git a/libs/minikin/LineBreakerUtil.h b/libs/minikin/LineBreakerUtil.h
index 8b383a4..5764c5e 100644
--- a/libs/minikin/LineBreakerUtil.h
+++ b/libs/minikin/LineBreakerUtil.h
@@ -62,13 +62,15 @@ inline Locale getEffectiveLocale(uint32_t localeListId) {
// Retrieves hyphenation break points from a word.
inline void populateHyphenationPoints(
- const U16StringPiece& textBuf, // A text buffer.
- const Run& run, // A run of this region.
- const Hyphenator& hyphenator, // A hyphenator to be used for hyphenation.
- const Range& contextRange, // A context range for measuring hyphenated piece.
- const Range& hyphenationTargetRange, // An actual range for the hyphenation target.
- std::vector<HyphenBreak>* out, // An output to be appended.
- LayoutPieces* pieces) { // An output of layout pieces. Maybe null.
+ const U16StringPiece& textBuf, // A text buffer.
+ const Run& run, // A run of this region.
+ const Hyphenator& hyphenator, // A hyphenator to be used for hyphenation.
+ const Range& contextRange, // A context range for measuring hyphenated piece.
+ const Range& hyphenationTargetRange, // An actual range for the hyphenation target.
+ const std::vector<float>& charWidths, // Char width used for hyphen piece estimation.
+ bool ignoreKerning, // True use full shaping for hyphenation piece.
+ std::vector<HyphenBreak>* out, // An output to be appended.
+ LayoutPieces* pieces) { // An output of layout pieces. Maybe null.
if (!run.getRange().contains(contextRange) || !contextRange.contains(hyphenationTargetRange)) {
return;
}
@@ -81,19 +83,45 @@ inline void populateHyphenationPoints(
continue; // Not a hyphenation point.
}
- auto hyphenPart = contextRange.split(i);
- U16StringPiece firstText = textBuf.substr(hyphenPart.first);
- U16StringPiece secondText = textBuf.substr(hyphenPart.second);
- const float first =
- run.measureHyphenPiece(firstText, Range(0, firstText.size()),
- StartHyphenEdit::NO_EDIT /* start hyphen edit */,
- editForThisLine(hyph) /* end hyphen edit */, pieces);
- const float second =
- run.measureHyphenPiece(secondText, Range(0, secondText.size()),
- editForNextLine(hyph) /* start hyphen edit */,
- EndHyphenEdit::NO_EDIT /* end hyphen edit */, pieces);
-
- out->emplace_back(i, hyph, first, second);
+ if (!ignoreKerning) {
+ auto hyphenPart = contextRange.split(i);
+ U16StringPiece firstText = textBuf.substr(hyphenPart.first);
+ U16StringPiece secondText = textBuf.substr(hyphenPart.second);
+ const float first =
+ run.measureHyphenPiece(firstText, Range(0, firstText.size()),
+ StartHyphenEdit::NO_EDIT /* start hyphen edit */,
+ editForThisLine(hyph) /* end hyphen edit */, pieces);
+ const float second =
+ run.measureHyphenPiece(secondText, Range(0, secondText.size()),
+ editForNextLine(hyph) /* start hyphen edit */,
+ EndHyphenEdit::NO_EDIT /* end hyphen edit */, pieces);
+
+ out->emplace_back(i, hyph, first, second);
+ } else {
+ float first = 0;
+ float second = 0;
+ for (uint32_t j = contextRange.getStart(); j < i; ++j) {
+ first += charWidths[j];
+ }
+ for (uint32_t j = i; j < contextRange.getEnd(); ++j) {
+ second += charWidths[j];
+ }
+
+ EndHyphenEdit endEdit = editForThisLine(hyph);
+ StartHyphenEdit startEdit = editForNextLine(hyph);
+
+ if (endEdit != EndHyphenEdit::NO_EDIT) {
+ auto [str, strSize] = getHyphenString(endEdit);
+ first += run.measureText(U16StringPiece(str, strSize));
+ }
+
+ if (startEdit != StartHyphenEdit::NO_EDIT) {
+ auto [str, strSize] = getHyphenString(startEdit);
+ second += run.measureText(U16StringPiece(str, strSize));
+ }
+
+ out->emplace_back(i, hyph, first, second);
+ }
}
}
@@ -151,7 +179,9 @@ struct CharProcessor {
uint32_t newLocaleListId = run.getLocaleListId();
if (localeListId != newLocaleListId) {
Locale locale = getEffectiveLocale(newLocaleListId);
- nextWordBreak = breaker.followingWithLocale(locale, run.getRange().getStart());
+ nextWordBreak = breaker.followingWithLocale(locale, run.lineBreakStyle(),
+ run.lineBreakWordStyle(),
+ run.getRange().getStart());
hyphenator = HyphenatorMap::lookup(locale);
localeListId = newLocaleListId;
}
diff --git a/libs/minikin/Locale.cpp b/libs/minikin/Locale.cpp
index 553f61a..d246020 100644
--- a/libs/minikin/Locale.cpp
+++ b/libs/minikin/Locale.cpp
@@ -233,36 +233,11 @@ void Locale::resolveUnicodeExtension(const char* buf, size_t length) {
if (pos != buf + length) {
pos += strlen(kPrefix);
const size_t remainingLength = length - (pos - buf);
- mLBStyle = resolveLineBreakStyle(pos, remainingLength);
mEmojiStyle = resolveEmojiStyle(pos, remainingLength);
}
}
// static
-// Lookup line break subtag and determine the line break style.
-LineBreakStyle Locale::resolveLineBreakStyle(const char* buf, size_t length) {
- // 8 is the length of "-u-lb-loose", which is the shortest line break subtag,
- // unnecessary comparison can be avoided if total length is smaller than 11.
- const size_t kMinSubtagLength = 8;
- if (length >= kMinSubtagLength) {
- static const char kPrefix[] = "lb-";
- const char* pos = std::search(buf, buf + length, kPrefix, kPrefix + strlen(kPrefix));
- if (pos != buf + length) { // found
- pos += strlen(kPrefix);
- const size_t remainingLength = length - (pos - buf);
- if (isSubtag(pos, remainingLength, "loose", 5)) {
- return LineBreakStyle::LOOSE;
- } else if (isSubtag(pos, remainingLength, "normal", 6)) {
- return LineBreakStyle::NORMAL;
- } else if (isSubtag(pos, remainingLength, "strict", 6)) {
- return LineBreakStyle::STRICT;
- }
- }
- }
- return LineBreakStyle::EMPTY;
-}
-
-// static
// Lookup emoji subtag and determine the emoji style.
EmojiStyle Locale::resolveEmojiStyle(const char* buf, size_t length) {
// 7 is the length of "-u-em-text", which is the shortest emoji subtag,
@@ -339,7 +314,78 @@ uint8_t Locale::scriptToSubScriptBits(uint32_t script) {
}
std::string Locale::getString() const {
- char buf[32] = {};
+ char buf[32];
+ int i = buildLocaleString(buf);
+ return std::string(buf, i);
+}
+
+std::string Locale::getStringWithLineBreakOption(LineBreakStyle lbStyle,
+ LineBreakWordStyle lbWordStyle) const {
+ char buf[48];
+ int i = buildLocaleString(buf);
+
+ // Add line break unicode extension.
+ if (lbStyle != LineBreakStyle::None || lbWordStyle != LineBreakWordStyle::None) {
+ buf[i++] = '-';
+ buf[i++] = 'u';
+ }
+
+ if (lbStyle != LineBreakStyle::None) {
+ buf[i++] = '-';
+ buf[i++] = 'l';
+ buf[i++] = 'b';
+ buf[i++] = '-';
+ switch (lbStyle) {
+ case LineBreakStyle::Loose:
+ buf[i++] = 'l';
+ buf[i++] = 'o';
+ buf[i++] = 'o';
+ buf[i++] = 's';
+ buf[i++] = 'e';
+ break;
+ case LineBreakStyle::Normal:
+ buf[i++] = 'n';
+ buf[i++] = 'o';
+ buf[i++] = 'r';
+ buf[i++] = 'm';
+ buf[i++] = 'a';
+ buf[i++] = 'l';
+ break;
+ case LineBreakStyle::Strict:
+ buf[i++] = 's';
+ buf[i++] = 't';
+ buf[i++] = 'r';
+ buf[i++] = 'i';
+ buf[i++] = 'c';
+ buf[i++] = 't';
+ break;
+ default:
+ MINIKIN_ASSERT(false, "Must not reached.");
+ }
+ }
+
+ if (lbWordStyle != LineBreakWordStyle::None) {
+ buf[i++] = '-';
+ buf[i++] = 'l';
+ buf[i++] = 'w';
+ buf[i++] = '-';
+ switch (lbWordStyle) {
+ case LineBreakWordStyle::Phrase:
+ buf[i++] = 'p';
+ buf[i++] = 'h';
+ buf[i++] = 'r';
+ buf[i++] = 'a';
+ buf[i++] = 's';
+ buf[i++] = 'e';
+ break;
+ default:
+ MINIKIN_ASSERT(false, "Must not reached.");
+ }
+ }
+ return std::string(buf, i);
+}
+
+int Locale::buildLocaleString(char* buf) const {
size_t i;
if (mLanguage == NO_LANGUAGE) {
buf[0] = 'u';
@@ -378,43 +424,7 @@ std::string Locale::getString() const {
MINIKIN_ASSERT(false, "Must not reached.");
}
}
- // Add line break unicode extension.
- if (mLBStyle != LineBreakStyle::EMPTY) {
- buf[i++] = '-';
- buf[i++] = 'u';
- buf[i++] = '-';
- buf[i++] = 'l';
- buf[i++] = 'b';
- buf[i++] = '-';
- switch (mLBStyle) {
- case LineBreakStyle::LOOSE:
- buf[i++] = 'l';
- buf[i++] = 'o';
- buf[i++] = 'o';
- buf[i++] = 's';
- buf[i++] = 'e';
- break;
- case LineBreakStyle::NORMAL:
- buf[i++] = 'n';
- buf[i++] = 'o';
- buf[i++] = 'r';
- buf[i++] = 'm';
- buf[i++] = 'a';
- buf[i++] = 'l';
- break;
- case LineBreakStyle::STRICT:
- buf[i++] = 's';
- buf[i++] = 't';
- buf[i++] = 'r';
- buf[i++] = 'i';
- buf[i++] = 'c';
- buf[i++] = 't';
- break;
- default:
- MINIKIN_ASSERT(false, "Must not reached.");
- }
- }
- return std::string(buf, i);
+ return i;
}
Locale Locale::getPartialLocale(SubtagBits bits) const {
@@ -449,7 +459,7 @@ bool Locale::supportsScript(uint8_t providedBits, uint8_t requestedBits) {
return requestedBits != 0 && (providedBits & requestedBits) == requestedBits;
}
-bool Locale::supportsHbScript(hb_script_t script) const {
+bool Locale::supportsScript(uint32_t script) const {
static_assert(unpackScript(packScript('J', 'p', 'a', 'n')) == HB_TAG('J', 'p', 'a', 'n'),
"The Minikin script and HarfBuzz hb_script_t have different encodings.");
uint32_t packedScript = packScript(script);
diff --git a/libs/minikin/Locale.h b/libs/minikin/Locale.h
index 7557301..285bb7b 100644
--- a/libs/minikin/Locale.h
+++ b/libs/minikin/Locale.h
@@ -17,12 +17,13 @@
#ifndef MINIKIN_LOCALE_LIST_H
#define MINIKIN_LOCALE_LIST_H
+#include <hb.h>
+
#include <string>
#include <vector>
-#include <hb.h>
-
#include "StringPiece.h"
+#include "minikin/LineBreakStyle.h"
namespace minikin {
@@ -63,14 +64,6 @@ enum class EmojiStyle : uint8_t {
TEXT = 3, // Text (black/white) emoji style is specified.
};
-// Enum for line break style.
-enum class LineBreakStyle : uint8_t {
- EMPTY = 0, // No line break style is specified.
- LOOSE = 1, // line break style is loose.
- NORMAL = 2, // line break style is normal.
- STRICT = 3, // line break style is strict.
-};
-
// Locale is a compact representation of a BCP 47 language tag.
// It does not capture all possible information, only what directly affects text layout:
// font rendering, hyphenation, word breaking, etc.
@@ -89,8 +82,7 @@ public:
mRegion(NO_REGION),
mSubScriptBits(0ul),
mVariant(Variant::NO_VARIANT),
- mEmojiStyle(EmojiStyle::EMPTY),
- mLBStyle(LineBreakStyle::EMPTY) {}
+ mEmojiStyle(EmojiStyle::EMPTY) {}
// Parse from string
Locale(const StringPiece& buf);
@@ -102,13 +94,12 @@ public:
mRegion(extractBits(identifier, 14, 15)),
mSubScriptBits(scriptToSubScriptBits(mScript)),
mVariant(static_cast<Variant>(extractBits(identifier, 0, 2))),
- mEmojiStyle(static_cast<EmojiStyle>(extractBits(identifier, 12, 2))),
- mLBStyle(static_cast<LineBreakStyle>(extractBits(identifier, 10, 2))) {}
+ mEmojiStyle(static_cast<EmojiStyle>(extractBits(identifier, 12, 2))) {}
bool operator==(const Locale& other) const {
return !isUnsupported() && isEqualScript(other) && mLanguage == other.mLanguage &&
mRegion == other.mRegion && mVariant == other.mVariant &&
- mLBStyle == other.mLBStyle && mEmojiStyle == other.mEmojiStyle;
+ mEmojiStyle == other.mEmojiStyle;
}
bool operator!=(const Locale other) const { return !(*this == other); }
@@ -117,12 +108,10 @@ public:
inline bool hasScript() const { return mScript != NO_SCRIPT; }
inline bool hasRegion() const { return mRegion != NO_REGION; }
inline bool hasVariant() const { return mVariant != Variant::NO_VARIANT; }
- inline bool hasLBStyle() const { return mLBStyle != LineBreakStyle::EMPTY; }
inline bool hasEmojiStyle() const { return mEmojiStyle != EmojiStyle::EMPTY; }
inline bool isSupported() const {
- return hasLanguage() || hasScript() || hasRegion() || hasVariant() || hasLBStyle() ||
- hasEmojiStyle();
+ return hasLanguage() || hasScript() || hasRegion() || hasVariant() || hasEmojiStyle();
}
inline bool isUnsupported() const { return !isSupported(); }
@@ -133,10 +122,13 @@ public:
// Returns true if this script supports the given script. For example, ja-Jpan supports Hira,
// ja-Hira doesn't support Jpan.
- bool supportsHbScript(hb_script_t script) const;
+ bool supportsScript(uint32_t script) const;
std::string getString() const;
+ std::string getStringWithLineBreakOption(LineBreakStyle lbStyle,
+ LineBreakWordStyle lbWordStyle) const;
+
// Calculates a matching score. This score represents how well the input locales cover this
// locale. The maximum score in the locale list is returned.
// 0 = no match, 1 = script match, 2 = script and primary language match.
@@ -148,12 +140,11 @@ public:
// ssssssssssssssssssss Script Code (20 bits)
// rrrrrrrrrrrrrrr Region Code (15 bits)
// ee Emoji Style (2 bits)
- // bb Line Break Style (2 bits)
- // XXXXXXXX Free (8 bits)
+ // XXXXXXXXXX Free (10 bits)
// vv German Variant (2 bits)
uint64_t getIdentifier() const {
return ((uint64_t)mLanguage << 49) | ((uint64_t)mScript << 29) | ((uint64_t)mRegion << 14) |
- ((uint64_t)mEmojiStyle << 12) | ((uint64_t)mLBStyle << 10) | (uint64_t)mVariant;
+ ((uint64_t)mEmojiStyle << 12) | (uint64_t)mVariant;
}
Locale getPartialLocale(SubtagBits bits) const;
@@ -187,7 +178,6 @@ private:
Variant mVariant;
EmojiStyle mEmojiStyle;
- LineBreakStyle mLBStyle;
void resolveUnicodeExtension(const char* buf, size_t length);
@@ -195,9 +185,10 @@ private:
return (value >> shift) & ((1 << nBits) - 1);
}
+ int buildLocaleString(char* buf) const;
+
static uint8_t scriptToSubScriptBits(uint32_t rawScript);
- static LineBreakStyle resolveLineBreakStyle(const char* buf, size_t length);
static EmojiStyle resolveEmojiStyle(const char* buf, size_t length);
static EmojiStyle scriptToEmojiStyle(uint32_t script);
diff --git a/libs/minikin/MeasuredText.cpp b/libs/minikin/MeasuredText.cpp
index 1bf3942..33dab38 100644
--- a/libs/minikin/MeasuredText.cpp
+++ b/libs/minikin/MeasuredText.cpp
@@ -75,6 +75,37 @@ void StyleRun::getMetrics(const U16StringPiece& textBuf, std::vector<float>* adv
}
}
+// Helper class for composing total advances.
+class TotalAdvancesCompositor {
+public:
+ TotalAdvancesCompositor() : mOut(0) {}
+
+ void operator()(const LayoutPiece& layoutPiece, const MinikinPaint&) {
+ for (float w : layoutPiece.advances()) {
+ mOut += w;
+ }
+ }
+
+ float getTotalAdvance() { return mOut; }
+
+private:
+ float mOut;
+};
+
+float StyleRun::measureText(const U16StringPiece& textBuf) const {
+ TotalAdvancesCompositor compositor;
+ const Bidi bidiFlag = mIsRtl ? Bidi::FORCE_RTL : Bidi::FORCE_LTR;
+ LayoutCache& layoutCache = LayoutCache::getInstance();
+ for (const BidiText::RunInfo info : BidiText(textBuf, Range(0, textBuf.length()), bidiFlag)) {
+ for (const auto [context, piece] : LayoutSplitter(textBuf, info.range, info.isRtl)) {
+ layoutCache.getOrCreate(textBuf.substr(context), piece - context.getStart(), mPaint,
+ info.isRtl, StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT,
+ compositor);
+ }
+ }
+ return compositor.getTotalAdvance();
+}
+
// Helper class for composing total amount of advance
class TotalAdvanceCompositor {
public:
@@ -125,7 +156,7 @@ float StyleRun::measureHyphenPiece(const U16StringPiece& textBuf, const Range& r
}
void MeasuredText::measure(const U16StringPiece& textBuf, bool computeHyphenation,
- bool computeLayout, MeasuredText* hint) {
+ bool computeLayout, bool ignoreHyphenKerning, MeasuredText* hint) {
if (textBuf.size() == 0) {
return;
}
@@ -153,7 +184,8 @@ void MeasuredText::measure(const U16StringPiece& textBuf, bool computeHyphenatio
}
populateHyphenationPoints(textBuf, *run, *proc.hyphenator, proc.contextRange(),
- proc.wordRange(), &hyphenBreaks, piecesOut);
+ proc.wordRange(), widths, ignoreHyphenKerning, &hyphenBreaks,
+ piecesOut);
}
}
}
diff --git a/libs/minikin/Measurement.cpp b/libs/minikin/Measurement.cpp
index 66886da..093dba8 100644
--- a/libs/minikin/Measurement.cpp
+++ b/libs/minikin/Measurement.cpp
@@ -157,4 +157,27 @@ void getBounds(const U16StringPiece& str, const Range& range, Bidi bidiFlag,
*out = bc.mBounds;
}
+struct ExtentComposer {
+ ExtentComposer() {}
+
+ void operator()(const LayoutPiece& layoutPiece, const MinikinPaint&) {
+ extent.extendBy(layoutPiece.extent());
+ }
+
+ MinikinExtent extent;
+};
+
+MinikinExtent getFontExtent(const U16StringPiece& textBuf, const Range& range, Bidi bidiFlag,
+ const MinikinPaint& paint) {
+ ExtentComposer composer;
+ for (const BidiText::RunInfo info : BidiText(textBuf, range, bidiFlag)) {
+ for (const auto [context, piece] : LayoutSplitter(textBuf, info.range, info.isRtl)) {
+ LayoutCache::getInstance().getOrCreate(
+ textBuf.substr(context), piece - context.getStart(), paint, info.isRtl,
+ StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, composer);
+ }
+ }
+ return composer.extent;
+}
+
} // namespace minikin
diff --git a/libs/minikin/WordBreaker.cpp b/libs/minikin/WordBreaker.cpp
index 7a9ab3a..fd0dea9 100644
--- a/libs/minikin/WordBreaker.cpp
+++ b/libs/minikin/WordBreaker.cpp
@@ -32,21 +32,24 @@
namespace minikin {
namespace {
-static UBreakIterator* createNewIterator(const Locale& locale) {
+static UBreakIterator* createNewIterator(const Locale& locale, LineBreakStyle lbStyle,
+ LineBreakWordStyle lbWordStyle) {
// TODO: handle failure status
UErrorCode status = U_ZERO_ERROR;
char localeID[ULOC_FULLNAME_CAPACITY] = {};
- uloc_forLanguageTag(locale.getString().c_str(), localeID, ULOC_FULLNAME_CAPACITY, nullptr,
- &status);
+ uloc_forLanguageTag(locale.getStringWithLineBreakOption(lbStyle, lbWordStyle).c_str(), localeID,
+ ULOC_FULLNAME_CAPACITY, nullptr, &status);
return ubrk_open(UBreakIteratorType::UBRK_LINE, localeID, nullptr, 0, &status);
}
} // namespace
-ICULineBreakerPool::Slot ICULineBreakerPoolImpl::acquire(const Locale& locale) {
+ICULineBreakerPool::Slot ICULineBreakerPoolImpl::acquire(const Locale& locale,
+ LineBreakStyle lbStyle,
+ LineBreakWordStyle lbWordStyle) {
const uint64_t id = locale.getIdentifier();
std::lock_guard<std::mutex> lock(mMutex);
for (auto i = mPool.begin(); i != mPool.end(); i++) {
- if (i->localeId == id) {
+ if (i->localeId == id && i->lbStyle == lbStyle && i->lbWordStyle == lbWordStyle) {
Slot slot = std::move(*i);
mPool.erase(i);
return slot;
@@ -54,7 +57,8 @@ ICULineBreakerPool::Slot ICULineBreakerPoolImpl::acquire(const Locale& locale) {
}
// Not found in pool. Create new one.
- return {id, IcuUbrkUniquePtr(createNewIterator(locale))};
+ return {id, lbStyle, lbWordStyle,
+ IcuUbrkUniquePtr(createNewIterator(locale, lbStyle, lbWordStyle))};
}
void ICULineBreakerPoolImpl::release(ICULineBreakerPool::Slot&& slot) {
@@ -75,8 +79,9 @@ WordBreaker::WordBreaker() : mPool(&ICULineBreakerPoolImpl::getInstance()) {}
WordBreaker::WordBreaker(ICULineBreakerPool* pool) : mPool(pool) {}
-ssize_t WordBreaker::followingWithLocale(const Locale& locale, size_t from) {
- mIcuBreaker = mPool->acquire(locale);
+ssize_t WordBreaker::followingWithLocale(const Locale& locale, LineBreakStyle lbStyle,
+ LineBreakWordStyle lbWordStyle, size_t from) {
+ mIcuBreaker = mPool->acquire(locale, lbStyle, lbWordStyle);
UErrorCode status = U_ZERO_ERROR;
MINIKIN_ASSERT(mText != nullptr, "setText must be called first");
// TODO: handle failure status
diff --git a/libs/minikin/WordBreaker.h b/libs/minikin/WordBreaker.h
index 99023a2..45bcd40 100644
--- a/libs/minikin/WordBreaker.h
+++ b/libs/minikin/WordBreaker.h
@@ -23,17 +23,17 @@
#ifndef MINIKIN_WORD_BREAKER_H
#define MINIKIN_WORD_BREAKER_H
+#include <unicode/ubrk.h>
+
#include <list>
#include <mutex>
-#include <unicode/ubrk.h>
-
+#include "Locale.h"
#include "minikin/IcuUtils.h"
+#include "minikin/LineBreakStyle.h"
#include "minikin/Macros.h"
#include "minikin/Range.h"
-#include "Locale.h"
-
namespace minikin {
// A class interface for providing pooling implementation of ICU's line breaker.
@@ -42,8 +42,12 @@ class ICULineBreakerPool {
public:
struct Slot {
Slot() : localeId(0), breaker(nullptr) {}
- Slot(uint64_t localeId, IcuUbrkUniquePtr&& breaker)
- : localeId(localeId), breaker(std::move(breaker)) {}
+ Slot(uint64_t localeId, LineBreakStyle lbStyle, LineBreakWordStyle lbWordStyle,
+ IcuUbrkUniquePtr&& breaker)
+ : localeId(localeId),
+ lbStyle(lbStyle),
+ lbWordStyle(lbWordStyle),
+ breaker(std::move(breaker)) {}
Slot(Slot&& other) = default;
Slot& operator=(Slot&& other) = default;
@@ -53,10 +57,13 @@ public:
Slot& operator=(const Slot&) = delete;
uint64_t localeId;
+ LineBreakStyle lbStyle;
+ LineBreakWordStyle lbWordStyle;
IcuUbrkUniquePtr breaker;
};
virtual ~ICULineBreakerPool() {}
- virtual Slot acquire(const Locale& locale) = 0;
+ virtual Slot acquire(const Locale& locale, LineBreakStyle lbStyle,
+ LineBreakWordStyle lbWordStyle) = 0;
virtual void release(Slot&& slot) = 0;
};
@@ -64,7 +71,8 @@ public:
// Since creating ICU line breaker instance takes some time. Pool it for later use.
class ICULineBreakerPoolImpl : public ICULineBreakerPool {
public:
- Slot acquire(const Locale& locale) override;
+ Slot acquire(const Locale& locale, LineBreakStyle lbStyle,
+ LineBreakWordStyle lbWordStyle) override;
void release(Slot&& slot) override;
static ICULineBreakerPoolImpl& getInstance() {
@@ -99,7 +107,8 @@ public:
// Advance iterator to the break just after "from" with using the new provided locale.
// Return offset, or -1 if EOT
- ssize_t followingWithLocale(const Locale& locale, size_t from);
+ ssize_t followingWithLocale(const Locale& locale, LineBreakStyle lbStyle,
+ LineBreakWordStyle lbWordStyle, size_t from);
// Current offset of iterator, equal to 0 at BOT or last return from next()
ssize_t current() const;
diff --git a/tests/data/Ascii.ttf b/tests/data/Ascii.ttf
index 2e6835b..dc56be5 100644
--- a/tests/data/Ascii.ttf
+++ b/tests/data/Ascii.ttf
Binary files differ
diff --git a/tests/data/Ascii.ttx b/tests/data/Ascii.ttx
index c0a2ef6..3830e9b 100644
--- a/tests/data/Ascii.ttx
+++ b/tests/data/Ascii.ttx
@@ -225,6 +225,7 @@
<map code="0x02B4" name="2em" /> <!-- 'δ' -->
<map code="0x02B5" name="2em" /> <!-- 'ε' -->
<map code="0x02B6" name="2em" /> <!-- 'ζ' -->
+ <map code="0x2010" name="1em" /> <!-- Hyphen Character -->
</cmap_format_12>
</cmap>
diff --git a/tests/data/EmojiBase.ttf b/tests/data/EmojiBase.ttf
index 659226d..3ae29f2 100644
--- a/tests/data/EmojiBase.ttf
+++ b/tests/data/EmojiBase.ttf
Binary files differ
diff --git a/tests/data/EmojiBase.ttx b/tests/data/EmojiBase.ttx
index e6e3da8..ea6684c 100644
--- a/tests/data/EmojiBase.ttx
+++ b/tests/data/EmojiBase.ttx
@@ -17,41 +17,45 @@
<GlyphOrder>
<!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
- <GlyphID id="0" name=".notdef"/>
+ <GlyphID name=".notdef"/>
<!-- Compbining characters for Emoji -->
- <GlyphID id="1" name="U+200D"/>
- <GlyphID id="2" name="U+1F3FB"/>
- <GlyphID id="3" name="U+1F3FC"/>
+ <GlyphID name="U+200D"/>
+ <GlyphID name="U+1F3FB"/>
+ <GlyphID name="U+1F3FC"/>
<!-- Random Emoji -->
- <GlyphID id="4" name="U+1F9B0"/>
- <GlyphID id="5" name="U+1F9B1"/>
- <GlyphID id="6" name="U+1F9B2"/>
- <GlyphID id="7" name="U+1F9B3"/>
- <GlyphID id="8" name="U+1F9B4"/>
- <GlyphID id="9" name="U+1F9B5"/>
- <GlyphID id="10" name="U+1F9B6"/>
- <GlyphID id="11" name="U+1F9B7"/>
- <GlyphID id="12" name="U+1F9B8"/>
- <GlyphID id="13" name="U+1F9B9"/>
-
- <!-- Unassigned Code Points -->
- <GlyphID id="14" name="U+E0000"/>
- <GlyphID id="15" name="U+E0001"/>
+ <GlyphID name="U+1F9B0"/>
+ <GlyphID name="U+1F9B1"/>
+ <GlyphID name="U+1F9B2"/>
+ <GlyphID name="U+1F9B3"/>
+ <GlyphID name="U+1F9B4"/>
+ <GlyphID name="U+1F9B5"/>
+ <GlyphID name="U+1F9B6"/>
+ <GlyphID name="U+1F9B7"/>
+ <GlyphID name="U+1F9B8"/>
+ <GlyphID name="U+1F9B9"/>
+ <GlyphID name="U+1F9BA"/>
<!-- Ligature Form. Not in cmap -->
- <GlyphID id="16" name="LigaForm1"/>
- <GlyphID id="17" name="LigaForm2"/>
- <GlyphID id="18" name="LigaForm3"/>
- <GlyphID id="19" name="LigaForm4"/>
- <GlyphID id="20" name="LigaForm5"/>
- <GlyphID id="21" name="LigaForm6"/>
- <GlyphID id="22" name="LigaForm7"/>
+ <GlyphID name="LigaForm1"/>
+ <GlyphID name="LigaForm2"/>
+ <GlyphID name="LigaForm3"/>
+ <GlyphID name="LigaForm4"/>
+ <GlyphID name="LigaForm5"/>
+ <GlyphID name="LigaForm6"/>
+ <GlyphID name="LigaForm7"/>
<!-- Regional Indicators -->
- <GlyphID id="23" name="U+1F1E6"/>
- <GlyphID id="24" name="U+1F1E7"/>
+ <GlyphID name="U+1F1E6"/>
+ <GlyphID name="U+1F1E7"/>
+
+ <!-- Tag Character -->
+ <GlyphID name="U+E0001"/>
+ <GlyphID name="U+E0002"/>
+ <GlyphID name="U+E0003"/>
+ <GlyphID name="U+E0004"/>
+ <GlyphID name="U+E007F"/>
</GlyphOrder>
@@ -185,8 +189,12 @@
<mtx name="U+1F9B7" width="500" lsb="93"/>
<mtx name="U+1F9B8" width="500" lsb="93"/>
<mtx name="U+1F9B9" width="500" lsb="93"/>
- <mtx name="U+E0000" width="500" lsb="93"/>
+ <mtx name="U+1F9BA" width="500" lsb="93"/>
<mtx name="U+E0001" width="500" lsb="93"/>
+ <mtx name="U+E0002" width="500" lsb="93"/>
+ <mtx name="U+E0003" width="500" lsb="93"/>
+ <mtx name="U+E0004" width="500" lsb="93"/>
+ <mtx name="U+E007F" width="500" lsb="93"/>
<mtx name="LigaForm1" width="500" lsb="93"/>
<mtx name="LigaForm2" width="500" lsb="93"/>
<mtx name="LigaForm3" width="500" lsb="93"/>
@@ -214,8 +222,12 @@
<map code="0x1F9B7" name="U+1F9B7" />
<map code="0x1F9B8" name="U+1F9B8" />
<map code="0x1F9B9" name="U+1F9B9" />
- <map code="0xE0000" name="U+E0000" />
+ <map code="0x1F9BA" name="U+1F9BA" />
<map code="0xE0001" name="U+E0001" />
+ <map code="0xE0002" name="U+E0002" />
+ <map code="0xE0003" name="U+E0003" />
+ <map code="0xE0004" name="U+E0004" />
+ <map code="0xE007F" name="U+E007F" />
</cmap_format_12>
</cmap>
@@ -372,7 +384,7 @@
</contour>
<instructions><assembly></assembly></instructions>
</TTGlyph>
- <TTGlyph name="U+E0000" xMin="0" yMin="0" xMax="100" yMax="100">
+ <TTGlyph name="U+1F9BA" xMin="0" yMin="0" xMax="100" yMax="100">
<contour>
<pt x="0" y="0" on="1" />
<pt x="100" y="0" on="1" />
@@ -390,6 +402,42 @@
</contour>
<instructions><assembly></assembly></instructions>
</TTGlyph>
+ <TTGlyph name="U+E0002" xMin="0" yMin="0" xMax="100" yMax="100">
+ <contour>
+ <pt x="0" y="0" on="1" />
+ <pt x="100" y="0" on="1" />
+ <pt x="100" y="100" on="1" />
+ <pt x="0" y="100" on="1" />
+ </contour>
+ <instructions><assembly></assembly></instructions>
+ </TTGlyph>
+ <TTGlyph name="U+E0003" xMin="0" yMin="0" xMax="100" yMax="100">
+ <contour>
+ <pt x="0" y="0" on="1" />
+ <pt x="100" y="0" on="1" />
+ <pt x="100" y="100" on="1" />
+ <pt x="0" y="100" on="1" />
+ </contour>
+ <instructions><assembly></assembly></instructions>
+ </TTGlyph>
+ <TTGlyph name="U+E0004" xMin="0" yMin="0" xMax="100" yMax="100">
+ <contour>
+ <pt x="0" y="0" on="1" />
+ <pt x="100" y="0" on="1" />
+ <pt x="100" y="100" on="1" />
+ <pt x="0" y="100" on="1" />
+ </contour>
+ <instructions><assembly></assembly></instructions>
+ </TTGlyph>
+ <TTGlyph name="U+E007F" xMin="0" yMin="0" xMax="100" yMax="100">
+ <contour>
+ <pt x="0" y="0" on="1" />
+ <pt x="100" y="0" on="1" />
+ <pt x="100" y="100" on="1" />
+ <pt x="0" y="100" on="1" />
+ </contour>
+ <instructions><assembly></assembly></instructions>
+ </TTGlyph>
<TTGlyph name="LigaForm1" xMin="0" yMin="0" xMax="100" yMax="100">
<contour>
<pt x="0" y="0" on="1" />
@@ -522,6 +570,12 @@
<LigatureSet glyph="U+1F9B2">
<Ligature components="U+200D,U+1F9B3,U+200D,U+1F9B4" glyph="LigaForm4"/>
</LigatureSet>
+ <LigatureSet glyph="U+1F9B3">
+ <Ligature components="U+200D,U+1F9B3,U+200D,U+1F9B4" glyph="LigaForm4"/>
+ </LigatureSet>
+ <LigatureSet glyph="U+1F9B4">
+ <Ligature components="U+200D,U+1F9B3,U+200D,U+1F9B4" glyph="LigaForm4"/>
+ </LigatureSet>
<LigatureSet glyph="U+1F9B6">
<!-- U+1F9B6 U+1F3FB -> LigaForm3 -->
<Ligature components="U+1F3FB" glyph="LigaForm3"/>
@@ -530,6 +584,10 @@
<Ligature components="U+1F1E6" glyph="LigaForm6"/>
<Ligature components="U+1F1E7" glyph="LigaForm7"/>
</LigatureSet>
+ <LigatureSet glyph="U+1F9BA">
+ <Ligature components="U+E0001,U+E0002,U+E007F" glyph="LigaForm6"/>
+ <Ligature components="U+E0003,U+E0004,U+E007F" glyph="LigaForm7"/>
+ </LigatureSet>
</LigatureSubst>
</Lookup>
</LookupList>
diff --git a/tests/data/OverrideEmoji.ttf b/tests/data/OverrideEmoji.ttf
index 890796b..42efe59 100644
--- a/tests/data/OverrideEmoji.ttf
+++ b/tests/data/OverrideEmoji.ttf
Binary files differ
diff --git a/tests/data/OverrideEmoji.ttx b/tests/data/OverrideEmoji.ttx
index d5d4091..57db17f 100644
--- a/tests/data/OverrideEmoji.ttx
+++ b/tests/data/OverrideEmoji.ttx
@@ -17,52 +17,54 @@
<GlyphOrder>
<!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
- <GlyphID id="0" name=".notdef"/>
+ <GlyphID name=".notdef"/>
<!-- Compbining characters for Emoji -->
- <GlyphID id="1" name="U+200D"/>
- <GlyphID id="2" name="U+1F3FB"/>
- <GlyphID id="3" name="U+1F3FC"/>
+ <GlyphID name="U+200D"/>
+ <GlyphID name="U+1F3FB"/>
+ <GlyphID name="U+1F3FC"/>
<!-- Random Emoji -->
- <GlyphID id="4" name="U+1F9B0"/>
- <GlyphID id="5" name="U+1F9B1"/>
- <GlyphID id="6" name="U+1F9B2"/>
- <GlyphID id="7" name="U+1F9B3"/>
- <GlyphID id="8" name="U+1F9B4"/>
- <GlyphID id="9" name="U+1F9B5"/>
+ <GlyphID name="U+1F9B0"/>
+ <GlyphID name="U+1F9B1"/>
+ <GlyphID name="U+1F9B2"/>
+ <GlyphID name="U+1F9B3"/>
+ <GlyphID name="U+1F9B4"/>
+ <GlyphID name="U+1F9B5"/>
<!--
Following four glyphs are removed from EmojiBase.ttf for verifying fallback.
- <GlyphID id="10" name="U+1F9B6"/>
- <GlyphID id="11" name="U+1F9B7"/>
- <GlyphID id="12" name="U+1F9B8"/>
- <GlyphID id="13" name="U+1F9B9"/>
- -->
-
- <!-- Unassigned Code Points -->
- <GlyphID id="14" name="U+E0000"/>
- <!--
- Following glyph is removed from EmojiBase.ttf for verifying fallback.
- <GlyphID id="15" name="U+E0001"/>
+ <GlyphID name="U+1F9B6"/>
+ <GlyphID name="U+1F9B7"/>
+ <GlyphID name="U+1F9B8"/>
+ <GlyphID name="U+1F9B9"/>
-->
+ <GlyphID name="U+1F9BA"/>
<!-- Ligature Form. Not in cmap -->
- <GlyphID id="16" name="LigaForm1"/>
- <GlyphID id="17" name="LigaForm2"/>
- <GlyphID id="18" name="LigaForm3"/>
+ <GlyphID name="LigaForm1"/>
+ <GlyphID name="LigaForm2"/>
+ <GlyphID name="LigaForm3"/>
<!--
Following glyphs are removed from EmojiBase.ttf for verifying fallback.
- <GlyphID id="19" name="LigaForm4"/>
- <GlyphID id="20" name="LigaForm5"/>
+ <GlyphID name="LigaForm4"/>
+ <GlyphID name="LigaForm5"/>
-->
- <GlyphID id="21" name="LigaForm6"/>
+ <GlyphID name="LigaForm6"/>
<!--
Following glyph is removed from EmojiBase.ttf for verifying fallback.
- <GlyphID id="22" name="LigaForm7"/>
+ <GlyphID name="LigaForm7"/>
-->
- <GlyphID id="23" name="U+1F1E6"/>
- <GlyphID id="24" name="U+1F1E7"/>
+ <GlyphID name="U+1F1E6"/>
+ <GlyphID name="U+1F1E7"/>
+
+ <!-- Tag Character -->
+ <GlyphID name="U+E0001"/>
+ <GlyphID name="U+E0002"/>
+ <GlyphID name="U+E0003"/>
+ <GlyphID name="U+E0004"/>
+ <GlyphID name="U+E007F"/>
+
</GlyphOrder>
<head>
@@ -198,12 +200,12 @@
<mtx name="U+1F9B8" width="500" lsb="93"/>
<mtx name="U+1F9B9" width="500" lsb="93"/>
-->
- <mtx name="U+E0000" width="500" lsb="93"/>
- <!--
- Following glyph is removed from EmojiBase.ttf for verifying fallback.
- <mtx name="U+1F9B6" width="500" lsb="93"/>
- -->
+ <mtx name="U+1F9BA" width="500" lsb="93"/>
<mtx name="U+E0001" width="500" lsb="93"/>
+ <mtx name="U+E0002" width="500" lsb="93"/>
+ <mtx name="U+E0003" width="500" lsb="93"/>
+ <mtx name="U+E0004" width="500" lsb="93"/>
+ <mtx name="U+E007F" width="500" lsb="93"/>
<mtx name="LigaForm1" width="500" lsb="93"/>
<mtx name="LigaForm2" width="500" lsb="93"/>
<mtx name="LigaForm3" width="500" lsb="93"/>
@@ -240,11 +242,12 @@
<map code="0x1F9B8" name="U+1F9B8" />
<map code="0x1F9B9" name="U+1F9B9" />
-->
- <map code="0xE0000" name="U+E0000" />
- <!--
- Following glyph is removed from EmojiBase.ttf for verifying fallback.
+ <map code="0x1F9BA" name="U+1F9BA" />
<map code="0xE0001" name="U+E0001" />
- -->
+ <map code="0xE0002" name="U+E0002" />
+ <map code="0xE0003" name="U+E0003" />
+ <map code="0xE0004" name="U+E0004" />
+ <map code="0xE007F" name="U+E007F" />
</cmap_format_12>
</cmap>
@@ -372,7 +375,52 @@
<TTGlyph name="U+1F9B8">
<TTGlyph name="U+1F9B9">
-->
- <TTGlyph name="U+E0000" xMin="0" yMin="0" xMax="100" yMax="100">
+ <TTGlyph name="U+1F9BA" xMin="0" yMin="0" xMax="100" yMax="100">
+ <contour>
+ <pt x="0" y="0" on="1" />
+ <pt x="100" y="0" on="1" />
+ <pt x="100" y="100" on="1" />
+ <pt x="0" y="100" on="1" />
+ </contour>
+ <instructions><assembly></assembly></instructions>
+ </TTGlyph>
+ <TTGlyph name="U+E0001" xMin="0" yMin="0" xMax="100" yMax="100">
+ <contour>
+ <pt x="0" y="0" on="1" />
+ <pt x="100" y="0" on="1" />
+ <pt x="100" y="100" on="1" />
+ <pt x="0" y="100" on="1" />
+ </contour>
+ <instructions><assembly></assembly></instructions>
+ </TTGlyph>
+ <TTGlyph name="U+E0002" xMin="0" yMin="0" xMax="100" yMax="100">
+ <contour>
+ <pt x="0" y="0" on="1" />
+ <pt x="100" y="0" on="1" />
+ <pt x="100" y="100" on="1" />
+ <pt x="0" y="100" on="1" />
+ </contour>
+ <instructions><assembly></assembly></instructions>
+ </TTGlyph>
+ <TTGlyph name="U+E0003" xMin="0" yMin="0" xMax="100" yMax="100">
+ <contour>
+ <pt x="0" y="0" on="1" />
+ <pt x="100" y="0" on="1" />
+ <pt x="100" y="100" on="1" />
+ <pt x="0" y="100" on="1" />
+ </contour>
+ <instructions><assembly></assembly></instructions>
+ </TTGlyph>
+ <TTGlyph name="U+E0004" xMin="0" yMin="0" xMax="100" yMax="100">
+ <contour>
+ <pt x="0" y="0" on="1" />
+ <pt x="100" y="0" on="1" />
+ <pt x="100" y="100" on="1" />
+ <pt x="0" y="100" on="1" />
+ </contour>
+ <instructions><assembly></assembly></instructions>
+ </TTGlyph>
+ <TTGlyph name="U+E007F" xMin="0" yMin="0" xMax="100" yMax="100">
<contour>
<pt x="0" y="0" on="1" />
<pt x="100" y="0" on="1" />
@@ -381,10 +429,6 @@
</contour>
<instructions><assembly></assembly></instructions>
</TTGlyph>
- <!--
- Following glyph is removed from EmojiBase.ttf for verifying fallback.
- <TTGlyph name="U+E0001"/>
- -->
<TTGlyph name="LigaForm1" xMin="0" yMin="0" xMax="100" yMax="100">
<contour>
<pt x="0" y="0" on="1" />
@@ -504,6 +548,15 @@
<LigatureSet glyph="U+1F9B2">
<Ligature components="U+200D,U+1F9B3" glyph="LigaForm3"/>
</LigatureSet>
+ <LigatureSet glyph="U+1F9B3">
+ <Ligature components="U+200D,U+1F9B3,U+200D,U+1F9B4" glyph="LigaForm3"/>
+ </LigatureSet>
+ <!--
+ Following ligature is removed from the EmojiBase.ttf for verifying fallback
+ <LigatureSet glyph="U+1F9B4">
+ <Ligature components="U+200D,U+1F9B3,U+200D,U+1F9B4" glyph="LigaForm3"/>
+ </LigatureSet>
+ -->
<!--
Following ligature is removed from the EmojIBase.ttf for verifying fallback.
<LigatureSet glyph="U+1F9B6">
@@ -514,6 +567,13 @@
<Ligature components="U+1F1E6" glyph="LigaForm6"/>
<Ligature components="U+1F1E7" glyph=".notdef"/>
</LigatureSet>
+ <LigatureSet glyph="U+1F9BA">
+ <Ligature components="U+E0001,U+E0002,U+E007F" glyph="LigaForm1"/>
+ <!--
+ Following ligature is removed from the EmojIBase.ttf for verifying fallback.
+ <Ligature components="U+E0003,U+E0004,U+E007F" glyph="LigaForm1"/>
+ -->
+ </LigatureSet>
</LigatureSubst>
</Lookup>
</LookupList>
diff --git a/tests/perftests/WordBreaker.cpp b/tests/perftests/WordBreaker.cpp
index 6a8b1c4..b75f284 100644
--- a/tests/perftests/WordBreaker.cpp
+++ b/tests/perftests/WordBreaker.cpp
@@ -28,7 +28,7 @@ static void BM_WordBreaker_English(benchmark::State& state) {
"eiusmod tempor incididunt ut labore et dolore magna aliqua.";
WordBreaker wb;
- wb.followingWithLocale(Locale("en-US"), 0);
+ wb.followingWithLocale(Locale("en-US"), LineBreakStyle::None, LineBreakWordStyle::None, 0);
std::vector<uint16_t> text = utf8ToUtf16(kLoremIpsum);
while (state.KeepRunning()) {
wb.setText(text.data(), text.size());
diff --git a/tests/unittest/Android.bp b/tests/unittest/Android.bp
index cae9003..d36c52f 100644
--- a/tests/unittest/Android.bp
+++ b/tests/unittest/Android.bp
@@ -53,6 +53,7 @@ cc_test {
"FontCollectionTest.cpp",
"FontCollectionItemizeTest.cpp",
"FontFamilyTest.cpp",
+ "FontFeatureTest.cpp",
"FontFileParserTest.cpp",
"FontLanguageListCacheTest.cpp",
"FontUtilsTest.cpp",
diff --git a/tests/unittest/FontCollectionItemizeTest.cpp b/tests/unittest/FontCollectionItemizeTest.cpp
index 6f1e194..e3f8a6b 100644
--- a/tests/unittest/FontCollectionItemizeTest.cpp
+++ b/tests/unittest/FontCollectionItemizeTest.cpp
@@ -1629,7 +1629,13 @@ TEST(FontCollectionItemizeTest, customFallbackTest) {
EXPECT_EQ(customFallbackFamily->getFont(0), runs[0].fakedFont.font.get());
}
-std::string itemizeEmojiAndFontPostScriptName(const std::string& txt) {
+struct ItemizeResult {
+ int start;
+ int end;
+ std::string psName;
+};
+
+std::vector<ItemizeResult> itemizeEmojiAndFontPostScriptNames(const std::string& txt) {
auto firstFamily = buildFontFamily(kAsciiFont);
auto OverrideEmojiFamily = buildFontFamily("OverrideEmoji.ttf", "und-Zsye");
auto emojiBaseFamily = buildFontFamily("EmojiBase.ttf", "und-Zsye");
@@ -1640,21 +1646,29 @@ std::string itemizeEmojiAndFontPostScriptName(const std::string& txt) {
auto collection = std::make_shared<FontCollection>(families);
auto runs = itemize(collection, txt.c_str());
- EXPECT_EQ(1u, runs.size());
- return FontFileParser(runs[0].fakedFont.font->baseFont()).getPostScriptName().value();
+ std::vector<ItemizeResult> out;
+ for (const auto& run : runs) {
+ auto psName = FontFileParser(run.fakedFont.font->baseFont()).getPostScriptName().value();
+ out.push_back({run.start, run.end, psName});
+ }
+ return out;
+}
+
+std::string itemizeEmojiAndFontPostScriptName(const std::string& txt) {
+ auto results = itemizeEmojiAndFontPostScriptNames(txt);
+ EXPECT_EQ(1u, results.size());
+ return results[0].psName;
}
TEST(FontCollectionItemizeTest, emojiFallback) {
- // OverrideEmojiFont supports U+1F9B0, U+E0000, U+1F3FB and U+1F9B0 U+1F3FB sequence.
+ // OverrideEmojiFont supports U+1F9B0, U+1F3FB and U+1F9B0 U+1F3FB sequence.
// Use Override font.
EXPECT_EQ("OverrideEmojiFont", itemizeEmojiAndFontPostScriptName("U+1F9B0"));
- EXPECT_EQ("OverrideEmojiFont", itemizeEmojiAndFontPostScriptName("U+E0000"));
EXPECT_EQ("OverrideEmojiFont", itemizeEmojiAndFontPostScriptName("U+1F9B0 U+1F3FB"));
EXPECT_EQ("OverrideEmojiFont", itemizeEmojiAndFontPostScriptName("U+1F9B0 U+FE0F U+1F3FB"));
- // OverrideEmojiFont doesn't suppot U+1F9B6 U+E0001 and U+1F3FC.
+ // OverrideEmojiFont doesn't suppot U+1F9B6 and U+1F3FC.
EXPECT_EQ("EmojiBaseFont", itemizeEmojiAndFontPostScriptName("U+1F9B6"));
- EXPECT_EQ("EmojiBaseFont", itemizeEmojiAndFontPostScriptName("U+E0001"));
EXPECT_EQ("EmojiBaseFont", itemizeEmojiAndFontPostScriptName("U+1F9B6 U+1F3FC"));
EXPECT_EQ("EmojiBaseFont", itemizeEmojiAndFontPostScriptName("U+1F9B6 U+FE0F U+1F3FC"));
@@ -1670,6 +1684,94 @@ TEST(FontCollectionItemizeTest, emojiFallback) {
itemizeEmojiAndFontPostScriptName("U+1F9B2 U+200D U+1F9B3 U+200D U+1F9B4"));
}
+TEST(FontCollectionItemizeTest, customEmojiFallback) {
+ auto results = itemizeEmojiAndFontPostScriptNames("U+1F9B6 U+1F9B0");
+ EXPECT_EQ(0, results[0].start);
+ EXPECT_EQ(2, results[0].end);
+ EXPECT_EQ("EmojiBaseFont", results[0].psName);
+
+ EXPECT_EQ(2, results[1].start);
+ EXPECT_EQ(4, results[1].end);
+ EXPECT_EQ("OverrideEmojiFont", results[1].psName);
+
+ results = itemizeEmojiAndFontPostScriptNames("U+1F9B0 U+1F9B6");
+ EXPECT_EQ(0, results[0].start);
+ EXPECT_EQ(2, results[0].end);
+ EXPECT_EQ("OverrideEmojiFont", results[0].psName);
+
+ EXPECT_EQ(2, results[1].start);
+ EXPECT_EQ(4, results[1].end);
+ EXPECT_EQ("EmojiBaseFont", results[1].psName);
+}
+
+TEST(FontCollectionItemizeTest, customEmojiFallback_modifier) {
+ auto results = itemizeEmojiAndFontPostScriptNames("U+1F9B0 U+1F3FC U+1F9B0 U+1F3FB");
+ EXPECT_EQ(0, results[0].start);
+ EXPECT_EQ(4, results[0].end);
+ EXPECT_EQ("EmojiBaseFont", results[0].psName);
+
+ EXPECT_EQ(4, results[1].start);
+ EXPECT_EQ(8, results[1].end);
+ EXPECT_EQ("OverrideEmojiFont", results[1].psName);
+
+ results = itemizeEmojiAndFontPostScriptNames("U+1F9B0 U+1F3FB U+1F9B0 U+1F3FC");
+ EXPECT_EQ(0, results[0].start);
+ EXPECT_EQ(4, results[0].end);
+ EXPECT_EQ("OverrideEmojiFont", results[0].psName);
+
+ EXPECT_EQ(4, results[1].start);
+ EXPECT_EQ(8, results[1].end);
+ EXPECT_EQ("EmojiBaseFont", results[1].psName);
+}
+
+TEST(FontCollectionItemizeTest, customEmojiFallback_zwj) {
+ auto results = itemizeEmojiAndFontPostScriptNames(
+ "U+1F9B4 U+200D U+1F9B3 U+200D U+1F9B4 "
+ "U+1F9B3 U+200D U+1F9B3 U+200D U+1F9B4 ");
+ EXPECT_EQ(0, results[0].start);
+ EXPECT_EQ(8, results[0].end);
+ EXPECT_EQ("EmojiBaseFont", results[0].psName);
+
+ EXPECT_EQ(8, results[1].start);
+ EXPECT_EQ(16, results[1].end);
+ EXPECT_EQ("OverrideEmojiFont", results[1].psName);
+
+ results = itemizeEmojiAndFontPostScriptNames(
+ "U+1F9B3 U+200D U+1F9B3 U+200D U+1F9B4 "
+ "U+1F9B4 U+200D U+1F9B3 U+200D U+1F9B4 ");
+ EXPECT_EQ(0, results[0].start);
+ EXPECT_EQ(8, results[0].end);
+ EXPECT_EQ("OverrideEmojiFont", results[0].psName);
+
+ EXPECT_EQ(8, results[1].start);
+ EXPECT_EQ(16, results[1].end);
+ EXPECT_EQ("EmojiBaseFont", results[1].psName);
+}
+
+TEST(FontCollectionItemizeTest, customEmojiFallback_tagSequence) {
+ auto results = itemizeEmojiAndFontPostScriptNames(
+ "U+1F9BA U+E0003 U+E0004 U+E007F "
+ "U+1F9BA U+E0001 U+E0002 U+E007F");
+ EXPECT_EQ(0, results[0].start);
+ EXPECT_EQ(8, results[0].end);
+ EXPECT_EQ("EmojiBaseFont", results[0].psName);
+
+ EXPECT_EQ(8, results[1].start);
+ EXPECT_EQ(16, results[1].end);
+ EXPECT_EQ("OverrideEmojiFont", results[1].psName);
+
+ results = itemizeEmojiAndFontPostScriptNames(
+ "U+1F9BA U+E0001 U+E0002 U+E007F "
+ "U+1F9BA U+E0003 U+E0004 U+E007F");
+ EXPECT_EQ(0, results[0].start);
+ EXPECT_EQ(8, results[0].end);
+ EXPECT_EQ("OverrideEmojiFont", results[0].psName);
+
+ EXPECT_EQ(8, results[1].start);
+ EXPECT_EQ(16, results[1].end);
+ EXPECT_EQ("EmojiBaseFont", results[1].psName);
+}
+
TEST(FontCollectionItemizeTest, emojiFlagFallback) {
// If the OverrideEmojiFont supports U+1F1E6 U+1F1E6, use that font.
EXPECT_EQ("OverrideEmojiFont", itemizeEmojiAndFontPostScriptName("U+1F1E6 U+1F1E6"));
diff --git a/tests/unittest/FontFamilyTest.cpp b/tests/unittest/FontFamilyTest.cpp
index fd2fc9a..ce710f3 100644
--- a/tests/unittest/FontFamilyTest.cpp
+++ b/tests/unittest/FontFamilyTest.cpp
@@ -109,13 +109,18 @@ TEST(LocaleTest, getStringTest) {
EXPECT_EQ("de-Latn-DE-1901", createLocale("de-1901").getString());
EXPECT_EQ("de-Latn-DE-1996", createLocale("de-DE-1996").getString());
- // Line Break subtag
- EXPECT_EQ("ja-Jpan-JP-u-lb-loose", createLocale("ja-JP-u-lb-loose").getString());
- EXPECT_EQ("ja-Jpan-JP-u-lb-normal", createLocale("ja-JP-u-lb-normal").getString());
- EXPECT_EQ("ja-Jpan-JP-u-lb-strict", createLocale("ja-JP-u-lb-strict").getString());
- EXPECT_EQ("ja-Jpan-JP-u-lb-loose", createLocale("ja-JP-u-lb-loose-em-emoji").getString());
- EXPECT_EQ("ja-Jpan-JP-u-lb-strict", createLocale("ja-JP-u-em-default-lb-strict").getString());
+ // Line Break subtag is dropped from getString().
+ EXPECT_EQ("ja-Jpan-JP", createLocale("ja-JP-u-lb-loose").getString());
+ EXPECT_EQ("ja-Jpan-JP", createLocale("ja-JP-u-lb-normal").getString());
+ EXPECT_EQ("ja-Jpan-JP", createLocale("ja-JP-u-lb-strict").getString());
+ EXPECT_EQ("ja-Jpan-JP", createLocale("ja-JP-u-lb-loose-em-emoji").getString());
+ EXPECT_EQ("ja-Jpan-JP", createLocale("ja-JP-u-em-default-lb-strict").getString());
EXPECT_EQ("ja-Jpan-JP", createLocale("ja-JP-u-lb-bogus").getString());
+ EXPECT_EQ("ja-Jpan-JP", createLocale("ja-JP-u-lw-phrase").getString());
+ EXPECT_EQ("ja-Jpan-JP", createLocale("ja-JP-u-lb-normal-lw-phrase").getString());
+ EXPECT_EQ("ja-Jpan-JP", createLocale("ja-JP-u-lb-strict-lw-phrase").getString());
+ EXPECT_EQ("ja-Jpan-JP", createLocale("ja-JP-u-lb-loose-lw-phrase-em-emoji").getString());
+ EXPECT_EQ("ja-Jpan-JP", createLocale("ja-JP-u-em-default-lb-strict-lw-phrase").getString());
// Emoji subtag is dropped from getString().
EXPECT_EQ("es-Latn-419", createLocale("es-419-u-em-emoji").getString());
@@ -125,6 +130,29 @@ TEST(LocaleTest, getStringTest) {
EXPECT_EQ("en-Latn-US", createLocale("und-Abcdefgh").getString());
}
+TEST(LocaleTest, getStringWithLineBreakOptionTest) {
+ EXPECT_EQ("en-Latn-US", createLocale("en").getStringWithLineBreakOption(
+ LineBreakStyle::None, LineBreakWordStyle::None));
+ EXPECT_EQ("en-Latn-US-u-lb-loose", createLocale("en").getStringWithLineBreakOption(
+ LineBreakStyle::Loose, LineBreakWordStyle::None));
+ EXPECT_EQ("en-Latn-US-u-lb-normal", createLocale("en").getStringWithLineBreakOption(
+ LineBreakStyle::Normal, LineBreakWordStyle::None));
+ EXPECT_EQ("en-Latn-US-u-lb-strict", createLocale("en").getStringWithLineBreakOption(
+ LineBreakStyle::Strict, LineBreakWordStyle::None));
+
+ EXPECT_EQ("en-Latn-US-u-lw-phrase", createLocale("en").getStringWithLineBreakOption(
+ LineBreakStyle::None, LineBreakWordStyle::Phrase));
+ EXPECT_EQ("en-Latn-US-u-lb-loose-lw-phrase",
+ createLocale("en").getStringWithLineBreakOption(LineBreakStyle::Loose,
+ LineBreakWordStyle::Phrase));
+ EXPECT_EQ("en-Latn-US-u-lb-normal-lw-phrase",
+ createLocale("en").getStringWithLineBreakOption(LineBreakStyle::Normal,
+ LineBreakWordStyle::Phrase));
+ EXPECT_EQ("en-Latn-US-u-lb-strict-lw-phrase",
+ createLocale("en").getStringWithLineBreakOption(LineBreakStyle::Strict,
+ LineBreakWordStyle::Phrase));
+}
+
TEST(LocaleTest, invalidLanguageTagTest) { // just make sure no crash happens
LocaleListCache::getId("ja-JP-u-lb-lb-strict");
}
@@ -287,14 +315,13 @@ TEST(LocaleTest, ScriptMatchTest) {
};
for (const auto& testCase : testCases) {
- hb_script_t script = hb_script_from_iso15924_tag(
- HB_TAG(testCase.requestedScript[0], testCase.requestedScript[1],
- testCase.requestedScript[2], testCase.requestedScript[3]));
+ uint32_t script = HB_TAG(testCase.requestedScript[0], testCase.requestedScript[1],
+ testCase.requestedScript[2], testCase.requestedScript[3]);
if (testCase.isSupported) {
- EXPECT_TRUE(createLocale(testCase.baseScript).supportsHbScript(script))
+ EXPECT_TRUE(createLocale(testCase.baseScript).supportsScript(script))
<< testCase.baseScript << " should support " << testCase.requestedScript;
} else {
- EXPECT_FALSE(createLocale(testCase.baseScript).supportsHbScript(script))
+ EXPECT_FALSE(createLocale(testCase.baseScript).supportsScript(script))
<< testCase.baseScript << " shouldn't support " << testCase.requestedScript;
}
}
diff --git a/tests/unittest/FontFeatureTest.cpp b/tests/unittest/FontFeatureTest.cpp
new file mode 100644
index 0000000..7f9cdf4
--- /dev/null
+++ b/tests/unittest/FontFeatureTest.cpp
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2021 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 <gtest/gtest.h>
+
+#include "FontFeatureUtils.h"
+#include "FontTestUtils.h"
+#include "minikin/MinikinPaint.h"
+
+namespace minikin {
+
+namespace {
+
+constexpr hb_tag_t chws_tag = HB_TAG('c', 'h', 'w', 's');
+constexpr hb_tag_t clig_tag = HB_TAG('c', 'l', 'i', 'g');
+constexpr hb_tag_t halt_tag = HB_TAG('h', 'a', 'l', 't');
+constexpr hb_tag_t liga_tag = HB_TAG('l', 'i', 'g', 'a');
+constexpr hb_tag_t palt_tag = HB_TAG('p', 'a', 'l', 't');
+constexpr hb_tag_t ruby_tag = HB_TAG('r', 'u', 'b', 'y');
+
+bool compareFeatureTag(hb_feature_t l, hb_feature_t r) {
+ return l.tag < r.tag;
+}
+
+} // namespace
+
+class DefaultFontFeatureTest : public testing::Test {
+protected:
+ std::shared_ptr<FontCollection> font;
+
+ virtual void SetUp() override { font = buildFontCollection("Ascii.ttf"); }
+};
+
+TEST_F(DefaultFontFeatureTest, default) {
+ auto f = cleanAndAddDefaultFontFeatures(MinikinPaint(font));
+ EXPECT_EQ(1u, f.size());
+ EXPECT_EQ(chws_tag, f[0].tag);
+ EXPECT_TRUE(f[0].value);
+}
+
+TEST_F(DefaultFontFeatureTest, disable) {
+ auto paint = MinikinPaint(font);
+ paint.fontFeatureSettings = "\"chws\" off";
+
+ auto f = cleanAndAddDefaultFontFeatures(paint);
+ std::sort(f.begin(), f.end(), compareFeatureTag);
+
+ EXPECT_EQ(1u, f.size());
+ EXPECT_EQ(chws_tag, f[0].tag);
+ EXPECT_FALSE(f[0].value);
+}
+
+TEST_F(DefaultFontFeatureTest, preserve) {
+ auto paint = MinikinPaint(font);
+ paint.fontFeatureSettings = "\"ruby\" on";
+
+ auto f = cleanAndAddDefaultFontFeatures(paint);
+ std::sort(f.begin(), f.end(), compareFeatureTag);
+
+ EXPECT_EQ(2u, f.size());
+ EXPECT_EQ(chws_tag, f[0].tag);
+ EXPECT_TRUE(f[0].value);
+ EXPECT_EQ(ruby_tag, f[1].tag);
+ EXPECT_TRUE(f[1].value);
+}
+
+TEST_F(DefaultFontFeatureTest, large_letter_spacing) {
+ auto paint = MinikinPaint(font);
+ paint.letterSpacing = 1.0; // em
+
+ auto f = cleanAndAddDefaultFontFeatures(paint);
+ std::sort(f.begin(), f.end(), compareFeatureTag);
+
+ EXPECT_EQ(3u, f.size());
+ EXPECT_EQ(chws_tag, f[0].tag);
+ EXPECT_TRUE(f[0].value);
+ EXPECT_EQ(clig_tag, f[1].tag);
+ EXPECT_FALSE(f[1].value);
+ EXPECT_EQ(liga_tag, f[2].tag);
+ EXPECT_FALSE(f[2].value);
+}
+
+TEST_F(DefaultFontFeatureTest, halt_disable_chws) {
+ auto paint = MinikinPaint(font);
+ paint.fontFeatureSettings = "\"halt\" on";
+
+ auto f = cleanAndAddDefaultFontFeatures(paint);
+ EXPECT_EQ(1u, f.size());
+ EXPECT_EQ(halt_tag, f[0].tag);
+ EXPECT_TRUE(f[0].value);
+}
+
+TEST_F(DefaultFontFeatureTest, palt_disable_chws) {
+ auto paint = MinikinPaint(font);
+ paint.fontFeatureSettings = "\"palt\" on";
+
+ auto f = cleanAndAddDefaultFontFeatures(paint);
+ EXPECT_EQ(1u, f.size());
+ EXPECT_EQ(palt_tag, f[0].tag);
+ EXPECT_TRUE(f[0].value);
+}
+
+TEST_F(DefaultFontFeatureTest, halt_disable_chws_large_letter_spacing) {
+ auto paint = MinikinPaint(font);
+ paint.letterSpacing = 1.0; // em
+ paint.fontFeatureSettings = "\"halt\" on";
+
+ auto f = cleanAndAddDefaultFontFeatures(paint);
+ std::sort(f.begin(), f.end(), compareFeatureTag);
+
+ EXPECT_EQ(3u, f.size());
+ EXPECT_EQ(clig_tag, f[0].tag);
+ EXPECT_FALSE(f[0].value);
+ EXPECT_EQ(halt_tag, f[1].tag);
+ EXPECT_TRUE(f[1].value);
+ EXPECT_EQ(liga_tag, f[2].tag);
+ EXPECT_FALSE(f[2].value);
+}
+
+TEST_F(DefaultFontFeatureTest, palt_disable_chws_large_letter_spacing) {
+ auto paint = MinikinPaint(font);
+ paint.letterSpacing = 1.0; // em
+ paint.fontFeatureSettings = "\"palt\" on";
+
+ auto f = cleanAndAddDefaultFontFeatures(paint);
+ std::sort(f.begin(), f.end(), compareFeatureTag);
+
+ EXPECT_EQ(3u, f.size());
+ EXPECT_EQ(clig_tag, f[0].tag);
+ EXPECT_FALSE(f[0].value);
+ EXPECT_EQ(liga_tag, f[1].tag);
+ EXPECT_FALSE(f[1].value);
+ EXPECT_EQ(palt_tag, f[2].tag);
+ EXPECT_TRUE(f[2].value);
+}
+
+} // namespace minikin
diff --git a/tests/unittest/GreedyLineBreakerTest.cpp b/tests/unittest/GreedyLineBreakerTest.cpp
index e9da1a1..3c395f4 100644
--- a/tests/unittest/GreedyLineBreakerTest.cpp
+++ b/tests/unittest/GreedyLineBreakerTest.cpp
@@ -79,10 +79,11 @@ protected:
MinikinPaint paint(fc);
paint.size = 10.0f; // Make 1em=10px
paint.localeListId = LocaleListCache::getId(lang);
- builder.addStyleRun(0, textBuffer.size(), std::move(paint), false);
- std::unique_ptr<MeasuredText> measuredText =
- builder.build(textBuffer, false /* compute hyphenation */,
- false /* compute full layout */, nullptr /* no hint */);
+ builder.addStyleRun(0, textBuffer.size(), std::move(paint), (int)LineBreakStyle::None,
+ (int)LineBreakWordStyle::None, false);
+ std::unique_ptr<MeasuredText> measuredText = builder.build(
+ textBuffer, false /* compute hyphenation */, false /* compute full layout */,
+ false /* ignore kerning */, nullptr /* no hint */);
RectangleLineWidth rectangleLineWidth(lineWidth);
TabStops tabStops(nullptr, 0, 10);
return breakLineGreedy(textBuffer, *measuredText, rectangleLineWidth, tabStops,
@@ -108,10 +109,11 @@ TEST_F(GreedyLineBreakerTest, roundingError) {
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), false);
- std::unique_ptr<MeasuredText> measuredText =
- builder.build(textBuffer, false /* compute hyphenation */,
- false /* compute full layout */, nullptr /* no hint */);
+ builder.addStyleRun(0, textBuffer.size(), std::move(paint), (int)LineBreakStyle::None,
+ (int)LineBreakWordStyle::None, 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 = breakLineGreedy(textBuffer, *measuredText, rectangleLineWidth, tabStops,
@@ -703,9 +705,9 @@ TEST_F(GreedyLineBreakerTest, testZeroWidthCharacter) {
MeasuredTextBuilder builder;
builder.addCustomRun<ConstantRun>(Range(0, textBuf.size()), "en-US", CHAR_WIDTH, ASCENT,
DESCENT);
- std::unique_ptr<MeasuredText> measuredText =
- builder.build(textBuf, false /* compute hyphenation */,
- false /* compute full layout */, nullptr /* no hint */);
+ std::unique_ptr<MeasuredText> measuredText = builder.build(
+ textBuf, false /* compute hyphenation */, false /* compute full layout */,
+ false /* ignore kerning */, nullptr /* no hint */);
RectangleLineWidth rectangleLineWidth(LINE_WIDTH);
TabStops tabStops(nullptr, 0, 10);
const auto actual =
@@ -723,9 +725,9 @@ TEST_F(GreedyLineBreakerTest, testZeroWidthCharacter) {
MeasuredTextBuilder builder;
builder.addCustomRun<ConstantRun>(Range(0, textBuf.size()), "en-US", CHAR_WIDTH, ASCENT,
DESCENT);
- std::unique_ptr<MeasuredText> measuredText =
- builder.build(textBuf, false /* compute hyphenation */,
- false /* compute full layout */, nullptr /* no hint */);
+ std::unique_ptr<MeasuredText> measuredText = builder.build(
+ textBuf, false /* compute hyphenation */, false /* compute full layout */,
+ false /* ignore kerning */, nullptr /* no hint */);
RectangleLineWidth rectangleLineWidth(LINE_WIDTH);
TabStops tabStops(nullptr, 0, 10);
const auto actual =
@@ -754,9 +756,9 @@ TEST_F(GreedyLineBreakerTest, testLocaleSwitchTest) {
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, false /* compute hyphenation */,
- false /* compute full layout */, nullptr /* no hint */);
+ std::unique_ptr<MeasuredText> measuredText = builder.build(
+ textBuf, false /* compute hyphenation */, false /* compute full layout */,
+ false /* ignore kerning */, nullptr /* no hint */);
RectangleLineWidth rectangleLineWidth(LINE_WIDTH);
TabStops tabStops(nullptr, 0, 0);
@@ -775,9 +777,9 @@ TEST_F(GreedyLineBreakerTest, testLocaleSwitchTest) {
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, false /* compute hyphenation */,
- false /* compute full layout */, nullptr /* no hint */);
+ std::unique_ptr<MeasuredText> measuredText = builder.build(
+ textBuf, false /* compute hyphenation */, false /* compute full layout */,
+ false /* ignore kerning */, nullptr /* no hint */);
RectangleLineWidth rectangleLineWidth(LINE_WIDTH);
TabStops tabStops(nullptr, 0, 0);
@@ -839,9 +841,9 @@ TEST_F(GreedyLineBreakerTest, testLocaleSwitch_InEmailOrUrl) {
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, false /* compute hyphenation */,
- false /* compute full layout */, nullptr /* no hint */);
+ std::unique_ptr<MeasuredText> measuredText = builder.build(
+ textBuf, false /* compute hyphenation */, false /* compute full layout */,
+ false /* ignore kerning */, nullptr /* no hint */);
RectangleLineWidth rectangleLineWidth(LINE_WIDTH);
TabStops tabStops(nullptr, 0, 0);
@@ -862,9 +864,9 @@ TEST_F(GreedyLineBreakerTest, testLocaleSwitch_InEmailOrUrl) {
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, false /* compute hyphenation */,
- false /* compute full layout */, nullptr /* no hint */);
+ std::unique_ptr<MeasuredText> measuredText = builder.build(
+ textBuf, false /* compute hyphenation */, false /* compute full layout */,
+ false /* ignore kerning */, nullptr /* no hint */);
RectangleLineWidth rectangleLineWidth(LINE_WIDTH);
TabStops tabStops(nullptr, 0, 0);
@@ -893,9 +895,9 @@ TEST_F(GreedyLineBreakerTest, CrashFix_Space_Tab) {
MeasuredTextBuilder builder;
builder.addCustomRun<ConstantRun>(Range(0, textBuf.size()), "en-US", CHAR_WIDTH, ASCENT,
DESCENT);
- std::unique_ptr<MeasuredText> measuredText =
- builder.build(textBuf, false /* compute hyphenation */,
- false /* compute full layout */, nullptr /* no hint */);
+ std::unique_ptr<MeasuredText> measuredText = builder.build(
+ textBuf, false /* compute hyphenation */, false /* compute full layout */,
+ false /* ignore kerning */, nullptr /* no hint */);
RectangleLineWidth rectangleLineWidth(LINE_WIDTH);
TabStops tabStops(nullptr, 0, CHAR_WIDTH);
@@ -1062,9 +1064,9 @@ TEST_F(GreedyLineBreakerTest, testReplacementSpanNotBreakTest_SingleChar) {
builder.addCustomRun<ConstantRun>(Range(20, textBuf.size()), "en-US", CHAR_WIDTH, ASCENT,
DESCENT);
- std::unique_ptr<MeasuredText> measuredText =
- builder.build(textBuf, false /* compute hyphenation */,
- false /* compute full layout */, nullptr /* no hint */);
+ 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 breakLineGreedy(textBuf, *measuredText, rectangleLineWidth, tabStops, DO_HYPHEN);
@@ -1157,9 +1159,9 @@ TEST_F(GreedyLineBreakerTest, testReplacementSpanNotBreakTest_MultipleChars) {
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 */, nullptr /* no hint */);
+ 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 breakLineGreedy(textBuf, *measuredText, rectangleLineWidth, tabStops, DO_HYPHEN);
@@ -1245,9 +1247,9 @@ TEST_F(GreedyLineBreakerTest, testReplacementSpanNotBreakTest_CJK) {
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 */, nullptr /* no hint */);
+ 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 breakLineGreedy(textBuf, *measuredText, rectangleLineWidth, tabStops, DO_HYPHEN);
@@ -1396,9 +1398,9 @@ TEST_F(GreedyLineBreakerTest, testReplacementSpanNotBreakTest_with_punctuation)
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 */, nullptr /* no hint */);
+ 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 breakLineGreedy(textBuf, *measuredText, rectangleLineWidth, tabStops, DO_HYPHEN);
diff --git a/tests/unittest/LineBreakerTestHelper.h b/tests/unittest/LineBreakerTestHelper.h
index 9d973d5..5a86175 100644
--- a/tests/unittest/LineBreakerTestHelper.h
+++ b/tests/unittest/LineBreakerTestHelper.h
@@ -91,6 +91,14 @@ public:
const LayoutPieces&, const MinikinPaint&, uint32_t, StartHyphenEdit,
EndHyphenEdit, Layout*) const {}
+ virtual float measureText(const U16StringPiece&) const { return 0; }
+
+ virtual LineBreakStyle lineBreakStyle() const override { return LineBreakStyle::None; }
+
+ virtual LineBreakWordStyle lineBreakWordStyle() const override {
+ return LineBreakWordStyle::None;
+ }
+
private:
MinikinPaint mPaint;
uint32_t mLocaleListId;
diff --git a/tests/unittest/MeasuredTextTest.cpp b/tests/unittest/MeasuredTextTest.cpp
index e5766a1..845b5f1 100644
--- a/tests/unittest/MeasuredTextTest.cpp
+++ b/tests/unittest/MeasuredTextTest.cpp
@@ -32,22 +32,24 @@ TEST(MeasuredTextTest, RunTests) {
constexpr uint32_t CHAR_COUNT = 6;
constexpr float REPLACEMENT_WIDTH = 20.0f;
auto font = buildFontCollection("Ascii.ttf");
+ int lbStyle = (int)LineBreakStyle::None;
+ int lbWordStyle = (int)LineBreakWordStyle::None;
MeasuredTextBuilder builder;
MinikinPaint paint1(font);
paint1.size = 10.0f; // make 1em = 10px
- builder.addStyleRun(0, 2, std::move(paint1), false /* is RTL */);
+ builder.addStyleRun(0, 2, std::move(paint1), lbStyle, lbWordStyle, false /* is RTL */);
builder.addReplacementRun(2, 4, REPLACEMENT_WIDTH, 0 /* locale list id */);
MinikinPaint paint2(font);
paint2.size = 10.0f; // make 1em = 10px
- builder.addStyleRun(4, 6, std::move(paint2), false /* is RTL */);
+ builder.addStyleRun(4, 6, std::move(paint2), lbStyle, lbWordStyle, false /* is RTL */);
std::vector<uint16_t> text(CHAR_COUNT, 'a');
std::unique_ptr<MeasuredText> measuredText =
builder.build(text, true /* compute hyphenation */, false /* compute full layout */,
- nullptr /* no hint */);
+ false /* ignore kerning */, nullptr /* no hint */);
ASSERT_TRUE(measuredText);
@@ -61,13 +63,15 @@ TEST(MeasuredTextTest, RunTests) {
TEST(MeasuredTextTest, getBoundsTest) {
auto text = utf8ToUtf16("Hello, World!");
auto font = buildFontCollection("Ascii.ttf");
+ int lbStyle = (int)LineBreakStyle::None;
+ int lbWordStyle = (int)LineBreakWordStyle::None;
MeasuredTextBuilder builder;
MinikinPaint paint(font);
paint.size = 10.0f;
- builder.addStyleRun(0, text.size(), std::move(paint), false /* is RTL */);
+ builder.addStyleRun(0, text.size(), std::move(paint), lbStyle, lbWordStyle, false /* is RTL */);
auto mt = builder.build(text, true /* hyphenation */, true /* full layout */,
- nullptr /* no hint */);
+ false /* ignore kerning */, nullptr /* no hint */);
EXPECT_EQ(MinikinRect(0.0f, 0.0f, 0.0f, 0.0f), mt->getBounds(text, Range(0, 0)));
EXPECT_EQ(MinikinRect(0.0f, 10.0f, 10.0f, 0.0f), mt->getBounds(text, Range(0, 1)));
@@ -79,13 +83,15 @@ TEST(MeasuredTextTest, getBoundsTest) {
TEST(MeasuredTextTest, getBoundsTest_LTR) {
auto text = utf8ToUtf16("\u0028"); // U+0028 has 1em in LTR, 3em in RTL.
auto font = buildFontCollection("Bbox.ttf");
+ int lbStyle = (int)LineBreakStyle::None;
+ int lbWordStyle = (int)LineBreakWordStyle::None;
MeasuredTextBuilder builder;
MinikinPaint paint(font);
paint.size = 10.0f;
- builder.addStyleRun(0, text.size(), std::move(paint), false /* is RTL */);
+ builder.addStyleRun(0, text.size(), std::move(paint), lbStyle, lbWordStyle, false /* is RTL */);
auto mt = builder.build(text, true /* hyphenation */, true /* full layout */,
- nullptr /* no hint */);
+ false /* ignore kerning */, nullptr /* no hint */);
EXPECT_EQ(MinikinRect(0.0f, 10.0f, 10.0f, 0.0f), mt->getBounds(text, Range(0, 1)));
}
@@ -93,13 +99,15 @@ TEST(MeasuredTextTest, getBoundsTest_LTR) {
TEST(MeasuredTextTest, getBoundsTest_RTL) {
auto text = utf8ToUtf16("\u0028"); // U+0028 has 1em in LTR, 3em in RTL.
auto font = buildFontCollection("Bbox.ttf");
+ int lbStyle = (int)LineBreakStyle::None;
+ int lbWordStyle = (int)LineBreakWordStyle::None;
MeasuredTextBuilder builder;
MinikinPaint paint(font);
paint.size = 10.0f;
- builder.addStyleRun(0, text.size(), std::move(paint), true /* is RTL */);
+ builder.addStyleRun(0, text.size(), std::move(paint), lbStyle, lbWordStyle, true /* is RTL */);
auto mt = builder.build(text, true /* hyphenation */, true /* full layout */,
- nullptr /* no hint */);
+ false /* ignore kerning */, nullptr /* no hint */);
EXPECT_EQ(MinikinRect(0.0f, 30.0f, 30.0f, 0.0f), mt->getBounds(text, Range(0, 2)));
}
@@ -108,16 +116,19 @@ TEST(MeasuredTextTest, getBoundsTest_multiStyle) {
auto text = utf8ToUtf16("Hello, World!");
auto font = buildFontCollection("Ascii.ttf");
uint32_t helloLength = 7; // length of "Hello, "
+ int lbStyle = (int)LineBreakStyle::None;
+ int lbWordStyle = (int)LineBreakWordStyle::None;
MeasuredTextBuilder builder;
MinikinPaint paint(font);
paint.size = 10.0f;
- builder.addStyleRun(0, helloLength, std::move(paint), false /* is RTL */);
+ builder.addStyleRun(0, helloLength, std::move(paint), lbStyle, lbWordStyle, false /* is RTL */);
MinikinPaint paint2(font);
paint2.size = 20.0f;
- builder.addStyleRun(helloLength, text.size(), std::move(paint2), false /* is RTL */);
+ builder.addStyleRun(helloLength, text.size(), std::move(paint2), lbStyle, lbWordStyle,
+ false /* is RTL */);
auto mt = builder.build(text, true /* hyphenation */, true /* full layout */,
- nullptr /* no hint */);
+ false /* ignore kerning */, nullptr /* no hint */);
EXPECT_EQ(MinikinRect(0.0f, 0.0f, 0.0f, 0.0f), mt->getBounds(text, Range(0, 0)));
EXPECT_EQ(MinikinRect(0.0f, 10.0f, 10.0f, 0.0f), mt->getBounds(text, Range(0, 1)));
@@ -132,13 +143,15 @@ TEST(MeasuredTextTest, getBoundsTest_multiStyle) {
TEST(MeasuredTextTest, getExtentTest) {
auto text = utf8ToUtf16("Hello, World!");
auto font = buildFontCollection("Ascii.ttf");
+ int lbStyle = (int)LineBreakStyle::None;
+ int lbWordStyle = (int)LineBreakWordStyle::None;
MeasuredTextBuilder builder;
MinikinPaint paint(font);
paint.size = 10.0f;
- builder.addStyleRun(0, text.size(), std::move(paint), false /* is RTL */);
+ builder.addStyleRun(0, text.size(), std::move(paint), lbStyle, lbWordStyle, false /* is RTL */);
auto mt = builder.build(text, true /* hyphenation */, true /* full layout */,
- nullptr /* no hint */);
+ false /* ignore kernign */, nullptr /* no hint */);
EXPECT_EQ(MinikinExtent(0.0f, 0.0f), mt->getExtent(text, Range(0, 0)));
EXPECT_EQ(MinikinExtent(-80.0f, 20.0f), mt->getExtent(text, Range(0, 1)));
@@ -151,16 +164,19 @@ TEST(MeasuredTextTest, getExtentTest_multiStyle) {
auto text = utf8ToUtf16("Hello, World!");
auto font = buildFontCollection("Ascii.ttf");
uint32_t helloLength = 7; // length of "Hello, "
+ int lbStyle = (int)LineBreakStyle::None;
+ int lbWordStyle = (int)LineBreakWordStyle::None;
MeasuredTextBuilder builder;
MinikinPaint paint(font);
paint.size = 10.0f;
- builder.addStyleRun(0, helloLength, std::move(paint), false /* is RTL */);
+ builder.addStyleRun(0, helloLength, std::move(paint), lbStyle, lbWordStyle, false /* is RTL */);
MinikinPaint paint2(font);
paint2.size = 20.0f;
- builder.addStyleRun(helloLength, text.size(), std::move(paint2), false /* is RTL */);
+ builder.addStyleRun(helloLength, text.size(), std::move(paint2), 0 /* no line break */,
+ 0 /* no line break word style */, false /* is RTL */);
auto mt = builder.build(text, true /* hyphenation */, true /* full layout */,
- nullptr /* no hint */);
+ false /* ignore kerning */, nullptr /* no hint */);
EXPECT_EQ(MinikinExtent(0.0f, 0.0f), mt->getExtent(text, Range(0, 0)));
EXPECT_EQ(MinikinExtent(-80.0f, 20.0f), mt->getExtent(text, Range(0, 1)));
@@ -176,13 +192,15 @@ TEST(MeasuredTextTest, buildLayoutTest) {
auto text = utf8ToUtf16("Hello, World!");
auto font = buildFontCollection("Ascii.ttf");
Range fullContext(0, text.size());
+ int lbStyle = (int)LineBreakStyle::None;
+ int lbWordStyle = (int)LineBreakWordStyle::None;
MeasuredTextBuilder builder;
MinikinPaint paint(font);
paint.size = 10.0f;
- builder.addStyleRun(0, text.size(), std::move(paint), false /* is RTL */);
+ builder.addStyleRun(0, text.size(), std::move(paint), lbStyle, lbWordStyle, false /* is RTL */);
auto mt = builder.build(text, true /* hyphenation */, true /* full layout */,
- nullptr /* no hint */);
+ false /* ignore kerning */, nullptr /* no hint */);
MinikinRect rect;
MinikinPaint samePaint(font);
@@ -256,16 +274,19 @@ TEST(MeasuredTextTest, buildLayoutTest_multiStyle) {
auto font = buildFontCollection("Ascii.ttf");
uint32_t helloLength = 7; // length of "Hello, "
Range fullContext(0, text.size());
+ int lbStyle = (int)LineBreakStyle::None;
+ int lbWordStyle = (int)LineBreakWordStyle::None;
MeasuredTextBuilder builder;
MinikinPaint paint(font);
paint.size = 10.0f;
- builder.addStyleRun(0, helloLength, std::move(paint), false /* is RTL */);
+ builder.addStyleRun(0, helloLength, std::move(paint), lbStyle, lbWordStyle, false /* is RTL */);
MinikinPaint paint2(font);
paint2.size = 20.0f;
- builder.addStyleRun(helloLength, text.size(), std::move(paint2), false /* is RTL */);
+ builder.addStyleRun(helloLength, text.size(), std::move(paint2), lbStyle, lbWordStyle,
+ false /* is RTL */);
auto mt = builder.build(text, true /* hyphenation */, true /* full layout */,
- nullptr /* no hint */);
+ false /* ignore kerning */, nullptr /* no hint */);
MinikinRect rect;
MinikinPaint samePaint(font);
@@ -341,13 +362,15 @@ TEST(MeasuredTextTest, buildLayoutTest_differentPaint) {
auto text = utf8ToUtf16("Hello, World!");
auto font = buildFontCollection("Ascii.ttf");
Range fullContext(0, text.size());
+ int lbStyle = (int)LineBreakStyle::None;
+ int lbWordStyle = (int)LineBreakWordStyle::None;
MeasuredTextBuilder builder;
MinikinPaint paint(font);
paint.size = 10.0f;
- builder.addStyleRun(0, text.size(), std::move(paint), false /* is RTL */);
+ builder.addStyleRun(0, text.size(), std::move(paint), lbStyle, lbWordStyle, false /* is RTL */);
auto mt = builder.build(text, true /* hyphenation */, true /* full layout */,
- nullptr /* no hint */);
+ false /* ignore kerning */, nullptr /* no hint */);
MinikinRect rect;
MinikinPaint differentPaint(font);
@@ -421,16 +444,19 @@ TEST(MeasuredTextTest, buildLayoutTest_multiStyle_differentPaint) {
auto font = buildFontCollection("Ascii.ttf");
uint32_t helloLength = 7; // length of "Hello, "
Range fullContext(0, text.size());
+ int lbStyle = (int)LineBreakStyle::None;
+ int lbWordStyle = (int)LineBreakWordStyle::None;
MeasuredTextBuilder builder;
MinikinPaint paint(font);
paint.size = 10.0f;
- builder.addStyleRun(0, helloLength, std::move(paint), false /* is RTL */);
+ builder.addStyleRun(0, helloLength, std::move(paint), lbStyle, lbWordStyle, false /* is RTL */);
MinikinPaint paint2(font);
paint2.size = 20.0f;
- builder.addStyleRun(helloLength, text.size(), std::move(paint2), false /* is RTL */);
+ builder.addStyleRun(helloLength, text.size(), std::move(paint2), lbStyle, lbWordStyle,
+ false /* is RTL */);
auto mt = builder.build(text, true /* hyphenation */, true /* full layout */,
- nullptr /* no hint */);
+ false /* ignore kerning */, nullptr /* no hint */);
MinikinRect rect;
MinikinPaint differentPaint(font);
@@ -532,4 +558,63 @@ TEST(MeasuredTextTest, buildLayoutTest_multiStyle_differentPaint) {
EXPECT_EQ(MinikinRect(0.0f, 30.0f, 390.0f, 0.0f), rect);
}
+TEST(MeasuredTextTest, testLineBreakStyle_from_builder) {
+ auto text = utf8ToUtf16("Hello, World!");
+ auto font = buildFontCollection("Ascii.ttf");
+ int lbStyle = (int)LineBreakStyle::Loose; // loose
+ int lbWordStyle = (int)LineBreakWordStyle::Phrase; // phrase
+
+ MeasuredTextBuilder looseStyleBuilder;
+ MinikinPaint paint(font);
+ looseStyleBuilder.addStyleRun(0, text.size(), std::move(paint), lbStyle, lbWordStyle, false);
+ auto mt = looseStyleBuilder.build(text, true /* hyphenation */, true /* full layout */,
+ false /* ignore kerning */, nullptr /* no hint */);
+
+ EXPECT_EQ((size_t)1, mt->runs.size());
+ EXPECT_EQ(LineBreakStyle::Loose, mt->runs[0]->lineBreakStyle());
+ EXPECT_EQ(LineBreakWordStyle::Phrase, mt->runs[0]->lineBreakWordStyle());
+
+ lbStyle = (int)LineBreakStyle::Normal; // normal
+ MeasuredTextBuilder normalStyleBuilder;
+ MinikinPaint normalStylePaint(font);
+ normalStyleBuilder.addStyleRun(0, text.size(), std::move(normalStylePaint), lbStyle,
+ lbWordStyle, false);
+ mt = normalStyleBuilder.build(text, true /* hyphenation */, true /* full layout */,
+ false /* ignore kerning */, nullptr /* no hint */);
+
+ EXPECT_EQ((size_t)1, mt->runs.size());
+ EXPECT_EQ(LineBreakStyle::Normal, mt->runs[0]->lineBreakStyle());
+ EXPECT_EQ(LineBreakWordStyle::Phrase, mt->runs[0]->lineBreakWordStyle());
+
+ lbStyle = (int)LineBreakStyle::Strict; // strict
+ lbWordStyle = (int)LineBreakWordStyle::None; // no word style
+ MeasuredTextBuilder strictStyleBuilder;
+ MinikinPaint strictStylePaint(font);
+ strictStyleBuilder.addStyleRun(0, text.size(), std::move(strictStylePaint), lbStyle,
+ lbWordStyle, false);
+ mt = strictStyleBuilder.build(text, true /* hyphenation */, true /* full layout */,
+ false /* ignore kerning */, nullptr /* no hint */);
+
+ EXPECT_EQ((size_t)1, mt->runs.size());
+ EXPECT_EQ(LineBreakStyle::Strict, mt->runs[0]->lineBreakStyle());
+ EXPECT_EQ(LineBreakWordStyle::None, mt->runs[0]->lineBreakWordStyle());
+}
+
+TEST(MeasuredTextTest, testLineBreakStyle_from_run) {
+ auto text = utf8ToUtf16("Hello, World!");
+ auto font = buildFontCollection("Ascii.ttf");
+ int lbStyle = (int)LineBreakStyle::Strict;
+ int lbWordStyle = (int)LineBreakWordStyle::Phrase;
+ Range range(0, text.size());
+ MinikinPaint paint(font);
+
+ StyleRun styleRun(range, std::move(paint), lbStyle, lbWordStyle, false /* isRtl */);
+ EXPECT_EQ(LineBreakStyle::Strict, styleRun.lineBreakStyle());
+ EXPECT_EQ(LineBreakWordStyle::Phrase, styleRun.lineBreakWordStyle());
+
+ ReplacementRun replacementRun(range, 10.0f /* width */, 0 /* locale list id */);
+ EXPECT_EQ(LineBreakStyle::None, replacementRun.lineBreakStyle());
+ EXPECT_EQ(LineBreakWordStyle::None, replacementRun.lineBreakWordStyle());
+}
+
} // namespace minikin
diff --git a/tests/unittest/OptimalLineBreakerTest.cpp b/tests/unittest/OptimalLineBreakerTest.cpp
index d6801cc..a5f8625 100644
--- a/tests/unittest/OptimalLineBreakerTest.cpp
+++ b/tests/unittest/OptimalLineBreakerTest.cpp
@@ -65,25 +65,21 @@ public:
protected:
LineBreakResult doLineBreak(const U16StringPiece& textBuffer, BreakStrategy strategy,
- HyphenationFrequency frequency, float lineWidth) {
- return doLineBreak(textBuffer, strategy, frequency, "en-US", lineWidth);
- }
-
- LineBreakResult doLineBreak(const U16StringPiece& textBuffer, BreakStrategy strategy,
HyphenationFrequency frequency, const std::string& lang,
- float lineWidth) {
+ 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=1px
+ paint.size = 10.0f; // Make 1em=10px
paint.localeListId = LocaleListCache::getId(lang);
- builder.addStyleRun(0, textBuffer.size(), std::move(paint), false);
+ builder.addStyleRun(0, textBuffer.size(), std::move(paint), 0, 0, false);
+ bool computeHyphen = frequency != HyphenationFrequency::None;
std::unique_ptr<MeasuredText> measuredText =
- builder.build(textBuffer, true /* compute hyphenation */,
- false /* compute full layout */, nullptr /* no hint */);
+ builder.build(textBuffer, computeHyphen, false /* compute full layout */,
+ ignoreKerning, nullptr /* no hint */);
return doLineBreak(textBuffer, *measuredText, strategy, frequency, lineWidth);
}
@@ -95,6 +91,33 @@ protected:
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;
};
@@ -116,44 +139,20 @@ TEST_F(OptimalLineBreakerTest, testBreakWithoutHyphenation) {
{"This is an example text.", 240, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
- auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
- actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
- actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
- actual = doLineBreak(textBuf, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
+ 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},
};
- auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
- actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
- actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
- actual = doLineBreak(textBuf, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
+ 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;
@@ -164,14 +163,8 @@ TEST_F(OptimalLineBreakerTest, testBreakWithoutHyphenation) {
};
// clang-format on
- auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
- actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
+ 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 = {
@@ -179,21 +172,17 @@ TEST_F(OptimalLineBreakerTest, testBreakWithoutHyphenation) {
{ "example text." , 130, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
- actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
+ 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
- actual = doLineBreak(textBuf, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
+ expectBreak(expect, textBuf, BALANCED, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
}
+ return;
{
constexpr float LINE_WIDTH = 170;
// clang-format off
@@ -203,40 +192,31 @@ TEST_F(OptimalLineBreakerTest, testBreakWithoutHyphenation) {
};
// clang-format on
- auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
+ 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
- actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
+ 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
- actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
+ 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
- actual = doLineBreak(textBuf, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
+ expectBreak(expect, textBuf, BALANCED, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
}
{
constexpr float LINE_WIDTH = 160;
@@ -246,41 +226,31 @@ TEST_F(OptimalLineBreakerTest, testBreakWithoutHyphenation) {
{ "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);
- auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_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 exam-" , 160, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
{ "ple text." , 90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
- actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
+ 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
- actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
+ 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
- actual = doLineBreak(textBuf, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
+ expectBreak(expect, textBuf, BALANCED, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
}
{
constexpr float LINE_WIDTH = 150;
@@ -290,41 +260,31 @@ TEST_F(OptimalLineBreakerTest, testBreakWithoutHyphenation) {
{ "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);
- auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_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 ex-", 140, NO_START_HYPHEN, END_HYPHEN, ASCENT, DESCENT },
{ "ample text." , 110, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
- actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
+ 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
- actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
+ 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
- actual = doLineBreak(textBuf, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
+ expectBreak(expect, textBuf, BALANCED, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
}
{
constexpr float LINE_WIDTH = 130;
@@ -335,22 +295,10 @@ TEST_F(OptimalLineBreakerTest, testBreakWithoutHyphenation) {
};
// clang-format on
- auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
- actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
- actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
- actual = doLineBreak(textBuf, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
+ 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;
@@ -362,18 +310,10 @@ TEST_F(OptimalLineBreakerTest, testBreakWithoutHyphenation) {
};
// clang-format on
- auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
- actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
- actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
+ 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 },
@@ -381,10 +321,8 @@ TEST_F(OptimalLineBreakerTest, testBreakWithoutHyphenation) {
{ "ple text.", 90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
- actual = doLineBreak(textBuf, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
+
+ expectBreak(expect, textBuf, BALANCED, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
}
{
constexpr float LINE_WIDTH = 90;
@@ -396,11 +334,8 @@ TEST_F(OptimalLineBreakerTest, testBreakWithoutHyphenation) {
{ "text." , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
+ expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHENATION, "en-US", LINE_WIDTH);
- auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
// clang-format off
expect = {
{ "This is " , 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
@@ -408,10 +343,8 @@ TEST_F(OptimalLineBreakerTest, testBreakWithoutHyphenation) {
{ "ple text.", 90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
- actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
+ 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 },
@@ -420,10 +353,8 @@ TEST_F(OptimalLineBreakerTest, testBreakWithoutHyphenation) {
{ "text." , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
- actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
+ 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 },
@@ -431,10 +362,7 @@ TEST_F(OptimalLineBreakerTest, testBreakWithoutHyphenation) {
{ "ple text.", 90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
- actual = doLineBreak(textBuf, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
+ expectBreak(expect, textBuf, BALANCED, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
}
{
constexpr float LINE_WIDTH = 80;
@@ -447,14 +375,9 @@ TEST_F(OptimalLineBreakerTest, testBreakWithoutHyphenation) {
};
// clang-format on
- auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
- actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
+ 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 },
@@ -463,14 +386,8 @@ TEST_F(OptimalLineBreakerTest, testBreakWithoutHyphenation) {
{ "text." , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
- actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
- actual = doLineBreak(textBuf, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
+ 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;
@@ -483,14 +400,8 @@ TEST_F(OptimalLineBreakerTest, testBreakWithoutHyphenation) {
};
// clang-format on
- auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
- actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
+ 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 },
@@ -499,14 +410,8 @@ TEST_F(OptimalLineBreakerTest, testBreakWithoutHyphenation) {
{ "text." , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
- actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
- actual = doLineBreak(textBuf, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
+ 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;
@@ -521,14 +426,8 @@ TEST_F(OptimalLineBreakerTest, testBreakWithoutHyphenation) {
};
// clang-format on
- auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
- actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
+ 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 },
@@ -538,14 +437,8 @@ TEST_F(OptimalLineBreakerTest, testBreakWithoutHyphenation) {
{ "text." , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
- actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
- actual = doLineBreak(textBuf, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
+ 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;
@@ -560,14 +453,8 @@ TEST_F(OptimalLineBreakerTest, testBreakWithoutHyphenation) {
};
// clang-format on
- auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
- actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
+ 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 },
@@ -577,14 +464,8 @@ TEST_F(OptimalLineBreakerTest, testBreakWithoutHyphenation) {
{ "text." , 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
- actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
- actual = doLineBreak(textBuf, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
+ 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;
@@ -600,11 +481,8 @@ TEST_F(OptimalLineBreakerTest, testBreakWithoutHyphenation) {
{ "." , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
+ expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHENATION, "en-US", LINE_WIDTH);
- auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
// clang-format off
expect = {
{ "This " , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
@@ -618,10 +496,8 @@ TEST_F(OptimalLineBreakerTest, testBreakWithoutHyphenation) {
{ "ext." , 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
- actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
+ expectBreak(expect, textBuf, BALANCED, NO_HYPHENATION, "en-US", LINE_WIDTH);
+
// clang-format off
expect = {
{ "This ", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
@@ -634,10 +510,7 @@ TEST_F(OptimalLineBreakerTest, testBreakWithoutHyphenation) {
{ "." , 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
- actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
+ 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 },
@@ -651,10 +524,7 @@ TEST_F(OptimalLineBreakerTest, testBreakWithoutHyphenation) {
{ "xt." , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
- actual = doLineBreak(textBuf, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
+ expectBreak(expect, textBuf, BALANCED, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
}
{
constexpr float LINE_WIDTH = 30;
@@ -673,11 +543,8 @@ TEST_F(OptimalLineBreakerTest, testBreakWithoutHyphenation) {
{ "t." , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
+ expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHENATION, "en-US", LINE_WIDTH);
- auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
// clang-format off
expect = {
// TODO: Is this desperate break working correctly?
@@ -694,10 +561,8 @@ TEST_F(OptimalLineBreakerTest, testBreakWithoutHyphenation) {
{ "xt." , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
- actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
+ expectBreak(expect, textBuf, BALANCED, NO_HYPHENATION, "en-US", LINE_WIDTH);
+
// clang-format off
expect = {
// TODO: Is this desperate break working correctly?
@@ -712,10 +577,8 @@ TEST_F(OptimalLineBreakerTest, testBreakWithoutHyphenation) {
{ "t." , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
- actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
+ expectBreak(expect, textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
+
// clang-format off
expect = {
// TODO: Is this desperate break working correctly?
@@ -731,10 +594,7 @@ TEST_F(OptimalLineBreakerTest, testBreakWithoutHyphenation) {
{"xt." , 30, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
// clang-format on
- actual = doLineBreak(textBuf, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
+ expectBreak(expect, textBuf, BALANCED, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
}
{
constexpr float LINE_WIDTH = 20;
@@ -754,15 +614,9 @@ TEST_F(OptimalLineBreakerTest, testBreakWithoutHyphenation) {
{ "." , 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);
- auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
- actual = doLineBreak(textBuf, 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 = {
{ "Th" , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
@@ -780,14 +634,8 @@ TEST_F(OptimalLineBreakerTest, testBreakWithoutHyphenation) {
{ "t." , 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT },
};
// clang-format on
- actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
- actual = doLineBreak(textBuf, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
+ 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;
@@ -816,22 +664,10 @@ TEST_F(OptimalLineBreakerTest, testBreakWithoutHyphenation) {
};
// clang-format on
- auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
- actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
- actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
- actual = doLineBreak(textBuf, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
+ 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);
}
}
@@ -852,11 +688,7 @@ TEST_F(OptimalLineBreakerTest, testHyphenationStartLineChange) {
{"czerwono-niebieska", 180, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
- const auto actual =
- doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "pl", LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
+ expectBreak(expect, textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "pl", LINE_WIDTH);
}
{
constexpr float LINE_WIDTH = 180;
@@ -864,11 +696,7 @@ TEST_F(OptimalLineBreakerTest, testHyphenationStartLineChange) {
{"czerwono-niebieska", 180, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
- const auto actual =
- doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "pl", LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
+ expectBreak(expect, textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "pl", LINE_WIDTH);
}
{
constexpr float LINE_WIDTH = 130;
@@ -879,11 +707,7 @@ TEST_F(OptimalLineBreakerTest, testHyphenationStartLineChange) {
};
// clang-format on
- const auto actual =
- doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "pl", LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
+ expectBreak(expect, textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "pl", LINE_WIDTH);
}
}
@@ -898,20 +722,14 @@ TEST_F(OptimalLineBreakerTest, testZeroWidthLine) {
{
const auto textBuf = utf8ToUtf16("");
std::vector<LineBreakExpectation> expect = {};
- const auto actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
+ 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},
};
- const auto actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
+ expectBreak(expect, textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
}
{
const auto textBuf = utf8ToUtf16("AB");
@@ -919,10 +737,7 @@ TEST_F(OptimalLineBreakerTest, testZeroWidthLine) {
{"A", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"B", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
- const auto actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
+ expectBreak(expect, textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, "en-US", LINE_WIDTH);
}
}
@@ -942,9 +757,9 @@ TEST_F(OptimalLineBreakerTest, testZeroWidthCharacter) {
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 */, nullptr /* no hint */);
+ 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);
@@ -961,9 +776,9 @@ TEST_F(OptimalLineBreakerTest, testZeroWidthCharacter) {
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 */, nullptr /* no hint */);
+ 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);
@@ -992,9 +807,9 @@ TEST_F(OptimalLineBreakerTest, testLocaleSwitchTest) {
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 */, nullptr /* no hint */);
+ 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);
@@ -1011,9 +826,9 @@ TEST_F(OptimalLineBreakerTest, testLocaleSwitchTest) {
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 */, nullptr /* no hint */);
+ 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
@@ -1040,28 +855,17 @@ TEST_F(OptimalLineBreakerTest, testEmailOrUrl) {
{".b", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
// clang-format on
- auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
- actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
+ 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
- actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
- actual = doLineBreak(textBuf, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
+ 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;
@@ -1073,22 +877,10 @@ TEST_F(OptimalLineBreakerTest, testEmailOrUrl) {
};
// clang-format on
- auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
- actual = doLineBreak(textBuf, HIGH_QUALITY, NORMAL_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
- actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
- actual = doLineBreak(textBuf, BALANCED, NORMAL_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
+ 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);
}
}
@@ -1109,9 +901,9 @@ TEST_F(OptimalLineBreakerTest, testLocaleSwitch_InEmailOrUrl) {
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 */, nullptr /* no hint */);
+ 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 = {
@@ -1152,9 +944,9 @@ TEST_F(OptimalLineBreakerTest, testLocaleSwitch_InEmailOrUrl) {
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 */, nullptr /* no hint */);
+ 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 = {
@@ -1196,10 +988,7 @@ TEST_F(OptimalLineBreakerTest, ExtentTest) {
CUSTOM_ASCENT, CUSTOM_DESCENT},
};
- auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHEN, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
+ expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHEN, "en-US", LINE_WIDTH);
}
{
constexpr float LINE_WIDTH = 200;
@@ -1208,10 +997,7 @@ TEST_F(OptimalLineBreakerTest, ExtentTest) {
CUSTOM_ASCENT, CUSTOM_DESCENT},
};
- auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHEN, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
+ expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHEN, "en-US", LINE_WIDTH);
}
{
constexpr float LINE_WIDTH = 190;
@@ -1220,10 +1006,7 @@ TEST_F(OptimalLineBreakerTest, ExtentTest) {
CUSTOM_DESCENT},
{"Japanese.", 90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
- auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHEN, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
+ expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHEN, "en-US", LINE_WIDTH);
}
{
constexpr float LINE_WIDTH = 90;
@@ -1233,10 +1016,7 @@ TEST_F(OptimalLineBreakerTest, ExtentTest) {
CUSTOM_DESCENT},
{"Japanese.", 90, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
- auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHEN, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
+ expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHEN, "en-US", LINE_WIDTH);
}
{
constexpr float LINE_WIDTH = 50;
@@ -1247,10 +1027,7 @@ TEST_F(OptimalLineBreakerTest, ExtentTest) {
{"Japan", 50, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{"ese.", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
- auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHEN, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
+ expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHEN, "en-US", LINE_WIDTH);
}
{
constexpr float LINE_WIDTH = 40;
@@ -1262,10 +1039,7 @@ TEST_F(OptimalLineBreakerTest, ExtentTest) {
{"nese", 40, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{".", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
- auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHEN, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
+ expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHEN, "en-US", LINE_WIDTH);
}
{
constexpr float LINE_WIDTH = 20;
@@ -1282,10 +1056,7 @@ TEST_F(OptimalLineBreakerTest, ExtentTest) {
{"se", 20, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{".", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
- auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHEN, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
+ expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHEN, "en-US", LINE_WIDTH);
}
{
constexpr float LINE_WIDTH = 10;
@@ -1308,10 +1079,7 @@ TEST_F(OptimalLineBreakerTest, ExtentTest) {
{"e", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
{".", 10, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
- auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHEN, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
+ expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHEN, "en-US", LINE_WIDTH);
}
}
@@ -1331,9 +1099,9 @@ TEST_F(OptimalLineBreakerTest, testReplacementSpanNotBreakTest_SingleChar) {
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 */, nullptr /* no hint */);
+ 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,
@@ -1424,9 +1192,9 @@ TEST_F(OptimalLineBreakerTest, testReplacementSpanNotBreakTest_MultipleChars) {
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 */, nullptr /* no hint */);
+ 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,
@@ -1512,9 +1280,9 @@ TEST_F(OptimalLineBreakerTest, testReplacementSpanNotBreakTest_continuedReplacem
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 */, nullptr /* no hint */);
+ 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,
@@ -1588,9 +1356,9 @@ TEST_F(OptimalLineBreakerTest, testReplacementSpanNotBreakTest_CJK) {
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 */, nullptr /* no hint */);
+ 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,
@@ -1745,9 +1513,9 @@ TEST_F(OptimalLineBreakerTest, testReplacementSpan_GraphemeLineBreakWithMultiple
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 */, nullptr /* no hint */);
+ 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,
@@ -1880,9 +1648,9 @@ TEST_F(OptimalLineBreakerTest, testReplacementSpanNotBreakTest_with_punctuation)
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 */, nullptr /* no hint */);
+ 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,
@@ -2070,14 +1838,8 @@ TEST_F(OptimalLineBreakerTest, testControllCharAfterSpace) {
{"\u2066example", 70, NO_START_HYPHEN, NO_END_HYPHEN, ASCENT, DESCENT},
};
- auto actual = doLineBreak(textBuf, HIGH_QUALITY, NO_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
- actual = doLineBreak(textBuf, BALANCED, NO_HYPHENATION, LINE_WIDTH);
- EXPECT_TRUE(sameLineBreak(expect, actual)) << toString(expect) << std::endl
- << " vs " << std::endl
- << toString(textBuf, actual);
+ expectBreak(expect, textBuf, HIGH_QUALITY, NO_HYPHENATION, "en-US", LINE_WIDTH);
+ expectBreak(expect, textBuf, BALANCED, NO_HYPHENATION, "en-US", LINE_WIDTH);
}
}
@@ -2096,10 +1858,10 @@ TEST_F(OptimalLineBreakerTest, roundingError) {
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), false);
- std::unique_ptr<MeasuredText> measuredText =
- builder.build(textBuffer, false /* compute hyphenation */,
- false /* compute full layout */, nullptr /* no hint */);
+ 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,
diff --git a/tests/unittest/WordBreakerTests.cpp b/tests/unittest/WordBreakerTests.cpp
index 26e7c19..4b5fc35 100644
--- a/tests/unittest/WordBreakerTests.cpp
+++ b/tests/unittest/WordBreakerTests.cpp
@@ -37,8 +37,9 @@ TEST(WordBreakerTest, basic) {
WordBreaker breaker;
breaker.setText(buf, NELEM(buf));
EXPECT_EQ(0, breaker.current());
- EXPECT_EQ(6, breaker.followingWithLocale(Locale("en-US"), 0)); // after "hello "
- EXPECT_EQ(0, breaker.wordStart()); // "hello"
+ EXPECT_EQ(6, breaker.followingWithLocale(Locale("en-US"), LineBreakStyle::None,
+ LineBreakWordStyle::None, 0)); // after "hello "
+ EXPECT_EQ(0, breaker.wordStart()); // "hello"
EXPECT_EQ(5, breaker.wordEnd());
EXPECT_EQ(0, breaker.breakBadness());
EXPECT_EQ(6, breaker.current());
@@ -51,11 +52,13 @@ TEST(WordBreakerTest, basic) {
TEST(WordBreakerTest, softHyphen) {
uint16_t buf[] = {'h', 'e', 'l', 0x00AD, 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd'};
+ auto lbStyle = LineBreakStyle::None;
+ auto lbWordStyle = LineBreakWordStyle::None;
WordBreaker breaker;
breaker.setText(buf, NELEM(buf));
EXPECT_EQ(0, breaker.current());
// after "hel{SOFT HYPHEN}lo "
- EXPECT_EQ(7, breaker.followingWithLocale(Locale("en-US"), 0));
+ EXPECT_EQ(7, breaker.followingWithLocale(Locale("en-US"), lbStyle, lbWordStyle, 0));
EXPECT_EQ(0, breaker.wordStart()); // "hel{SOFT HYPHEN}lo"
EXPECT_EQ(6, breaker.wordEnd());
EXPECT_EQ(0, breaker.breakBadness());
@@ -68,10 +71,13 @@ TEST(WordBreakerTest, softHyphen) {
TEST(WordBreakerTest, hardHyphen) {
// Hyphens should not allow breaks anymore.
uint16_t buf[] = {'s', 'u', 'g', 'a', 'r', '-', 'f', 'r', 'e', 'e'};
+ auto lbStyle = LineBreakStyle::None;
+ auto lbWordStyle = LineBreakWordStyle::None;
WordBreaker breaker;
breaker.setText(buf, NELEM(buf));
EXPECT_EQ(0, breaker.current());
- EXPECT_EQ((ssize_t)NELEM(buf), breaker.followingWithLocale(Locale("en-US"), 0));
+ EXPECT_EQ((ssize_t)NELEM(buf),
+ breaker.followingWithLocale(Locale("en-US"), lbStyle, lbWordStyle, 0));
EXPECT_EQ(0, breaker.wordStart());
EXPECT_EQ((ssize_t)NELEM(buf), breaker.wordEnd());
EXPECT_EQ(0, breaker.breakBadness());
@@ -79,12 +85,15 @@ TEST(WordBreakerTest, hardHyphen) {
TEST(WordBreakerTest, postfixAndPrefix) {
uint16_t buf[] = {'U', 'S', 0x00A2, ' ', 'J', 'P', 0x00A5}; // US¢ JP¥
+ auto lbStyle = LineBreakStyle::None;
+ auto lbWordStyle = LineBreakWordStyle::None;
WordBreaker breaker;
breaker.setText(buf, NELEM(buf));
EXPECT_EQ(0, breaker.current());
- EXPECT_EQ(4, breaker.followingWithLocale(Locale("en-US"), 0)); // after CENT SIGN
- EXPECT_EQ(0, breaker.wordStart()); // "US¢"
+ EXPECT_EQ(4, breaker.followingWithLocale(Locale("en-US"), lbStyle, lbWordStyle,
+ 0)); // after CENT SIGN
+ EXPECT_EQ(0, breaker.wordStart()); // "US¢"
EXPECT_EQ(3, breaker.wordEnd());
EXPECT_EQ((ssize_t)NELEM(buf), breaker.next()); // end of string
@@ -94,12 +103,15 @@ TEST(WordBreakerTest, postfixAndPrefix) {
TEST(WordBreakerTest, myanmarKinzi) {
uint16_t buf[] = {0x1004, 0x103A, 0x1039, 0x1000, 0x102C}; // NGA, ASAT, VIRAMA, KA, UU
+ auto lbStyle = LineBreakStyle::None;
+ auto lbWordStyle = LineBreakWordStyle::None;
WordBreaker breaker;
breaker.setText(buf, NELEM(buf));
EXPECT_EQ(0, breaker.current());
// end of string
- EXPECT_EQ((ssize_t)NELEM(buf), breaker.followingWithLocale(Locale("en-US"), 0));
+ EXPECT_EQ((ssize_t)NELEM(buf),
+ breaker.followingWithLocale(Locale("en-US"), lbStyle, lbWordStyle, 0));
EXPECT_EQ(0, breaker.wordStart());
EXPECT_EQ((ssize_t)NELEM(buf), breaker.wordEnd());
}
@@ -115,11 +127,13 @@ TEST(WordBreakerTest, zwjEmojiSequences) {
// CAT FACE + zwj + BUST IN SILHOUETTE
UTF16(0x1F431), 0x200D, UTF16(0x1F464),
};
+ auto lbStyle = LineBreakStyle::None;
+ auto lbWordStyle = LineBreakWordStyle::None;
WordBreaker breaker;
breaker.setText(buf, NELEM(buf));
EXPECT_EQ(0, breaker.current());
// after man + zwj + heart + zwj + man
- EXPECT_EQ(7, breaker.followingWithLocale(Locale("en-US"), 0));
+ EXPECT_EQ(7, breaker.followingWithLocale(Locale("en-US"), lbStyle, lbWordStyle, 0));
EXPECT_EQ(0, breaker.wordStart());
EXPECT_EQ(7, breaker.wordEnd());
EXPECT_EQ(17, breaker.next()); // after woman + zwj + heart + zwj + woman
@@ -139,11 +153,13 @@ TEST(WordBreakerTest, emojiWithModifier) {
0x270C, 0xFE0F,
UTF16(0x1F3FF) // victory hand + emoji style + type 6 fitzpatrick modifier
};
+ auto lbStyle = LineBreakStyle::None;
+ auto lbWordStyle = LineBreakWordStyle::None;
WordBreaker breaker;
breaker.setText(buf, NELEM(buf));
EXPECT_EQ(0, breaker.current());
// after boy + type 1-2 fitzpatrick modifier
- EXPECT_EQ(4, breaker.followingWithLocale(Locale("en-US"), 0));
+ EXPECT_EQ(4, breaker.followingWithLocale(Locale("en-US"), lbStyle, lbWordStyle, 0));
EXPECT_EQ(0, breaker.wordStart());
EXPECT_EQ(4, breaker.wordEnd());
EXPECT_EQ((ssize_t)NELEM(buf), breaker.next()); // end
@@ -163,10 +179,12 @@ TEST(WordBreakerTest, unicode10Emoji) {
// WHITE SMILING FACE + VS16 + SLED
0x263A, 0xFE0F, UTF16(0x1F6F7),
};
+ auto lbStyle = LineBreakStyle::None;
+ auto lbWordStyle = LineBreakWordStyle::None;
WordBreaker breaker;
breaker.setText(buf, NELEM(buf));
EXPECT_EQ(0, breaker.current());
- EXPECT_EQ(2, breaker.followingWithLocale(Locale("en"), 0));
+ EXPECT_EQ(2, breaker.followingWithLocale(Locale("en"), lbStyle, lbWordStyle, 0));
EXPECT_EQ(0, breaker.wordStart());
EXPECT_EQ(2, breaker.wordEnd());
@@ -209,12 +227,14 @@ TEST(WordBreakerTest, flagsSequenceSingleFlag) {
uint16_t buf[BUF_SIZE];
size_t size;
ParseUnicode(buf, BUF_SIZE, flags.c_str(), &size, nullptr);
+ auto lbStyle = LineBreakStyle::None;
+ auto lbWordStyle = LineBreakWordStyle::None;
WordBreaker breaker;
breaker.setText(buf, size);
EXPECT_EQ(0, breaker.current());
// end of the first flag
- EXPECT_EQ(kFlagLength, breaker.followingWithLocale(Locale("en-US"), 0));
+ EXPECT_EQ(kFlagLength, breaker.followingWithLocale(Locale("en-US"), lbStyle, lbWordStyle, 0));
EXPECT_EQ(0, breaker.wordStart());
EXPECT_EQ(kFlagLength, breaker.wordEnd());
EXPECT_EQ(static_cast<ssize_t>(size), breaker.next());
@@ -234,12 +254,14 @@ TEST(WordBreakerTest, flagsSequence) {
uint16_t buf[BUF_SIZE];
size_t size;
ParseUnicode(buf, BUF_SIZE, flagSequence.c_str(), &size, nullptr);
+ auto lbStyle = LineBreakStyle::None;
+ auto lbWordStyle = LineBreakWordStyle::None;
WordBreaker breaker;
breaker.setText(buf, size);
EXPECT_EQ(0, breaker.current());
// end of the first flag sequence
- EXPECT_EQ(kFlagLength, breaker.followingWithLocale(Locale("en-US"), 0));
+ EXPECT_EQ(kFlagLength, breaker.followingWithLocale(Locale("en-US"), lbStyle, lbWordStyle, 0));
EXPECT_EQ(0, breaker.wordStart());
EXPECT_EQ(kFlagLength, breaker.wordEnd());
EXPECT_EQ(static_cast<ssize_t>(size), breaker.next());
@@ -250,11 +272,14 @@ TEST(WordBreakerTest, flagsSequence) {
TEST(WordBreakerTest, punct) {
uint16_t buf[] = {0x00A1, 0x00A1, 'h', 'e', 'l', 'l', 'o', ',',
' ', 'w', 'o', 'r', 'l', 'd', '!', '!'};
+ auto lbStyle = LineBreakStyle::None;
+ auto lbWordStyle = LineBreakWordStyle::None;
WordBreaker breaker;
breaker.setText(buf, NELEM(buf));
EXPECT_EQ(0, breaker.current());
- EXPECT_EQ(9, breaker.followingWithLocale(Locale("en-US"), 0)); // after "¡¡hello, "
- EXPECT_EQ(2, breaker.wordStart()); // "hello"
+ EXPECT_EQ(9, breaker.followingWithLocale(Locale("en-US"), lbStyle, lbWordStyle,
+ 0)); // after "¡¡hello, "
+ EXPECT_EQ(2, breaker.wordStart()); // "hello"
EXPECT_EQ(7, breaker.wordEnd());
EXPECT_EQ(0, breaker.breakBadness());
EXPECT_EQ((ssize_t)NELEM(buf), breaker.next()); // end
@@ -266,10 +291,13 @@ TEST(WordBreakerTest, punct) {
TEST(WordBreakerTest, email) {
uint16_t buf[] = {'f', 'o', 'o', '@', 'e', 'x', 'a', 'm', 'p',
'l', 'e', '.', 'c', 'o', 'm', ' ', 'x'};
+ auto lbStyle = LineBreakStyle::None;
+ auto lbWordStyle = LineBreakWordStyle::None;
WordBreaker breaker;
breaker.setText(buf, NELEM(buf));
EXPECT_EQ(0, breaker.current());
- EXPECT_EQ(11, breaker.followingWithLocale(Locale("en-US"), 0)); // after "foo@example"
+ EXPECT_EQ(11, breaker.followingWithLocale(Locale("en-US"), lbStyle, lbWordStyle,
+ 0)); // after "foo@example"
EXPECT_TRUE(breaker.wordStart() >= breaker.wordEnd());
EXPECT_EQ(1, breaker.breakBadness());
EXPECT_EQ(16, breaker.next()); // after ".com "
@@ -284,10 +312,13 @@ TEST(WordBreakerTest, email) {
TEST(WordBreakerTest, mailto) {
uint16_t buf[] = {'m', 'a', 'i', 'l', 't', 'o', ':', 'f', 'o', 'o', '@', 'e',
'x', 'a', 'm', 'p', 'l', 'e', '.', 'c', 'o', 'm', ' ', 'x'};
+ auto lbStyle = LineBreakStyle::None;
+ auto lbWordStyle = LineBreakWordStyle::None;
WordBreaker breaker;
breaker.setText(buf, NELEM(buf));
EXPECT_EQ(0, breaker.current());
- EXPECT_EQ(7, breaker.followingWithLocale(Locale("en-US"), 0)); // after "mailto:"
+ EXPECT_EQ(7, breaker.followingWithLocale(Locale("en-US"), lbStyle, lbWordStyle,
+ 0)); // after "mailto:"
EXPECT_TRUE(breaker.wordStart() >= breaker.wordEnd());
EXPECT_EQ(1, breaker.breakBadness());
EXPECT_EQ(18, breaker.next()); // after "foo@example"
@@ -307,10 +338,13 @@ TEST(WordBreakerTest, mailto) {
TEST(WordBreakerTest, emailNonAscii) {
uint16_t buf[] = {'f', 'o', 'o', '@', 'e', 'x', 'a', 'm',
'p', 'l', 'e', '.', 'c', 'o', 'm', 0x4E00};
+ auto lbStyle = LineBreakStyle::None;
+ auto lbWordStyle = LineBreakWordStyle::None;
WordBreaker breaker;
breaker.setText(buf, NELEM(buf));
EXPECT_EQ(0, breaker.current());
- EXPECT_EQ(11, breaker.followingWithLocale(Locale("en-US"), 0)); // after "foo@example"
+ EXPECT_EQ(11, breaker.followingWithLocale(Locale("en-US"), lbStyle, lbWordStyle,
+ 0)); // after "foo@example"
EXPECT_TRUE(breaker.wordStart() >= breaker.wordEnd());
EXPECT_EQ(1, breaker.breakBadness());
EXPECT_EQ(15, breaker.next()); // after ".com"
@@ -325,10 +359,13 @@ TEST(WordBreakerTest, emailNonAscii) {
TEST(WordBreakerTest, emailCombining) {
uint16_t buf[] = {'f', 'o', 'o', '@', 'e', 'x', 'a', 'm', 'p',
'l', 'e', '.', 'c', 'o', 'm', 0x0303, ' ', 'x'};
+ auto lbStyle = LineBreakStyle::None;
+ auto lbWordStyle = LineBreakWordStyle::None;
WordBreaker breaker;
breaker.setText(buf, NELEM(buf));
EXPECT_EQ(0, breaker.current());
- EXPECT_EQ(11, breaker.followingWithLocale(Locale("en-US"), 0)); // after "foo@example"
+ EXPECT_EQ(11, breaker.followingWithLocale(Locale("en-US"), lbStyle, lbWordStyle,
+ 0)); // after "foo@example"
EXPECT_TRUE(breaker.wordStart() >= breaker.wordEnd());
EXPECT_EQ(1, breaker.breakBadness());
EXPECT_EQ(17, breaker.next()); // after ".com̃ "
@@ -342,10 +379,13 @@ TEST(WordBreakerTest, emailCombining) {
TEST(WordBreakerTest, lonelyAt) {
uint16_t buf[] = {'a', ' ', '@', ' ', 'b'};
+ auto lbStyle = LineBreakStyle::None;
+ auto lbWordStyle = LineBreakWordStyle::None;
WordBreaker breaker;
breaker.setText(buf, NELEM(buf));
EXPECT_EQ(0, breaker.current());
- EXPECT_EQ(2, breaker.followingWithLocale(Locale("en-US"), 0)); // after "a "
+ EXPECT_EQ(2,
+ breaker.followingWithLocale(Locale("en-US"), lbStyle, lbWordStyle, 0)); // after "a "
EXPECT_EQ(0, breaker.wordStart()); // "a"
EXPECT_EQ(1, breaker.wordEnd());
EXPECT_EQ(0, breaker.breakBadness());
@@ -361,10 +401,13 @@ TEST(WordBreakerTest, lonelyAt) {
TEST(WordBreakerTest, url) {
uint16_t buf[] = {'h', 't', 't', 'p', ':', '/', '/', 'e', 'x', 'a',
'm', 'p', 'l', 'e', '.', 'c', 'o', 'm', ' ', 'x'};
+ auto lbStyle = LineBreakStyle::None;
+ auto lbWordStyle = LineBreakWordStyle::None;
WordBreaker breaker;
breaker.setText(buf, NELEM(buf));
EXPECT_EQ(0, breaker.current());
- EXPECT_EQ(5, breaker.followingWithLocale(Locale("en-US"), 0)); // after "http:"
+ EXPECT_EQ(5, breaker.followingWithLocale(Locale("en-US"), lbStyle, lbWordStyle,
+ 0)); // after "http:"
EXPECT_TRUE(breaker.wordStart() >= breaker.wordEnd());
EXPECT_EQ(1, breaker.breakBadness());
EXPECT_EQ(7, breaker.next()); // after "//"
@@ -387,10 +430,13 @@ TEST(WordBreakerTest, urlBreakChars) {
uint16_t buf[] = {'h', 't', 't', 'p', ':', '/', '/', 'a', '.', 'b', '/',
'~', 'c', ',', 'd', '-', 'e', '?', 'f', '=', 'g', '&',
'h', '#', 'i', '%', 'j', '_', 'k', '/', 'l'};
+ auto lbStyle = LineBreakStyle::None;
+ auto lbWordStyle = LineBreakWordStyle::None;
WordBreaker breaker;
breaker.setText(buf, NELEM(buf));
EXPECT_EQ(0, breaker.current());
- EXPECT_EQ(5, breaker.followingWithLocale(Locale("en-US"), 0)); // after "http:"
+ EXPECT_EQ(5, breaker.followingWithLocale(Locale("en-US"), lbStyle, lbWordStyle,
+ 0)); // after "http:"
EXPECT_TRUE(breaker.wordStart() >= breaker.wordEnd());
EXPECT_EQ(1, breaker.breakBadness());
EXPECT_EQ(7, breaker.next()); // after "//"
@@ -445,10 +491,13 @@ TEST(WordBreakerTest, urlBreakChars) {
TEST(WordBreakerTest, urlNoHyphenBreak) {
uint16_t buf[] = {'h', 't', 't', 'p', ':', '/', '/', 'a', '-', '/', 'b'};
+ auto lbStyle = LineBreakStyle::None;
+ auto lbWordStyle = LineBreakWordStyle::None;
WordBreaker breaker;
breaker.setText(buf, NELEM(buf));
EXPECT_EQ(0, breaker.current());
- EXPECT_EQ(5, breaker.followingWithLocale(Locale("en-US"), 0)); // after "http:"
+ EXPECT_EQ(5, breaker.followingWithLocale(Locale("en-US"), lbStyle, lbWordStyle,
+ 0)); // after "http:"
EXPECT_TRUE(breaker.wordStart() >= breaker.wordEnd());
EXPECT_EQ(7, breaker.next()); // after "//"
EXPECT_TRUE(breaker.wordStart() >= breaker.wordEnd());
@@ -460,10 +509,13 @@ TEST(WordBreakerTest, urlNoHyphenBreak) {
TEST(WordBreakerTest, urlEndsWithSlash) {
uint16_t buf[] = {'h', 't', 't', 'p', ':', '/', '/', 'a', '/'};
+ auto lbStyle = LineBreakStyle::None;
+ auto lbWordStyle = LineBreakWordStyle::None;
WordBreaker breaker;
breaker.setText(buf, NELEM(buf));
EXPECT_EQ(0, breaker.current());
- EXPECT_EQ(5, breaker.followingWithLocale(Locale("en-US"), 0)); // after "http:"
+ EXPECT_EQ(5, breaker.followingWithLocale(Locale("en-US"), lbStyle, lbWordStyle,
+ 0)); // after "http:"
EXPECT_TRUE(breaker.wordStart() >= breaker.wordEnd());
EXPECT_EQ(7, breaker.next()); // after "//"
EXPECT_TRUE(breaker.wordStart() >= breaker.wordEnd());
@@ -475,19 +527,25 @@ TEST(WordBreakerTest, urlEndsWithSlash) {
TEST(WordBreakerTest, emailStartsWithSlash) {
uint16_t buf[] = {'/', 'a', '@', 'b'};
+ auto lbStyle = LineBreakStyle::None;
+ auto lbWordStyle = LineBreakWordStyle::None;
WordBreaker breaker;
breaker.setText(buf, NELEM(buf));
EXPECT_EQ(0, breaker.current());
- EXPECT_EQ((ssize_t)NELEM(buf), breaker.followingWithLocale(Locale("en-US"), 0)); // end
+ EXPECT_EQ((ssize_t)NELEM(buf),
+ breaker.followingWithLocale(Locale("en-US"), lbStyle, lbWordStyle, 0)); // end
EXPECT_TRUE(breaker.wordStart() >= breaker.wordEnd());
}
TEST(WordBreakerTest, setLocaleInsideUrl) {
std::vector<uint16_t> buf = utf8ToUtf16("Hello http://abc/d.html World");
+ auto lbStyle = LineBreakStyle::None;
+ auto lbWordStyle = LineBreakWordStyle::None;
WordBreaker breaker;
breaker.setText(buf.data(), buf.size());
EXPECT_EQ(0, breaker.current());
- EXPECT_EQ(6, breaker.followingWithLocale(Locale("en-US"), 0)); // after "Hello "
+ EXPECT_EQ(6, breaker.followingWithLocale(Locale("en-US"), lbStyle, lbWordStyle,
+ 0)); // after "Hello "
EXPECT_EQ(0, breaker.wordStart());
EXPECT_EQ(5, breaker.wordEnd());
@@ -495,14 +553,16 @@ TEST(WordBreakerTest, setLocaleInsideUrl) {
EXPECT_EQ(11, breaker.next()); // after "http:"
// Restart from middle point of the URL. It should return the same previous break point.
- EXPECT_EQ(11, breaker.followingWithLocale(Locale("en-US"), 6)); // after "http:"
+ EXPECT_EQ(11, breaker.followingWithLocale(Locale("en-US"), lbStyle, lbWordStyle,
+ 6)); // after "http:"
EXPECT_TRUE(breaker.wordStart() >= breaker.wordEnd());
EXPECT_EQ(13, breaker.next()); // after "//"
EXPECT_TRUE(breaker.wordStart() >= breaker.wordEnd());
// Restart from middle point of the URL. It should return the same previous break point.
- EXPECT_EQ(13, breaker.followingWithLocale(Locale("en-US"), 12)); // after "//"
+ EXPECT_EQ(13, breaker.followingWithLocale(Locale("en-US"), lbStyle, lbWordStyle,
+ 12)); // after "//"
EXPECT_TRUE(breaker.wordStart() >= breaker.wordEnd());
EXPECT_EQ(16, breaker.next()); // after "abc"
EXPECT_TRUE(breaker.wordStart() >= breaker.wordEnd());
@@ -525,6 +585,8 @@ TEST(WordBreakerTest, spaceAfterSpace) {
};
constexpr uint16_t CHAR_SPACE = 0x0020;
+ auto lbStyle = LineBreakStyle::None;
+ auto lbWordStyle = LineBreakWordStyle::None;
for (uint16_t sp : SPACES) {
char msg[64] = {};
@@ -536,7 +598,8 @@ TEST(WordBreakerTest, spaceAfterSpace) {
breaker.setText(buf.data(), buf.size());
EXPECT_EQ(0, breaker.current());
- EXPECT_EQ(2, breaker.followingWithLocale(Locale("en-US"), 0)); // after "a "
+ EXPECT_EQ(2, breaker.followingWithLocale(Locale("en-US"), lbStyle, lbWordStyle,
+ 0)); // after "a "
EXPECT_EQ(0, breaker.wordStart());
EXPECT_EQ(1, breaker.wordEnd());
@@ -567,21 +630,37 @@ TEST(WordBreakerTest, LineBreakerPool_acquire_without_release) {
const Locale frFR("fr-Latn-FR");
// All following three breakers must be the different instances.
- ICULineBreakerPool::Slot enUSBreaker = pool.acquire(enUS);
- ICULineBreakerPool::Slot enUSBreaker2 = pool.acquire(enUS);
- ICULineBreakerPool::Slot frFRBreaker = pool.acquire(frFR);
+ ICULineBreakerPool::Slot enUSBreaker =
+ pool.acquire(enUS, LineBreakStyle::Loose, LineBreakWordStyle::None);
+ ICULineBreakerPool::Slot enUSBreaker2 =
+ pool.acquire(enUS, LineBreakStyle::Loose, LineBreakWordStyle::None);
+ ICULineBreakerPool::Slot enUSBreaker3 =
+ pool.acquire(enUS, LineBreakStyle::Strict, LineBreakWordStyle::None);
+ ICULineBreakerPool::Slot frFRBreaker =
+ pool.acquire(frFR, LineBreakStyle::None, LineBreakWordStyle::None);
+ ICULineBreakerPool::Slot frFRBreaker2 =
+ pool.acquire(frFR, LineBreakStyle::None, LineBreakWordStyle::Phrase);
EXPECT_NE(nullptr, enUSBreaker.breaker.get());
EXPECT_NE(nullptr, enUSBreaker2.breaker.get());
+ EXPECT_NE(nullptr, enUSBreaker3.breaker.get());
EXPECT_NE(nullptr, frFRBreaker.breaker.get());
+ EXPECT_NE(nullptr, frFRBreaker2.breaker.get());
EXPECT_NE(enUSBreaker.breaker.get(), enUSBreaker2.breaker.get());
+ EXPECT_NE(enUSBreaker.breaker.get(), enUSBreaker3.breaker.get());
EXPECT_NE(enUSBreaker.breaker.get(), frFRBreaker.breaker.get());
EXPECT_NE(enUSBreaker2.breaker.get(), frFRBreaker.breaker.get());
+ EXPECT_NE(enUSBreaker2.breaker.get(), frFRBreaker2.breaker.get());
+ EXPECT_NE(enUSBreaker2.breaker.get(), enUSBreaker3.breaker.get());
EXPECT_EQ(enUSBreaker.localeId, enUSBreaker2.localeId);
+ EXPECT_EQ(enUSBreaker.localeId, enUSBreaker3.localeId);
EXPECT_NE(enUSBreaker.localeId, frFRBreaker.localeId);
+ EXPECT_NE(enUSBreaker.localeId, frFRBreaker2.localeId);
EXPECT_NE(enUSBreaker2.localeId, frFRBreaker.localeId);
+ EXPECT_NE(enUSBreaker2.localeId, frFRBreaker2.localeId);
+ EXPECT_EQ(frFRBreaker.localeId, frFRBreaker2.localeId);
}
TEST(WordBreakerTest, LineBreakerPool_acquire_with_release) {
@@ -591,7 +670,8 @@ TEST(WordBreakerTest, LineBreakerPool_acquire_with_release) {
const Locale frFR("fr-Latn-FR");
// All following three breakers must be the different instances.
- ICULineBreakerPool::Slot enUSBreaker = pool.acquire(enUS);
+ ICULineBreakerPool::Slot enUSBreaker =
+ pool.acquire(enUS, LineBreakStyle::Loose, LineBreakWordStyle::None);
uint64_t enUSBreakerLocaleId = enUSBreaker.localeId;
UBreakIterator* enUSBreakerPtr = enUSBreaker.breaker.get();
@@ -600,14 +680,28 @@ TEST(WordBreakerTest, LineBreakerPool_acquire_with_release) {
EXPECT_EQ(nullptr, enUSBreaker.breaker.get());
// acquire must return a different instance if the locale is different.
- ICULineBreakerPool::Slot frFRBreaker = pool.acquire(frFR);
+ ICULineBreakerPool::Slot frFRBreaker =
+ pool.acquire(frFR, LineBreakStyle::Loose, LineBreakWordStyle::None);
EXPECT_NE(enUSBreakerPtr, frFRBreaker.breaker.get());
EXPECT_NE(enUSBreakerLocaleId, frFRBreaker.localeId);
// acquire must return the same instance as released before if the locale is the same.
- ICULineBreakerPool::Slot enUSBreaker2 = pool.acquire(enUS);
+ ICULineBreakerPool::Slot enUSBreaker2 =
+ pool.acquire(enUS, LineBreakStyle::Loose, LineBreakWordStyle::None);
EXPECT_EQ(enUSBreakerPtr, enUSBreaker2.breaker.get());
EXPECT_EQ(enUSBreakerLocaleId, enUSBreaker2.localeId);
+
+ // acquire must return a different instance if the line break is different.
+ ICULineBreakerPool::Slot frFRBreaker2 =
+ pool.acquire(frFR, LineBreakStyle::Normal, LineBreakWordStyle::None);
+ ICULineBreakerPool::Slot frFRBreaker3 =
+ pool.acquire(frFR, LineBreakStyle::Normal, LineBreakWordStyle::Phrase);
+ EXPECT_NE(frFRBreaker.breaker.get(), frFRBreaker2.breaker.get());
+ EXPECT_NE(frFRBreaker.breaker.get(), frFRBreaker3.breaker.get());
+ EXPECT_NE(frFRBreaker2.breaker.get(), frFRBreaker3.breaker.get());
+ EXPECT_EQ(frFRBreaker.localeId, frFRBreaker2.localeId);
+ EXPECT_EQ(frFRBreaker.localeId, frFRBreaker3.localeId);
+ EXPECT_EQ(frFRBreaker2.localeId, frFRBreaker3.localeId);
}
TEST(WordBreakerTest, LineBreakerPool_exceeds_pool_size) {
@@ -620,7 +714,7 @@ TEST(WordBreakerTest, LineBreakerPool_exceeds_pool_size) {
// Make pool full.
for (size_t i = 0; i < MAX_POOL_SIZE * 2; i++) {
- slots[i] = pool.acquire(enUS);
+ slots[i] = pool.acquire(enUS, LineBreakStyle::None, LineBreakWordStyle::None);
EXPECT_EQ(0U, pool.getPoolSize());
}
diff --git a/tools/mk_hyb_file.py b/tools/mk_hyb_file.py
index a76db93..89949f9 100755
--- a/tools/mk_hyb_file.py
+++ b/tools/mk_hyb_file.py
@@ -542,8 +542,26 @@ def verify_hyb_file(hyb_fn, pat_fn, chr_fn, hyp_fn):
# EXCEPTION for Armenian (hy), we don't really deal with the uppercase form of U+0587
if u'\u0587' in reconstructed_chr:
- reconstructed_chr.remove(u'\u0587')
- reconstructed_chr.append(u'\u0587\u0535\u0552')
+ reconstructed_chr.remove(u'\u0587')
+ reconstructed_chr.append(u'\u0587\u0535\u0552')
+
+ # EXCEPTION for Greek (el), we don't really deal with the uppercase form of
+ # U+03C2, U+03C3, U+0390, U+03B0
+ if u'\u03C2' in reconstructed_chr:
+ reconstructed_chr.remove(u'\u03C2')
+ reconstructed_chr.append(u'\u03C2\u03A3')
+
+ if u'\u03C3' in reconstructed_chr:
+ reconstructed_chr.remove(u'\u03C3')
+ reconstructed_chr.append(u'\u03C3\u03A3')
+
+ if u'\u0390' in reconstructed_chr:
+ reconstructed_chr.remove(u'\u0390')
+ reconstructed_chr.append(u'\u0390\u0390')
+
+ if u'\u03B0' in reconstructed_chr:
+ reconstructed_chr.remove(u'\u03B0')
+ reconstructed_chr.append(u'\u03B0\u03B0')
assert verify_file_sorted(reconstructed_chr, chr_fn), 'alphabet table not verified'