From 53c0077256afebb0312f01ef4f60a7445da1f5d9 Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Mon, 14 Apr 2014 14:11:02 -0700 Subject: [PATCH 1/2] Add elegantTextHeight text appearance attribute This patch adds an elegantTextHeight text appearance attribute and plumbs it through to the paint. This attribute selects the elegant variant of fonts (when appropriate, which is typically Arabic and indic scripts), and also specifies larger vertical metrics, to avoid clipping. The intent is for this to be the default for quantum themes, but this patch doesn't change any default behavior, just adds the attribute. The larger vertical metrics are applied to top and bottom, but should not affect line spacing in the common case. Also, with the setting, metrics are no longer dependent on the font, so setting a custom font will preserve layout and spacing. Change-Id: If3b7d41f141deff50ca078f479ca90c2aa07829a --- api/current.txt | 3 ++ core/java/android/widget/TextView.java | 26 ++++++++++++++ core/jni/android/graphics/Paint.cpp | 44 +++++++++++++++++++++-- core/res/res/values/attrs.xml | 4 +++ graphics/java/android/graphics/Paint.java | 17 +++++++++ 5 files changed, 92 insertions(+), 2 deletions(-) diff --git a/api/current.txt b/api/current.txt index ffc7ad453362e..3b2ce93c358c3 100644 --- a/api/current.txt +++ b/api/current.txt @@ -10035,6 +10035,7 @@ package android.graphics { method public android.graphics.Xfermode getXfermode(); method public final boolean isAntiAlias(); method public final boolean isDither(); + method public boolean isElegantTextHeight(); method public final boolean isFakeBoldText(); method public final boolean isFilterBitmap(); method public final boolean isLinearText(); @@ -10053,6 +10054,7 @@ package android.graphics { method public void setColor(int); method public android.graphics.ColorFilter setColorFilter(android.graphics.ColorFilter); method public void setDither(boolean); + method public void setElegantTextHeight(boolean); method public void setFakeBoldText(boolean); method public void setFilterBitmap(boolean); method public void setFlags(int); @@ -34903,6 +34905,7 @@ package android.widget { method public void setCursorVisible(boolean); method public void setCustomSelectionActionModeCallback(android.view.ActionMode.Callback); method public final void setEditableFactory(android.text.Editable.Factory); + method public void setElegantTextHeight(boolean); method public void setEllipsize(android.text.TextUtils.TruncateAt); method public void setEms(int); method public void setError(java.lang.CharSequence); diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index a7278dae4497c..b91111d5e336c 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -652,6 +652,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener boolean allCaps = false; int shadowcolor = 0; float dx = 0, dy = 0, r = 0; + boolean elegant = false; final Resources.Theme theme = context.getTheme(); @@ -728,6 +729,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener case com.android.internal.R.styleable.TextAppearance_shadowRadius: r = appearance.getFloat(attr, 0); break; + + case com.android.internal.R.styleable.TextAppearance_elegantTextHeight: + elegant = appearance.getBoolean(attr, false); + break; } } @@ -1065,6 +1070,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener case com.android.internal.R.styleable.TextView_textAllCaps: allCaps = a.getBoolean(attr, false); break; + + case com.android.internal.R.styleable.TextView_elegantTextHeight: + elegant = a.getBoolean(attr, false); + break; } } a.recycle(); @@ -1245,6 +1254,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener setHighlightColor(textColorHighlight); } setRawTextSize(textSize); + setElegantTextHeight(elegant); if (allCaps) { setTransformationMethod(new AllCapsTransformationMethod(getContext())); @@ -2468,6 +2478,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener setTransformationMethod(new AllCapsTransformationMethod(getContext())); } + if (appearance.hasValue(com.android.internal.R.styleable.TextAppearance_elegantTextHeight)) { + setElegantTextHeight(appearance.getBoolean( + com.android.internal.R.styleable.TextAppearance_elegantTextHeight, false)); + } + appearance.recycle(); } @@ -2614,6 +2629,17 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return mTextPaint.getTypeface(); } + /** + * Set the TextView's elegant height metrics flag. This setting selects font + * variants that have not been compacted to fit Latin-based vertical + * metrics, and also increases top and bottom bounds to provide more space. + * + * @param elegant set the paint's elegant metrics flag. + */ + public void setElegantTextHeight(boolean elegant) { + mTextPaint.setElegantTextHeight(elegant); + } + /** * Sets the text color for all the states (normal, selected, * focused) to be this color. diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp index f77a389cc45a5..08a88d15974e2 100644 --- a/core/jni/android/graphics/Paint.cpp +++ b/core/jni/android/graphics/Paint.cpp @@ -357,6 +357,24 @@ public: obj->setPaintOptionsAndroid(paintOpts); } + static jboolean isElegantTextHeight(JNIEnv* env, jobject paint) { + NPE_CHECK_RETURN_ZERO(env, paint); + SkPaint* obj = GraphicsJNI::getNativePaint(env, paint); + SkPaintOptionsAndroid paintOpts = obj->getPaintOptionsAndroid(); + return paintOpts.getFontVariant() == SkPaintOptionsAndroid::kElegant_Variant; + } + + static void setElegantTextHeight(JNIEnv* env, jobject paint, jboolean aa) { + NPE_CHECK_RETURN_VOID(env, paint); + SkPaint* obj = GraphicsJNI::getNativePaint(env, paint); + SkPaintOptionsAndroid::FontVariant variant = + aa ? SkPaintOptionsAndroid::kElegant_Variant : + SkPaintOptionsAndroid::kDefault_Variant; + SkPaintOptionsAndroid paintOpts = obj->getPaintOptionsAndroid(); + paintOpts.setFontVariant(variant); + obj->setPaintOptionsAndroid(paintOpts); + } + static jfloat getTextSize(JNIEnv* env, jobject paint) { NPE_CHECK_RETURN_ZERO(env, paint); return SkScalarToFloat(GraphicsJNI::getNativePaint(env, paint)->getTextSize()); @@ -401,10 +419,30 @@ public: return SkScalarToFloat(metrics.fDescent); } + static SkScalar getMetricsInternal(SkPaint *paint, SkPaint::FontMetrics *metrics) { + const int kElegantTop = 2500; + const int kElegantBottom = -1000; + const int kElegantAscent = 1946; + const int kElegantDescent = -512; + const int kElegantLeading = 0; + SkScalar spacing = paint->getFontMetrics(metrics); + SkPaintOptionsAndroid paintOpts = paint->getPaintOptionsAndroid(); + if (paintOpts.getFontVariant() == SkPaintOptionsAndroid::kElegant_Variant) { + SkScalar size = paint->getTextSize(); + metrics->fTop = -size * kElegantTop / 2048; + metrics->fBottom = -size * kElegantBottom / 2048; + metrics->fAscent = -size * kElegantAscent / 2048; + metrics->fDescent = -size * kElegantDescent / 2048; + metrics->fLeading = size * kElegantLeading / 2048; + spacing = metrics->fDescent - metrics->fAscent + metrics->fLeading; + } + return spacing; + } + static jfloat getFontMetrics(JNIEnv* env, jobject paint, jobject metricsObj) { NPE_CHECK_RETURN_ZERO(env, paint); SkPaint::FontMetrics metrics; - SkScalar spacing = GraphicsJNI::getNativePaint(env, paint)->getFontMetrics(&metrics); + SkScalar spacing = getMetricsInternal(GraphicsJNI::getNativePaint(env, paint), &metrics); if (metricsObj) { SkASSERT(env->IsInstanceOf(metricsObj, gFontMetrics_class)); @@ -421,7 +459,7 @@ public: NPE_CHECK_RETURN_ZERO(env, paint); SkPaint::FontMetrics metrics; - GraphicsJNI::getNativePaint(env, paint)->getFontMetrics(&metrics); + getMetricsInternal(GraphicsJNI::getNativePaint(env, paint), &metrics); int ascent = SkScalarRoundToInt(metrics.fAscent); int descent = SkScalarRoundToInt(metrics.fDescent); int leading = SkScalarRoundToInt(metrics.fLeading); @@ -894,6 +932,8 @@ static JNINativeMethod methods[] = { {"native_getTextAlign","(J)I", (void*) SkPaintGlue::getTextAlign}, {"native_setTextAlign","(JI)V", (void*) SkPaintGlue::setTextAlign}, {"native_setTextLocale","(JLjava/lang/String;)V", (void*) SkPaintGlue::setTextLocale}, + {"isElegantTextHeight","()Z", (void*) SkPaintGlue::isElegantTextHeight}, + {"setElegantTextHeight","(Z)V", (void*) SkPaintGlue::setElegantTextHeight}, {"getTextSize","()F", (void*) SkPaintGlue::getTextSize}, {"setTextSize","(F)V", (void*) SkPaintGlue::setTextSize}, {"getTextScaleX","()F", (void*) SkPaintGlue::getTextScaleX}, diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index f364bd02b358e..88f2c54a09162 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -3357,6 +3357,8 @@ + + + + diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java index 916cb5a793790..1e1128e873601 100644 --- a/graphics/java/android/graphics/Paint.java +++ b/graphics/java/android/graphics/Paint.java @@ -500,6 +500,7 @@ public class Paint { mBidiFlags = BIDI_DEFAULT_LTR; setTextLocale(Locale.getDefault()); + setElegantTextHeight(false); } /** @@ -1220,6 +1221,22 @@ public class Paint { native_setTextLocale(mNativePaint, locale.toString()); } + /** + * Get the elegant metrics flag. + * + * @return true if elegant metrics are enabled for text drawing. + */ + public native boolean isElegantTextHeight(); + + /** + * Set the paint's elegant height metrics flag. This setting selects font + * variants that have not been compacted to fit Latin-based vertical + * metrics, and also increases top and bottom bounds to provide more space. + * + * @param elegant set the paint's elegant metrics flag for drawing text. + */ + public native void setElegantTextHeight(boolean elegant); + /** * Return the paint's text size. * From 37938fca72bfe4b1f76d8cde076c2ec01a025759 Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Mon, 14 Apr 2014 14:38:51 -0700 Subject: [PATCH 2/2] Enable elegant text metrics in Quantum theme This changes the default quantum theme to use elegant text metrics, which make use of a bit of extra vertical space to better render complex scripts. It depends on the new elegantMetrics text attribute being available. Change-Id: Ia693f5129057fe8b9a633609c72eef388b988fea --- core/res/res/values/styles_quantum.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/core/res/res/values/styles_quantum.xml b/core/res/res/values/styles_quantum.xml index 595dc793afe42..54103a5cbe81a 100644 --- a/core/res/res/values/styles_quantum.xml +++ b/core/res/res/values/styles_quantum.xml @@ -97,6 +97,7 @@ please see styles_device_defaults.xml. ?textColorLink @dimen/text_size_body_1_quantum @string/font_family_body_1_quantum + true