前人未踏の領域へ アプリ開発編

Android, iOSアプリ開発に関する調査メモ置き場。ほとんどAndroid。古い記事にはアプリ以外も含まれます。

Android:Jetpack ComposeでNoto Sans JPフォントを使えるようにする

課題

これまでアプリでGoogle FontsからNotoフォントをダウンロードして使っていたが、Jetpack Composeに適用とすると上下に余白が出てしまう。

f:id:takeR:20220116200830p:plain
上下に余白が出る例。Robotoフォントとの比較
どうすればよいか。

対応

参考ページにもあるようにISSUE化はされているが現時点で対応されていない。
仕方がないのでフォントセット自体を修正して対応することに。
幸い同ISSUEの方にfonttoolsを使って上下幅を設定できるという投稿があり、それを試してみることにした。以下はその手順。

対応手順

前提条件:fonttoolsはpythonで書かれているのでpip3コマンドを使えるようにする。

  1. fonttoolsをインストール
  2. otfファイルのヘッダー情報を取得する
  3. ttxファイルを更新する
  4. otfファイルを作成する
  5. otfファイルを差し替え

fonttoolsをインストール

fonttoolsをインストールする

$ pip3 install fonttools

otfファイルのヘッダー情報を取得する。

以下のコマンド使ってotfファイルのヘッダー情報を取得する

$ ttx -t head noto_sans_jp_regular.otf

すると noto_sans_jp_regular.ttxというファイルが生成される。

ttxファイルを更新する

ヘッダー情報を更新する。Robotoフォントの上下幅の設定を参考にする。以下は関係する部分の抜粋

  • 1em辺りのデータサイズ(この辺は適当)が unitsPerEm で定義されている
  • roboto.ttxではunitsPerEm2048に対して notoでは1000が設定されている。なので設定値比率は2048 : 1000である
  • roboto.ttxのyMinとyMaxを参考にnotoの値をセットする
// roboto.ttx
<unitsPerEm value="2048"/>
<yMin value="-555"/>
<yMax value="2163"/>

2048 : 1000 なので -555 : yMin、 2163 : yMaxでRobotと同じ余白になるように合わせる

// noto_sans_jp_regular.ttx
<unitsPerEm value="1000"/>
<yMin value="-270"/>
<yMax value="1056"/>

otfファイルを作成する

$ ttx -m noto_sans_jp_regular.otf -b noto_sans_jp_regular.ttx

するとnoto_sans_jp_regular#1.otf というファイルが生成される。

otfファイルを差し替え

それをnoto_sans_jp_regular.otfに上書きコピーすればOK

mv -f noto_sans_jp_regular#1.otf noto_sans_jp_regular.otf

f:id:takeR:20220116204838p:plain
無事余白がRobotoと揃った

備考

Style(Bold, Mediumなど)ごとに微妙にyMin,yMaxが異なるのでそれに合わせて1ポイントずつ余白を少なくした方がいい感じになる。
使用される文字によってはおかしくなる場合があるかもしれない。

参考

検証に使ったコード

@Composable
fun NotoText() {
    Text(text = "Noto Sans JP", fontFamily = NotoSansJpFamily, fontWeight = FontWeight.Medium)
}

@Composable
fun RobotoText() {
    Text(text = "Roboto", fontFamily = RobotoFamily, fontWeight = FontWeight.Medium)
}

@Preview
@Composable
fun NotoPreview() {
    Surface(Modifier.background(Color.White)) {
        NotoText()
    }
}

@Preview
@Composable
fun RobotoPreview() {
    Surface(Modifier.background(Color.White)) {
        RobotoText()
    }
}