🛠️ Improve EPUB, add cancellation
* Improved EPUB parser * Added coroutine cancellation points to parsers
This commit is contained in:
parent
d3148f3e64
commit
d0c79879ad
7 changed files with 157 additions and 85 deletions
|
|
@ -0,0 +1,52 @@
|
||||||
|
package ua.acclorite.book_story.data.parser
|
||||||
|
|
||||||
|
import kotlinx.coroutines.yield
|
||||||
|
import org.jsoup.nodes.Document
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class DocumentParser @Inject constructor() {
|
||||||
|
/**
|
||||||
|
* Parses document to get it's text.
|
||||||
|
* If [fragment] is not null, searches document for specific [fragment].
|
||||||
|
*
|
||||||
|
* @return Parsed text line by line.
|
||||||
|
*/
|
||||||
|
suspend fun Document.parseDocument(fragment: String?): List<String> {
|
||||||
|
val lines = mutableListOf<String>()
|
||||||
|
|
||||||
|
yield()
|
||||||
|
|
||||||
|
body()
|
||||||
|
.select("p")
|
||||||
|
.apply {
|
||||||
|
forEach { element ->
|
||||||
|
yield()
|
||||||
|
|
||||||
|
val cleanedText = element.html().replace(Regex("\\n+"), " ")
|
||||||
|
element.html(cleanedText)
|
||||||
|
}
|
||||||
|
|
||||||
|
append("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
yield()
|
||||||
|
|
||||||
|
body()
|
||||||
|
.run {
|
||||||
|
fragment?.let { return@run getElementById(it) ?: this }
|
||||||
|
this
|
||||||
|
}
|
||||||
|
.wholeText()
|
||||||
|
.lines()
|
||||||
|
.forEach { line ->
|
||||||
|
yield()
|
||||||
|
if (line.isNotBlank()) {
|
||||||
|
lines.add(line.trim())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
yield()
|
||||||
|
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,8 +4,10 @@ import android.net.Uri
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import kotlinx.coroutines.yield
|
||||||
import org.jsoup.Jsoup
|
import org.jsoup.Jsoup
|
||||||
import ua.acclorite.book_story.R
|
import ua.acclorite.book_story.R
|
||||||
|
import ua.acclorite.book_story.data.parser.DocumentParser
|
||||||
import ua.acclorite.book_story.data.parser.TextParser
|
import ua.acclorite.book_story.data.parser.TextParser
|
||||||
import ua.acclorite.book_story.domain.model.Chapter
|
import ua.acclorite.book_story.domain.model.Chapter
|
||||||
import ua.acclorite.book_story.domain.model.ChapterWithText
|
import ua.acclorite.book_story.domain.model.ChapterWithText
|
||||||
|
|
@ -18,7 +20,9 @@ import javax.inject.Inject
|
||||||
|
|
||||||
private const val EPUB_TAG = "EPUB Parser"
|
private const val EPUB_TAG = "EPUB Parser"
|
||||||
|
|
||||||
class EpubTextParser @Inject constructor() : TextParser {
|
class EpubTextParser @Inject constructor(
|
||||||
|
private val documentParser: DocumentParser
|
||||||
|
) : TextParser {
|
||||||
|
|
||||||
override suspend fun parse(file: File): Resource<List<ChapterWithText>> {
|
override suspend fun parse(file: File): Resource<List<ChapterWithText>> {
|
||||||
Log.i(EPUB_TAG, "Started EPUB parsing: ${file.name}.")
|
Log.i(EPUB_TAG, "Started EPUB parsing: ${file.name}.")
|
||||||
|
|
@ -26,8 +30,12 @@ class EpubTextParser @Inject constructor() : TextParser {
|
||||||
return try {
|
return try {
|
||||||
val chapters = mutableListOf<ChapterWithText>()
|
val chapters = mutableListOf<ChapterWithText>()
|
||||||
|
|
||||||
|
yield()
|
||||||
|
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
ZipFile(file).use { zip ->
|
ZipFile(file).use { zip ->
|
||||||
|
yield()
|
||||||
|
|
||||||
zip.entries().asSequence().find { entry ->
|
zip.entries().asSequence().find { entry ->
|
||||||
entry.name.endsWith("toc.ncx", ignoreCase = true)
|
entry.name.endsWith("toc.ncx", ignoreCase = true)
|
||||||
}.apply {
|
}.apply {
|
||||||
|
|
@ -45,10 +53,14 @@ class EpubTextParser @Inject constructor() : TextParser {
|
||||||
|
|
||||||
chapters.addAll(this)
|
chapters.addAll(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
yield()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
yield()
|
||||||
|
|
||||||
if (chapters.isEmpty()) {
|
if (chapters.isEmpty()) {
|
||||||
return Resource.Error(UIText.StringResource(R.string.error_file_empty))
|
return Resource.Error(UIText.StringResource(R.string.error_file_empty))
|
||||||
}
|
}
|
||||||
|
|
@ -71,12 +83,16 @@ class EpubTextParser @Inject constructor() : TextParser {
|
||||||
*
|
*
|
||||||
* @return Null if could not parse.
|
* @return Null if could not parse.
|
||||||
*/
|
*/
|
||||||
private fun parseWithoutToc(zip: ZipFile): List<ChapterWithText>? {
|
private suspend fun parseWithoutToc(zip: ZipFile): List<ChapterWithText>? {
|
||||||
val chapters = mutableListOf<ChapterWithText>()
|
val chapters = mutableListOf<ChapterWithText>()
|
||||||
var chapterTextIndex = -1
|
var chapterTextIndex = -1
|
||||||
var chapterIndex = 1
|
var chapterIndex = 1
|
||||||
|
|
||||||
|
yield()
|
||||||
|
|
||||||
zip.entries().asSequence().sortedBy { it.name }.forEach { entry ->
|
zip.entries().asSequence().sortedBy { it.name }.forEach { entry ->
|
||||||
|
yield()
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!entry.name.endsWith(".xhtml")
|
!entry.name.endsWith(".xhtml")
|
||||||
&& !entry.name.endsWith(".html")
|
&& !entry.name.endsWith(".html")
|
||||||
|
|
@ -85,7 +101,13 @@ class EpubTextParser @Inject constructor() : TextParser {
|
||||||
|| entry.name.endsWith("container.xml")
|
|| entry.name.endsWith("container.xml")
|
||||||
) return@forEach
|
) return@forEach
|
||||||
|
|
||||||
val chapter = zip.parseDocument(entry = entry, fragment = null)
|
val content = zip.getInputStream(entry)
|
||||||
|
.bufferedReader()
|
||||||
|
.use {
|
||||||
|
it.readText()
|
||||||
|
}
|
||||||
|
|
||||||
|
val chapter = documentParser.run { Jsoup.parse(content).parseDocument(fragment = null) }
|
||||||
if (chapter.isEmpty()) {
|
if (chapter.isEmpty()) {
|
||||||
Log.w(EPUB_TAG, "Chapter ${entry.name} is empty.")
|
Log.w(EPUB_TAG, "Chapter ${entry.name} is empty.")
|
||||||
return@forEach
|
return@forEach
|
||||||
|
|
@ -106,6 +128,8 @@ class EpubTextParser @Inject constructor() : TextParser {
|
||||||
chapterIndex++
|
chapterIndex++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
yield()
|
||||||
|
|
||||||
if (chapters.isEmpty()) {
|
if (chapters.isEmpty()) {
|
||||||
Log.e(EPUB_TAG, "Could not parse file without toc.ncx")
|
Log.e(EPUB_TAG, "Could not parse file without toc.ncx")
|
||||||
return null
|
return null
|
||||||
|
|
@ -119,7 +143,7 @@ class EpubTextParser @Inject constructor() : TextParser {
|
||||||
*
|
*
|
||||||
* @return Null if could not parse toc.ncx.
|
* @return Null if could not parse toc.ncx.
|
||||||
*/
|
*/
|
||||||
private fun parseWithToc(tocEntry: ZipEntry, zip: ZipFile): List<ChapterWithText>? {
|
private suspend fun parseWithToc(tocEntry: ZipEntry, zip: ZipFile): List<ChapterWithText>? {
|
||||||
Log.i(EPUB_TAG, "TOC Entry: ${tocEntry.name}")
|
Log.i(EPUB_TAG, "TOC Entry: ${tocEntry.name}")
|
||||||
|
|
||||||
val chapters = mutableListOf<ChapterWithText>()
|
val chapters = mutableListOf<ChapterWithText>()
|
||||||
|
|
@ -127,12 +151,18 @@ class EpubTextParser @Inject constructor() : TextParser {
|
||||||
var chapterTextIndex = -1
|
var chapterTextIndex = -1
|
||||||
var chapterIndex = 1
|
var chapterIndex = 1
|
||||||
|
|
||||||
val tocContent = zip.getInputStream(tocEntry)
|
yield()
|
||||||
.bufferedReader()
|
|
||||||
.use { it.readText() }
|
val tocContent = withContext(Dispatchers.IO) {
|
||||||
|
zip.getInputStream(tocEntry)
|
||||||
|
}.bufferedReader().use { it.readText() }
|
||||||
val tocDocument = Jsoup.parse(tocContent)
|
val tocDocument = Jsoup.parse(tocContent)
|
||||||
|
|
||||||
|
yield()
|
||||||
|
|
||||||
tocDocument.select("navPoint").forEach { navPoint ->
|
tocDocument.select("navPoint").forEach { navPoint ->
|
||||||
|
yield()
|
||||||
|
|
||||||
val chapterTitle = navPoint.selectFirst("navLabel > text")?.text()?.trim()
|
val chapterTitle = navPoint.selectFirst("navLabel > text")?.text()?.trim()
|
||||||
?: "Chapter $chapterIndex"
|
?: "Chapter $chapterIndex"
|
||||||
val chapterSrc = navPoint.selectFirst("content")?.attr("src")?.trim()
|
val chapterSrc = navPoint.selectFirst("content")?.attr("src")?.trim()
|
||||||
|
|
@ -154,12 +184,19 @@ class EpubTextParser @Inject constructor() : TextParser {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
val chapter = zip.parseDocument(
|
val content = zip.getInputStream(this)
|
||||||
entry = this,
|
.bufferedReader()
|
||||||
|
.use {
|
||||||
|
it.readText()
|
||||||
|
}
|
||||||
|
|
||||||
|
val chapter = documentParser.run {
|
||||||
|
Jsoup.parse(content).parseDocument(
|
||||||
fragment = chapterSrc.second
|
fragment = chapterSrc.second
|
||||||
).dropWhile {
|
).dropWhile {
|
||||||
it == chapterTitle // Remove chapter title if present
|
it == chapterTitle // Remove chapter title if present
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (chapter.isEmpty()) {
|
if (chapter.isEmpty()) {
|
||||||
Log.w(EPUB_TAG, "Chapter $chapterTitle is empty.")
|
Log.w(EPUB_TAG, "Chapter $chapterTitle is empty.")
|
||||||
emptyChapters += 1
|
emptyChapters += 1
|
||||||
|
|
@ -182,6 +219,8 @@ class EpubTextParser @Inject constructor() : TextParser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
yield()
|
||||||
|
|
||||||
if (chapters.isEmpty()) {
|
if (chapters.isEmpty()) {
|
||||||
Log.e(EPUB_TAG, "Could not parse text with toc.ncx")
|
Log.e(EPUB_TAG, "Could not parse text with toc.ncx")
|
||||||
return null
|
return null
|
||||||
|
|
@ -194,44 +233,4 @@ class EpubTextParser @Inject constructor() : TextParser {
|
||||||
|
|
||||||
return chapters
|
return chapters
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses [entry] to get it's text.
|
|
||||||
*
|
|
||||||
* @return Parsed text line by line. Can have line break issues due to bad [entry] formatting.
|
|
||||||
*/
|
|
||||||
private fun ZipFile.parseDocument(entry: ZipEntry, fragment: String?): List<String> {
|
|
||||||
val lines = mutableListOf<String>()
|
|
||||||
val content = getInputStream(entry)
|
|
||||||
.bufferedReader()
|
|
||||||
.use {
|
|
||||||
it.readText()
|
|
||||||
}
|
|
||||||
|
|
||||||
val document = Jsoup.parse(content)
|
|
||||||
document
|
|
||||||
.body()
|
|
||||||
.select("p")
|
|
||||||
.append("\n")
|
|
||||||
.forEach { element ->
|
|
||||||
val cleanedText = element.html().replace(Regex("\\n+"), " ")
|
|
||||||
element.html(cleanedText)
|
|
||||||
}
|
|
||||||
|
|
||||||
document
|
|
||||||
.body()
|
|
||||||
.run {
|
|
||||||
fragment?.let { return@run getElementById(it) ?: this }
|
|
||||||
this
|
|
||||||
}
|
|
||||||
.wholeText()
|
|
||||||
.lines()
|
|
||||||
.forEach { line ->
|
|
||||||
if (line.isNotBlank()) {
|
|
||||||
lines.add(line.trim())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return lines
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -3,6 +3,7 @@ package ua.acclorite.book_story.data.parser.fb2
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import kotlinx.coroutines.yield
|
||||||
import org.w3c.dom.Element
|
import org.w3c.dom.Element
|
||||||
import org.w3c.dom.NodeList
|
import org.w3c.dom.NodeList
|
||||||
import ua.acclorite.book_story.R
|
import ua.acclorite.book_story.R
|
||||||
|
|
@ -38,11 +39,15 @@ class Fb2TextParser @Inject constructor() : TextParser {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
yield()
|
||||||
|
|
||||||
val unformattedLines = mutableListOf<String>()
|
val unformattedLines = mutableListOf<String>()
|
||||||
val bodyNode = bodyNodes.item(0) as Element
|
val bodyNode = bodyNodes.item(0) as Element
|
||||||
val paragraphNodes = bodyNode.getElementsByTagName("p")
|
val paragraphNodes = bodyNode.getElementsByTagName("p")
|
||||||
|
|
||||||
for (element in paragraphNodes.asList()) {
|
for (element in paragraphNodes.asList()) {
|
||||||
|
yield()
|
||||||
|
|
||||||
if (element.textContent.isBlank()) {
|
if (element.textContent.isBlank()) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
@ -52,9 +57,13 @@ class Fb2TextParser @Inject constructor() : TextParser {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
yield()
|
||||||
|
|
||||||
val lines = mutableListOf<String>()
|
val lines = mutableListOf<String>()
|
||||||
unformattedLines.forEachIndexed { index, string ->
|
unformattedLines.forEachIndexed { index, string ->
|
||||||
try {
|
try {
|
||||||
|
yield()
|
||||||
|
|
||||||
val line = string.trim()
|
val line = string.trim()
|
||||||
|
|
||||||
if (index == 0) {
|
if (index == 0) {
|
||||||
|
|
@ -96,10 +105,15 @@ class Fb2TextParser @Inject constructor() : TextParser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
yield()
|
||||||
|
|
||||||
lines.forEach { line ->
|
lines.forEach { line ->
|
||||||
|
yield()
|
||||||
formattedLines.add(line.trim())
|
formattedLines.add(line.trim())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
yield()
|
||||||
|
|
||||||
if (formattedLines.isEmpty()) {
|
if (formattedLines.isEmpty()) {
|
||||||
return Resource.Error(UIText.StringResource(R.string.error_file_empty))
|
return Resource.Error(UIText.StringResource(R.string.error_file_empty))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
package ua.acclorite.book_story.data.parser.htm
|
package ua.acclorite.book_story.data.parser.htm
|
||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import kotlinx.coroutines.yield
|
||||||
import org.jsoup.Jsoup
|
import org.jsoup.Jsoup
|
||||||
import ua.acclorite.book_story.R
|
import ua.acclorite.book_story.R
|
||||||
|
import ua.acclorite.book_story.data.parser.DocumentParser
|
||||||
import ua.acclorite.book_story.data.parser.TextParser
|
import ua.acclorite.book_story.data.parser.TextParser
|
||||||
import ua.acclorite.book_story.domain.model.Chapter
|
import ua.acclorite.book_story.domain.model.Chapter
|
||||||
import ua.acclorite.book_story.domain.model.ChapterWithText
|
import ua.acclorite.book_story.domain.model.ChapterWithText
|
||||||
|
|
@ -13,26 +15,17 @@ import javax.inject.Inject
|
||||||
|
|
||||||
private const val HTM_TAG = "HTM Parser"
|
private const val HTM_TAG = "HTM Parser"
|
||||||
|
|
||||||
class HtmTextParser @Inject constructor() : TextParser {
|
class HtmTextParser @Inject constructor(
|
||||||
|
private val documentParser: DocumentParser
|
||||||
|
) : TextParser {
|
||||||
|
|
||||||
override suspend fun parse(file: File): Resource<List<ChapterWithText>> {
|
override suspend fun parse(file: File): Resource<List<ChapterWithText>> {
|
||||||
Log.i(HTM_TAG, "Started HTM parsing: ${file.name}.")
|
Log.i(HTM_TAG, "Started HTM parsing: ${file.name}.")
|
||||||
|
|
||||||
return try {
|
return try {
|
||||||
val lines = mutableListOf<String>()
|
val lines = documentParser.run { Jsoup.parse(file).parseDocument(null) }
|
||||||
|
|
||||||
val document = Jsoup.parse(file)
|
yield()
|
||||||
document.select("p").append("\n")
|
|
||||||
document.select("head > title").remove()
|
|
||||||
|
|
||||||
document
|
|
||||||
.wholeText()
|
|
||||||
.lines()
|
|
||||||
.forEach { line ->
|
|
||||||
if (line.isNotBlank()) {
|
|
||||||
lines.add(line.trim())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lines.isEmpty()) {
|
if (lines.isEmpty()) {
|
||||||
return Resource.Error(UIText.StringResource(R.string.error_file_empty))
|
return Resource.Error(UIText.StringResource(R.string.error_file_empty))
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
package ua.acclorite.book_story.data.parser.html
|
package ua.acclorite.book_story.data.parser.html
|
||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import kotlinx.coroutines.yield
|
||||||
import org.jsoup.Jsoup
|
import org.jsoup.Jsoup
|
||||||
import ua.acclorite.book_story.R
|
import ua.acclorite.book_story.R
|
||||||
|
import ua.acclorite.book_story.data.parser.DocumentParser
|
||||||
import ua.acclorite.book_story.data.parser.TextParser
|
import ua.acclorite.book_story.data.parser.TextParser
|
||||||
import ua.acclorite.book_story.domain.model.Chapter
|
import ua.acclorite.book_story.domain.model.Chapter
|
||||||
import ua.acclorite.book_story.domain.model.ChapterWithText
|
import ua.acclorite.book_story.domain.model.ChapterWithText
|
||||||
|
|
@ -13,26 +15,17 @@ import javax.inject.Inject
|
||||||
|
|
||||||
private const val HTML_TAG = "HTML Parser"
|
private const val HTML_TAG = "HTML Parser"
|
||||||
|
|
||||||
class HtmlTextParser @Inject constructor() : TextParser {
|
class HtmlTextParser @Inject constructor(
|
||||||
|
private val documentParser: DocumentParser
|
||||||
|
) : TextParser {
|
||||||
|
|
||||||
override suspend fun parse(file: File): Resource<List<ChapterWithText>> {
|
override suspend fun parse(file: File): Resource<List<ChapterWithText>> {
|
||||||
Log.i(HTML_TAG, "Started HTML parsing: ${file.name}.")
|
Log.i(HTML_TAG, "Started HTML parsing: ${file.name}.")
|
||||||
|
|
||||||
return try {
|
return try {
|
||||||
val lines = mutableListOf<String>()
|
val lines = documentParser.run { Jsoup.parse(file).parseDocument(null) }
|
||||||
|
|
||||||
val document = Jsoup.parse(file)
|
yield()
|
||||||
document.select("p").append("\n")
|
|
||||||
document.select("head > title").remove()
|
|
||||||
|
|
||||||
document
|
|
||||||
.wholeText()
|
|
||||||
.lines()
|
|
||||||
.forEach { line ->
|
|
||||||
if (line.isNotBlank()) {
|
|
||||||
lines.add(line.trim())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lines.isEmpty()) {
|
if (lines.isEmpty()) {
|
||||||
return Resource.Error(UIText.StringResource(R.string.error_file_empty))
|
return Resource.Error(UIText.StringResource(R.string.error_file_empty))
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package ua.acclorite.book_story.data.parser.pdf
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.tom_roush.pdfbox.pdmodel.PDDocument
|
import com.tom_roush.pdfbox.pdmodel.PDDocument
|
||||||
import com.tom_roush.pdfbox.text.PDFTextStripper
|
import com.tom_roush.pdfbox.text.PDFTextStripper
|
||||||
|
import kotlinx.coroutines.yield
|
||||||
import ua.acclorite.book_story.R
|
import ua.acclorite.book_story.R
|
||||||
import ua.acclorite.book_story.data.parser.TextParser
|
import ua.acclorite.book_story.data.parser.TextParser
|
||||||
import ua.acclorite.book_story.domain.model.ChapterWithText
|
import ua.acclorite.book_story.domain.model.ChapterWithText
|
||||||
|
|
@ -20,18 +21,24 @@ class PdfTextParser @Inject constructor() : TextParser {
|
||||||
Log.i(PDF_TAG, "Started PDF parsing: ${file.name}.")
|
Log.i(PDF_TAG, "Started PDF parsing: ${file.name}.")
|
||||||
|
|
||||||
return try {
|
return try {
|
||||||
val document = PDDocument.load(file)
|
yield()
|
||||||
val strings = mutableListOf<String>()
|
|
||||||
|
val oldText: String
|
||||||
|
|
||||||
val pdfStripper = PDFTextStripper()
|
val pdfStripper = PDFTextStripper()
|
||||||
pdfStripper.paragraphStart = "</br>"
|
pdfStripper.paragraphStart = "</br>"
|
||||||
|
|
||||||
val oldText = pdfStripper.getText(document)
|
PDDocument.load(file).use {
|
||||||
|
oldText = pdfStripper.getText(it)
|
||||||
.replace("\r", "")
|
.replace("\r", "")
|
||||||
|
}
|
||||||
|
|
||||||
document.close()
|
yield()
|
||||||
|
|
||||||
|
val strings = mutableListOf<String>()
|
||||||
val text = oldText.filterIndexed { index, c ->
|
val text = oldText.filterIndexed { index, c ->
|
||||||
|
yield()
|
||||||
|
|
||||||
if (c == ' ') {
|
if (c == ' ') {
|
||||||
oldText[index - 1] != ' '
|
oldText[index - 1] != ' '
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -39,12 +46,18 @@ class PdfTextParser @Inject constructor() : TextParser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
yield()
|
||||||
|
|
||||||
val unformattedLines = text.split("${pdfStripper.paragraphStart}|\\n".toRegex())
|
val unformattedLines = text.split("${pdfStripper.paragraphStart}|\\n".toRegex())
|
||||||
.filter { it.isNotBlank() }
|
.filter { it.isNotBlank() }
|
||||||
|
|
||||||
|
yield()
|
||||||
|
|
||||||
val lines = mutableListOf<String>()
|
val lines = mutableListOf<String>()
|
||||||
unformattedLines.forEachIndexed { index, string ->
|
unformattedLines.forEachIndexed { index, string ->
|
||||||
try {
|
try {
|
||||||
|
yield()
|
||||||
|
|
||||||
val line = string.trim()
|
val line = string.trim()
|
||||||
|
|
||||||
if (index == 0) {
|
if (index == 0) {
|
||||||
|
|
@ -86,10 +99,15 @@ class PdfTextParser @Inject constructor() : TextParser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
yield()
|
||||||
|
|
||||||
lines.forEach { line ->
|
lines.forEach { line ->
|
||||||
|
yield()
|
||||||
strings.add(line.trim())
|
strings.add(line.trim())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
yield()
|
||||||
|
|
||||||
if (strings.isEmpty()) {
|
if (strings.isEmpty()) {
|
||||||
return Resource.Error(UIText.StringResource(R.string.error_file_empty))
|
return Resource.Error(UIText.StringResource(R.string.error_file_empty))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package ua.acclorite.book_story.data.parser.txt
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import kotlinx.coroutines.yield
|
||||||
import ua.acclorite.book_story.R
|
import ua.acclorite.book_story.R
|
||||||
import ua.acclorite.book_story.data.parser.TextParser
|
import ua.acclorite.book_story.data.parser.TextParser
|
||||||
import ua.acclorite.book_story.domain.model.ChapterWithText
|
import ua.acclorite.book_story.domain.model.ChapterWithText
|
||||||
|
|
@ -34,6 +35,8 @@ class TxtTextParser @Inject constructor() : TextParser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
yield()
|
||||||
|
|
||||||
if (lines.isEmpty()) {
|
if (lines.isEmpty()) {
|
||||||
return Resource.Error(UIText.StringResource(R.string.error_file_empty))
|
return Resource.Error(UIText.StringResource(R.string.error_file_empty))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue