This commit is contained in:
Horis
2025-02-20 22:58:30 +08:00
parent 5b9115ae48
commit 4e50417624
2 changed files with 71 additions and 137 deletions
@@ -35,7 +35,8 @@ data class TextPage(
var chapterSize: Int = 0, var chapterSize: Int = 0,
var chapterIndex: Int = 0, var chapterIndex: Int = 0,
var height: Float = 0f, var height: Float = 0f,
var leftLineSize: Int = 0 var leftLineSize: Int = 0,
var renderHeight: Int = 0
) { ) {
companion object { companion object {
@@ -332,8 +333,7 @@ data class TextPage(
fun render(view: ContentTextView): Boolean { fun render(view: ContentTextView): Boolean {
if (!isCompleted) return false if (!isCompleted) return false
val height = lines.lastOrNull()?.lineBottom?.toInt() ?: 0 return canvasRecorder.recordIfNeeded(view.width, renderHeight) {
return canvasRecorder.recordIfNeeded(view.width, height) {
drawPage(view, this) drawPage(view, this)
} }
} }
@@ -86,6 +86,9 @@ class TextChapterLayout(
var channel = Channel<TextPage>(Int.MAX_VALUE) var channel = Channel<TextPage>(Int.MAX_VALUE)
var durY = 0f
var absStartX = paddingLeft
init { init {
job = Coroutine.async( job = Coroutine.async(
scope, scope,
@@ -127,7 +130,7 @@ class TextChapterLayout(
} }
private fun onPageCompleted() { private fun onPageCompleted() {
val textPage = textPages.last() val textPage = pendingTextPage
textPage.index = textPages.lastIndex textPage.index = textPages.lastIndex
textPage.chapterIndex = bookChapter.index textPage.chapterIndex = bookChapter.index
textPage.chapterSize = chaptersSize textPage.chapterSize = chaptersSize
@@ -137,6 +140,8 @@ class TextChapterLayout(
textPage.isCompleted = true textPage.isCompleted = true
textPage.textChapter = textChapter textPage.textChapter = textChapter
textPage.upLinesPosition() textPage.upLinesPosition()
textPage.renderHeight = textPage.lines.maxOf { it.lineBottom.toInt() }
textPages.add(textPage)
channel.trySend(textPage) channel.trySend(textPage)
try { try {
listener?.onLayoutPageCompleted(textPages.lastIndex, textPage) listener?.onLayoutPageCompleted(textPages.lastIndex, textPage)
@@ -184,16 +189,15 @@ class TextChapterLayout(
bookContent: BookContent, bookContent: BookContent,
) { ) {
val contents = bookContent.textList val contents = bookContent.textList
var absStartX = paddingLeft
var durY = 0f
val imageStyle = book.getImageStyle() val imageStyle = book.getImageStyle()
val isSingleImageStyle = imageStyle.equals(Book.imgStyleSingle, true) val isSingleImageStyle = imageStyle.equals(Book.imgStyleSingle, true)
val isTextImageStyle = imageStyle.equals(Book.imgStyleText, true)
if (ReadBookConfig.titleMode != 2 || bookChapter.isVolume || contents.isEmpty()) { if (ReadBookConfig.titleMode != 2 || bookChapter.isVolume || contents.isEmpty()) {
//标题非隐藏 //标题非隐藏
displayTitle.splitNotBlank("\n").forEach { text -> displayTitle.splitNotBlank("\n").forEach { text ->
setTypeText( setTypeText(
book, absStartX, durY, book,
if (AppConfig.enableReview) text + ChapterProvider.reviewChar else text, if (AppConfig.enableReview) text + ChapterProvider.reviewChar else text,
titlePaint, titlePaint,
titlePaintTextHeight, titlePaintTextHeight,
@@ -202,10 +206,7 @@ class TextChapterLayout(
isTitle = true, isTitle = true,
emptyContent = contents.isEmpty(), emptyContent = contents.isEmpty(),
isVolumeTitle = bookChapter.isVolume isVolumeTitle = bookChapter.isVolume
).let { )
absStartX = it.first
durY = it.second
}
} }
pendingTextPage.lines.last().isParagraphEnd = true pendingTextPage.lines.last().isParagraphEnd = true
stringBuilder.append("\n") stringBuilder.append("\n")
@@ -213,19 +214,15 @@ class TextChapterLayout(
// 如果是单图模式且当前页有内容,强制分页 // 如果是单图模式且当前页有内容,强制分页
if (isSingleImageStyle && pendingTextPage.lines.isNotEmpty()) { if (isSingleImageStyle && pendingTextPage.lines.isNotEmpty()) {
pendingTextPage.height = durY prepareNextPageIfNeed()
textPages.add(pendingTextPage)
onPageCompleted()
pendingTextPage = TextPage()
stringBuilder.clear()
absStartX = paddingLeft
durY = 0f
} }
} }
val sb = StringBuffer() val sb = StringBuffer()
var isSetTypedImage = false
contents.forEach { content -> contents.forEach { content ->
coroutineContext.ensureActive() coroutineContext.ensureActive()
if (book.getImageStyle().equals(Book.imgStyleText, true)) { if (isTextImageStyle) {
//图片样式为文字嵌入类型 //图片样式为文字嵌入类型
var text = content.replace(ChapterProvider.srcReplaceChar, "") var text = content.replace(ChapterProvider.srcReplaceChar, "")
val srcList = LinkedList<String>() val srcList = LinkedList<String>()
@@ -241,19 +238,18 @@ class TextChapterLayout(
text = sb.toString() text = sb.toString()
setTypeText( setTypeText(
book, book,
absStartX,
durY,
text, text,
contentPaint, contentPaint,
contentPaintTextHeight, contentPaintTextHeight,
contentPaintFontMetrics, contentPaintFontMetrics,
book.getImageStyle(), book.getImageStyle(),
srcList = srcList srcList = srcList
).let { )
absStartX = it.first
durY = it.second
}
} else { } else {
if (isSingleImageStyle && isSetTypedImage) {
isSetTypedImage = false
prepareNextPageIfNeed()
}
val matcher = AppPattern.imgPattern.matcher(content) val matcher = AppPattern.imgPattern.matcher(content)
var start = 0 var start = 0
while (matcher.find()) { while (matcher.find()) {
@@ -262,57 +258,45 @@ class TextChapterLayout(
if (text.isNotBlank()) { if (text.isNotBlank()) {
setTypeText( setTypeText(
book, book,
absStartX,
durY,
text, text,
contentPaint, contentPaint,
contentPaintTextHeight, contentPaintTextHeight,
contentPaintFontMetrics, contentPaintFontMetrics,
book.getImageStyle(), book.getImageStyle(),
isFirstLine = start == 0 isFirstLine = start == 0
).let { )
absStartX = it.first
durY = it.second
}
} }
setTypeImage( setTypeImage(
book, book,
matcher.group(1)!!, matcher.group(1)!!,
absStartX,
durY,
contentPaintTextHeight, contentPaintTextHeight,
stringBuilder,
book.getImageStyle() book.getImageStyle()
).let { )
absStartX = it.first isSetTypedImage = true
durY = it.second
}
start = matcher.end() start = matcher.end()
} }
if (start < content.length) { if (start < content.length) {
if (isSingleImageStyle && isSetTypedImage) {
isSetTypedImage = false
prepareNextPageIfNeed()
}
val text = content.substring(start, content.length) val text = content.substring(start, content.length)
if (text.isNotBlank()) { if (text.isNotBlank()) {
setTypeText( setTypeText(
book, absStartX, durY, book,
if (AppConfig.enableReview) text + ChapterProvider.reviewChar else text, if (AppConfig.enableReview) text + ChapterProvider.reviewChar else text,
contentPaint, contentPaint,
contentPaintTextHeight, contentPaintTextHeight,
contentPaintFontMetrics, contentPaintFontMetrics,
book.getImageStyle(), book.getImageStyle(),
isFirstLine = start == 0 isFirstLine = start == 0
).let { )
absStartX = it.first
durY = it.second
} }
} }
} }
}
if (pendingTextPage.lines.isNotEmpty()) {
pendingTextPage.lines.last().isParagraphEnd = true pendingTextPage.lines.last().isParagraphEnd = true
}
stringBuilder.append("\n") stringBuilder.append("\n")
} }
textPages.add(pendingTextPage)
val textPage = pendingTextPage val textPage = pendingTextPage
val endPadding = 20.dpToPx() val endPadding = 20.dpToPx()
val durYPadding = durY + endPadding val durYPadding = durY + endPadding
@@ -333,29 +317,12 @@ class TextChapterLayout(
private suspend fun setTypeImage( private suspend fun setTypeImage(
book: Book, book: Book,
src: String, src: String,
x: Int,
y: Float,
textHeight: Float, textHeight: Float,
stringBuilder: StringBuilder,
imageStyle: String?, imageStyle: String?,
): Pair<Int, Float> { ) {
var absStartX = x
var durY = y
val size = ImageProvider.getImageSize(book, src, ReadBook.bookSource) val size = ImageProvider.getImageSize(book, src, ReadBook.bookSource)
if (size.width > 0 && size.height > 0) { if (size.width > 0 && size.height > 0) {
if (durY > visibleHeight) { prepareNextPageIfNeed(durY)
val textPage = pendingTextPage
if (textPage.height < durY) {
textPage.height = durY
}
textPage.text = stringBuilder.toString().ifEmpty { "本页无文字内容" }
stringBuilder.clear()
textPages.add(textPage)
coroutineContext.ensureActive()
onPageCompleted()
pendingTextPage = TextPage()
durY = 0f
}
var height = size.height var height = size.height
var width = size.width var width = size.width
when (imageStyle?.uppercase()) { when (imageStyle?.uppercase()) {
@@ -372,17 +339,7 @@ class TextChapterLayout(
height = visibleHeight height = visibleHeight
} }
if (durY > 0f) { if (durY > 0f) {
val textPage = pendingTextPage prepareNextPageIfNeed()
if (textPage.height < durY) {
textPage.height = durY
}
textPage.text = stringBuilder.toString().ifEmpty { "本页无文字内容" }
stringBuilder.clear()
textPages.add(textPage)
coroutineContext.ensureActive()
onPageCompleted()
pendingTextPage = TextPage()
durY = 0f
} }
// 图片竖直方向居中:调整 Y 坐标 // 图片竖直方向居中:调整 Y 坐标
@@ -401,30 +358,7 @@ class TextChapterLayout(
width = width * visibleHeight / height width = width * visibleHeight / height
height = visibleHeight height = visibleHeight
} }
if (durY + height > visibleHeight) { prepareNextPageIfNeed(durY + height)
val textPage = pendingTextPage
// 双页的 durY 不正确,可能会小于实际高度
if (textPage.height < durY) {
textPage.height = durY
}
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().ifEmpty { "本页无文字内容" }
stringBuilder.clear()
textPages.add(textPage)
coroutineContext.ensureActive()
onPageCompleted()
pendingTextPage = TextPage()
}
durY = 0f
}
} }
} }
val textLine = TextLine(isImage = true) val textLine = TextLine(isImage = true)
@@ -439,13 +373,13 @@ class TextChapterLayout(
Pair(0f, width.toFloat()) Pair(0f, width.toFloat())
} }
textLine.addColumn( textLine.addColumn(
ImageColumn(start = x + start, end = x + end, src = src) ImageColumn(start = absStartX + start, end = absStartX + end, src = src)
) )
calcTextLinePosition(textPages, textLine, stringBuilder.length) calcTextLinePosition(textPages, textLine, stringBuilder.length)
stringBuilder.append(" ") // 确保翻页时索引计算正确 stringBuilder.append(" ") // 确保翻页时索引计算正确
pendingTextPage.addLine(textLine) pendingTextPage.addLine(textLine)
} }
return absStartX to durY + textHeight * paragraphSpacing / 10f durY += textHeight * paragraphSpacing / 10f
} }
/** /**
@@ -454,8 +388,6 @@ class TextChapterLayout(
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
private suspend fun setTypeText( private suspend fun setTypeText(
book: Book, book: Book,
x: Int,
y: Float,
text: String, text: String,
textPaint: TextPaint, textPaint: TextPaint,
textHeight: Float, textHeight: Float,
@@ -466,8 +398,7 @@ class TextChapterLayout(
emptyContent: Boolean = false, emptyContent: Boolean = false,
isVolumeTitle: Boolean = false, isVolumeTitle: Boolean = false,
srcList: LinkedList<String>? = null srcList: LinkedList<String>? = null
): Pair<Int, Float> { ) {
var absStartX = x
val widthsArray = FloatArray(text.length) val widthsArray = FloatArray(text.length)
textPaint.getTextWidths(text, widthsArray) textPaint.getTextWidths(text, widthsArray)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
@@ -485,7 +416,7 @@ class TextChapterLayout(
} else { } else {
StaticLayout(text, textPaint, visibleWidth, Layout.Alignment.ALIGN_NORMAL, 0f, 0f, true) StaticLayout(text, textPaint, visibleWidth, Layout.Alignment.ALIGN_NORMAL, 0f, 0f, true)
} }
var durY = when { durY = when {
//标题y轴居中 //标题y轴居中
emptyContent && textPages.isEmpty() -> { emptyContent && textPages.isEmpty() -> {
val textPage = pendingTextPage val textPage = pendingTextPage
@@ -503,7 +434,7 @@ class TextChapterLayout(
it.lineBase -= textLayoutHeight it.lineBase -= textLayoutHeight
it.lineBottom -= textLayoutHeight it.lineBottom -= textLayoutHeight
} }
y - textLayoutHeight durY - textLayoutHeight
} }
} }
@@ -514,39 +445,15 @@ class TextChapterLayout(
if (ty > titleTopSpacing) ty else titleTopSpacing.toFloat() if (ty > titleTopSpacing) ty else titleTopSpacing.toFloat()
} }
else -> y + titleTopSpacing else -> durY + titleTopSpacing
} }
} }
else -> y else -> durY
} }
for (lineIndex in 0 until layout.lineCount) { for (lineIndex in 0 until layout.lineCount) {
val textLine = TextLine(isTitle = isTitle) val textLine = TextLine(isTitle = isTitle)
if (durY + textHeight > visibleHeight) { prepareNextPageIfNeed(durY + textHeight)
val textPage = pendingTextPage
if (textPage.height < durY) {
textPage.height = durY
}
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()
textPages.add(textPage)
coroutineContext.ensureActive()
onPageCompleted()
//新建页面
pendingTextPage = TextPage()
stringBuilder.clear()
absStartX = paddingLeft
}
durY = 0f
}
val lineStart = layout.getLineStart(lineIndex) val lineStart = layout.getLineStart(lineIndex)
val lineEnd = layout.getLineEnd(lineIndex) val lineEnd = layout.getLineEnd(lineIndex)
val lineText = text.substring(lineStart, lineEnd) val lineText = text.substring(lineStart, lineEnd)
@@ -615,7 +522,6 @@ class TextChapterLayout(
} }
} }
durY += textHeight * paragraphSpacing / 10f durY += textHeight * paragraphSpacing / 10f
return Pair(absStartX, durY)
} }
private fun calcTextLinePosition( private fun calcTextLinePosition(
@@ -850,6 +756,34 @@ class TextChapterLayout(
} }
} }
private suspend fun prepareNextPageIfNeed(requestHeight: Float = -1f) {
if (requestHeight > visibleHeight || requestHeight == -1f) {
val textPage = pendingTextPage
// 双页的 durY 不正确,可能会小于实际高度
if (textPage.height < durY) {
textPage.height = durY
}
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()
coroutineContext.ensureActive()
onPageCompleted()
//新建页面
pendingTextPage = TextPage()
stringBuilder.clear()
absStartX = paddingLeft
}
durY = 0f
}
}
private fun measureTextSplit( private fun measureTextSplit(
text: String, text: String,
widthsArray: FloatArray, widthsArray: FloatArray,