General fixes (#102)
* Improved PDF embedded annotation hit detection logic * Updated EPUB and PDF reader logic to improve position tracking and TTS stability. Key changes: - Refined CFI and locator handling in `EpubReaderScreen` with improved initialization. - Updated `BookPaginator` to more accurately calculate text offsets in chunks and improve CFI matching logic. - Modified `TtsPlaybackManager` to insert media items at the correct index, ensuring proper playback order. - Simplified `EpubReaderTts` page scrolling by removing conditional checks for backward jumps. - Disabled OCR fallback in `PdfViewerScreen` during text extraction for TTS. * Updated extractTextWithCfiFromTop to use getBoundingClientRect for identifying the starting block
This commit is contained in:
parent
ddcd253c7b
commit
fbe8e7aa56
9 changed files with 95 additions and 184 deletions
|
|
@ -138,6 +138,7 @@ import kotlin.math.PI
|
|||
import kotlin.math.abs
|
||||
import kotlin.math.atan2
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.roundToInt
|
||||
|
|
@ -919,20 +920,27 @@ internal fun PdfPageComposable(
|
|||
if (count > 0) {
|
||||
val allAnnots = (0 until count).mapNotNull { i ->
|
||||
val subtype = NativePdfiumBridge.getAnnotSubtype(pagePtr, i)
|
||||
if (subtype == annotLink) return@mapNotNull null // skip links here
|
||||
if (subtype == annotLink) return@mapNotNull null
|
||||
|
||||
var contents = NativePdfiumBridge.getAnnotString(pagePtr, i, "Contents")
|
||||
if (contents.isNullOrBlank()) {
|
||||
contents = NativePdfiumBridge.getAnnotString(pagePtr, i, "RC")
|
||||
}
|
||||
|
||||
val contents = NativePdfiumBridge.getAnnotString(pagePtr, i, "Contents")
|
||||
val name = NativePdfiumBridge.getAnnotString(pagePtr, i, "NM")
|
||||
val irt = NativePdfiumBridge.getAnnotString(pagePtr, i, "IRT")
|
||||
val author = NativePdfiumBridge.getAnnotString(pagePtr, i, "T")
|
||||
|
||||
val pdfRectArray = NativePdfiumBridge.getAnnotRect(pagePtr, i)
|
||||
val pdfRectF = if (pdfRectArray != null) {
|
||||
android.graphics.RectF(pdfRectArray[0], pdfRectArray[3], pdfRectArray[2], pdfRectArray[1])
|
||||
android.graphics.RectF(
|
||||
min(pdfRectArray[0], pdfRectArray[2]),
|
||||
max(pdfRectArray[1], pdfRectArray[3]),
|
||||
max(pdfRectArray[0], pdfRectArray[2]),
|
||||
min(pdfRectArray[1], pdfRectArray[3])
|
||||
)
|
||||
} else android.graphics.RectF()
|
||||
|
||||
Timber.tag("PdfCommentDebug").v("Extracted Annot[$i]: Name=$name, IRT=$irt, Subtype=$subtype, Text=${contents?.take(10)}...")
|
||||
|
||||
EmbeddedAnnotation(i, subtype, pdfRectF, contents, author, name, irt)
|
||||
}
|
||||
|
||||
|
|
@ -2405,6 +2413,8 @@ internal fun PdfPageComposable(
|
|||
|
||||
detectTapGestures(onTap = { tapOffset ->
|
||||
val tapInContentCoords = screenToContentCoordinates(tapOffset)
|
||||
val tapXInBitmap = tapInContentCoords.x
|
||||
val tapYInBitmap = tapInContentCoords.y
|
||||
|
||||
coroutineScope.launch {
|
||||
val wasHandled = withContext(Dispatchers.IO) {
|
||||
|
|
@ -2439,9 +2449,6 @@ internal fun PdfPageComposable(
|
|||
}
|
||||
}
|
||||
|
||||
val tapXInBitmap = tapInContentCoords.x
|
||||
val tapYInBitmap = tapInContentCoords.y
|
||||
|
||||
val annotHitTolerance = with(density) { 24.dp.toPx() } / inputScale
|
||||
val hitTolerance = with(density) { 16.dp.toPx() } / inputScale
|
||||
|
||||
|
|
@ -2466,14 +2473,24 @@ internal fun PdfPageComposable(
|
|||
} else false
|
||||
}
|
||||
|
||||
val standardHit = standardAnnotScreenRects.findLast { (_, screenRect) ->
|
||||
val standardHit = standardAnnotScreenRects.findLast { (annot, screenRect) ->
|
||||
if (annot.subtype == 2) return@findLast false
|
||||
|
||||
val left = min(screenRect.left, screenRect.right)
|
||||
val right = max(screenRect.left, screenRect.right)
|
||||
val top = min(screenRect.top, screenRect.bottom)
|
||||
val bottom = max(screenRect.top, screenRect.bottom)
|
||||
|
||||
val inflatedHitBox = Rect(
|
||||
(screenRect.left - annotHitTolerance).toInt(),
|
||||
(screenRect.top - annotHitTolerance).toInt(),
|
||||
(screenRect.right + annotHitTolerance).toInt(),
|
||||
(screenRect.bottom + annotHitTolerance).toInt()
|
||||
(left - annotHitTolerance).toInt(),
|
||||
(top - annotHitTolerance).toInt(),
|
||||
(right + annotHitTolerance).toInt(),
|
||||
(bottom + annotHitTolerance).toInt()
|
||||
)
|
||||
inflatedHitBox.contains(tapInContentCoords.x.toInt(), tapInContentCoords.y.toInt())
|
||||
|
||||
val isHit = inflatedHitBox.contains(tapInContentCoords.x.toInt(), tapInContentCoords.y.toInt())
|
||||
|
||||
isHit
|
||||
}
|
||||
|
||||
if (standardHit != null) {
|
||||
|
|
|
|||
|
|
@ -2792,93 +2792,14 @@ fun PdfViewerScreen(
|
|||
withContext(Dispatchers.IO) { tempTextPage?.close() }
|
||||
}
|
||||
|
||||
ocrUsedForCurrentPageTts = false
|
||||
withContext(Dispatchers.IO) {
|
||||
tempPage?.close()
|
||||
}
|
||||
if (rawPageText.isNullOrBlank()) {
|
||||
Timber.i(
|
||||
"TTS: Pdfium text is blank or extraction failed. Attempting OCR for page $pageToRead."
|
||||
)
|
||||
ocrAttempted = true
|
||||
ocrUsedForCurrentPageTts = true
|
||||
var ocrBitmap: Bitmap? = null
|
||||
try {
|
||||
if (tempPage == null) {
|
||||
withContext(Dispatchers.IO) {
|
||||
tempPage?.close()
|
||||
Timber.d("TTS/OCR: Re-opening page $pageToRead for bitmap rendering.")
|
||||
tempPage = pdfDocument!!.openPage(pageToRead)
|
||||
}
|
||||
}
|
||||
|
||||
val pageForOcr = tempPage ?: throw IllegalStateException(
|
||||
"TTS/OCR: PDF page couldn't be opened for OCR."
|
||||
)
|
||||
|
||||
val ocrBitmapWidth = 1080
|
||||
val ocrBitmapHeight: Int
|
||||
|
||||
withContext(Dispatchers.IO) {
|
||||
val originalWidthPoints = pageForOcr.getPageWidthPoint()
|
||||
val originalHeightPoints = pageForOcr.getPageHeightPoint()
|
||||
|
||||
if (originalWidthPoints <= 0 || originalHeightPoints <= 0) {
|
||||
throw IllegalStateException(
|
||||
"TTS/OCR: Invalid page dimensions (points) from Pdfium for page $pageToRead."
|
||||
)
|
||||
}
|
||||
val aspectRatio =
|
||||
originalWidthPoints.toFloat() / originalHeightPoints.toFloat()
|
||||
ocrBitmapHeight = (ocrBitmapWidth / aspectRatio).toInt()
|
||||
|
||||
if (ocrBitmapHeight <= 0) {
|
||||
throw IllegalStateException(
|
||||
"TTS/OCR: Calculated invalid bitmap dimensions for OCR ($ocrBitmapWidth x $ocrBitmapHeight) for page $pageToRead."
|
||||
)
|
||||
}
|
||||
|
||||
Timber.d(
|
||||
"TTS/OCR: Rendering page $pageToRead to bitmap of size ${ocrBitmapWidth}x$ocrBitmapHeight."
|
||||
)
|
||||
ocrBitmap = createBitmap(ocrBitmapWidth, ocrBitmapHeight)
|
||||
pageForOcr.renderPageBitmap(
|
||||
bitmap = ocrBitmap,
|
||||
startX = 0,
|
||||
startY = 0,
|
||||
drawSizeX = ocrBitmapWidth,
|
||||
drawSizeY = ocrBitmapHeight,
|
||||
renderAnnot = false
|
||||
)
|
||||
}
|
||||
Timber.d("TTS/OCR: Bitmap rendered for page $pageToRead. Attempting OCR.")
|
||||
|
||||
rawPageText = OcrHelper.extractTextFromBitmap(ocrBitmap!!) {
|
||||
isOcrModelDownloading = true
|
||||
}?.text
|
||||
|
||||
if (!rawPageText.isNullOrBlank()) {
|
||||
Timber.i(
|
||||
"TTS: Text extracted via OCR for page $pageToRead (length: ${rawPageText?.length})."
|
||||
)
|
||||
} else {
|
||||
Timber.w(
|
||||
"TTS: OCR process completed for page $pageToRead but returned no text or blank text."
|
||||
)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "TTS: Error during OCR process for page $pageToRead")
|
||||
} finally {
|
||||
ocrBitmap?.recycle()
|
||||
withContext(Dispatchers.IO) {
|
||||
tempPage?.close()
|
||||
Timber.d("TTS/OCR: Closed page $pageToRead after OCR attempt.")
|
||||
}
|
||||
}
|
||||
Timber.i("TTS: Pdfium text is blank or extraction failed. OCR fallback is temporarily disabled.")
|
||||
} else {
|
||||
ocrUsedForCurrentPageTts = false
|
||||
withContext(Dispatchers.IO) {
|
||||
tempPage?.close()
|
||||
Timber.d(
|
||||
"TTS: Closed page $pageToRead after successful Pdfium text extraction."
|
||||
)
|
||||
}
|
||||
Timber.d("TTS: Closed page $pageToRead after successful Pdfium text extraction.")
|
||||
}
|
||||
|
||||
if (rawPageText != null && rawPageText!!.isNotBlank()) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue