From 14b5edc771fbca751fdce25ba75cdf306ebc84c4 Mon Sep 17 00:00:00 2001 From: zhongfly <11155705+zhongfly@users.noreply.github.com> Date: Mon, 1 Sep 2025 17:09:29 +0800 Subject: [PATCH] Zaimanhua: Improve comment rendering (#10340) - Better text layout and spacing for comments. - Ensure the generated image is not too short, to avoid being displayed as a double-page spread. --- src/zh/zaimanhua/build.gradle | 2 +- .../zh/zaimanhua/CommentsInterceptor.kt | 105 +++++++++++++----- 2 files changed, 76 insertions(+), 31 deletions(-) diff --git a/src/zh/zaimanhua/build.gradle b/src/zh/zaimanhua/build.gradle index 995b37687..6b86e235c 100644 --- a/src/zh/zaimanhua/build.gradle +++ b/src/zh/zaimanhua/build.gradle @@ -1,7 +1,7 @@ ext { extName = 'Zaimanhua' extClass = '.Zaimanhua' - extVersionCode = 11 + extVersionCode = 12 isNsfw = false } diff --git a/src/zh/zaimanhua/src/eu/kanade/tachiyomi/extension/zh/zaimanhua/CommentsInterceptor.kt b/src/zh/zaimanhua/src/eu/kanade/tachiyomi/extension/zh/zaimanhua/CommentsInterceptor.kt index a61ad263a..915302c45 100644 --- a/src/zh/zaimanhua/src/eu/kanade/tachiyomi/extension/zh/zaimanhua/CommentsInterceptor.kt +++ b/src/zh/zaimanhua/src/eu/kanade/tachiyomi/extension/zh/zaimanhua/CommentsInterceptor.kt @@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.extension.zh.zaimanhua import android.graphics.Bitmap import android.graphics.Canvas import android.graphics.Color +import android.graphics.Typeface import android.text.Layout import android.text.StaticLayout import android.text.TextPaint @@ -13,21 +14,26 @@ import okhttp3.Response import okhttp3.ResponseBody.Companion.asResponseBody import okio.Buffer -fun parseChapterComments(response: Response, count: Int): List { +fun parseChapterComments(response: Response): List { val result = response.parseAs>() val comments = result.data.toCommentList() return if (result.errmsg.isNotBlank()) { throw Exception(result.errmsg) } else { - comments.take(count) + comments } } object CommentsInterceptor : Interceptor { private const val MAX_HEIGHT = 1920 private const val WIDTH = 1080 - private const val UNIT = 32 - private const val UNIT_F = UNIT.toFloat() + private const val X_PADDING: Float = 50f + private const val Y_PADDING: Float = 25f + private const val SPACING_MULT: Float = 1f + private const val SPACING_ADD: Float = 0f + private const val HEADING_FONT_SIZE: Float = 36f + private const val BODY_FONT_SIZE: Float = 30f + private const val SPACING: Float = BODY_FONT_SIZE * SPACING_MULT + SPACING_ADD override fun intercept(chain: Interceptor.Chain): Response { val request = chain.request() @@ -35,46 +41,85 @@ object CommentsInterceptor : Interceptor { if (request.tag(String::class) != COMMENTS_FLAG) { return response } - val comments = parseChapterComments(response, MAX_HEIGHT / (UNIT * 2) - 1).toMutableList() - comments.add(0, "章末吐槽:") - val paint = TextPaint().apply { + val paintHeading = TextPaint().apply { color = Color.BLACK - textSize = UNIT_F + textSize = HEADING_FONT_SIZE + typeface = Typeface.DEFAULT_BOLD isAntiAlias = true } - var height = UNIT - val layouts = comments.map { + @Suppress("DEPRECATION") + val heading = StaticLayout( + "章末吐槽:", + paintHeading, + (WIDTH - 2 * X_PADDING).toInt(), + Layout.Alignment.ALIGN_NORMAL, + SPACING_MULT, + SPACING_ADD, + true, + ) + + val comments = parseChapterComments(response).toMutableList() + val paintBody = TextPaint().apply { + color = Color.BLACK + textSize = BODY_FONT_SIZE + typeface = Typeface.DEFAULT + isAntiAlias = true + } + + var currentHeight = Y_PADDING + heading.height + val bodyLayouts = mutableListOf() + for (comment in comments) { @Suppress("DEPRECATION") - StaticLayout(it, paint, WIDTH - 2 * UNIT, Layout.Alignment.ALIGN_NORMAL, 1f, 0f, false) - }.takeWhile { - val lineHeight = it.height + UNIT - if (height + lineHeight <= MAX_HEIGHT) { - height += lineHeight - true - } else { - false + val layout = StaticLayout( + comment, + paintBody, + (WIDTH - 2 * X_PADDING).toInt(), + Layout.Alignment.ALIGN_NORMAL, + SPACING_MULT, + SPACING_ADD, + true, + ) + val lineHeight = SPACING + layout.height + // If adding this comment doesn't exceed the max height, add it. + // If it does exceed and it's a single line, stop adding more comments. + // Otherwise, if it's multi-line, just skip it and try the next comment. + if (currentHeight + lineHeight <= MAX_HEIGHT) { + bodyLayouts.add(layout) + currentHeight += lineHeight + } else if (layout.lineCount == 1) { + break } } - val bitmap = Bitmap.createBitmap(WIDTH, height, Bitmap.Config.ARGB_8888) - bitmap.eraseColor(Color.WHITE) - val canvas = Canvas(bitmap) + // The bitmap height must be no more than MAX_HEIGHT + // and no less than its width to prevent automatic double-page splitting. + val bitmapHeight = (currentHeight + Y_PADDING).toInt().coerceIn(WIDTH, MAX_HEIGHT) - var y = UNIT - for (layout in layouts) { - canvas.save() - canvas.translate(UNIT_F, y.toFloat()) - layout.draw(canvas) - canvas.restore() - y += layout.height + UNIT + val bitmap = Bitmap.createBitmap(WIDTH, bitmapHeight, Bitmap.Config.ARGB_8888) + Canvas(bitmap).apply { + drawColor(Color.WHITE) + heading.draw(this, X_PADDING, Y_PADDING) + var y = Y_PADDING + heading.height + SPACING + for (layout in bodyLayouts) { + layout.draw(this, X_PADDING, y) + y += layout.height + SPACING + } } val responseBody = Buffer().run { - bitmap.compress(Bitmap.CompressFormat.JPEG, 90, outputStream()) - asResponseBody("image/jpeg".toMediaType()) + bitmap.compress(Bitmap.CompressFormat.PNG, 0, outputStream()) + asResponseBody("image/png".toMediaType()) } return response.newBuilder().body(responseBody).build() } + + @Suppress("SameParameterValue") + private fun StaticLayout.draw(canvas: Canvas, x: Float, y: Float) { + canvas.save() + canvas.translate(x, y) + this.draw(canvas) + canvas.restore() + } }