Pdf reflow upgrade (#63)

* Replaced PDF-to-Markdown reflow with an enhanced PDF-to-HTML generator.

Specific changes include:
- Replaced `PdfToMarkdownGenerator` and `PdfReflowGenerator` with `PdfToHtmlGenerator`.
- Added native bridge methods in `NativePdfiumBridge.kt` and `pdfium_bridge.cpp` to extract character bounding boxes, page objects, and image pixels.
- Implemented vertical merging of text and images in `PdfToHtmlGenerator` to maintain document layout.
- Added logic to detect and filter repeating headers and footers across PDF pages.
- Updated `ReflowWorker` to generate `.html` files instead of `.md` files and updated `FileType` handling.
- Simplified `SingleFileImporter` by removing dependencies on the legacy Markdown generator.

* Updated PDF to HTML generation to include page breaks and split HTML files into individual chapters based on page markers.

* Added junk character filtering and normalization to PDF text extraction

* - Added `allRecentFiles` to `ReaderScreenState` to track all files, including reflowed versions.
- Updated `recentFiles` in `MainViewModel` to filter out reflowed files (`_reflow`) from the main library view.
- Implemented `deleteBookPermanently` in `MainViewModel` to handle book deletion and cache cleanup.
- Added a "Delete Text View" option to the `EpubReader` controls for reflowed files.
- Improved reflow file detection in `PdfViewerScreen` by checking against `allRecentFiles`.

* Implemented a centralized data-saving mechanism in `PdfViewerScreen` using a debounced `saveAllData` function. This refactor consolidates the saving of annotations, text boxes, highlights, bookmarks, and scroll positions, adding lifecycle-aware triggers and a mutex to ensure data integrity during pauses or document navigation.

* Optimized PDF selection and annotation performance by offloading heavy operations to background threads and improving UI responsiveness.

- Moved PDF page/text opening, character range calculations, and text extraction to `Dispatchers.IO`.
- Wrapped UI updates in `updateSelectionVisuals` and selection logic with `withContext(Dispatchers.Main)`.
- Implemented a more efficient `Popup`-based magnifier to replace manual offsets and transformations.
- Updated `PdfSelectionMenuPopup` properties to prevent focus and click-outside dismissal conflicts.
This commit is contained in:
Aryan 2026-03-13 17:02:09 +05:30 committed by GitHub
parent 9a95b4afcd
commit 843a77d0ef
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 1270 additions and 895 deletions

View file

@ -67,13 +67,13 @@ import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.automirrored.filled.MenuBook
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.ArrowDropDown
import androidx.compose.material.icons.filled.Check
import androidx.compose.material.icons.filled.ChevronLeft
import androidx.compose.material.icons.filled.ChevronRight
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.GraphicEq
import androidx.compose.material.icons.filled.Menu
import androidx.compose.material.icons.filled.MoreVert
@ -153,6 +153,7 @@ fun EpubReaderTopBar(
searchFocusRequester: androidx.compose.ui.focus.FocusRequester,
modifier: Modifier = Modifier,
onToggleReflow: (() -> Unit)? = null,
onDeleteReflow: (() -> Unit)? = null,
) {
AnimatedVisibility(
visible = isVisible,
@ -226,6 +227,27 @@ fun EpubReaderTopBar(
HorizontalDivider()
}
onDeleteReflow?.let {
HorizontalDivider()
DropdownMenuItem(
text = { Text("Delete Text View") },
onClick = {
showMoreMenu = false
it()
},
leadingIcon = {
Icon(
imageVector = Icons.Default.Delete,
contentDescription = null,
tint = MaterialTheme.colorScheme.error
)
},
colors = androidx.compose.material3.MenuDefaults.itemColors(
textColor = MaterialTheme.colorScheme.error
)
)
}
DropdownMenuItem(
text = { Text("Reading Mode: Vertical") },
enabled = !isTtsActive,
@ -386,7 +408,7 @@ fun EpubReaderBottomBar(
Icon(imageVector = Icons.Default.Search, contentDescription = "Search")
}
@Suppress("KotlinConstantConditions")
@Suppress("KotlinConstantConditions", "SimplifyBooleanWithConstants")
if (BuildConfig.FLAVOR != "oss") {
Box {
var showAiFeaturesMenu by remember { mutableStateOf(false) }

View file

@ -363,7 +363,16 @@ fun EpubReaderScreen(
onRenderModeChange = onRenderModeChange,
customFonts = customFonts,
onImportFont = onImportFont,
onToggleReflow = onOpenOriginal
onToggleReflow = onOpenOriginal,
onDeleteReflow = if (isReflowFile) {
{
uiState.selectedBookId?.let { id ->
viewModel.deleteBookPermanently(id) {
onNavigateBack()
}
}
}
} else null
)
}
@ -388,7 +397,8 @@ fun EpubReaderHost(
onRenderModeChange: (RenderMode) -> Unit,
customFonts: List<CustomFontEntity>,
onImportFont: (Uri) -> Unit,
onToggleReflow: ((Int) -> Unit)? = null
onToggleReflow: ((Int) -> Unit)? = null,
onDeleteReflow: (() -> Unit)? = null
) {
val view = LocalView.current
val context = LocalContext.current
@ -3124,6 +3134,7 @@ fun EpubReaderHost(
onToggleReflow(activeChapter)
}
} else null,
onDeleteReflow = onDeleteReflow
)
val autoScrollPadding by androidx.compose.animation.core.animateDpAsState(