Pdf reader improvments (#113)
* Improve PDF rendering quality and performance. - Set `FilterQuality.High` when drawing base bitmaps and high-res tiles in `PdfPageComposable`. - Implement `inSampleSize` calculation in `UniversalDocument` to optimize memory usage during region decoding. - Enhance bitmap drawing quality by adding `ANTI_ALIAS_FLAG` and `DITHER_FLAG` to the paint configuration. * Implement custom reader themes for PDF viewer. This change integrates the `ReaderThemePanel` and theme management logic into the PDF viewer, allowing users to apply preset or custom themes. Previously, the PDF viewer only supported a simple dark mode toggle. Specific changes: - Moved `ReaderTheme`, `ReaderTexture`, and theme-related utility functions from `EpubReaderScreen.kt` to `Common.kt` to allow sharing between EPUB and PDF readers. - Updated `PdfPageComposable` to use `activeTheme` instead of `isDarkMode`, implementing a `ColorMatrix` to apply custom background and text colors to PDF pages. - Replaced the dark mode toggle in `PdfViewerScreen` with a theme selection button that opens `ReaderThemePanel`. - Introduced `PdfBuiltInThemes` to provide PDF-specific theme presets. * Reset page transformation on constraint changes and refine tile rendering logic. - Reset scale to 1.0 and offset to zero in `PdfPageComposable` when constraints change. - Update `UniversalDocument` to use floating-point precision for source rectangle calculations and `RectF` for destination mapping to improve rendering accuracy. * Improve eraser hit detection and visual thickness settings in PDF reader.
This commit is contained in:
parent
4db2c97a30
commit
60dacf9c12
9 changed files with 834 additions and 741 deletions
|
|
@ -259,6 +259,7 @@ import com.aryan.reader.DeviceVoiceSettingsSheet
|
|||
import com.aryan.reader.FileType
|
||||
import com.aryan.reader.MainViewModel
|
||||
import com.aryan.reader.R
|
||||
import com.aryan.reader.ReaderThemePanel
|
||||
import com.aryan.reader.SearchResult
|
||||
import com.aryan.reader.SearchTopBar
|
||||
import com.aryan.reader.SummarizationPopup
|
||||
|
|
@ -270,6 +271,7 @@ import com.aryan.reader.epubreader.AutoScrollControls
|
|||
import com.aryan.reader.epubreader.DictionarySettingsDialog
|
||||
import com.aryan.reader.epubreader.ExternalDictionaryHelper
|
||||
import com.aryan.reader.fetchAiDefinition
|
||||
import com.aryan.reader.loadCustomThemes
|
||||
import com.aryan.reader.paginatedreader.TtsChunk
|
||||
import com.aryan.reader.pdf.data.AnnotationSettingsRepository
|
||||
import com.aryan.reader.pdf.data.PdfAnnotation
|
||||
|
|
@ -281,6 +283,7 @@ import com.aryan.reader.pdf.data.SmartSearchResult
|
|||
import com.aryan.reader.pdf.data.TextStyleConfig
|
||||
import com.aryan.reader.pdf.data.VirtualPage
|
||||
import com.aryan.reader.rememberSearchState
|
||||
import com.aryan.reader.saveCustomThemes
|
||||
import com.aryan.reader.summarizationUrl
|
||||
import com.aryan.reader.tts.SpeakerSamplePlayer
|
||||
import com.aryan.reader.tts.TtsPlaybackManager
|
||||
|
|
@ -343,6 +346,27 @@ private const val PREF_USE_ONLINE_DICT = "use_online_dictionary"
|
|||
private const val PREF_EXTERNAL_DICT_PKG = "external_dictionary_package"
|
||||
private const val PREF_EXTERNAL_TRANSLATE_PKG = "external_translate_package"
|
||||
private const val PREF_EXTERNAL_SEARCH_PKG = "external_search_package"
|
||||
private const val PDF_THEME_KEY = "pdf_reader_theme"
|
||||
|
||||
private fun savePdfThemeId(context: Context, themeId: String) {
|
||||
val prefs = context.getSharedPreferences(SETTINGS_PREFS_NAME, Context.MODE_PRIVATE)
|
||||
prefs.edit { putString(PDF_THEME_KEY, themeId) }
|
||||
}
|
||||
|
||||
private fun loadPdfThemeId(context: Context): String {
|
||||
val prefs = context.getSharedPreferences(SETTINGS_PREFS_NAME, Context.MODE_PRIVATE)
|
||||
return prefs.getString(PDF_THEME_KEY, "no_theme") ?: "no_theme"
|
||||
}
|
||||
|
||||
val PdfBuiltInThemes = listOf(
|
||||
com.aryan.reader.ReaderTheme("no_theme", "No Theme", Color.Unspecified, Color.Unspecified, false),
|
||||
com.aryan.reader.ReaderTheme("reverse", "Reverse", Color.Black, Color.White, true),
|
||||
com.aryan.reader.ReaderTheme("light", "Light", Color(0xFFFFFFFF), Color(0xFF000000), false),
|
||||
com.aryan.reader.ReaderTheme("dark", "Dark", Color(0xFF121212), Color(0xFFE0E0E0), true),
|
||||
com.aryan.reader.ReaderTheme("sepia", "Sepia", Color(0xFFFBF0D9), Color(0xFF5F4B32), false),
|
||||
com.aryan.reader.ReaderTheme("slate", "Slate", Color(0xFF2E3440), Color(0xFFECEFF4), true),
|
||||
com.aryan.reader.ReaderTheme("oled", "OLED", Color(0xFF000000), Color(0xFFB0B0B0), true)
|
||||
)
|
||||
|
||||
object PdfiumCoreProvider {
|
||||
val core: PdfiumCoreKt by lazy {
|
||||
|
|
@ -1065,7 +1089,16 @@ fun PdfViewerScreen(
|
|||
val focusManager = LocalFocusManager.current
|
||||
val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed)
|
||||
var displayMode by remember { mutableStateOf(loadDisplayMode(context)) }
|
||||
var isPdfDarkMode by remember { mutableStateOf(loadPdfDarkMode(context)) }
|
||||
var showThemePanel by remember { mutableStateOf(false) }
|
||||
var currentThemeId by remember { mutableStateOf(loadPdfThemeId(context)) }
|
||||
var customThemes by remember { mutableStateOf(loadCustomThemes(context)) }
|
||||
|
||||
val activeTheme = remember(currentThemeId, customThemes) {
|
||||
PdfBuiltInThemes.find { it.id == currentThemeId }
|
||||
?: customThemes.find { it.id == currentThemeId }
|
||||
?: PdfBuiltInThemes[0]
|
||||
}
|
||||
val isPdfDarkMode = activeTheme.isDark || activeTheme.id == "reverse"
|
||||
var pageAspectRatios by remember { mutableStateOf<List<Float>>(emptyList()) }
|
||||
var showBars by rememberSaveable { mutableStateOf(true) }
|
||||
var isFullScreen by remember { mutableStateOf(false) }
|
||||
|
|
@ -1364,7 +1397,6 @@ fun PdfViewerScreen(
|
|||
LaunchedEffect(ocrLanguage) { OcrHelper.init(ocrLanguage) }
|
||||
|
||||
LaunchedEffect(displayMode) { saveDisplayMode(context, displayMode) }
|
||||
LaunchedEffect(isPdfDarkMode) { savePdfDarkMode(context, isPdfDarkMode) }
|
||||
|
||||
val annotationSettingsRepo = remember(context) { AnnotationSettingsRepository(context) }
|
||||
val toolSettings by annotationSettingsRepo.settings.collectAsState()
|
||||
|
|
@ -2724,21 +2756,24 @@ fun PdfViewerScreen(
|
|||
): Boolean {
|
||||
if (annotation.points.isEmpty()) return false
|
||||
|
||||
val effectiveThreshold = threshold + (annotation.strokeWidth / 2f)
|
||||
val thresholdSq = effectiveThreshold * effectiveThreshold
|
||||
|
||||
if (annotation.points.size == 1) {
|
||||
val p = annotation.points[0]
|
||||
val dx = (p.x - hitPoint.x) * pageAspectRatio
|
||||
val dy = (p.y - hitPoint.y)
|
||||
return (dx * dx + dy * dy) < (threshold * threshold)
|
||||
val dx = (p.x - hitPoint.x)
|
||||
val dy = (p.y - hitPoint.y) / pageAspectRatio
|
||||
return (dx * dx + dy * dy) < thresholdSq
|
||||
}
|
||||
|
||||
for (i in 0 until annotation.points.size - 1) {
|
||||
val a = annotation.points[i]
|
||||
val b = annotation.points[i + 1]
|
||||
|
||||
val pax = (hitPoint.x - a.x) * pageAspectRatio
|
||||
val pay = (hitPoint.y - a.y)
|
||||
val bax = (b.x - a.x) * pageAspectRatio
|
||||
val bay = (b.y - a.y)
|
||||
val pax = (hitPoint.x - a.x)
|
||||
val pay = (hitPoint.y - a.y) / pageAspectRatio
|
||||
val bax = (b.x - a.x)
|
||||
val bay = (b.y - a.y) / pageAspectRatio
|
||||
|
||||
val segmentLenSq = (bax * bax + bay * bay).coerceAtLeast(1e-6f)
|
||||
val t = (pax * bax + pay * bay) / segmentLenSq
|
||||
|
|
@ -2749,7 +2784,7 @@ fun PdfViewerScreen(
|
|||
|
||||
val distSq = (pax - closestX) * (pax - closestX) + (pay - closestY) * (pay - closestY)
|
||||
|
||||
if (distSq < (threshold * threshold)) return true
|
||||
if (distSq < thresholdSq) return true
|
||||
}
|
||||
|
||||
return false
|
||||
|
|
@ -3580,6 +3615,7 @@ fun PdfViewerScreen(
|
|||
}
|
||||
|
||||
showTtsSettingsSheet -> showTtsSettingsSheet = false
|
||||
showThemePanel -> showThemePanel = false
|
||||
|
||||
else -> {
|
||||
saveStateAndExit()
|
||||
|
|
@ -4257,7 +4293,7 @@ fun PdfViewerScreen(
|
|||
pageIndex = pageIndex,
|
||||
virtualPage = virtualPage,
|
||||
totalPages = totalDisplayPages,
|
||||
isDarkMode = isPdfDarkMode,
|
||||
activeTheme = activeTheme,
|
||||
isScrollLocked = isScrollLocked,
|
||||
onScaleChanged = { newScale ->
|
||||
if (pagerState.currentPage == pageIndex) {
|
||||
|
|
@ -4636,7 +4672,7 @@ fun PdfViewerScreen(
|
|||
PdfVerticalReader(
|
||||
state = verticalReaderState,
|
||||
pdfDocument = docHolder,
|
||||
isDarkMode = isPdfDarkMode,
|
||||
activeTheme = activeTheme,
|
||||
isScrollLocked = isScrollLocked,
|
||||
totalPages = totalDisplayPages,
|
||||
pageAspectRatios = ratiosHolder,
|
||||
|
|
@ -5223,22 +5259,14 @@ fun PdfViewerScreen(
|
|||
)
|
||||
|
||||
TooltipIconButton(
|
||||
text = if (isPdfDarkMode)
|
||||
stringResource(R.string.tooltip_dark_mode_off)
|
||||
else
|
||||
stringResource(R.string.tooltip_dark_mode_on),
|
||||
description = if (isPdfDarkMode)
|
||||
stringResource(R.string.tooltip_dark_mode_off_desc)
|
||||
else
|
||||
stringResource(R.string.tooltip_dark_mode_on_desc),
|
||||
onClick = { isPdfDarkMode = !isPdfDarkMode }
|
||||
text = "Theme",
|
||||
description = "Theme Settings",
|
||||
onClick = { showThemePanel = true }
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.dark_mode),
|
||||
contentDescription = if (isPdfDarkMode) "Disable Dark Mode"
|
||||
else "Enable Dark Mode",
|
||||
tint = if (isPdfDarkMode) MaterialTheme.colorScheme.primary
|
||||
else MaterialTheme.colorScheme.onSurfaceVariant
|
||||
painter = painterResource(id = R.drawable.palette),
|
||||
contentDescription = "Theme Settings",
|
||||
tint = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -6926,6 +6954,25 @@ fun PdfViewerScreen(
|
|||
)
|
||||
}
|
||||
|
||||
if (showThemePanel) {
|
||||
ReaderThemePanel(
|
||||
isVisible = true,
|
||||
currentThemeId = currentThemeId,
|
||||
builtInThemes = PdfBuiltInThemes,
|
||||
onThemeSelected = {
|
||||
currentThemeId = it
|
||||
savePdfThemeId(context, it)
|
||||
showThemePanel = false
|
||||
},
|
||||
onDismiss = { showThemePanel = false },
|
||||
customThemes = customThemes,
|
||||
onCustomThemesUpdated = {
|
||||
customThemes = it
|
||||
saveCustomThemes(context, it)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
if (clickedLinkUrl != null) {
|
||||
val url = clickedLinkUrl!!
|
||||
AlertDialog(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue