diff --git a/app/src/main/java/io/legado/app/help/config/AppConfig.kt b/app/src/main/java/io/legado/app/help/config/AppConfig.kt index 4cd3dd4b5..4b573c73e 100644 --- a/app/src/main/java/io/legado/app/help/config/AppConfig.kt +++ b/app/src/main/java/io/legado/app/help/config/AppConfig.kt @@ -213,7 +213,7 @@ object AppConfig : SharedPreferences.OnSharedPreferenceChangeListener { get() = appCtx.getPrefBoolean(PreferKey.autoRefresh) var enableReview: Boolean - get() = appCtx.getPrefBoolean(PreferKey.enableReview, false) && BuildConfig.DEBUG + get() = BuildConfig.DEBUG && appCtx.getPrefBoolean(PreferKey.enableReview, false) set(value) { appCtx.putPrefBoolean(PreferKey.enableReview, value) } diff --git a/app/src/main/java/io/legado/app/model/ImageProvider.kt b/app/src/main/java/io/legado/app/model/ImageProvider.kt index 60211d1c6..7099175ef 100644 --- a/app/src/main/java/io/legado/app/model/ImageProvider.kt +++ b/app/src/main/java/io/legado/app/model/ImageProvider.kt @@ -129,14 +129,14 @@ object ImageProvider { bookSource: BookSource? ): Size { val file = cacheImage(book, src, bookSource) - //svg size - val size = SvgUtils.getSize(file.absolutePath) - if (size != null) return size val op = BitmapFactory.Options() // inJustDecodeBounds如果设置为true,仅仅返回图片实际的宽和高,宽和高是赋值给opts.outWidth,opts.outHeight; op.inJustDecodeBounds = true BitmapFactory.decodeFile(file.absolutePath, op) if (op.outWidth < 1 && op.outHeight < 1) { + //svg size + val size = SvgUtils.getSize(file.absolutePath) + if (size != null) return size putDebug("ImageProvider: $src Unsupported image type") //file.delete() 重复下载 return Size(errorBitmap.width, errorBitmap.height) diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextLine.kt b/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextLine.kt index 1cb279ca1..65f53cb47 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextLine.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextLine.kt @@ -1,5 +1,6 @@ package io.legado.app.ui.book.read.page.entities +import android.graphics.Paint.FontMetrics import android.text.TextPaint import androidx.annotation.Keep import io.legado.app.ui.book.read.page.entities.column.BaseColumn @@ -51,10 +52,10 @@ data class TextLine( return textColumns.size } - fun upTopBottom(durY: Float, textPaint: TextPaint) { + fun upTopBottom(durY: Float, textHeight: Float, fontMetrics: FontMetrics) { lineTop = ChapterProvider.paddingTop + durY - lineBottom = lineTop + textPaint.textHeight - lineBase = lineBottom - textPaint.fontMetrics.descent + lineBottom = lineTop + textHeight + lineBase = lineBottom - fontMetrics.descent } fun isTouch(x: Float, y: Float, relativeOffset: Float): Boolean { diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextPage.kt b/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextPage.kt index 540495c5e..a5b7dc976 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextPage.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextPage.kt @@ -8,7 +8,6 @@ import io.legado.app.help.config.ReadBookConfig import io.legado.app.model.ReadBook import io.legado.app.ui.book.read.page.entities.column.TextColumn import io.legado.app.ui.book.read.page.provider.ChapterProvider -import io.legado.app.utils.textHeight import splitties.init.appCtx import java.text.DecimalFormat import kotlin.math.min @@ -76,7 +75,7 @@ data class TextPage( val lastLine = textLines[leftLineSize - 1] if (lastLine.isImage) return@run val lastLineHeight = with(lastLine) { lineBottom - lineTop } - val pageHeight = lastLine.lineBottom + contentPaint.textHeight * lineSpacingExtra + val pageHeight = lastLine.lineBottom + contentPaintTextHeight * lineSpacingExtra if (visibleHeight - pageHeight >= lastLineHeight) return@run val surplus = (visibleBottom - lastLine.lineBottom) if (surplus == 0f) return@run @@ -94,7 +93,7 @@ data class TextPage( val lastLine = textLines.last() if (lastLine.isImage) return@run val lastLineHeight = with(lastLine) { lineBottom - lineTop } - val pageHeight = lastLine.lineBottom + contentPaint.textHeight * lineSpacingExtra + val pageHeight = lastLine.lineBottom + contentPaintTextHeight * lineSpacingExtra if (visibleHeight - pageHeight >= lastLineHeight) return@run val surplus = (visibleBottom - lastLine.lineBottom) if (surplus == 0f) return@run diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/provider/ChapterProvider.kt b/app/src/main/java/io/legado/app/ui/book/read/page/provider/ChapterProvider.kt index 5a37e3bef..f9d776ae3 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/provider/ChapterProvider.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/provider/ChapterProvider.kt @@ -1,6 +1,7 @@ package io.legado.app.ui.book.read.page.provider import android.graphics.Paint +import android.graphics.Paint.FontMetrics import android.graphics.Typeface import android.net.Uri import android.os.Build @@ -25,6 +26,7 @@ import io.legado.app.ui.book.read.page.entities.column.TextColumn import io.legado.app.utils.* import splitties.init.appCtx import java.util.* +import kotlin.collections.ArrayList /** * 解析内容生成章节和页面 @@ -97,7 +99,13 @@ object ChapterProvider { private var titlePaintTextHeight = 0f @JvmStatic - private var contentPaintTextHeight = 0f + var contentPaintTextHeight = 0f + + @JvmStatic + private var titlePaintFontMetrics = FontMetrics() + + @JvmStatic + var contentPaintFontMetrics = FontMetrics() @JvmStatic var typeface: Typeface? = Typeface.DEFAULT @@ -146,6 +154,7 @@ object ChapterProvider { stringBuilder, titlePaint, titlePaintTextHeight, + titlePaintFontMetrics, isTitle = true, emptyContent = contents.isEmpty(), isVolumeTitle = bookChapter.isVolume @@ -180,6 +189,7 @@ object ChapterProvider { stringBuilder, contentPaint, contentPaintTextHeight, + contentPaintFontMetrics, srcList = srcList ).let { absStartX = it.first @@ -199,7 +209,8 @@ object ChapterProvider { textPages, stringBuilder, contentPaint, - contentPaintTextHeight + contentPaintTextHeight, + contentPaintFontMetrics ).let { absStartX = it.first durY = it.second @@ -220,7 +231,8 @@ object ChapterProvider { textPages, stringBuilder, contentPaint, - contentPaintTextHeight + contentPaintTextHeight, + contentPaintFontMetrics ).let { absStartX = it.first durY = it.second @@ -331,6 +343,7 @@ object ChapterProvider { stringBuilder: StringBuilder, textPaint: TextPaint, textHeight: Float, + fontMetrics: FontMetrics, isTitle: Boolean = false, emptyContent: Boolean = false, isVolumeTitle: Boolean = false, @@ -342,6 +355,9 @@ object ChapterProvider { } else { StaticLayout(text, textPaint, visibleWidth, Layout.Alignment.ALIGN_NORMAL, 0f, 0f, true) } + val widthsArray = FloatArray(text.length) + textPaint.getTextWidths(text, widthsArray) + val widthsList = widthsArray.asList() var durY = when { //标题y轴居中 emptyContent && textPages.size == 1 -> { @@ -371,7 +387,7 @@ object ChapterProvider { } for (lineIndex in 0 until layout.lineCount) { val textLine = TextLine(isTitle = isTitle) - if (durY + textPaint.textHeight > visibleHeight) { + if (durY + textHeight > visibleHeight) { val textPage = textPages.last() if (doublePage && absStartX < viewWidth / 2) { textPage.leftLineSize = textPage.lineSize @@ -390,16 +406,18 @@ object ChapterProvider { } durY = 0f } - val words = - text.substring(layout.getLineStart(lineIndex), layout.getLineEnd(lineIndex)) - val desiredWidth = layout.getLineWidth(lineIndex) + val lineStart = layout.getLineStart(lineIndex) + val lineEnd = layout.getLineEnd(lineIndex) + val words = text.substring(lineStart, lineEnd) + val textWidths = widthsList.subList(lineStart, lineEnd) + val desiredWidth = textWidths.sum() when { lineIndex == 0 && layout.lineCount > 1 && !isTitle -> { //第一行 非标题 textLine.text = words addCharsToLineFirst( - book, absStartX, textLine, words.toStringArray(), - textPaint, desiredWidth, srcList + book, absStartX, textLine, words, + textPaint, desiredWidth, textWidths, srcList ) } @@ -412,13 +430,13 @@ object ChapterProvider { isTitle && (ReadBookConfig.isMiddleTitle || emptyContent || isVolumeTitle) ) { - (visibleWidth - layout.getLineWidth(lineIndex)) / 2 + (visibleWidth - desiredWidth) / 2 } else { 0f } addCharsToLineNatural( - book, absStartX, textLine, words.toStringArray(), textPaint, - startX, !isTitle && lineIndex == 0, srcList + book, absStartX, textLine, words, textPaint, + startX, !isTitle && lineIndex == 0, textWidths, srcList ) } @@ -428,17 +446,17 @@ object ChapterProvider { (ReadBookConfig.isMiddleTitle || emptyContent || isVolumeTitle) ) { //标题居中 - val startX = (visibleWidth - layout.getLineWidth(lineIndex)) / 2 + val startX = (visibleWidth - desiredWidth) / 2 addCharsToLineNatural( - book, absStartX, textLine, words.toStringArray(), - textPaint, startX, false, srcList + book, absStartX, textLine, words, + textPaint, startX, false, textWidths, srcList ) } else { //中间行 textLine.text = words addCharsToLineMiddle( - book, absStartX, textLine, words.toStringArray(), - textPaint, desiredWidth, 0f, srcList + book, absStartX, textLine, words, + textPaint, desiredWidth, 0f, textWidths, srcList ) } } @@ -462,7 +480,7 @@ object ChapterProvider { } ?: 0) + sbLength textLine.pagePosition = sbLength textPages.last().addLine(textLine) - textLine.upTopBottom(durY, textPaint) + textLine.upTopBottom(durY, textHeight, fontMetrics) durY += textHeight * lineSpacingExtra textPages.last().height = durY } @@ -477,17 +495,18 @@ object ChapterProvider { book: Book, absStartX: Int, textLine: TextLine, - words: Array, + text: String, textPaint: TextPaint, /**自然排版长度**/ desiredWidth: Float, + textWidths: List, srcList: LinkedList? ) { var x = 0f if (!ReadBookConfig.textFullJustify) { addCharsToLineNatural( - book, absStartX, textLine, words, textPaint, - x, true, srcList + book, absStartX, textLine, text, textPaint, + x, true, textWidths, srcList ) return } @@ -504,11 +523,12 @@ object ChapterProvider { x = x1 textLine.indentWidth = x } - if (words.size > bodyIndent.length) { - val words1 = words.copyOfRange(bodyIndent.length, words.size) + if (text.length > bodyIndent.length) { + val text1 = text.substring(bodyIndent.length, text.length) + val textWidths1 = textWidths.subList(bodyIndent.length, textWidths.size) addCharsToLineMiddle( - book, absStartX, textLine, words1, - textPaint, desiredWidth, x, srcList + book, absStartX, textLine, text1, + textPaint, desiredWidth, x, textWidths1, srcList ) } } @@ -520,28 +540,30 @@ object ChapterProvider { book: Book, absStartX: Int, textLine: TextLine, - words: Array, + text: String, textPaint: TextPaint, /**自然排版长度**/ desiredWidth: Float, /**起始x坐标**/ startX: Float, + textWidths: List, srcList: LinkedList? ) { if (!ReadBookConfig.textFullJustify) { addCharsToLineNatural( - book, absStartX, textLine, words, textPaint, - startX, false, srcList + book, absStartX, textLine, text, textPaint, + startX, false, textWidths, srcList ) return } val residualWidth = visibleWidth - desiredWidth - val spaceSize = words.filter { it == " " }.size + val spaceSize = text.count { it == ' ' } + val (words, widths) = getStringArrayAndTextWidths(text, textWidths) if (spaceSize > 1) { val d = residualWidth / spaceSize var x = startX words.forEachIndexed { index, char -> - val cw = StaticLayout.getDesiredWidth(char, textPaint) + val cw = widths[index] val x1 = if (char == " ") { if (index != words.lastIndex) (x + cw + d) else (x + cw) } else { @@ -558,7 +580,7 @@ object ChapterProvider { val d = residualWidth / gapCount var x = startX words.forEachIndexed { index, char -> - val cw = StaticLayout.getDesiredWidth(char, textPaint) + val cw = widths[index] val x1 = if (index != words.lastIndex) (x + cw + d) else (x + cw) addCharToLine( book, absStartX, textLine, char, @@ -567,7 +589,7 @@ object ChapterProvider { x = x1 } } - exceed(absStartX, textLine, words) + exceed(absStartX, textLine) } /** @@ -577,16 +599,18 @@ object ChapterProvider { book: Book, absStartX: Int, textLine: TextLine, - words: Array, + text: String, textPaint: TextPaint, startX: Float, hasIndent: Boolean, + textWidths: List, srcList: LinkedList? ) { val indentLength = ReadBookConfig.paragraphIndent.length var x = startX + val (words, widths) = getStringArrayAndTextWidths(text, textWidths) words.forEachIndexed { index, char -> - val cw = StaticLayout.getDesiredWidth(char, textPaint) + val cw = widths[index] val x1 = x + cw addCharToLine(book, absStartX, textLine, char, x, x1, index + 1 == words.size, srcList) x = x1 @@ -594,7 +618,28 @@ object ChapterProvider { textLine.indentWidth = x } } - exceed(absStartX, textLine, words) + exceed(absStartX, textLine) + } + + private fun getStringArrayAndTextWidths( + text: String, + textWidths: List + ): Pair, List> { + val strList = arrayListOf() + val textWidthList = arrayListOf() + for (i in textWidths.indices) { + if (text[i].isLowSurrogate()) { + continue + } + val char = if (i + 1 < text.lastIndex && text[i + 1].isLowSurrogate()) { + text.substring(i, i + 2) + } else { + text.substring(i, i + 1) + } + strList.add(char) + textWidthList.add(textWidths[i]) + } + return strList to textWidthList } /** @@ -643,14 +688,15 @@ object ChapterProvider { /** * 超出边界处理 */ - private fun exceed(absStartX: Int, textLine: TextLine, words: Array) { + private fun exceed(absStartX: Int, textLine: TextLine) { val visibleEnd = absStartX + visibleWidth - val endX = textLine.columns.lastOrNull()?.end ?: return + val columns = textLine.columns + val endX = columns.lastOrNull()?.end ?: return if (endX > visibleEnd) { - val cc = (endX - visibleEnd) / words.size - for (i in 0..words.lastIndex) { + val cc = (endX - visibleEnd) / columns.size + for (i in 0..columns.lastIndex) { textLine.getColumnReverseAt(i).let { - val py = cc * (words.size - i) + val py = cc * (columns.size - i) it.start = it.start - py it.end = it.end - py } @@ -679,6 +725,8 @@ object ChapterProvider { indentCharWidth = StaticLayout.getDesiredWidth(bodyIndent, contentPaint) / bodyIndent.length titlePaintTextHeight = titlePaint.textHeight contentPaintTextHeight = contentPaint.textHeight + titlePaintFontMetrics = titlePaint.fontMetrics + contentPaintFontMetrics = contentPaint.fontMetrics upLayout() }