")
+ "image" -> {
+ // Safely extract href checking all possible namespace stripped versions
+ val href = parser.getAttributeValue(null, "l:href")
+ ?: parser.getAttributeValue(null, "xlink:href")
+ ?: parser.getAttributeValue("http://www.w3.org/1999/xlink", "href")
+ ?: parser.getAttributeValue(null, "href")
+
+ if (href != null) {
+ val id = href.removePrefix("#")
+ if (!inBody) {
+ coverImageId = id
+ } else {
+ currentChapterHtml.append("

")
+ }
+ }
+ }
+ "binary" -> {
+ val id = parser.getAttributeValue(null, "id")
+ if (id != null) {
+ val base64Data = parser.nextText()
+ try {
+ val bytes = Base64.decode(base64Data, Base64.DEFAULT)
+ val imgFile = File(extractionDir, id)
+ withContext(Dispatchers.IO) {
+ FileOutputStream(imgFile).use { it.write(bytes) }
+ }
+
+ // Add the image to the EpubBook image index
+ images.add(EpubImage(absPath = id))
+
+ if (id == coverImageId || (coverImageId == null && id.contains("cover", ignoreCase = true))) {
+ coverBytes = bytes
+ coverImageId = id
+ }
+ } catch (e: Exception) {
+ Timber.e(e, "Failed to decode binary image $id")
+ }
+ }
+ }
+ }
+ }
+ XmlPullParser.TEXT -> {
+ val text = parser.text?.replace("&", "&")?.replace("<", "<")?.replace(">", ">")
+ if (!text.isNullOrBlank()) {
+ if (inTitle) {
+ titleBuilder.append(text) // Append to buffer since it could be split by
tags
+ currentChapterHtml.append(text)
+ } else if (inBody && !skipElement) {
+ currentChapterHtml.append(text)
+ }
+ }
+ }
+ XmlPullParser.END_TAG -> {
+ val name = parser.name.lowercase()
+ when (name) {
+ "body" -> {
+ skipElement = false
+ inBody = false
+ }
+ "title" -> {
+ if (inTitle) {
+ currentChapterTitle = titleBuilder.toString().trim()
+ inTitle = false
+ }
+ currentChapterHtml.append("\n")
+ }
+ "p", "v" -> if (!inTitle) currentChapterHtml.append("
\n")
+ "subtitle" -> currentChapterHtml.append("\n")
+ "strong" -> currentChapterHtml.append("")
+ "emphasis" -> currentChapterHtml.append("")
+ "strikethrough" -> currentChapterHtml.append("")
+ "sup" -> currentChapterHtml.append("")
+ "sub" -> currentChapterHtml.append("")
+ "epigraph" -> currentChapterHtml.append("
\n")
+ }
+ }
+ }
+
+ // Calling nextText() moves the parser directly to END_TAG.
+ // We ensure we don't accidentally read past the EOF.
+ if (eventType != XmlPullParser.END_DOCUMENT) {
+ eventType = parser.next()
+ }
+ }
+
+ saveChapter() // Save the final chunk of content
+
+ if (chapters.isEmpty()) {
+ if (currentChapterHtml.isNotBlank()) {
+ saveChapter()
+ } else {
+ throw Exception("No valid content found in FB2 file.")
+ }
+ }
+
+ val coverBitmap = coverBytes?.let {
+ try {
+ BitmapFactory.decodeByteArray(it, 0, it.size)
+ } catch (e: Exception) {
+ Timber.e(e, "Failed to decode cover bitmap for FB2")
+ null
+ }
+ }
+
+ return EpubBook(
+ fileName = originalBookNameHint,
+ title = title,
+ author = author,
+ language = "en",
+ coverImage = coverBitmap,
+ chapters = chapters,
+ chaptersForPagination = chapters,
+ images = images, // Extracted images attached!
+ pageList = emptyList(),
+ tableOfContents = emptyList(),
+ extractionBasePath = extractionDir.absolutePath,
+ css = emptyMap()
+ )
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/aryan/reader/pdf/PdfHelper.kt b/app/src/main/java/com/aryan/reader/pdf/PdfHelper.kt
index 845e5c2..01cdcab 100644
--- a/app/src/main/java/com/aryan/reader/pdf/PdfHelper.kt
+++ b/app/src/main/java/com/aryan/reader/pdf/PdfHelper.kt
@@ -143,19 +143,19 @@ internal object OcrHelper {
}
internal suspend fun findWordBoundaries(
- textPage: PdfTextPageKt,
+ textPage: ReaderTextPage,
initialCharIndex: Int,
pageCharCount: Int
): Pair