[go: nahoru, domu]

blob: 3a3edbbf7968b5759eb7adb3b4e98e11096f24a4 [file] [log] [blame]
/*
* Copyright 2019 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.
*/
package androidx.ui.text.matchers
import android.graphics.Typeface
import android.os.Build
import android.text.TextPaint
import androidx.ui.text.FontTestData
import androidx.ui.text.font.FontStyle
import androidx.ui.text.font.FontWeight
import androidx.ui.text.matchers.TypefaceSubject.Companion.DEFINED_CHARACTERS
import com.google.common.truth.FailureMetadata
import com.google.common.truth.Subject
import com.google.common.truth.Subject.Factory
private const val FONT_SIZE = 10f
/**
* Truth extension for Typeface.
*
* Checks if a given [Typeface] has a given [FontWeight] and [FontStyle]. Since [Typeface] does
* not contain the required information before API 28, it uses the set of specific fonts to infer
* which type of font was loaded. Check [DEFINED_CHARACTERS] and [FontTestData] for the set of
* fonts designed for this class.
*
* Each font contains [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r] characters and [wide,narrow] glyph
* types. Each font file includes one character that is represented with wide glyph, and others
* with narrow. This is used in the tests in order to differentiate the font that is loaded for
* a specific weight/style in the FontFamily.
*
* The size difference between wide and narrow glyph is 3 (wide = 3 * narrow).
*
* - 200 italic has "a" with wide glyph
* - 200 regular has "b" with wide glyph
* - ...
* - 900 italic has "q" with wide glyph
* - 900 regular has "r" with wide glyph
*/
internal class TypefaceSubject private constructor(
failureMetadata: FailureMetadata?,
private val subject: Typeface?
) : Subject(failureMetadata, subject) {
companion object {
internal val SUBJECT_FACTORY: Factory<TypefaceSubject?, Typeface?> =
Factory { failureMetadata, subject -> TypefaceSubject(failureMetadata, subject) }
internal val DEFINED_CHARACTERS = arrayOf(
CharacterInfo('a', FontWeight.W100, FontStyle.Italic),
CharacterInfo('b', FontWeight.W100, FontStyle.Normal),
CharacterInfo('c', FontWeight.W200, FontStyle.Italic),
CharacterInfo('d', FontWeight.W200, FontStyle.Normal),
CharacterInfo('e', FontWeight.W300, FontStyle.Italic),
CharacterInfo('f', FontWeight.W300, FontStyle.Normal),
CharacterInfo('g', FontWeight.W400, FontStyle.Italic),
CharacterInfo('h', FontWeight.W400, FontStyle.Normal),
CharacterInfo('i', FontWeight.W500, FontStyle.Italic),
CharacterInfo('j', FontWeight.W500, FontStyle.Normal),
CharacterInfo('k', FontWeight.W600, FontStyle.Italic),
CharacterInfo('l', FontWeight.W600, FontStyle.Normal),
CharacterInfo('m', FontWeight.W700, FontStyle.Italic),
CharacterInfo('n', FontWeight.W700, FontStyle.Normal),
CharacterInfo('o', FontWeight.W800, FontStyle.Italic),
CharacterInfo('p', FontWeight.W800, FontStyle.Normal),
CharacterInfo('q', FontWeight.W900, FontStyle.Italic),
CharacterInfo('r', FontWeight.W900, FontStyle.Normal)
)
}
private fun getPaint(typeface: Typeface): TextPaint {
return TextPaint().apply {
this.typeface = typeface
this.textSize = FONT_SIZE
}
}
private fun isSelectedFont(typeface: Typeface, character: Char): Boolean {
val string = Character.toString(character)
val measuredWidth = getPaint(typeface).measureText(string)
// wide glyphs are 3 times the width of narrow glyphs. Therefore for the selected character
// if the right font is selected the width should be 3 times the font size.
return java.lang.Float.compare(measuredWidth, FONT_SIZE * 3) == 0
}
/**
* Verifies that [Typeface] object has the given [FontWeight] and [FontStyle].
*
* @param [fontWeight] expected [FontWeight]
* @param [fontStyle] expected [FontStyle]
*/
fun isTypefaceOf(fontWeight: FontWeight, fontStyle: FontStyle) {
check("isNotNull()").that(subject).isNotNull()
val typeface = subject as Typeface
val charInfo = DEFINED_CHARACTERS.find {
it.fontWeight == fontWeight && it.fontStyle == fontStyle
}!!
val isSelectedFont = isSelectedFont(typeface, charInfo.character)
if (Build.VERSION.SDK_INT >= 28) {
check("sameTypeface()")
.that(isSelectedFont && typeface.weight == fontWeight.weight).isTrue()
// cannot check typeface.isItalic == (fontStyle == FontStyle.Italic) since it is for
// fake italic, and for cases where synthesis is disable this does not give correct
// signal
} else {
check("sameTypeface()").that(isSelectedFont).isTrue()
}
}
override fun actualCustomStringRepresentation(): String {
return if (subject != null) {
var selectedFont = DEFINED_CHARACTERS.find { isSelectedFont(subject, it.character) }
selectedFont?.toString() ?: "unknown"
} else {
super.actualCustomStringRepresentation()
}
}
}
internal class CharacterInfo(
val character: Char,
val fontWeight: FontWeight,
val fontStyle: FontStyle
) {
override fun toString(): String {
return toString(fontWeight, fontStyle)
}
}
internal fun toString(fontWeight: FontWeight, fontStyle: FontStyle): String {
return "{fontWeight: $fontWeight, fontStyle: $fontStyle}"
}