summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRaph Levien <raph@google.com>2015-11-02 17:17:24 -0800
committerThe Android Automerger <android-build@google.com>2015-11-03 14:35:18 -0800
commit86542797761632890092ef89b7fb58c2c2cdfc11 (patch)
tree6c0cc09fad685d13b9e7a797842bc6a143626560
parentc5af5f6f47d346e78c5920f4a6851b38929a71e5 (diff)
downloadandroid_frameworks_minikin-86542797761632890092ef89b7fb58c2c2cdfc11.tar.gz
android_frameworks_minikin-86542797761632890092ef89b7fb58c2c2cdfc11.tar.bz2
android_frameworks_minikin-86542797761632890092ef89b7fb58c2c2cdfc11.zip
Suppress linebreaks in emoji ZWJ sequences - DO NOT MERGE
Due to the way emoji ZWJ sequences are defined, the ICU line breaking algorithm determines that there are valid line breaks inside the sequence. This patch suppresses these line breaks. Bug: 25433289 Change-Id: I225ebebc0f4186e4b8f48fee399c4a62b3f0218a
-rw-r--r--libs/minikin/LineBreaker.cpp33
1 files changed, 31 insertions, 2 deletions
diff --git a/libs/minikin/LineBreaker.cpp b/libs/minikin/LineBreaker.cpp
index a832ca2..77374fe 100644
--- a/libs/minikin/LineBreaker.cpp
+++ b/libs/minikin/LineBreaker.cpp
@@ -17,6 +17,7 @@
#define VERBOSE_DEBUG 0
#include <limits>
+#include <unicode/utf16.h>
#define LOG_TAG "Minikin"
#include <cutils/log.h>
@@ -30,6 +31,7 @@ namespace android {
const int CHAR_TAB = 0x0009;
const uint16_t CHAR_SOFT_HYPHEN = 0x00AD;
+const uint16_t CHAR_ZWJ = 0x200D;
// Large scores in a hierarchy; we prefer desperate breaks to an overfull line. All these
// constants are larger than any reasonable actual width score.
@@ -123,6 +125,32 @@ static bool isLineBreakingHyphen(uint16_t c) {
c == 0x2E40); // DOUBLE HYPHEN
}
+/**
+ * Determine whether a line break at position i within the buffer buf is valid. This
+ * represents customization beyond the ICU behavior, because plain ICU provides some
+ * line break opportunities that we don't want.
+ **/
+static bool isBreakValid(uint16_t codeUnit, const uint16_t* buf, size_t bufEnd, size_t i) {
+ if (codeUnit == CHAR_SOFT_HYPHEN) {
+ return false;
+ }
+ if (codeUnit == CHAR_ZWJ) {
+ // Possible emoji ZWJ sequence
+ uint32_t next_codepoint;
+ U16_NEXT(buf, i, bufEnd, next_codepoint);
+ if (next_codepoint == 0x2764 || // HEAVY BLACK HEART
+ next_codepoint == 0x1F466 || // BOY
+ next_codepoint == 0x1F467 || // GIRL
+ next_codepoint == 0x1F468 || // MAN
+ next_codepoint == 0x1F469 || // WOMAN
+ next_codepoint == 0x1F48B || // KISS MARK
+ next_codepoint == 0x1F5E8) { // LEFT SPEECH BUBBLE
+ return false;
+ }
+ }
+ return true;
+}
+
// Ordinarily, this method measures the text in the range given. However, when paint
// is nullptr, it assumes the widths have already been calculated and stored in the
// width buffer.
@@ -175,8 +203,9 @@ float LineBreaker::addStyleRun(MinikinPaint* paint, const FontCollection* typefa
}
if (i + 1 == current) {
// Override ICU's treatment of soft hyphen as a break opportunity, because we want it
- // to be a hyphen break, with penalty and drawing behavior.
- if (c != CHAR_SOFT_HYPHEN) {
+ // to be a hyphen break, with penalty and drawing behavior. Also, suppress line
+ // breaks within emoji ZWJ sequences.
+ if (isBreakValid(c, mTextBuf.data(), end, i + 1)) {
// TODO: Add a new type of HyphenEdit for breaks whose hyphen already exists, so
// we can pass the whole word down to Hyphenator like the soft hyphen case.
bool wordEndsInHyphen = isLineBreakingHyphen(c);