Explorar o código

feat: 阅读器相关问题

HapeLee hai 2 meses
pai
achega
4bdba04cab

+ 12 - 2
app/src/main/java/io/legado/app/feature/reader/core/layout/ReaderChapterBlockMeasurer.kt

@@ -75,6 +75,7 @@ data class ReaderChapterMeasureStyle(
     val bodyIndentText: String? = null,
     val imageLayoutMode: ReaderImageLayoutMode = ReaderImageLayoutMode.AUTO,
     val imageAvailableWidthPx: Float? = null,
+    val imageAvailableHeightPx: Float? = null,
 )
 
 sealed interface ReaderChapterMeasureResult {
@@ -259,8 +260,17 @@ class ReaderChapterBlockMeasurer(
                                 pageBreakAfter = mode == ReaderImageLayoutMode.SINGLE_PAGE,
                             )
                         } else {
-                            val maxHeight = lineHeight ?: baseStyle.fontSizePx
-                            val scale = minOf(1f, maxHeight / size.heightPx.coerceAtLeast(1f))
+                            // The View reader replaces the inline placeholder with an image
+                            // fitted to the visible text area. Do the final fitting during
+                            // measurement so following content gets the same stable geometry.
+                            val availableWidth = style.imageAvailableWidthPx
+                                ?.takeIf { it > 0f } ?: size.widthPx
+                            val availableHeight = style.imageAvailableHeightPx
+                                ?.takeIf { it > 0f } ?: size.heightPx
+                            val scale = minOf(
+                                availableWidth / size.widthPx.coerceAtLeast(1f),
+                                availableHeight / size.heightPx.coerceAtLeast(1f),
+                            )
                             inline += ReaderMeasuredInlineItem.Image(
                                 source = item.source,
                                 widthPx = size.widthPx * scale,

+ 6 - 0
app/src/main/java/io/legado/app/feature/reader/legacy/LegacyReaderChapterPaginator.kt

@@ -180,6 +180,12 @@ object LegacyReaderChapterPaginator {
                         paginationStyle.paddingLeftPx - paginationStyle.paddingRightPx -
                         contentPaddingLeftPx - contentPaddingRightPx
                     ).coerceAtLeast(0).toFloat(),
+                imageAvailableHeightPx = (
+                        viewportHeightPx.toFloat() - paginationStyle.paddingTopPx - paginationStyle.paddingBottomPx -
+                                contentPaddingTopPx - contentPaddingBottomPx -
+                                LegacyReaderPageDecorationFactory.headerExtentPx() -
+                                LegacyReaderPageDecorationFactory.footerExtentPx()
+                        ).coerceAtLeast(0f),
                 bodyLineHeightPx = paginationStyle.bodyTextHeightPx,
                 bodyBaselineOffsetPx = paginationStyle.bodyBaselineOffsetPx,
                 titleLineHeightPx = paginationStyle.titleTextHeightPx,

+ 5 - 15
app/src/main/java/io/legado/app/feature/reader/legacy/LegacyReaderImageOptionsResolver.kt

@@ -3,7 +3,6 @@ package io.legado.app.feature.reader.legacy
 import io.legado.app.feature.reader.core.layout.ReaderImageLayoutMode
 import io.legado.app.feature.reader.core.layout.ReaderImageOptions
 import io.legado.app.feature.reader.core.layout.ReaderImageOptionsResolver
-import io.legado.app.feature.reader.core.layout.ReaderTextAlignment
 import io.legado.app.model.analyzeRule.AnalyzeUrl.Companion.paramPattern
 import io.legado.app.utils.GSON
 import io.legado.app.utils.fromJsonObject
@@ -16,25 +15,16 @@ object LegacyReaderImageOptionsResolver : ReaderImageOptionsResolver {
             source.substring(separator.range.last + 1),
         ).getOrNull() ?: return null
         val style = values["style"]?.uppercase()
-        val width = values["width"]
+        val action = values["click"]?.takeIf(String::isNotBlank)
+        // The View reader only treats a source-level TEXT style as an override. FULL,
+        // SINGLE, alignment and width metadata do not replace the reader's selected image
+        // style; retaining that precedence keeps existing book-source rendering compatible.
         return ReaderImageOptions(
             layoutMode = when (style) {
-                null -> null
                 "TEXT" -> ReaderImageLayoutMode.INLINE
-                "FULL" -> ReaderImageLayoutMode.FULL_WIDTH
-                "SINGLE" -> ReaderImageLayoutMode.SINGLE_PAGE
-                else -> ReaderImageLayoutMode.STANDALONE
-            },
-            requestedWidthPx = width?.takeUnless { it.endsWith('%') }?.toFloatOrNull()
-                ?.takeIf { it > 0f },
-            requestedWidthFraction = width?.takeIf { it.endsWith('%') }
-                ?.dropLast(1)?.toFloatOrNull()?.div(100f)?.takeIf { it > 0f },
-            horizontalAlignment = when (style) {
-                "LEFT" -> ReaderTextAlignment.START
-                "RIGHT" -> ReaderTextAlignment.END
                 else -> null
             },
-            action = values["click"]?.takeIf(String::isNotBlank),
+            action = action,
         )
     }
 }

+ 1 - 1
app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt

@@ -1107,8 +1107,8 @@ class ReadBookViewModel(
                 ReadBook.book?.setImageStyle(intent.style)
                 if (intent.style == Book.imgStyleSingle) {
                     ReadBook.book?.setPageAnim(0)
-                    _effects.tryEmit(ReadBookEffect.MenuImageStyleChanged(intent.style))
                 }
+                _effects.tryEmit(ReadBookEffect.MenuImageStyleChanged(intent.style))
                 ReadBook.loadContent(false)
             }
 

+ 16 - 13
app/src/test/java/io/legado/app/feature/reader/core/layout/ReaderImageOptionsTest.kt

@@ -37,28 +37,31 @@ class ReaderImageOptionsTest {
         assertTrue(result.blocks.single() is ReaderMeasuredBlock.InlineParagraph)
     }
 
-    @Test fun perImageFullModeOverridesTextModeAndFillsWidth() = runBlocking {
+    @Test
+    fun inlineImageFitsTheVisibleTextArea() = runBlocking {
         val result = measure(
-            ReaderImageOptions(ReaderImageLayoutMode.FULL_WIDTH, action = "run()"),
-            baseStyle.copy(imageLayoutMode = ReaderImageLayoutMode.INLINE),
+            ReaderImageOptions(ReaderImageLayoutMode.INLINE, action = "run()"),
+            baseStyle.copy(
+                imageLayoutMode = ReaderImageLayoutMode.INLINE,
+                imageAvailableWidthPx = 100f,
+                imageAvailableHeightPx = 40f,
+            ),
             ReaderImageDimensions(20f, 10f),
         )
-        val block = result.blocks.single() as ReaderMeasuredBlock.Image
-        assertEquals(ReaderImageScaleMode.FIT_WIDTH, block.scaleMode)
-        assertEquals("run()", block.action)
-        val image = ReaderPaginator.paginateBlocks(result.blocks, config)
-            .single().elements.single() as ReaderElement.Image
-        assertEquals(100f, image.bounds.width, 0f)
-        assertEquals(50f, image.bounds.height, 0f)
+        val paragraph = result.blocks.single() as ReaderMeasuredBlock.InlineParagraph
+        val image = paragraph.items.single() as ReaderMeasuredInlineItem.Image
+        assertEquals(80f, image.widthPx, 0f)
+        assertEquals(40f, image.heightPx, 0f)
         assertEquals("run()", image.action)
     }
 
-    @Test fun percentWidthParticipatesInSmallImageClassification() = runBlocking {
+    @Test
+    fun requestedWidthIsPreservedForInlineImageGeometry() = runBlocking {
         val result = measure(ReaderImageOptions(requestedWidthFraction = .5f))
         val paragraph = result.blocks.single() as ReaderMeasuredBlock.InlineParagraph
         val image = paragraph.items.single() as ReaderMeasuredInlineItem.Image
-        assertEquals(20f, image.widthPx, 0f)
-        assertEquals(10f, image.heightPx, 0f)
+        assertEquals(50f, image.widthPx, 0f)
+        assertEquals(25f, image.heightPx, 0f)
     }
 
     @Test fun rightAlignmentAndSinglePageOverridesReachPaginator() = runBlocking {

+ 10 - 9
app/src/test/java/io/legado/app/feature/reader/legacy/LegacyReaderImageOptionsResolverTest.kt

@@ -1,26 +1,27 @@
 package io.legado.app.feature.reader.legacy
 
 import io.legado.app.feature.reader.core.layout.ReaderImageLayoutMode
-import io.legado.app.feature.reader.core.layout.ReaderTextAlignment
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertNull
 import org.junit.Test
 
 class LegacyReaderImageOptionsResolverTest {
-    @Test fun parsesTypedImageUrlOptions() {
+    @Test
+    fun sourceTextStyleIsTheOnlyLayoutOverride() {
         val options = LegacyReaderImageOptionsResolver.resolve(
-            "https://example/image, {\"style\":\"right\",\"width\":\"37.5%\",\"click\":\"open(\\\"x\\\")\"}",
+            "https://example/image, {\"style\":\"text\",\"width\":\"37.5%\",\"click\":\"open(\\\"x\\\")\"}",
         )!!
-        assertEquals(ReaderImageLayoutMode.STANDALONE, options.layoutMode)
-        assertEquals(ReaderTextAlignment.END, options.horizontalAlignment)
-        assertEquals(.375f, options.requestedWidthFraction!!, 0f)
+        assertEquals(ReaderImageLayoutMode.INLINE, options.layoutMode)
+        assertNull(options.requestedWidthFraction)
+        assertNull(options.horizontalAlignment)
         assertEquals("open(\"x\")", options.action)
     }
 
-    @Test fun parsesAbsoluteWidthAndKnownModesCaseInsensitively() {
+    @Test
+    fun nonTextSourceStyleDoesNotOverrideReaderImageStyle() {
         val options = LegacyReaderImageOptionsResolver.resolve("x,{\"style\":\"full\",\"width\":\"123\"}")!!
-        assertEquals(ReaderImageLayoutMode.FULL_WIDTH, options.layoutMode)
-        assertEquals(123f, options.requestedWidthPx!!, 0f)
+        assertNull(options.layoutMode)
+        assertNull(options.requestedWidthPx)
     }
 
     @Test fun malformedOrAbsentOptionsDoNotInventOverrides() {