Skip to content

Commit

Permalink
feat(player): Add option to change fonts (#1185)
Browse files Browse the repository at this point in the history
Co-authored-by: jmir1 <[email protected]>
  • Loading branch information
abdallahmehiz and jmir1 authored Nov 2, 2023
1 parent 082d9e3 commit 5567be6
Show file tree
Hide file tree
Showing 8 changed files with 124 additions and 22 deletions.
2 changes: 2 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,8 @@ dependencies {
implementation(libs.arthenica.smartexceptions)
// seeker seek bar
implementation(libs.seeker)
// true type parser
implementation(libs.truetypeparser)
}

androidComponents {
Expand Down
16 changes: 16 additions & 0 deletions app/src/main/java/eu/kanade/tachiyomi/ui/player/PlayerActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import android.media.AudioManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.os.Handler
import android.os.Looper
import android.os.ParcelFileDescriptor
Expand Down Expand Up @@ -573,6 +574,21 @@ class PlayerActivity : BaseActivity() {
MPVLib.setPropertyDouble("sub-delay", subtitlesDelay().get() / 1000.0)
}

MPVLib.setPropertyString(
"sub-fonts-dir",
File(
Environment.getExternalStorageDirectory().absolutePath + File.separator +
getString(R.string.app_name),
"fonts",
).path,
)

if (playerPreferences.subtitleFont().get().trim() != "") {
MPVLib.setPropertyString("sub-font", playerPreferences.subtitleFont().get())
} else {
MPVLib.setPropertyString("sub-font", "Sans Serif")
}

MPVLib.setPropertyString("sub-bold", if (boldSubtitles().get()) "yes" else "no")
MPVLib.setPropertyString("sub-italic", if (italicSubtitles().get()) "yes" else "no")
MPVLib.setPropertyInt("sub-font-size", subtitleFontSize().get())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ class PlayerPreferences(

fun overrideSubsASS() = preferenceStore.getBoolean("pref_override_subtitles_ass", false)

fun subtitleFont() = preferenceStore.getString("pref_subtitle_font", "Sans Serif")

fun subtitleFontSize() = preferenceStore.getInt("pref_subtitles_font_size", 55)
fun boldSubtitles() = preferenceStore.getBoolean("pref_bold_subtitles", false)
fun italicSubtitles() = preferenceStore.getBoolean("pref_italic_subtitles", false)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ private fun SubtitleColors(
val borderColorPref = screenModel.preferences.borderColorSubtitles()
val backgroundColorPref = screenModel.preferences.backgroundColorSubtitles()

val font by screenModel.preferences.subtitleFont().collectAsState()

Row(horizontalArrangement = Arrangement.SpaceEvenly, modifier = Modifier.fillMaxWidth()) {
SubtitleColorSelector(
label = R.string.player_subtitle_text_color,
Expand All @@ -89,6 +91,7 @@ private fun SubtitleColors(

Column(horizontalAlignment = Alignment.CenterHorizontally) {
SubtitlePreview(
font = font,
isBold = screenModel.preferences.boldSubtitles().collectAsState().value,
isItalic = screenModel.preferences.italicSubtitles().collectAsState().value,
textColor = Color(textColorPref.collectAsState().value),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,37 @@
package eu.kanade.tachiyomi.ui.player.settings.sheets.subtitle

import android.os.Environment
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.size
import androidx.compose.material.SnackbarDefaults.backgroundColor
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Check
import androidx.compose.material.icons.outlined.FormatBold
import androidx.compose.material.icons.outlined.FormatItalic
import androidx.compose.material.icons.outlined.FormatSize
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.yubyf.truetypeparser.TTFFile
import eu.kanade.presentation.components.DropdownMenu
import eu.kanade.presentation.components.OutlinedNumericChooser
import eu.kanade.presentation.util.collectAsState
import eu.kanade.tachiyomi.R
Expand All @@ -29,6 +40,7 @@ import eu.kanade.tachiyomi.ui.player.settings.PlayerSettingsScreenModel
import `is`.xyz.mpv.MPVLib
import tachiyomi.presentation.core.components.material.ReadItemAlpha
import tachiyomi.presentation.core.components.material.padding
import java.io.File

@Composable
fun SubtitleFontPage(screenModel: PlayerSettingsScreenModel) {
Expand All @@ -41,6 +53,7 @@ fun SubtitleFontPage(screenModel: PlayerSettingsScreenModel) {
private fun SubtitleFont(
screenModel: PlayerSettingsScreenModel,
) {
val font by screenModel.preferences.subtitleFont().collectAsState()
val boldSubtitles by screenModel.preferences.boldSubtitles().collectAsState()
val italicSubtitles by screenModel.preferences.italicSubtitles().collectAsState()
val subtitleFontSize by screenModel.preferences.subtitleFontSize().collectAsState()
Expand All @@ -65,6 +78,30 @@ private fun SubtitleFont(
screenModel.preferences.subtitleFontSize().set(it)
}

val updateFont: (String) -> Unit = {
MPVLib.setPropertyString("sub-font", it)
screenModel.preferences.subtitleFont().set(it)
}

val context = LocalContext.current
val fontList by remember {
derivedStateOf {
val customFonts = File(
Environment.getExternalStorageDirectory().absolutePath +
File.separator + context.getString(R.string.app_name) +
File.separator,
"fonts",
).listFiles { file ->
file.extension.equals("ttf", true) ||
file.extension.equals("otf", true)
}?.map {
TTFFile.open(it).families.values.toTypedArray()[0] to it.absolutePath
} ?: emptyList()
listOf("Sans Serif" to "") + customFonts
}
}
var selectingFont by remember { mutableStateOf(false) }

Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(MaterialTheme.padding.tiny),
Expand All @@ -74,11 +111,13 @@ private fun SubtitleFont(
horizontalArrangement = Arrangement.SpaceEvenly,
modifier = Modifier.fillMaxWidth(),
) {
Icon(
imageVector = Icons.Outlined.FormatSize,
contentDescription = null,
modifier = Modifier.size(32.dp),
)
IconButton(onClick = { selectingFont = true }) {
Icon(
imageVector = Icons.Outlined.FormatSize,
contentDescription = null,
modifier = Modifier.size(32.dp),
)
}

OutlinedNumericChooser(
label = stringResource(id = R.string.player_font_size_text_field),
Expand Down Expand Up @@ -111,7 +150,26 @@ private fun SubtitleFont(
)
}

DropdownMenu(expanded = selectingFont, onDismissRequest = { selectingFont = false }) {
fontList.map {
val fontName = it.first
DropdownMenuItem(
text = { Text(fontName) },
onClick = { updateFont(fontName) },
trailingIcon = {
if (font == fontName) {
Icon(
imageVector = Icons.Default.Check,
contentDescription = null,
)
}
},
)
}
}

SubtitlePreview(
font = font,
isBold = boldSubtitles,
isItalic = italicSubtitles,
textColor = Color(textColor),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.ui.player.settings.sheets.subtitle
import android.graphics.Rect
import android.graphics.Typeface
import android.os.Build
import android.os.Environment
import androidx.annotation.RequiresApi
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.background
Expand All @@ -25,18 +26,21 @@ import androidx.compose.ui.graphics.Shadow
import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
import androidx.compose.ui.graphics.nativeCanvas
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.yubyf.truetypeparser.TTFFile
import eu.kanade.presentation.components.TabbedDialog
import eu.kanade.presentation.components.TabbedDialogPaddings
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.player.settings.PlayerSettingsScreenModel
import tachiyomi.presentation.core.components.material.padding
import java.io.File

@Composable
fun SubtitleSettingsSheet(
Expand Down Expand Up @@ -71,41 +75,38 @@ fun SubtitleSettingsSheet(
@Composable
fun OutLineText(
text: String,
font: Typeface,
outlineColor: Color = Color.Black,
textColor: Color = Color.White,
backgroundColor: Color = Color.Black,
isBold: Boolean = false,
isItalic: Boolean = false,
backgroundColor: Color = Color.Black,
) {
val textPaintStroke = Paint().asFrameworkPaint().apply {
typeface = Typeface.create(
Typeface.SANS_SERIF,
if (isBold) FontWeight.Bold.weight else FontWeight.Normal.weight,
isItalic,
)
typeface = font
isAntiAlias = true
style = android.graphics.Paint.Style.STROKE
textSize = 16f
textSize = 48f
color = outlineColor.toArgb()
strokeWidth = 2f
strokeMiter = 2f
strokeWidth = 12f
strokeMiter = 8f
strokeJoin = android.graphics.Paint.Join.ROUND
// change the text alignment from left to center (basically shift the anchor point of the text)
// keep in mind that this only affects horizontal alignment
// https://developer.android.com/reference/android/graphics/Paint.Align
textAlign = android.graphics.Paint.Align.CENTER
isFakeBoldText = isBold
textSkewX = if (isItalic) -0.25f else 0f
}
val textPaint = Paint().asFrameworkPaint().apply {
typeface = Typeface.create(
Typeface.SANS_SERIF,
if (isBold) FontWeight.Bold.weight else FontWeight.Normal.weight,
isItalic,
)
typeface = font
isAntiAlias = true
style = android.graphics.Paint.Style.FILL
textSize = 16f
textSize = 48f
color = textColor.toArgb()
textAlign = android.graphics.Paint.Align.CENTER
isFakeBoldText = isBold
textSkewX = if (isItalic) -0.25f else 0f
}
Canvas(modifier = Modifier.fillMaxSize()) {
drawIntoCanvas {
Expand Down Expand Up @@ -142,15 +143,31 @@ fun OutLineText(
}
}

@RequiresApi(Build.VERSION_CODES.P)
@Composable
fun SubtitlePreview(
font: String,
isBold: Boolean,
isItalic: Boolean,
textColor: Color,
borderColor: Color,
backgroundColor: Color,
) {
val fontMap = File(
Environment.getExternalStorageDirectory().absolutePath +
File.separator + LocalContext.current.getString(R.string.app_name) +
File.separator,
"fonts",
).listFiles { file ->
file.extension.equals("ttf", true) ||
file.extension.equals("otf", true)
}?.associateBy(
{ TTFFile.open(it).families.values.toTypedArray()[0] },
{ it.absolutePath },
) ?: emptyMap()

val fontFile = fontMap.keys.firstOrNull { it.contains(font, true) }
?.let { Typeface.createFromFile(fontMap[it]?.let(::File)) } ?: Typeface.SANS_SERIF

Box(
modifier = Modifier
.padding(vertical = MaterialTheme.padding.medium)
Expand All @@ -160,6 +177,7 @@ fun SubtitlePreview(
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
OutLineText(
text = stringResource(R.string.player_subtitle_settings_example),
font = fontFile,
outlineColor = borderColor,
textColor = textColor,
isBold = isBold,
Expand Down
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ arthenica-smartexceptions = "com.arthenica:smart-exception-java:0.1.1"

seeker = "io.github.2307vivek:seeker:1.1.1"

truetypeparser = "io.github.yubyf:truetypeparser-light:2.1.4"

[bundles]
reactivex = ["rxandroid", "rxjava", "rxrelay"]
okhttp = ["okhttp-core", "okhttp-logging", "okhttp-dnsoverhttps"]
Expand Down
1 change: 1 addition & 0 deletions i18n/src/main/res/values/strings-aniyomi.xml
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,7 @@
<string name="player_subtitle_text_color">Text</string>
<string name="player_subtitle_border_color">Border</string>
<string name="player_subtitle_background_color">Background</string>
<string name="player_subtitle_font_text_field">Font Name</string>

<!-- TachiyomiSY -->
<string name="data_saver_exclude">Exclude from data saver</string>
Expand Down

0 comments on commit 5567be6

Please sign in to comment.