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:
parent
acf282d4c7
commit
c61a264a65
11 changed files with 940 additions and 419 deletions
88
app/src/main/cpp/pdfium_bridge.cpp
vendored
Normal file
88
app/src/main/cpp/pdfium_bridge.cpp
vendored
Normal 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;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue