Pdf reflow rework (#51)

* perf(pdf): native Pdfium-based reflow engine with auto-open

Migrate PDF-to-Markdown reflow generation from PDFBox to a custom
native implementation using Pdfium. This transition improves
processing speed by ~10x and significantly reduces memory overhead.

- Native JNI Bridge: Implemented `pdfium_bridge.cpp` using `dlopen`
  to hook into the existing `libpdfium.so` memory space.
- Optimized Extraction: Replaced character-by-character JNI calls
  with bulk array retrieval (`getPageFontSizes`, `getPageFontWeights`),
  drastically reducing JNI boundary overhead.
- Enhanced Accuracy: Improved Markdown formatting logic by using
  native font weight (bold) and relative font size variance (headers).
- Thread Safety: Refactored generator to process pages sequentially
  while synchronized with the global `PdfiumCore.lock` to ensure
  stability across concurrent UI operations.
- Seamless UX: Implemented a reactive auto-open system in the
  PDF viewer that tracks user intent and navigates to the reflow
  view immediately upon background task completion.

* Improved PDF to Markdown conversion and added cache cleanup.

- Implemented automated cleanup of imported file caches when deleting books.
- Enhanced `PdfToMarkdownGenerator` with support for font flags (italics), improved kerning, and smarter paragraph wrapping.
- Updated `NativePdfiumBridge` and C++ JNI code to extract font information flags from PDFium.

* Implemented seamless file switching and enhanced reflow transition logic.

Key changes include:
- Added `switchToFileSeamlessly` to `MainViewModel` to handle state transitions and navigation when switching between PDF and reflowed text views.
- Updated `generateAndImportReflowFile` to support automatic opening of the generated file at a specific page/chapter.
- Integrated `NavigationEvent` and `CompletableDeferred` to manage asynchronous navigation and state updates during file switches.
- Modified `EpubReaderScreen` and `PdfViewerScreen` to pass the current position when toggling between PDF and text modes.
- Updated `AppNavigation` to move navigation logic out of the `NavHost` and added loading overlays to viewers to improve UI feedback during transitions.
This commit is contained in:
Aryan 2026-03-10 10:59:44 +05:30 committed by GitHub
parent acf282d4c7
commit c61a264a65
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 940 additions and 419 deletions

View file

@ -65,12 +65,12 @@ set_target_properties(mobi PROPERTIES C_VISIBILITY_PRESET default)
# FINAL NATIVE LIBRARY FOR THE APP
# ===================================================================
# 6. Define our final JNI wrapper library.
# This single .so file will be loaded by the Android app.
add_library(
native-lib
SHARED
Woff2Converter.cpp
mobi_jni_bridge.c # The placeholder file you created
mobi_jni_bridge.c
pdfium_bridge.cpp # Add this new file
)
# 7. Tell our library where to find all necessary header files.
@ -88,8 +88,9 @@ find_library(z-lib z)
target_link_libraries(
native-lib
PRIVATE
woff2dec # From woff2
mobi # From libmobi
woff2dec
mobi
${log-lib}
${z-lib} # libmobi requires zlib
${z-lib}
dl
)

88
app/src/main/cpp/pdfium_bridge.cpp vendored Normal file
View file

@ -0,0 +1,88 @@
#include <jni.h>
#include <dlfcn.h>
#include <android/log.h>
#define LOG_TAG "PdfiumBridge"
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
typedef double (*FPDFText_GetFontSize_t)(void* text_page, int index);
typedef int (*FPDFText_GetFontWeight_t)(void* text_page, int index);
typedef int (*FPDFText_GetFontInfo_t)(void* text_page, int index, void* buffer, unsigned long buflen, int* flags);
static void* pdfium_handle = nullptr;
static FPDFText_GetFontSize_t get_font_size_func = nullptr;
static FPDFText_GetFontWeight_t get_font_weight_func = nullptr;
static FPDFText_GetFontInfo_t get_font_info_func = nullptr;
static bool init_pdfium() {
if (pdfium_handle) return true;
pdfium_handle = dlopen("libpdfium.so", RTLD_LAZY);
if (!pdfium_handle) {
LOGE("Failed to hook into libpdfium.so: %s", dlerror());
return false;
}
get_font_size_func = (FPDFText_GetFontSize_t) dlsym(pdfium_handle, "FPDFText_GetFontSize");
get_font_weight_func = (FPDFText_GetFontWeight_t) dlsym(pdfium_handle, "FPDFText_GetFontWeight");
get_font_info_func = (FPDFText_GetFontInfo_t) dlsym(pdfium_handle, "FPDFText_GetFontInfo");
return get_font_size_func != nullptr && get_font_weight_func != nullptr && get_font_info_func != nullptr;
}
extern "C" JNIEXPORT jdouble JNICALL
Java_com_aryan_reader_pdf_NativePdfiumBridge_getFontSize(JNIEnv *env, jclass clazz, jlong textPagePtr, jint index) {
if (!init_pdfium() || !get_font_size_func) return 0.0;
return get_font_size_func(reinterpret_cast<void*>(textPagePtr), index);
}
extern "C" JNIEXPORT jint JNICALL
Java_com_aryan_reader_pdf_NativePdfiumBridge_getFontWeight(JNIEnv *env, jclass clazz, jlong textPagePtr, jint index) {
if (!init_pdfium() || !get_font_weight_func) return 0;
return get_font_weight_func(reinterpret_cast<void*>(textPagePtr), index);
}
// Bulk extraction for blazing fast formatting processing
extern "C" JNIEXPORT jfloatArray JNICALL
Java_com_aryan_reader_pdf_NativePdfiumBridge_getPageFontSizes(JNIEnv *env, jclass clazz, jlong textPagePtr, jint count) {
if (!init_pdfium() || !get_font_size_func || count <= 0) return nullptr;
jfloatArray result = env->NewFloatArray(count);
jfloat *fill = new jfloat[count];
for(int i = 0; i < count; i++) {
fill[i] = (jfloat)get_font_size_func(reinterpret_cast<void*>(textPagePtr), i);
}
env->SetFloatArrayRegion(result, 0, count, fill);
delete[] fill;
return result;
}
extern "C" JNIEXPORT jintArray JNICALL
Java_com_aryan_reader_pdf_NativePdfiumBridge_getPageFontWeights(JNIEnv *env, jclass clazz, jlong textPagePtr, jint count) {
if (!init_pdfium() || !get_font_weight_func || count <= 0) return nullptr;
jintArray result = env->NewIntArray(count);
jint *fill = new jint[count];
for(int i = 0; i < count; i++) {
fill[i] = (jint)get_font_weight_func(reinterpret_cast<void*>(textPagePtr), i);
}
env->SetIntArrayRegion(result, 0, count, fill);
delete[] fill;
return result;
}
extern "C" JNIEXPORT jintArray JNICALL
Java_com_aryan_reader_pdf_NativePdfiumBridge_getPageFontFlags(JNIEnv *env, jclass clazz, jlong textPagePtr, jint count) {
if (!init_pdfium() || !get_font_info_func || count <= 0) return nullptr;
jintArray result = env->NewIntArray(count);
jint *fill = new jint[count];
for(int i = 0; i < count; i++) {
int flags = 0;
get_font_info_func(reinterpret_cast<void*>(textPagePtr), i, nullptr, 0, &flags);
fill[i] = (jint)flags;
}
env->SetIntArrayRegion(result, 0, count, fill);
delete[] fill;
return result;
}