diff options
-rw-r--r-- | libs/minikin/Layout.cpp | 49 | ||||
-rw-r--r-- | libs/minikin/LineBreaker.cpp | 12 | ||||
-rw-r--r-- | libs/minikin/Measurement.cpp | 3 |
3 files changed, 51 insertions, 13 deletions
diff --git a/libs/minikin/Layout.cpp b/libs/minikin/Layout.cpp index 17ce596..bac5fc7 100644 --- a/libs/minikin/Layout.cpp +++ b/libs/minikin/Layout.cpp @@ -514,6 +514,29 @@ static size_t getNextWordBreak(const uint16_t* chars, size_t offset, size_t len) return len; } +/** + * Disable certain scripts (mostly those with cursive connection) from having letterspacing + * applied. See https://github.com/behdad/harfbuzz/issues/64 for more details. + */ +static bool isScriptOkForLetterspacing(hb_script_t script) { + return !( + script == HB_SCRIPT_ARABIC || + script == HB_SCRIPT_NKO || + script == HB_SCRIPT_PSALTER_PAHLAVI || + script == HB_SCRIPT_MANDAIC || + script == HB_SCRIPT_MONGOLIAN || + script == HB_SCRIPT_PHAGS_PA || + script == HB_SCRIPT_DEVANAGARI || + script == HB_SCRIPT_BENGALI || + script == HB_SCRIPT_GURMUKHI || + script == HB_SCRIPT_MODI || + script == HB_SCRIPT_SHARADA || + script == HB_SCRIPT_SYLOTI_NAGRI || + script == HB_SCRIPT_TIRHUTA || + script == HB_SCRIPT_OGHAM + ); +} + void Layout::doLayout(const uint16_t* buf, size_t start, size_t count, size_t bufSize, int bidiFlags, const FontStyle &style, const MinikinPaint &paint) { AutoMutex _l(gMinikinLock); @@ -678,15 +701,6 @@ void Layout::doLayoutRun(const uint16_t* buf, size_t start, size_t count, size_t double size = ctx->paint.size; double scaleX = ctx->paint.scaleX; - double letterSpace = ctx->paint.letterSpacing * size * scaleX; - double letterSpaceHalfLeft; - if ((ctx->paint.paintFlags & LinearTextFlag) == 0) { - letterSpace = round(letterSpace); - letterSpaceHalfLeft = floor(letterSpace * 0.5); - } else { - letterSpaceHalfLeft = letterSpace * 0.5; - } - double letterSpaceHalfRight = letterSpace - letterSpaceHalfLeft; float x = mAdvance; float y = 0; @@ -716,6 +730,21 @@ void Layout::doLayoutRun(const uint16_t* buf, size_t start, size_t count, size_t srunend = srunstart; hb_script_t script = getScriptRun(buf + start, run.end, &srunend); + double letterSpace = 0.0; + double letterSpaceHalfLeft = 0.0; + double letterSpaceHalfRight = 0.0; + + if (ctx->paint.letterSpacing != 0.0 && isScriptOkForLetterspacing(script)) { + letterSpace = ctx->paint.letterSpacing * size * scaleX; + if ((ctx->paint.paintFlags & LinearTextFlag) == 0) { + letterSpace = round(letterSpace); + letterSpaceHalfLeft = floor(letterSpace * 0.5); + } else { + letterSpaceHalfLeft = letterSpace * 0.5; + } + letterSpaceHalfRight = letterSpace - letterSpaceHalfLeft; + } + hb_buffer_clear_contents(buffer); hb_buffer_set_script(buffer, script); hb_buffer_set_direction(buffer, isRtl? HB_DIRECTION_RTL : HB_DIRECTION_LTR); @@ -728,7 +757,7 @@ void Layout::doLayoutRun(const uint16_t* buf, size_t start, size_t count, size_t if (ctx->paint.hyphenEdit.hasHyphen() && srunend > srunstart) { // TODO: check whether this is really the desired semantics. It could have the // effect of assigning the hyphen width to a nonspacing mark - unsigned int lastCluster = srunend - 1; + unsigned int lastCluster = start + srunend - 1; hb_codepoint_t hyphenChar = 0x2010; // HYPHEN hb_codepoint_t glyph; diff --git a/libs/minikin/LineBreaker.cpp b/libs/minikin/LineBreaker.cpp index 8275a6c..baf2dfa 100644 --- a/libs/minikin/LineBreaker.cpp +++ b/libs/minikin/LineBreaker.cpp @@ -353,12 +353,17 @@ void LineBreaker::computeBreaksOptimal(bool isRectangle) { float delta = mCandidates[j].preBreak - leftEdge; // compute width score for line + + // Note: the "bestHope" optimization makes the assumption that, when delta is + // non-negative, widthScore will increase monotonically as successive candidate + // breaks are considered. float widthScore = 0.0f; + float additionalPenalty = 0.0f; if (delta < 0) { widthScore = SCORE_OVERFULL; } else if (atEnd && mStrategy != kBreakStrategy_Balanced) { // increase penalty for hyphen on last line - widthScore = LAST_LINE_PENALTY_MULTIPLIER * mCandidates[j].penalty; + additionalPenalty = LAST_LINE_PENALTY_MULTIPLIER * mCandidates[j].penalty; } else { widthScore = delta * delta; } @@ -369,7 +374,7 @@ void LineBreaker::computeBreaksOptimal(bool isRectangle) { bestHope = widthScore; } - float score = jScore + widthScore; + float score = jScore + widthScore + additionalPenalty; if (score <= best) { best = score; bestPrev = j; @@ -378,6 +383,9 @@ void LineBreaker::computeBreaksOptimal(bool isRectangle) { mCandidates[i].score = best + mCandidates[i].penalty + mLinePenalty; mCandidates[i].prev = bestPrev; mCandidates[i].lineNumber = mCandidates[bestPrev].lineNumber + 1; +#if VERBOSE_DEBUG + ALOGD("break %d: score=%g, prev=%d", i, mCandidates[i].score, mCandidates[i].prev); +#endif } finishBreaksOptimal(); } diff --git a/libs/minikin/Measurement.cpp b/libs/minikin/Measurement.cpp index 0b68ac5..a7bc64b 100644 --- a/libs/minikin/Measurement.cpp +++ b/libs/minikin/Measurement.cpp @@ -41,7 +41,8 @@ static float getRunAdvance(Layout& layout, const uint16_t* buf, size_t layoutSta clusterWidth = charAdvance; } } - if (offset < start + count && layout.getCharAdvance(offset - layoutStart) == 0.0f) { + if (offset < start + count && layout.getCharAdvance(offset - layoutStart) == 0.0f && + !GraphemeBreak::isGraphemeBreak(buf, start, count, offset)) { // In the middle of a cluster, distribute width of cluster so that each grapheme cluster // gets an equal share. // TODO: get caret information out of font when that's available |