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 0e7dc657f..da5f29f4f 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 @@ -10,7 +10,7 @@ import splitties.init.appCtx import java.text.DecimalFormat import kotlin.math.min -@Suppress("unused") +@Suppress("unused", "MemberVisibilityCanBePrivate") data class TextPage( var index: Int = 0, var text: String = appCtx.getString(R.string.data_loading), @@ -20,6 +20,7 @@ data class TextPage( var chapterSize: Int = 0, var chapterIndex: Int = 0, var height: Float = 0f, + var leftLineSize: Int = 0 ) { val lineSize get() = textLines.size @@ -31,28 +32,53 @@ data class TextPage( } } - fun upLinesPosition() = ChapterProvider.apply { - if (!ReadBookConfig.textBottomJustify) return@apply - if (textLines.size <= 1) return@apply - if (textLines.last().isImage) return@apply - if (visibleHeight - height >= with(textLines.last()) { lineBottom - lineTop }) return@apply - val surplus = (visibleBottom - textLines.last().lineBottom) - if (surplus == 0f) return@apply - height += surplus - val tj = surplus / (textLines.size - 1) - for (i in 1 until textLines.size) { - val line = textLines[i] - line.lineTop = line.lineTop + tj * i - line.lineBase = line.lineBase + tj * i - line.lineBottom = line.lineBottom + tj * i + fun upLinesPosition() { + if (!ReadBookConfig.textBottomJustify) return + if (textLines.size <= 1) return + if (leftLineSize == 0) { + leftLineSize = lineSize + } + ChapterProvider.run { + val lastLine = textLines[leftLineSize - 1] + if (lastLine.isImage) return@run + val lastLineHeight = with(lastLine) { lineBottom - lineTop } + if (visibleHeight - lastLine.lineBottom >= lastLineHeight) return@run + val surplus = (visibleBottom - lastLine.lineBottom) + if (surplus == 0f) return@run + height += surplus + val tj = surplus / (leftLineSize - 1) + for (i in 1 until leftLineSize) { + val line = textLines[i] + line.lineTop = line.lineTop + tj * i + line.lineBase = line.lineBase + tj * i + line.lineBottom = line.lineBottom + tj * i + } + } + if (leftLineSize == lineSize) return + ChapterProvider.run { + val lastLine = textLines.last() + if (lastLine.isImage) return@run + val lastLineHeight = with(lastLine) { lineBottom - lineTop } + if (visibleHeight - lastLine.lineBottom >= lastLineHeight) return@run + val surplus = (visibleBottom - lastLine.lineBottom) + if (surplus == 0f) return@run + val tj = surplus / (textLines.size - leftLineSize - 1) + for (i in leftLineSize + 1 until textLines.size) { + val line = textLines[i] + val surplusIndex = i - leftLineSize + line.lineTop = line.lineTop + tj * surplusIndex + line.lineBase = line.lineBase + tj * surplusIndex + line.lineBottom = line.lineBottom + tj * surplusIndex + } } } @Suppress("DEPRECATION") fun format(): TextPage { - if (textLines.isEmpty() && ChapterProvider.visibleWidth > 0) { + if (textLines.isEmpty() && ChapterProvider.viewWidth > 0) { + val visibleWidth = ChapterProvider.visibleRight - ChapterProvider.paddingLeft val layout = StaticLayout( - text, ChapterProvider.contentPaint, ChapterProvider.visibleWidth, + text, ChapterProvider.contentPaint, visibleWidth, Layout.Alignment.ALIGN_NORMAL, 1f, 0f, false ) var y = (ChapterProvider.visibleHeight - layout.height) / 2f @@ -65,7 +91,7 @@ data class TextPage( textLine.lineBottom = ChapterProvider.paddingTop + y + layout.getLineBottom(lineIndex) var x = ChapterProvider.paddingLeft + - (ChapterProvider.visibleWidth - layout.getLineMax(lineIndex)) / 2 + (visibleWidth - layout.getLineMax(lineIndex)) / 2 textLine.text = text.substring(layout.getLineStart(lineIndex), layout.getLineEnd(lineIndex)) for (i in textLine.text.indices) { 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 46421e260..13b2763a3 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 @@ -29,25 +29,40 @@ object ChapterProvider { private const val srcReplaceChar = "▩" @JvmStatic - private var viewWidth = 0 + var viewWidth = 0 + private set @JvmStatic - private var viewHeight = 0 + var viewHeight = 0 + private set @JvmStatic var paddingLeft = 0 + private set @JvmStatic var paddingTop = 0 + private set + + @JvmStatic + var paddingRight = 0 + private set + + @JvmStatic + var paddingBottom = 0 + private set @JvmStatic var visibleWidth = 0 + private set @JvmStatic var visibleHeight = 0 + private set @JvmStatic var visibleRight = 0 + private set @JvmStatic var visibleBottom = 0 @@ -73,7 +88,7 @@ object ChapterProvider { @JvmStatic val contentPaint: TextPaint = TextPaint() - var isHorizontal = false + var doublePage = false init { @@ -92,6 +107,7 @@ object ChapterProvider { ): TextChapter { val textPages = arrayListOf() val stringBuilder = StringBuilder() + var absStartX = paddingLeft var durY = 0f textPages.add(TextPage()) contents.forEachIndexed { index, content -> @@ -112,10 +128,13 @@ object ChapterProvider { val isTitle = index == 0 val textPaint = if (isTitle) titlePaint else contentPaint if (!(isTitle && ReadBookConfig.titleMode == 2)) { - durY = setTypeText( - text, durY, textPages, stringBuilder, + setTypeText( + absStartX, durY, text, textPages, stringBuilder, isTitle, textPaint, srcList - ) + ).let { + absStartX = it.first + durY = it.second + } } } else if (book.getImageStyle() != Book.imgStyleText) { val matcher = AppPattern.imgPattern.matcher(content) @@ -126,10 +145,13 @@ object ChapterProvider { val isTitle = index == 0 val textPaint = if (isTitle) titlePaint else contentPaint if (!(isTitle && ReadBookConfig.titleMode == 2)) { - durY = setTypeText( - text, durY, textPages, + setTypeText( + absStartX, durY, text, textPages, stringBuilder, isTitle, textPaint - ) + ).let { + absStartX = it.first + durY = it.second + } } } durY = setTypeImage( @@ -144,10 +166,13 @@ object ChapterProvider { val isTitle = index == 0 val textPaint = if (isTitle) titlePaint else contentPaint if (!(isTitle && ReadBookConfig.titleMode == 2)) { - durY = setTypeText( - text, durY, textPages, + setTypeText( + absStartX, durY, text, textPages, stringBuilder, isTitle, textPaint - ) + ).let { + absStartX = it.first + durY = it.second + } } } } @@ -240,14 +265,16 @@ object ChapterProvider { * 排版文字 */ private fun setTypeText( - text: String, + x: Int, y: Float, + text: String, textPages: ArrayList, stringBuilder: StringBuilder, isTitle: Boolean, textPaint: TextPaint, srcList: LinkedList? = null - ): Float { + ): Pair { + var absStartX = x var durY = if (isTitle) y + titleTopSpacing else y val layout = if (ReadBookConfig.useZhLayout) { ZhLayout(text, textPaint, visibleWidth) @@ -256,6 +283,25 @@ object ChapterProvider { ) for (lineIndex in 0 until layout.lineCount) { val textLine = TextLine(isTitle = isTitle) + if (durY + textPaint.textHeight > visibleHeight) { + val textPage = textPages.last() + if (doublePage && absStartX < viewWidth / 2) { + textPage.leftLineSize = textPage.lineSize + absStartX = viewWidth / 2 + paddingLeft + } else { + //当前页面结束,设置各种值 + if (textPage.leftLineSize == 0) { + textPage.leftLineSize = textPage.lineSize + } + textPage.text = stringBuilder.toString() + textPage.height = durY + //新建页面 + textPages.add(TextPage()) + stringBuilder.clear() + absStartX = paddingLeft + } + durY = 0f + } val words = text.substring(layout.getLineStart(lineIndex), layout.getLineEnd(lineIndex)) val desiredWidth = layout.getLineWidth(lineIndex) @@ -264,6 +310,7 @@ object ChapterProvider { //第一行 textLine.text = words addCharsToLineFirst( + absStartX, textLine, words.toStringArray(), textPaint, @@ -274,14 +321,22 @@ object ChapterProvider { //最后一行 textLine.text = "$words\n" isLastLine = true - val x = if (isTitle && ReadBookConfig.titleMode == 1) + val startX = if (isTitle && ReadBookConfig.titleMode == 1) (visibleWidth - layout.getLineWidth(lineIndex)) / 2 else 0f - addCharsToLineLast(textLine, words.toStringArray(), textPaint, x, srcList) + addCharsToLineLast( + absStartX, + textLine, + words.toStringArray(), + textPaint, + startX, + srcList + ) } else { //中间行 textLine.text = words addCharsToLineMiddle( + absStartX, textLine, words.toStringArray(), textPaint, @@ -290,15 +345,6 @@ object ChapterProvider { srcList ) } - if (durY + textPaint.textHeight > visibleHeight) { - //当前页面结束,设置各种值 - textPages.last().text = stringBuilder.toString() - textPages.last().height = durY - //新建页面 - textPages.add(TextPage()) - stringBuilder.clear() - durY = 0f - } stringBuilder.append(words) if (isLastLine) stringBuilder.append("\n") textPages.last().textLines.add(textLine) @@ -308,13 +354,14 @@ object ChapterProvider { } if (isTitle) durY += titleBottomSpacing durY += textPaint.textHeight * paragraphSpacing / 10f - return durY + return Pair(absStartX, durY) } /** * 有缩进,两端对齐 */ private fun addCharsToLineFirst( + absStartX: Int, textLine: TextLine, words: Array, textPaint: TextPaint, @@ -323,34 +370,23 @@ object ChapterProvider { ) { var x = 0f if (!ReadBookConfig.textFullJustify) { - addCharsToLineLast(textLine, words, textPaint, x, srcList) + addCharsToLineLast(absStartX, textLine, words, textPaint, x, srcList) return } val bodyIndent = ReadBookConfig.paragraphIndent val icw = StaticLayout.getDesiredWidth(bodyIndent, textPaint) / bodyIndent.length bodyIndent.toStringArray().forEach { val x1 = x + icw - if (srcList != null && it == srcReplaceChar) { - textLine.textChars.add( - TextChar( - srcList.removeFirst(), - start = paddingLeft + x, - end = paddingLeft + x1, - isImage = true - ) - ) - } else { - textLine.textChars.add( - TextChar( - it, start = paddingLeft + x, end = paddingLeft + x1 - ) + textLine.textChars.add( + TextChar( + it, start = absStartX + x, end = absStartX + x1 ) - } + ) x = x1 } if (words.size > bodyIndent.length) { val words1 = words.copyOfRange(bodyIndent.length, words.size) - addCharsToLineMiddle(textLine, words1, textPaint, desiredWidth, x, srcList) + addCharsToLineMiddle(absStartX, textLine, words1, textPaint, desiredWidth, x, srcList) } } @@ -358,6 +394,7 @@ object ChapterProvider { * 无缩进,两端对齐 */ private fun addCharsToLineMiddle( + absStartX: Int, textLine: TextLine, words: Array, textPaint: TextPaint, @@ -366,7 +403,7 @@ object ChapterProvider { srcList: LinkedList? ) { if (!ReadBookConfig.textFullJustify) { - addCharsToLineLast(textLine, words, textPaint, startX, srcList) + addCharsToLineLast(absStartX, textLine, words, textPaint, startX, srcList) return } val gapCount: Int = words.lastIndex @@ -379,15 +416,15 @@ object ChapterProvider { textLine.textChars.add( TextChar( srcList.removeFirst(), - start = paddingLeft + x, - end = paddingLeft + x1, + start = absStartX + x, + end = absStartX + x1, isImage = true ) ) } else { textLine.textChars.add( TextChar( - s, start = paddingLeft + x, end = paddingLeft + x1 + s, start = absStartX + x, end = absStartX + x1 ) ) } @@ -400,6 +437,7 @@ object ChapterProvider { * 最后一行,自然排列 */ private fun addCharsToLineLast( + absStartX: Int, textLine: TextLine, words: Array, textPaint: TextPaint, @@ -414,15 +452,15 @@ object ChapterProvider { textLine.textChars.add( TextChar( srcList.removeFirst(), - start = paddingLeft + x, - end = paddingLeft + x1, + start = absStartX + x, + end = absStartX + x1, isImage = true ) ) } else { textLine.textChars.add( TextChar( - it, start = paddingLeft + x, end = paddingLeft + x1 + it, start = absStartX + x, end = absStartX + x1 ) ) } @@ -435,17 +473,17 @@ object ChapterProvider { * 超出边界处理 */ private fun exceed(textLine: TextLine, words: Array) { - val endX = textLine.textChars.lastOrNull()?.end ?: return - if (endX > visibleRight) { - val cc = (endX - visibleRight) / words.size - for (i in 0..words.lastIndex) { - textLine.getTextCharReverseAt(i).let { - val py = cc * (words.size - i) - it.start = it.start - py - it.end = it.end - py - } - } - } +// val endX = textLine.textChars.lastOrNull()?.end ?: return +// if (endX > visibleRight) { +// val cc = (endX - visibleRight) / words.size +// for (i in 0..words.lastIndex) { +// textLine.getTextCharReverseAt(i).let { +// val py = cc * (words.size - i) +// it.start = it.start - py +// it.end = it.end - py +// } +// } +// } } /** @@ -529,7 +567,7 @@ object ChapterProvider { if (width > 0 && height > 0 && (width != viewWidth || height != viewHeight)) { viewWidth = width viewHeight = height - isHorizontal = width > height + doublePage = width > height upVisibleSize() postEvent(EventBus.UP_CONFIG, true) } @@ -542,13 +580,15 @@ object ChapterProvider { if (viewWidth > 0 && viewHeight > 0) { paddingLeft = ReadBookConfig.paddingLeft.dp paddingTop = ReadBookConfig.paddingTop.dp - visibleWidth = if (isHorizontal) { - viewWidth / 2 - paddingLeft - ReadBookConfig.paddingRight.dp + paddingRight = ReadBookConfig.paddingRight.dp + paddingBottom = ReadBookConfig.paddingBottom.dp + visibleWidth = if (doublePage) { + viewWidth / 2 - paddingLeft - paddingRight } else { - viewWidth - paddingLeft - ReadBookConfig.paddingRight.dp + viewWidth - paddingLeft - paddingRight } - visibleHeight = viewHeight - paddingTop - ReadBookConfig.paddingBottom.dp - visibleRight = paddingLeft + visibleWidth + visibleHeight = viewHeight - paddingTop - paddingBottom + visibleRight = viewWidth - paddingRight visibleBottom = paddingTop + visibleHeight } }