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.
This commit is contained in:
parent
314b8f3848
commit
14b5edc771
@ -1,7 +1,7 @@
|
||||
ext {
|
||||
extName = 'Zaimanhua'
|
||||
extClass = '.Zaimanhua'
|
||||
extVersionCode = 11
|
||||
extVersionCode = 12
|
||||
isNsfw = false
|
||||
}
|
||||
|
||||
|
@ -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<String> {
|
||||
fun parseChapterComments(response: Response): List<String> {
|
||||
val result = response.parseAs<ResponseDto<CommentDataDto>>()
|
||||
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<StaticLayout>()
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user