diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt index d2ca66adc6..1064c25265 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt @@ -21,6 +21,8 @@ import eu.kanade.tachiyomi.ui.reader.loader.DownloadPageLoader import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter import eu.kanade.tachiyomi.ui.reader.model.ReaderPage import eu.kanade.tachiyomi.ui.reader.model.ViewerChapters +import eu.kanade.tachiyomi.util.lang.byteSize +import eu.kanade.tachiyomi.util.lang.takeBytes import eu.kanade.tachiyomi.util.storage.DiskUtil import eu.kanade.tachiyomi.util.system.ImageUtil import java.io.File @@ -450,7 +452,7 @@ class ReaderPresenter( // Build destination file. val filenameSuffix = " - ${page.number}.${type.extension}" val filename = DiskUtil.buildValidFilename( - "${manga.title} - ${chapter.name}".take(MAX_FILE_NAME_LENGTH - filenameSuffix.length) + "${manga.title} - ${chapter.name}".takeBytes(MAX_FILE_NAME_BYTES - filenameSuffix.byteSize()) ) + filenameSuffix val destFile = File(directory, filename) @@ -641,7 +643,7 @@ class ReaderPresenter( } companion object { - // Safe max filename size is 255 bytes and 1 char = 2 bytes - private const val MAX_FILE_NAME_LENGTH = 127 + // Safe theoretical max filename size is 255 bytes and 1 char = 2-4 bytes (UTF-8) + private const val MAX_FILE_NAME_BYTES = 250 } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/lang/StringExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/lang/StringExtensions.kt index de83295a96..68d2786c96 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/lang/StringExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/lang/StringExtensions.kt @@ -32,3 +32,24 @@ fun String.truncateCenter(count: Int, replacement: String = "..."): String { fun String.compareToCaseInsensitiveNaturalOrder(other: String): Int { return String.CASE_INSENSITIVE_ORDER.then(naturalOrder()).compare(this, other) } + +/** + * Returns the size of the string as the number of bytes. + */ +fun String.byteSize(): Int { + return toByteArray(Charsets.UTF_8).size +} + +/** + * Returns a string containing the first [n] bytes from this string, or the entire string if this + * string is shorter. + */ +@UseExperimental(ExperimentalStdlibApi::class) +fun String.takeBytes(n: Int): String { + val bytes = toByteArray(Charsets.UTF_8) + return if (bytes.size <= n) { + this + } else { + bytes.decodeToString(endIndex = n).replace("\uFFFD", "") + } +}