Get underline position and thickness from the font
Previously, font underline position and thickness were fixed in Android. Although a custom font may have specified a different value, such a value would never be used. Now we use the values from the font if they are provided and fall back to the old default values only if needed. Bug: 62353930 Test: adb shell am instrument -w -e package android.graphics com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner Test: adb shell am instrument -w -e package android.text com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner Test: cts-tradefed run cts-dev --module CtsTextTestCases Test: Manual Change-Id: I6bf21000dd69a2780c894b231638bc0c122e41f4
This commit is contained in:
@@ -827,7 +827,9 @@ class TextLine {
|
||||
underlineXLeft, underlineXRight, y);
|
||||
}
|
||||
if (info.isUnderlineText) {
|
||||
drawUnderline(wp, c, wp.getColor(), ((Paint) wp).getUnderlineThickness(),
|
||||
final float thickness =
|
||||
Math.max(((Paint) wp).getUnderlineThickness(), 1.0f);
|
||||
drawUnderline(wp, c, wp.getColor(), thickness,
|
||||
underlineXLeft, underlineXRight, y);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -961,6 +961,30 @@ namespace PaintGlue {
|
||||
return SkScalarToFloat(metrics.fDescent);
|
||||
}
|
||||
|
||||
static jfloat getUnderlinePosition(jlong paintHandle, jlong typefaceHandle) {
|
||||
Paint::FontMetrics metrics;
|
||||
getMetricsInternal(paintHandle, typefaceHandle, &metrics);
|
||||
SkScalar position;
|
||||
if (metrics.hasUnderlinePosition(&position)) {
|
||||
return SkScalarToFloat(position);
|
||||
} else {
|
||||
const SkScalar textSize = reinterpret_cast<Paint*>(paintHandle)->getTextSize();
|
||||
return SkScalarToFloat(Paint::kStdUnderline_Top * textSize);
|
||||
}
|
||||
}
|
||||
|
||||
static jfloat getUnderlineThickness(jlong paintHandle, jlong typefaceHandle) {
|
||||
Paint::FontMetrics metrics;
|
||||
getMetricsInternal(paintHandle, typefaceHandle, &metrics);
|
||||
SkScalar thickness;
|
||||
if (metrics.hasUnderlineThickness(&thickness)) {
|
||||
return SkScalarToFloat(thickness);
|
||||
} else {
|
||||
const SkScalar textSize = reinterpret_cast<Paint*>(paintHandle)->getTextSize();
|
||||
return SkScalarToFloat(Paint::kStdUnderline_Thickness * textSize);
|
||||
}
|
||||
}
|
||||
|
||||
static void setShadowLayer(jlong paintHandle, jfloat radius,
|
||||
jfloat dx, jfloat dy, jint color) {
|
||||
Paint* paint = reinterpret_cast<Paint*>(paintHandle);
|
||||
@@ -1072,6 +1096,8 @@ static const JNINativeMethod methods[] = {
|
||||
{"nSetHyphenEdit", "(JI)V", (void*) PaintGlue::setHyphenEdit},
|
||||
{"nAscent","(JJ)F", (void*) PaintGlue::ascent},
|
||||
{"nDescent","(JJ)F", (void*) PaintGlue::descent},
|
||||
{"nGetUnderlinePosition","(JJ)F", (void*) PaintGlue::getUnderlinePosition},
|
||||
{"nGetUnderlineThickness","(JJ)F", (void*) PaintGlue::getUnderlineThickness},
|
||||
{"nSetShadowLayer", "(JFFFI)V", (void*)PaintGlue::setShadowLayer},
|
||||
{"nHasShadowLayer", "(J)Z", (void*)PaintGlue::hasShadowLayer}
|
||||
};
|
||||
|
||||
BIN
core/tests/coretests/assets/fonts/underlineTestFont.ttf
Normal file
BIN
core/tests/coretests/assets/fonts/underlineTestFont.ttf
Normal file
Binary file not shown.
163
core/tests/coretests/assets/fonts/underlineTestFont.ttx
Normal file
163
core/tests/coretests/assets/fonts/underlineTestFont.ttx
Normal file
@@ -0,0 +1,163 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- 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.
|
||||
-->
|
||||
<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.9">
|
||||
|
||||
<GlyphOrder>
|
||||
<GlyphID id="0" name=".notdef"/>
|
||||
<GlyphID id="1" name="a"/>
|
||||
</GlyphOrder>
|
||||
|
||||
<head>
|
||||
<tableVersion value="1.0"/>
|
||||
<fontRevision value="1.0"/>
|
||||
<checkSumAdjustment value="0"/>
|
||||
<magicNumber value="0x5f0f3cf5"/>
|
||||
<flags value="00000000 00000011"/>
|
||||
<unitsPerEm value="1000"/>
|
||||
<created value="Fri Mar 17 07:26:00 2017"/>
|
||||
<macStyle value="00000000 00000000"/>
|
||||
<lowestRecPPEM value="7"/>
|
||||
<fontDirectionHint value="2"/>
|
||||
<glyphDataFormat value="0"/>
|
||||
</head>
|
||||
|
||||
<hhea>
|
||||
<tableVersion value="0x00010000"/>
|
||||
<ascent value="1000"/>
|
||||
<descent value="-200"/>
|
||||
<lineGap 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"/>
|
||||
</hhea>
|
||||
|
||||
<maxp>
|
||||
<tableVersion value="0x10000"/>
|
||||
<maxZones value="0"/>
|
||||
<maxTwilightPoints value="0"/>
|
||||
<maxStorage value="0"/>
|
||||
<maxFunctionDefs value="0"/>
|
||||
<maxInstructionDefs value="0"/>
|
||||
<maxStackElements value="0"/>
|
||||
<maxSizeOfInstructions value="0"/>
|
||||
<maxComponentElements value="0"/>
|
||||
</maxp>
|
||||
|
||||
<OS_2>
|
||||
<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"/>
|
||||
<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="a" width="500" lsb="93"/>
|
||||
</hmtx>
|
||||
|
||||
<cmap>
|
||||
<tableVersion version="0"/>
|
||||
<cmap_format_4 platformID="3" platEncID="10" language="0">
|
||||
<map code="0x61" name="a"/><!-- LATIN SMALL LETTER A -->
|
||||
</cmap_format_4>
|
||||
</cmap>
|
||||
|
||||
<loca>
|
||||
<!-- The 'loca' table will be calculated by the compiler -->
|
||||
</loca>
|
||||
|
||||
<glyf>
|
||||
<TTGlyph name=".notdef"/><!-- contains no outline data -->
|
||||
|
||||
<TTGlyph name="a"/><!-- contains no outline data -->
|
||||
|
||||
</glyf>
|
||||
|
||||
<name>
|
||||
<namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
|
||||
Sample Font
|
||||
</namerecord>
|
||||
<namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
|
||||
Regular
|
||||
</namerecord>
|
||||
<namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
|
||||
Sample Font
|
||||
</namerecord>
|
||||
<namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
|
||||
SampleFont-Regular
|
||||
</namerecord>
|
||||
</name>
|
||||
|
||||
<post>
|
||||
<formatType value="3.0"/>
|
||||
<italicAngle value="0.0"/>
|
||||
<underlinePosition value="-200"/>
|
||||
<underlineThickness value="300"/>
|
||||
<isFixedPitch value="0"/>
|
||||
<minMemType42 value="0"/>
|
||||
<maxMemType42 value="0"/>
|
||||
<minMemType1 value="0"/>
|
||||
<maxMemType1 value="0"/>
|
||||
</post>
|
||||
|
||||
</ttFont>
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package android.graphics;
|
||||
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
|
||||
import android.graphics.Paint;
|
||||
import android.test.InstrumentationTestCase;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
@@ -365,4 +367,31 @@ public class PaintTest extends InstrumentationTestCase {
|
||||
p.setWordSpacing(-2.0f);
|
||||
assertEquals(-2.0f, p.getWordSpacing());
|
||||
}
|
||||
|
||||
public void testGetUnderlinePositionAndThickness() {
|
||||
final Typeface fontTypeface = Typeface.createFromAsset(
|
||||
getInstrumentation().getContext().getAssets(), "fonts/underlineTestFont.ttf");
|
||||
final Paint p = new Paint();
|
||||
final int textSize = 100;
|
||||
p.setTextSize(textSize);
|
||||
|
||||
final float origPosition = p.getUnderlinePosition();
|
||||
final float origThickness = p.getUnderlineThickness();
|
||||
|
||||
p.setTypeface(fontTypeface);
|
||||
assertNotEquals(origPosition, p.getUnderlinePosition());
|
||||
assertNotEquals(origThickness, p.getUnderlineThickness());
|
||||
|
||||
// -200 (underlinePosition in 'post' table, negative means below the baseline)
|
||||
// ÷ 1000 (unitsPerEm in 'head' table)
|
||||
// × 100 (text size)
|
||||
// × -1 (negated, since we consider below the baseline positive)
|
||||
// = 20
|
||||
assertEquals(20.0f, p.getUnderlinePosition(), 0.5f);
|
||||
// 300 (underlineThickness in 'post' table)
|
||||
// ÷ 1000 (unitsPerEm in 'head' table)
|
||||
// × 100 (text size)
|
||||
// = 30
|
||||
assertEquals(30.0f, p.getUnderlineThickness(), 0.5f);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -822,18 +822,14 @@ public class Paint {
|
||||
* @hide
|
||||
*/
|
||||
public float getUnderlinePosition() {
|
||||
// kStdUnderline_Offset = 1/9, defined in SkTextFormatParams.h
|
||||
// TODO: replace with position from post and MVAR tables (b/62353930).
|
||||
return (1.0f / 9.0f) * getTextSize();
|
||||
return nGetUnderlinePosition(mNativePaint, mNativeTypeface);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public float getUnderlineThickness() {
|
||||
// kStdUnderline_Thickness = 1/18, defined in SkTextFormatParams.h
|
||||
// TODO: replace with thickness from post and MVAR tables (b/62353930).
|
||||
return (1.0f / 18.0f) * getTextSize();
|
||||
return nGetUnderlineThickness(mNativePaint, mNativeTypeface);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2997,5 +2993,9 @@ public class Paint {
|
||||
@CriticalNative
|
||||
private static native float nDescent(long paintPtr, long typefacePtr);
|
||||
@CriticalNative
|
||||
private static native float nGetUnderlinePosition(long paintPtr, long typefacePtr);
|
||||
@CriticalNative
|
||||
private static native float nGetUnderlineThickness(long paintPtr, long typefacePtr);
|
||||
@CriticalNative
|
||||
private static native void nSetTextSize(long paintPtr, float textSize);
|
||||
}
|
||||
|
||||
@@ -46,23 +46,31 @@ void Canvas::drawTextDecorations(float x, float y, float length, const SkPaint&
|
||||
flags = paint.getFlags();
|
||||
}
|
||||
if (flags & (SkPaint::kUnderlineText_ReserveFlag | SkPaint::kStrikeThruText_ReserveFlag)) {
|
||||
// Same values used by Skia
|
||||
const float kStdStrikeThru_Offset = (-6.0f / 21.0f);
|
||||
const float kStdUnderline_Offset = (1.0f / 9.0f);
|
||||
const float kStdUnderline_Thickness = (1.0f / 18.0f);
|
||||
|
||||
SkScalar left = x;
|
||||
SkScalar right = x + length;
|
||||
float textSize = paint.getTextSize();
|
||||
float strokeWidth = fmax(textSize * kStdUnderline_Thickness, 1.0f);
|
||||
const SkScalar left = x;
|
||||
const SkScalar right = x + length;
|
||||
if (flags & SkPaint::kUnderlineText_ReserveFlag) {
|
||||
SkScalar top = y + textSize * kStdUnderline_Offset - 0.5f * strokeWidth;
|
||||
SkScalar bottom = y + textSize * kStdUnderline_Offset + 0.5f * strokeWidth;
|
||||
Paint::FontMetrics metrics;
|
||||
paint.getFontMetrics(&metrics);
|
||||
SkScalar position;
|
||||
if (!metrics.hasUnderlinePosition(&position)) {
|
||||
position = paint.getTextSize() * Paint::kStdUnderline_Top;
|
||||
}
|
||||
SkScalar thickness;
|
||||
if (!metrics.hasUnderlineThickness(&thickness)) {
|
||||
thickness = paint.getTextSize() * Paint::kStdUnderline_Thickness;
|
||||
}
|
||||
const float strokeWidth = fmax(thickness, 1.0f);
|
||||
const SkScalar top = y + position;
|
||||
const SkScalar bottom = top + strokeWidth;
|
||||
drawRect(left, top, right, bottom, paint);
|
||||
}
|
||||
if (flags & SkPaint::kStrikeThruText_ReserveFlag) {
|
||||
SkScalar top = y + textSize * kStdStrikeThru_Offset - 0.5f * strokeWidth;
|
||||
SkScalar bottom = y + textSize * kStdStrikeThru_Offset + 0.5f * strokeWidth;
|
||||
const float textSize = paint.getTextSize();
|
||||
const float position = textSize * Paint::kStdStrikeThru_Offset;
|
||||
const float strokeWidth = fmax(textSize * Paint::kStdUnderline_Thickness, 1.0f);
|
||||
const SkScalar top = y + position - 0.5f * strokeWidth;
|
||||
const SkScalar bottom = y + position + 0.5f * strokeWidth;
|
||||
drawRect(left, top, right, bottom, paint);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,15 @@ namespace android {
|
||||
|
||||
class ANDROID_API Paint : public SkPaint {
|
||||
public:
|
||||
// Default values for underlined and strikethrough text,
|
||||
// as defined by Skia in SkTextFormatParams.h.
|
||||
constexpr static float kStdStrikeThru_Offset = (-6.0f / 21.0f);
|
||||
constexpr static float kStdUnderline_Offset = (1.0f / 9.0f);
|
||||
constexpr static float kStdUnderline_Thickness = (1.0f / 18.0f);
|
||||
|
||||
constexpr static float kStdUnderline_Top =
|
||||
kStdUnderline_Offset - 0.5f * kStdUnderline_Thickness;
|
||||
|
||||
Paint();
|
||||
Paint(const Paint& paint);
|
||||
Paint(const SkPaint& paint); // NOLINT(implicit)
|
||||
|
||||
Reference in New Issue
Block a user