Drop codepoints that are outside the Unicode range - DO NOT MERGE am: 0e441db0f7 -s ours am: 437445d1b7 am: c25ec29dfc
am: 55d1d9cb97
Change-Id: Ib0df8ed1a84a0e313b1147f42030db95db02668b
diff --git a/include/minikin/LineBreaker.h b/include/minikin/LineBreaker.h
index c91c0b3..d7a20e9 100644
--- a/include/minikin/LineBreaker.h
+++ b/include/minikin/LineBreaker.h
@@ -22,11 +22,14 @@
#ifndef MINIKIN_LINE_BREAKER_H
#define MINIKIN_LINE_BREAKER_H
+#include <gtest/gtest_prod.h>
#include "unicode/brkiter.h"
#include "unicode/locid.h"
#include <cmath>
#include <vector>
+#include "minikin/FontCollection.h"
#include "minikin/Hyphenator.h"
+#include "minikin/MinikinFont.h"
#include "minikin/WordBreaker.h"
namespace minikin {
@@ -113,7 +116,7 @@
// That logic could be here but it's better for performance that it's upstream because of
// the cost of constructing and comparing the ICU Locale object.
// Note: caller is responsible for managing lifetime of hyphenator
- void setLocale(const icu::Locale& locale, Hyphenator* hyphenator);
+ void setLocales(const char* locales, const std::vector<Hyphenator*>& hyphenators);
void resize(size_t size) {
mTextBuf.resize(size);
@@ -252,6 +255,8 @@
uint32_t mLastHyphenation; // hyphen edit of last break kept for next line
int mFirstTabIndex;
size_t mSpaceCount;
+
+ FRIEND_TEST(LineBreakerTest, setLocales);
};
} // namespace minikin
diff --git a/libs/minikin/Android.bp b/libs/minikin/Android.bp
index c0fbbea..df8ab9a 100644
--- a/libs/minikin/Android.bp
+++ b/libs/minikin/Android.bp
@@ -82,6 +82,7 @@
header_libs: ["libminikin_headers"],
export_header_lib_headers: ["libminikin_headers"],
export_shared_lib_headers: ["libicuuc"],
+ whole_static_libs: ["libgtest_prod"],
clang: true,
}
diff --git a/libs/minikin/CmapCoverage.cpp b/libs/minikin/CmapCoverage.cpp
index c56d07c..c6318f5 100644
--- a/libs/minikin/CmapCoverage.cpp
+++ b/libs/minikin/CmapCoverage.cpp
@@ -254,6 +254,9 @@
}
if (end > MAX_UNICODE_CODE_POINT) {
// file is inclusive, vector is exclusive
+ if (end == 0xFFFFFFFF) {
+ android_errorWriteLog(0x534e4554, "62134807");
+ }
return addRange(coverage, start, MAX_UNICODE_CODE_POINT + 1);
}
if (!addRange(coverage, start, end + 1)) { // file is inclusive, vector is exclusive
diff --git a/libs/minikin/FontCollection.cpp b/libs/minikin/FontCollection.cpp
index 871d974..9abe84d 100644
--- a/libs/minikin/FontCollection.cpp
+++ b/libs/minikin/FontCollection.cpp
@@ -296,22 +296,39 @@
return vs == 0 ? mFamilies[mFamilyVec[bestFamilyIndex]] : mFamilies[bestFamilyIndex];
}
-const uint32_t NBSP = 0x00A0;
-const uint32_t SOFT_HYPHEN = 0x00AD;
-const uint32_t ZWJ = 0x200C;
-const uint32_t ZWNJ = 0x200D;
-const uint32_t HYPHEN = 0x2010;
-const uint32_t NB_HYPHEN = 0x2011;
-const uint32_t NNBSP = 0x202F;
-const uint32_t FEMALE_SIGN = 0x2640;
-const uint32_t MALE_SIGN = 0x2642;
-const uint32_t STAFF_OF_AESCULAPIUS = 0x2695;
+// Characters where we want to continue using existing font run for (or stick to the next run if
+// they start a string), even if the font does not support them explicitly. These are handled
+// properly by Minikin or HarfBuzz even if the font does not explicitly support them and it's
+// usually meaningless to switch to a different font to display them.
+static bool doesNotNeedFontSupport(uint32_t c) {
+ return c == 0x00AD // SOFT HYPHEN
+ || c == 0x034F // COMBINING GRAPHEME JOINER
+ || c == 0x061C // ARABIC LETTER MARK
+ || (0x200C <= c && c <= 0x200F) // ZERO WIDTH NON-JOINER..RIGHT-TO-LEFT MARK
+ || (0x202A <= c && c <= 0x202E) // LEFT-TO-RIGHT EMBEDDING..RIGHT-TO-LEFT OVERRIDE
+ || (0x2066 <= c && c <= 0x2069) // LEFT-TO-RIGHT ISOLATE..POP DIRECTIONAL ISOLATE
+ || c == 0xFEFF // BYTE ORDER MARK
+ || isVariationSelector(c);
+}
// Characters where we want to continue using existing font run instead of
// recomputing the best match in the fallback list.
static const uint32_t stickyWhitelist[] = {
- '!', ',', '-', '.', ':', ';', '?', NBSP, ZWJ, ZWNJ,
- HYPHEN, NB_HYPHEN, NNBSP, FEMALE_SIGN, MALE_SIGN, STAFF_OF_AESCULAPIUS };
+ '!',
+ ',',
+ '-',
+ '.',
+ ':',
+ ';',
+ '?',
+ 0x00A0, // NBSP
+ 0x2010, // HYPHEN
+ 0x2011, // NB_HYPHEN
+ 0x202F, // NNBSP
+ 0x2640, // FEMALE_SIGN,
+ 0x2642, // MALE_SIGN,
+ 0x2695, // STAFF_OF_AESCULAPIUS
+};
static bool isStickyWhitelisted(uint32_t c) {
for (size_t i = 0; i < sizeof(stickyWhitelist) / sizeof(stickyWhitelist[0]); i++) {
@@ -320,6 +337,10 @@
return false;
}
+static inline bool isCombining(uint32_t c) {
+ return (U_GET_GC_MASK(c) & U_GC_M_MASK) != 0;
+}
+
bool FontCollection::hasVariationSelector(uint32_t baseCodepoint,
uint32_t variationSelector) const {
if (!isVariationSelector(variationSelector)) {
@@ -355,12 +376,14 @@
return false;
}
+constexpr uint32_t REPLACEMENT_CHARACTER = 0xFFFD;
+
void FontCollection::itemize(const uint16_t *string, size_t string_size, FontStyle style,
vector<Run>* result) const {
const uint32_t langListId = style.getLanguageListId();
int variant = style.getVariant();
const FontFamily* lastFamily = nullptr;
- Run* run = NULL;
+ Run* run = nullptr;
if (string_size == 0) {
return;
@@ -373,6 +396,9 @@
size_t nextUtf16Pos = 0;
size_t readLength = 0;
U16_NEXT(string, readLength, string_size, nextCh);
+ if (U_IS_SURROGATE(nextCh)) {
+ nextCh = REPLACEMENT_CHARACTER;
+ }
do {
const uint32_t ch = nextCh;
@@ -380,19 +406,20 @@
nextUtf16Pos = readLength;
if (readLength < string_size) {
U16_NEXT(string, readLength, string_size, nextCh);
+ if (U_IS_SURROGATE(nextCh)) {
+ nextCh = REPLACEMENT_CHARACTER;
+ }
} else {
nextCh = kEndOfString;
}
bool shouldContinueRun = false;
- if (lastFamily != nullptr) {
- if (isStickyWhitelisted(ch)) {
- // Continue using existing font as long as it has coverage and is whitelisted
- shouldContinueRun = lastFamily->getCoverage().get(ch);
- } else if (ch == SOFT_HYPHEN || isVariationSelector(ch)) {
- // Always continue if the character is the soft hyphen or a variation selector.
- shouldContinueRun = true;
- }
+ if (doesNotNeedFontSupport(ch)) {
+ // Always continue if the character is a format character not needed to be in the font.
+ shouldContinueRun = true;
+ } else if (lastFamily != nullptr && (isStickyWhitelisted(ch) || isCombining(ch))) {
+ // Continue using existing font as long as it has coverage and is whitelisted.
+ shouldContinueRun = lastFamily->getCoverage().get(ch);
}
if (!shouldContinueRun) {
@@ -406,24 +433,40 @@
// character to the new run. U+20E3 COMBINING ENCLOSING KEYCAP, used in emoji, is
// handled properly by this since it's a combining mark too.
if (utf16Pos != 0 &&
- ((U_GET_GC_MASK(ch) & U_GC_M_MASK) != 0 ||
- (isEmojiModifier(ch) && isEmojiBase(prevCh))) &&
+ (isCombining(ch) || (isEmojiModifier(ch) && isEmojiBase(prevCh))) &&
family != nullptr && family->getCoverage().get(prevCh)) {
const size_t prevChLength = U16_LENGTH(prevCh);
- run->end -= prevChLength;
- if (run->start == run->end) {
- result->pop_back();
+ if (run != nullptr) {
+ run->end -= prevChLength;
+ if (run->start == run->end) {
+ result->pop_back();
+ }
}
start -= prevChLength;
}
+ if (lastFamily == nullptr) {
+ // This is the first family ever assigned. We are either seeing the very first
+ // character (which means start would already be zero), or we have only seen
+ // characters that don't need any font support (which means we need to adjust
+ // start to be 0 to include those characters).
+ start = 0;
+ }
result->push_back({family->getClosestMatch(style), static_cast<int>(start), 0});
run = &result->back();
lastFamily = family.get();
}
}
prevCh = ch;
- run->end = nextUtf16Pos; // exclusive
+ if (run != nullptr) {
+ run->end = nextUtf16Pos; // exclusive
+ }
} while (nextCh != kEndOfString);
+
+ if (lastFamily == nullptr) {
+ // No character needed any font support, so it doesn't really matter which font they end up
+ // getting displayed in. We put the whole string in one run, using the first font.
+ result->push_back({mFamilies[0]->getClosestMatch(style), 0, static_cast<int>(string_size)});
+ }
}
FakedFont FontCollection::baseFontFaked(FontStyle style) {
diff --git a/libs/minikin/Layout.cpp b/libs/minikin/Layout.cpp
index 4007007..cbfb430 100644
--- a/libs/minikin/Layout.cpp
+++ b/libs/minikin/Layout.cpp
@@ -326,26 +326,12 @@
}
static hb_codepoint_t decodeUtf16(const uint16_t* chars, size_t len, ssize_t* iter) {
- const uint16_t v = chars[(*iter)++];
- // test whether v in (0xd800..0xdfff), lead or trail surrogate
- if ((v & 0xf800) == 0xd800) {
- // test whether v in (0xd800..0xdbff), lead surrogate
- if (size_t(*iter) < len && (v & 0xfc00) == 0xd800) {
- const uint16_t v2 = chars[(*iter)++];
- // test whether v2 in (0xdc00..0xdfff), trail surrogate
- if ((v2 & 0xfc00) == 0xdc00) {
- // (0xd800 0xdc00) in utf-16 maps to 0x10000 in ucs-32
- const hb_codepoint_t delta = (0xd800 << 10) + 0xdc00 - 0x10000;
- return (((hb_codepoint_t)v) << 10) + v2 - delta;
- }
- (*iter) -= 1;
- return 0xFFFDu;
- } else {
- return 0xFFFDu;
- }
- } else {
- return v;
+ UChar32 result;
+ U16_NEXT(chars, *iter, (ssize_t) len, result);
+ if (U_IS_SURROGATE(result)) { // isolated surrogate
+ result = 0xFFFDu; // U+FFFD REPLACEMENT CHARACTER
}
+ return (hb_codepoint_t) result;
}
static hb_script_t getScriptRun(const uint16_t* chars, size_t len, ssize_t* iter) {
@@ -546,10 +532,15 @@
if (!U_SUCCESS(status) || rc < 0) {
ALOGW("error counting bidi runs, status = %d", status);
}
- if (!U_SUCCESS(status) || rc <= 1) {
+ if (!U_SUCCESS(status) || rc <= 0) {
mIsRtl = (paraDir == kBidi_RTL);
return;
}
+ if (rc == 1) {
+ const UBiDiDirection runDir = ubidi_getVisualRun(mBidi, 0, nullptr, nullptr);
+ mIsRtl = (runDir == UBIDI_RTL);
+ return;
+ }
mRunCount = rc;
}
diff --git a/libs/minikin/LineBreaker.cpp b/libs/minikin/LineBreaker.cpp
index d247644..bccc299 100644
--- a/libs/minikin/LineBreaker.cpp
+++ b/libs/minikin/LineBreaker.cpp
@@ -61,10 +61,38 @@
// Maximum amount that spaces can shrink, in justified text.
const float SHRINKABILITY = 1.0 / 3.0;
-void LineBreaker::setLocale(const icu::Locale& locale, Hyphenator* hyphenator) {
- mWordBreaker.setLocale(locale);
- mLocale = locale;
- mHyphenator = hyphenator;
+void LineBreaker::setLocales(const char* locales, const std::vector<Hyphenator*>& hyphenators) {
+ bool goodLocaleFound = false;
+ const ssize_t numLocales = hyphenators.size();
+ // For now, we ignore all locales except the first valid one.
+ // TODO: Support selecting the locale based on the script of the text.
+ const char* localeStart = locales;
+ for (ssize_t i = 0; i < numLocales - 1; i++) { // Loop over all locales, except the last one.
+ const char* localeEnd = strchr(localeStart, ',');
+ const size_t localeNameLength = localeEnd - localeStart;
+ char localeName[localeNameLength + 1];
+ strncpy(localeName, localeStart, localeNameLength);
+ localeName[localeNameLength] = '\0';
+ mLocale = icu::Locale::createFromName(localeName);
+ goodLocaleFound = !mLocale.isBogus();
+ if (goodLocaleFound) {
+ mHyphenator = hyphenators[i];
+ break;
+ } else {
+ localeStart = localeEnd + 1;
+ }
+ }
+ if (!goodLocaleFound) { // Try the last locale.
+ mLocale = icu::Locale::createFromName(localeStart);
+ if (mLocale.isBogus()) {
+ // No good locale.
+ mLocale = icu::Locale::getRoot();
+ mHyphenator = nullptr;
+ } else {
+ mHyphenator = numLocales == 0 ? nullptr : hyphenators[numLocales - 1];
+ }
+ }
+ mWordBreaker.setLocale(mLocale);
}
void LineBreaker::setText() {
diff --git a/tests/Android.bp b/tests/Android.bp
index ddc5012..887f057 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -1,8 +1,10 @@
filegroup {
name: "minikin-test-data",
srcs: [
+ "data/Arabic.ttf",
"data/Bold.ttf",
"data/BoldItalic.ttf",
+ "data/Cherokee.ttf",
"data/ColorEmojiFont.ttf",
"data/ColorTextMixedEmojiFont.ttf",
"data/Emoji.ttf",
diff --git a/tests/data/Arabic.ttf b/tests/data/Arabic.ttf
new file mode 100644
index 0000000..faa1f3d
--- /dev/null
+++ b/tests/data/Arabic.ttf
Binary files differ
diff --git a/tests/data/Arabic.ttx b/tests/data/Arabic.ttx
new file mode 100644
index 0000000..778af33
--- /dev/null
+++ b/tests/data/Arabic.ttx
@@ -0,0 +1,196 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2015 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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.9">
+
+ <GlyphOrder>
+ <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="uni061C"/>
+ <GlyphID id="2" name="uni200D"/>
+ </GlyphOrder>
+
+ <head>
+ <!-- Most of this table will be recalculated by the compiler -->
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x27902029"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Wed Sep 9 08:01:17 2015"/>
+ <modified value="Mon Jun 12 22:51:58 2017"/>
+ <xMin value="0"/>
+ <yMin value="0"/>
+ <xMax value="0"/>
+ <yMax value="0"/>
+ <macStyle value="00000000 00000000"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <indexToLocFormat value="0"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="0x00010000"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <advanceWidthMax value="500"/>
+ <minLeftSideBearing value="0"/>
+ <minRightSideBearing value="0"/>
+ <xMaxExtent value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ <numberOfHMetrics value="1"/>
+ </hhea>
+
+ <maxp>
+ <!-- Most of this table will be recalculated by the compiler -->
+ <tableVersion value="0x10000"/>
+ <numGlyphs value="3"/>
+ <maxPoints value="0"/>
+ <maxContours value="0"/>
+ <maxCompositePoints value="0"/>
+ <maxCompositeContours value="0"/>
+ <maxZones value="2"/>
+ <maxTwilightPoints value="12"/>
+ <maxStorage value="28"/>
+ <maxFunctionDefs value="119"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="61"/>
+ <maxSizeOfInstructions value="2967"/>
+ <maxComponentElements value="0"/>
+ <maxComponentDepth value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="400"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 01000000"/>
+ <usFirstCharIndex value="1564"/>
+ <usLastCharIndex value="8205"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="uni061C" width="0" lsb="0"/>
+ <mtx name="uni200D" width="0" lsb="0"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_4 platformID="3" platEncID="10" language="0">
+ <map code="0x61c" name="uni061C"/><!-- ARABIC LETTER MARK -->
+ <map code="0x200d" name="uni200D"/><!-- ZERO WIDTH JOINER -->
+ </cmap_format_4>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+
+ <!-- The xMin, yMin, xMax and yMax values
+ will be recalculated by the compiler. -->
+
+ <TTGlyph name=".notdef"/><!-- contains no outline data -->
+
+ <TTGlyph name="uni061C"/><!-- contains no outline data -->
+
+ <TTGlyph name="uni200D"/><!-- contains no outline data -->
+
+ </glyf>
+
+ <name>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ ArabicFont Test
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ ArabicFont Test
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ ArabicFontTest-Regular
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/tests/data/Cherokee.ttf b/tests/data/Cherokee.ttf
new file mode 100644
index 0000000..f66252e
--- /dev/null
+++ b/tests/data/Cherokee.ttf
Binary files differ
diff --git a/tests/data/Cherokee.ttx b/tests/data/Cherokee.ttx
new file mode 100644
index 0000000..71d80f0
--- /dev/null
+++ b/tests/data/Cherokee.ttx
@@ -0,0 +1,196 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2015 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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.9">
+
+ <GlyphOrder>
+ <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="uni0301"/>
+ <GlyphID id="2" name="uni13A0"/>
+ </GlyphOrder>
+
+ <head>
+ <!-- Most of this table will be recalculated by the compiler -->
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x27902029"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Wed Sep 9 08:01:17 2015"/>
+ <modified value="Mon Jun 12 22:51:58 2017"/>
+ <xMin value="0"/>
+ <yMin value="0"/>
+ <xMax value="0"/>
+ <yMax value="0"/>
+ <macStyle value="00000000 00000000"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <indexToLocFormat value="0"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="0x00010000"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <advanceWidthMax value="500"/>
+ <minLeftSideBearing value="0"/>
+ <minRightSideBearing value="0"/>
+ <xMaxExtent value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ <numberOfHMetrics value="1"/>
+ </hhea>
+
+ <maxp>
+ <!-- Most of this table will be recalculated by the compiler -->
+ <tableVersion value="0x10000"/>
+ <numGlyphs value="3"/>
+ <maxPoints value="0"/>
+ <maxContours value="0"/>
+ <maxCompositePoints value="0"/>
+ <maxCompositeContours value="0"/>
+ <maxZones value="2"/>
+ <maxTwilightPoints value="12"/>
+ <maxStorage value="28"/>
+ <maxFunctionDefs value="119"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="61"/>
+ <maxSizeOfInstructions value="2967"/>
+ <maxComponentElements value="0"/>
+ <maxComponentDepth value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="400"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 01000000"/>
+ <usFirstCharIndex value="1564"/>
+ <usLastCharIndex value="8205"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="0"/>
+ <mtx name="uni0301" width="0" lsb="0"/>
+ <mtx name="uni13A0" width="0" lsb="0"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_4 platformID="3" platEncID="10" language="0">
+ <map code="0x0301" name="uni0301"/><!-- COMBINING ACUTE ACCENT -->
+ <map code="0x13A0" name="uni13A0"/><!-- CHEROKEE LETTER A -->
+ </cmap_format_4>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+
+ <!-- The xMin, yMin, xMax and yMax values
+ will be recalculated by the compiler. -->
+
+ <TTGlyph name=".notdef"/><!-- contains no outline data -->
+
+ <TTGlyph name="uni0301"/><!-- contains no outline data -->
+
+ <TTGlyph name="uni13A0"/><!-- contains no outline data -->
+
+ </glyf>
+
+ <name>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ CherokeeFont Test
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ CherokeeFont Test
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ CherokeeFontTest-Regular
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/tests/data/Emoji.ttf b/tests/data/Emoji.ttf
index a3413b3..d0ae55d 100644
--- a/tests/data/Emoji.ttf
+++ b/tests/data/Emoji.ttf
Binary files differ
diff --git a/tests/data/Emoji.ttx b/tests/data/Emoji.ttx
index 3318c59..2e9b52d 100644
--- a/tests/data/Emoji.ttx
+++ b/tests/data/Emoji.ttx
@@ -13,7 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.9">
<GlyphOrder>
<!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
@@ -24,6 +24,7 @@
<GlyphID id="4" name="0"/>
<GlyphID id="5" name="U+1F470"/>
<GlyphID id="6" name="U+203C_VS16"/>
+ <GlyphID id="7" name="U+200D"/>
</GlyphOrder>
<head>
@@ -36,10 +37,10 @@
<unitsPerEm value="1000"/>
<created value="Wed Sep 9 08:01:17 2015"/>
<modified value="Wed Sep 9 08:48:07 2015"/>
- <xMin value="30"/>
- <yMin value="-200"/>
- <xMax value="629"/>
- <yMax value="800"/>
+ <xMin value="0"/>
+ <yMin value="0"/>
+ <xMax value="0"/>
+ <yMax value="0"/>
<macStyle value="00000000 00000000"/>
<lowestRecPPEM value="7"/>
<fontDirectionHint value="2"/>
@@ -48,14 +49,14 @@
</head>
<hhea>
- <tableVersion value="1.0"/>
+ <tableVersion value="0x00010000"/>
<ascent value="1000"/>
<descent value="-200"/>
<lineGap value="0"/>
- <advanceWidthMax value="659"/>
+ <advanceWidthMax value="500"/>
<minLeftSideBearing value="0"/>
- <minRightSideBearing value="30"/>
- <xMaxExtent value="629"/>
+ <minRightSideBearing value="0"/>
+ <xMaxExtent value="0"/>
<caretSlopeRise value="1"/>
<caretSlopeRun value="0"/>
<caretOffset value="0"/>
@@ -64,15 +65,15 @@
<reserved2 value="0"/>
<reserved3 value="0"/>
<metricDataFormat value="0"/>
- <numberOfHMetrics value="18"/>
+ <numberOfHMetrics value="1"/>
</hhea>
<maxp>
<!-- Most of this table will be recalculated by the compiler -->
<tableVersion value="0x10000"/>
- <numGlyphs value="54"/>
- <maxPoints value="73"/>
- <maxContours value="10"/>
+ <numGlyphs value="8"/>
+ <maxPoints value="0"/>
+ <maxContours value="0"/>
<maxCompositePoints value="0"/>
<maxCompositeContours value="0"/>
<maxZones value="2"/>
@@ -123,8 +124,8 @@
<ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
<achVendID value="UKWN"/>
<fsSelection value="00000000 01000000"/>
- <usFirstCharIndex value="32"/>
- <usLastCharIndex value="122"/>
+ <usFirstCharIndex value="48"/>
+ <usLastCharIndex value="65535"/>
<sTypoAscender value="800"/>
<sTypoDescender value="-200"/>
<sTypoLineGap value="200"/>
@@ -147,15 +148,17 @@
<mtx name="0" width="500" lsb="93"/>
<mtx name="U+1F470" width="500" lsb="93"/>
<mtx name="U+203C_VS16" width="500" lsb="93"/>
+ <mtx name="U+200D" width="500" lsb="93"/>
</hmtx>
<cmap>
<tableVersion version="0"/>
- <cmap_format_12 format="12" reserved="0" length="3" nGroups="1" platformID="3" platEncID="1" language="0">
- <map code="0x1F469" name="U+1F469" />
- <map code="0x1F467" name="U+1F467" />
- <map code="0x20E3" name="U+20E3" />
+ <cmap_format_12 format="12" reserved="0" length="3" nGroups="6" platformID="3" platEncID="1" language="0">
<map code="0x0030" name="0" />
+ <map code="0x200D" name="U+200D" />
+ <map code="0x20E3" name="U+20E3" />
+ <map code="0x1F467" name="U+1F467" />
+ <map code="0x1F469" name="U+1F469" />
<map code="0x1F470" name="U+1F470" />
</cmap_format_12>
<cmap_format_14 format="14" platformID="0" platEncID="5" length="30" numVarSelectorRecords="1">
@@ -172,43 +175,24 @@
<!-- The xMin, yMin, xMax and yMax values
will be recalculated by the compiler. -->
- <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0">
- <contour></contour><instructions><assembly></assembly></instructions>
- </TTGlyph>
+ <TTGlyph name=".notdef"/><!-- contains no outline data -->
- <TTGlyph name="U+1F469" xMin="0" yMin="0" xMax="0" yMax="0">
- <contour></contour><instructions><assembly></assembly></instructions>
- </TTGlyph>
- <TTGlyph name="U+1F467" xMin="0" yMin="0" xMax="0" yMax="0">
- <contour></contour><instructions><assembly></assembly></instructions>
- </TTGlyph>
- <TTGlyph name="U+20E3" xMin="0" yMin="0" xMax="0" yMax="0">
- <contour></contour><instructions><assembly></assembly></instructions>
- </TTGlyph>
- <TTGlyph name="0" xMin="0" yMin="0" xMax="0" yMax="0">
- <contour></contour><instructions><assembly></assembly></instructions>
- </TTGlyph>
- <TTGlyph name="U+1F470" xMin="0" yMin="0" xMax="0" yMax="0">
- <contour></contour><instructions><assembly></assembly></instructions>
- </TTGlyph>
- <TTGlyph name="U+203C_VS16" xMin="0" yMin="0" xMax="0" yMax="0">
- <contour></contour><instructions><assembly></assembly></instructions>
- </TTGlyph>
+ <TTGlyph name="U+1F469"/><!-- contains no outline data -->
+
+ <TTGlyph name="U+1F467"/><!-- contains no outline data -->
+
+ <TTGlyph name="U+20E3"/><!-- contains no outline data -->
+
+ <TTGlyph name="0"/><!-- contains no outline data -->
+
+ <TTGlyph name="U+1F470"/><!-- contains no outline data -->
+
+ <TTGlyph name="U+203C_VS16"/><!-- contains no outline data -->
+
+ <TTGlyph name="U+200D"/><!-- contains no outline data -->
</glyf>
<name>
- <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
- EmojiFont Test
- </namerecord>
- <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
- Regular
- </namerecord>
- <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
- EmojiFont Test
- </namerecord>
- <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
- EmojiFontTest-Regular
- </namerecord>
<namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
EmojiFont Test
</namerecord>
diff --git a/tests/data/itemize.xml b/tests/data/itemize.xml
index 32a5ab0..722f923 100644
--- a/tests/data/itemize.xml
+++ b/tests/data/itemize.xml
@@ -21,6 +21,12 @@
<font weight="700" style="italic">BoldItalic.ttf</font>
</family>
+ <family lang="und-Arab">
+ <font weight="400" style="normal">Arabic.ttf</font>
+ </family>
+ <family lang="und-Cher">
+ <font weight="400" style="normal">Cherokee.ttf</font>
+ </family>
<family lang="zh-Hans">
<font weight="400" style="normal">ZhHans.ttf</font>
</family>
diff --git a/tests/perftests/how_to_run.txt b/tests/perftests/how_to_run.txt
index f55a8ac..e7d268c 100644
--- a/tests/perftests/how_to_run.txt
+++ b/tests/perftests/how_to_run.txt
@@ -1,3 +1,3 @@
-mmm -j8 frameworks/minikin/tests/perftests &&
+mmm -j frameworks/minikin/tests/perftests &&
adb sync data &&
adb shell /data/benchmarktest/minikin_perftests/minikin_perftests
diff --git a/tests/stresstest/how_to_run.txt b/tests/stresstest/how_to_run.txt
index ba4dbdf..853d363 100644
--- a/tests/stresstest/how_to_run.txt
+++ b/tests/stresstest/how_to_run.txt
@@ -1,3 +1,3 @@
-mmm -j8 frameworks/minikin/tests/stresstest &&
+mmm -j frameworks/minikin/tests/stresstest &&
adb sync data &&
adb shell /data/nativetest/minikin_tests/minikin_stress_tests
diff --git a/tests/unittest/Android.bp b/tests/unittest/Android.bp
index 2353d4c..348d186 100644
--- a/tests/unittest/Android.bp
+++ b/tests/unittest/Android.bp
@@ -51,6 +51,7 @@
"GraphemeBreakTests.cpp",
"LayoutTest.cpp",
"LayoutUtilsTest.cpp",
+ "LineBreakerTest.cpp",
"MeasurementTests.cpp",
"SparseBitSetTest.cpp",
"UnicodeUtilsTest.cpp",
diff --git a/tests/unittest/FontCollectionItemizeTest.cpp b/tests/unittest/FontCollectionItemizeTest.cpp
index 78bfa3b..a031908 100644
--- a/tests/unittest/FontCollectionItemizeTest.cpp
+++ b/tests/unittest/FontCollectionItemizeTest.cpp
@@ -30,6 +30,7 @@
namespace minikin {
const char kItemizeFontXml[] = kTestFontDir "itemize.xml";
+const char kCherokeeFont[] = kTestFontDir "Cherokee.ttf";
const char kEmojiFont[] = kTestFontDir "Emoji.ttf";
const char kJAFont[] = kTestFontDir "Ja.ttf";
const char kKOFont[] = kTestFontDir "Ko.ttf";
@@ -135,7 +136,7 @@
EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeBold());
EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeItalic());
- // U+0301(COMBINING ACUTE ACCENT) must be in the same run with preceding
+ // U+0301 (COMBINING ACUTE ACCENT) must be in the same run with preceding
// chars if the font supports it.
itemize(collection, "'a' U+0301", kRegularStyle, &runs);
ASSERT_EQ(1U, runs.size());
@@ -146,6 +147,51 @@
EXPECT_FALSE(runs[0].fakedFont.fakery.isFakeItalic());
}
+TEST_F(FontCollectionItemizeTest, itemize_combining) {
+ // The regular font and the Cherokee font both support U+0301 (COMBINING ACUTE ACCENT). Since
+ // it's a combining mark, it should come from whatever font the base character comes from.
+ std::shared_ptr<FontCollection> collection(getFontCollection(kTestFontDir, kItemizeFontXml));
+ std::vector<FontCollection::Run> runs;
+
+ const FontStyle kRegularStyle = FontStyle();
+
+ itemize(collection, "'a' U+0301", kRegularStyle, &runs);
+ ASSERT_EQ(1U, runs.size());
+ EXPECT_EQ(0, runs[0].start);
+ EXPECT_EQ(2, runs[0].end);
+ EXPECT_EQ(kLatinFont, getFontPath(runs[0]));
+
+ // CHEROKEE LETTER A, COMBINING ACUTE ACCENT
+ itemize(collection, "U+13A0 U+0301", kRegularStyle, &runs);
+ ASSERT_EQ(1U, runs.size());
+ EXPECT_EQ(0, runs[0].start);
+ EXPECT_EQ(2, runs[0].end);
+ EXPECT_EQ(kCherokeeFont, getFontPath(runs[0]));
+
+ // CHEROKEE LETTER A, COMBINING ACUTE ACCENT, COMBINING ACUTE ACCENT
+ itemize(collection, "U+13A0 U+0301 U+0301", kRegularStyle, &runs);
+ ASSERT_EQ(1U, runs.size());
+ EXPECT_EQ(0, runs[0].start);
+ EXPECT_EQ(3, runs[0].end);
+ EXPECT_EQ(kCherokeeFont, getFontPath(runs[0]));
+
+ itemize(collection, "U+0301", kRegularStyle, &runs);
+ ASSERT_EQ(1U, runs.size());
+ EXPECT_EQ(0, runs[0].start);
+ EXPECT_EQ(1, runs[0].end);
+ EXPECT_EQ(kLatinFont, getFontPath(runs[0]));
+
+ // COMBINING ACUTE ACCENT, CHEROKEE LETTER A, COMBINING ACUTE ACCENT
+ itemize(collection, "U+0301 U+13A0 U+0301", kRegularStyle, &runs);
+ ASSERT_EQ(2U, runs.size());
+ EXPECT_EQ(0, runs[0].start);
+ EXPECT_EQ(1, runs[0].end);
+ EXPECT_EQ(kLatinFont, getFontPath(runs[0]));
+ EXPECT_EQ(1, runs[1].start);
+ EXPECT_EQ(3, runs[1].end);
+ EXPECT_EQ(kCherokeeFont, getFontPath(runs[1]));
+}
+
TEST_F(FontCollectionItemizeTest, itemize_emoji) {
std::shared_ptr<FontCollection> collection(getFontCollection(kTestFontDir, kItemizeFontXml));
std::vector<FontCollection::Run> runs;
@@ -691,6 +737,73 @@
EXPECT_EQ(kVSTestFont, getFontPath(runs[0]));
}
+TEST_F(FontCollectionItemizeTest, itemize_format_chars) {
+ std::shared_ptr<FontCollection> collection(getFontCollection(kTestFontDir, kItemizeFontXml));
+ std::vector<FontCollection::Run> runs;
+
+ const FontStyle kDefaultFontStyle;
+
+ itemize(collection, "'a' U+061C 'b'", kDefaultFontStyle, &runs);
+ ASSERT_EQ(1U, runs.size());
+ EXPECT_EQ(0, runs[0].start);
+ EXPECT_EQ(3, runs[0].end);
+ EXPECT_EQ(kLatinFont, getFontPath(runs[0]));
+
+ itemize(collection, "'a' U+200D 'b'", kDefaultFontStyle, &runs);
+ ASSERT_EQ(1U, runs.size());
+ EXPECT_EQ(0, runs[0].start);
+ EXPECT_EQ(3, runs[0].end);
+ EXPECT_EQ(kLatinFont, getFontPath(runs[0]));
+
+ itemize(collection, "U+3042 U+061C U+3042", kDefaultFontStyle, &runs);
+ ASSERT_EQ(1U, runs.size());
+ EXPECT_EQ(0, runs[0].start);
+ EXPECT_EQ(3, runs[0].end);
+ EXPECT_EQ(kJAFont, getFontPath(runs[0]));
+
+ itemize(collection, "U+061C 'b'", kDefaultFontStyle, &runs);
+ ASSERT_EQ(1U, runs.size());
+ EXPECT_EQ(0, runs[0].start);
+ EXPECT_EQ(2, runs[0].end);
+ EXPECT_EQ(kLatinFont, getFontPath(runs[0]));
+
+ itemize(collection, "U+061C U+3042", kDefaultFontStyle, &runs);
+ ASSERT_EQ(1U, runs.size());
+ EXPECT_EQ(0, runs[0].start);
+ EXPECT_EQ(2, runs[0].end);
+ EXPECT_EQ(kJAFont, getFontPath(runs[0]));
+
+ itemize(collection, "U+061C", kDefaultFontStyle, &runs);
+ ASSERT_EQ(1U, runs.size());
+ EXPECT_EQ(0, runs[0].start);
+ EXPECT_EQ(1, runs[0].end);
+ EXPECT_EQ(kLatinFont, getFontPath(runs[0]));
+
+ itemize(collection, "U+061C U+061C U+061C", kDefaultFontStyle, &runs);
+ ASSERT_EQ(1U, runs.size());
+ EXPECT_EQ(0, runs[0].start);
+ EXPECT_EQ(3, runs[0].end);
+ EXPECT_EQ(kLatinFont, getFontPath(runs[0]));
+
+ itemize(collection, "U+200D U+20E3", kDefaultFontStyle, &runs);
+ ASSERT_EQ(1U, runs.size());
+ EXPECT_EQ(0, runs[0].start);
+ EXPECT_EQ(2, runs[0].end);
+ EXPECT_EQ(kEmojiFont, getFontPath(runs[0]));
+
+ itemize(collection, "U+200D", kDefaultFontStyle, &runs);
+ ASSERT_EQ(1U, runs.size());
+ EXPECT_EQ(0, runs[0].start);
+ EXPECT_EQ(1, runs[0].end);
+ EXPECT_EQ(kLatinFont, getFontPath(runs[0]));
+
+ itemize(collection, "U+20E3", kDefaultFontStyle, &runs);
+ ASSERT_EQ(1U, runs.size());
+ EXPECT_EQ(0, runs[0].start);
+ EXPECT_EQ(1, runs[0].end);
+ EXPECT_EQ(kEmojiFont, getFontPath(runs[0]));
+}
+
TEST_F(FontCollectionItemizeTest, itemize_LanguageScore) {
struct TestCase {
std::string userPreferredLanguages;
diff --git a/tests/unittest/LayoutTest.cpp b/tests/unittest/LayoutTest.cpp
index 1770d3a..f89ac9e 100644
--- a/tests/unittest/LayoutTest.cpp
+++ b/tests/unittest/LayoutTest.cpp
@@ -347,18 +347,19 @@
}
}
+// Test that a forced-RTL layout correctly mirros a forced-LTR layout.
TEST_F(LayoutTest, doLayoutTest_rtlTest) {
MinikinPaint paint;
std::vector<uint16_t> text = parseUnicodeString("'a' 'b' U+3042 U+3043 'c' 'd'");
Layout ltrLayout;
- ltrLayout.doLayout(text.data(), 0, text.size(), text.size(), kBidi_LTR, FontStyle(), paint,
- mCollection);
+ ltrLayout.doLayout(text.data(), 0, text.size(), text.size(), kBidi_Force_LTR, FontStyle(),
+ paint, mCollection);
Layout rtlLayout;
- rtlLayout.doLayout(text.data(), 0, text.size(), text.size(), kBidi_RTL, FontStyle(), paint,
- mCollection);
+ rtlLayout.doLayout(text.data(), 0, text.size(), text.size(), kBidi_Force_RTL, FontStyle(),
+ paint, mCollection);
ASSERT_EQ(ltrLayout.nGlyphs(), rtlLayout.nGlyphs());
ASSERT_EQ(6u, ltrLayout.nGlyphs());
@@ -370,6 +371,38 @@
}
}
+// Test that single-run RTL layouts of LTR-only text is laid out identical to an LTR layout.
+TEST_F(LayoutTest, singleRunBidiTest) {
+ MinikinPaint paint;
+
+ std::vector<uint16_t> text = parseUnicodeString("'1' '2' '3'");
+
+ Layout ltrLayout;
+ ltrLayout.doLayout(text.data(), 0, text.size(), text.size(), kBidi_LTR, FontStyle(),
+ paint, mCollection);
+
+ Layout rtlLayout;
+ rtlLayout.doLayout(text.data(), 0, text.size(), text.size(), kBidi_RTL, FontStyle(),
+ paint, mCollection);
+
+ Layout defaultRtlLayout;
+ defaultRtlLayout.doLayout(text.data(), 0, text.size(), text.size(), kBidi_Default_RTL,
+ FontStyle(), paint, mCollection);
+
+ const size_t nGlyphs = ltrLayout.nGlyphs();
+ ASSERT_EQ(3u, nGlyphs);
+
+ ASSERT_EQ(nGlyphs, rtlLayout.nGlyphs());
+ ASSERT_EQ(nGlyphs, defaultRtlLayout.nGlyphs());
+
+ for (size_t i = 0; i < nGlyphs; ++i) {
+ EXPECT_EQ(ltrLayout.getFont(i), rtlLayout.getFont(i));
+ EXPECT_EQ(ltrLayout.getGlyphId(i), rtlLayout.getGlyphId(i));
+ EXPECT_EQ(ltrLayout.getFont(i), defaultRtlLayout.getFont(i));
+ EXPECT_EQ(ltrLayout.getGlyphId(i), defaultRtlLayout.getGlyphId(i));
+ }
+}
+
TEST_F(LayoutTest, hyphenationTest) {
Layout layout;
std::vector<uint16_t> text;
diff --git a/tests/unittest/LineBreakerTest.cpp b/tests/unittest/LineBreakerTest.cpp
new file mode 100644
index 0000000..4b18c8c
--- /dev/null
+++ b/tests/unittest/LineBreakerTest.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2017 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 "ICUTestBase.h"
+#include <minikin/LineBreaker.h>
+#include <unicode/locid.h>
+
+namespace minikin {
+
+typedef ICUTestBase LineBreakerTest;
+
+TEST_F(LineBreakerTest, setLocales) {
+ {
+ LineBreaker lineBreaker;
+ Hyphenator hyphenator;
+ std::vector<Hyphenator*> hyphenators;
+ hyphenators.push_back(&hyphenator);
+ lineBreaker.setLocales("en-US", hyphenators);
+ EXPECT_EQ(icu::Locale::getUS(), lineBreaker.mLocale);
+ EXPECT_EQ(&hyphenator, lineBreaker.mHyphenator);
+ }
+ {
+ LineBreaker lineBreaker;
+ Hyphenator hyphenator1, hyphenator2;
+ std::vector<Hyphenator*> hyphenators;
+ hyphenators.push_back(&hyphenator1);
+ hyphenators.push_back(&hyphenator2);
+ lineBreaker.setLocales("fr-FR,en-US", hyphenators);
+ EXPECT_EQ(icu::Locale::getFrance(), lineBreaker.mLocale);
+ EXPECT_EQ(&hyphenator1, lineBreaker.mHyphenator);
+ }
+ {
+ LineBreaker lineBreaker;
+ std::vector<Hyphenator*> hyphenators;
+ lineBreaker.setLocales("", hyphenators);
+ EXPECT_EQ(icu::Locale::getRoot(), lineBreaker.mLocale);
+ EXPECT_EQ(nullptr, lineBreaker.mHyphenator);
+ }
+ {
+ LineBreaker lineBreaker;
+ std::vector<Hyphenator*> hyphenators;
+ Hyphenator hyphenator;
+ hyphenators.push_back(&hyphenator);
+ lineBreaker.setLocales("THISISABOGUSLANGUAGE", hyphenators);
+ EXPECT_EQ(icu::Locale::getRoot(), lineBreaker.mLocale);
+ EXPECT_EQ(nullptr, lineBreaker.mHyphenator);
+ }
+ {
+ LineBreaker lineBreaker;
+ Hyphenator hyphenator1, hyphenator2;
+ std::vector<Hyphenator*> hyphenators;
+ hyphenators.push_back(&hyphenator1);
+ hyphenators.push_back(&hyphenator2);
+ lineBreaker.setLocales("THISISABOGUSLANGUAGE,en-US", hyphenators);
+ EXPECT_EQ(icu::Locale::getUS(), lineBreaker.mLocale);
+ EXPECT_EQ(&hyphenator2, lineBreaker.mHyphenator);
+ }
+ {
+ LineBreaker lineBreaker;
+ Hyphenator hyphenator1, hyphenator2;
+ std::vector<Hyphenator*> hyphenators;
+ hyphenators.push_back(&hyphenator1);
+ hyphenators.push_back(&hyphenator2);
+ lineBreaker.setLocales("THISISABOGUSLANGUAGE,ANOTHERBOGUSLANGUAGE", hyphenators);
+ EXPECT_EQ(icu::Locale::getRoot(), lineBreaker.mLocale);
+ EXPECT_EQ(nullptr, lineBreaker.mHyphenator);
+ }
+}
+
+} // namespace minikin
diff --git a/tests/unittest/how_to_run.txt b/tests/unittest/how_to_run.txt
index 20aa5ab..dd40670 100644
--- a/tests/unittest/how_to_run.txt
+++ b/tests/unittest/how_to_run.txt
@@ -1,3 +1,3 @@
-mmm -j8 frameworks/minikin/tests/unittest &&
+mmm -j frameworks/minikin/tests/unittest &&
adb sync data &&
adb shell /data/nativetest/minikin_tests/minikin_tests